merged with master

This commit is contained in:
itsmattkc
2019-03-25 12:43:23 +11:00
83 changed files with 7823 additions and 1469 deletions
+2 -1
View File
@@ -26,7 +26,8 @@
/**
* @brief The AboutDialog class
*
* The About dialog (accessible through Help > About). Contains license and version information.
* The About dialog (accessible through Help > About). Contains license and version information. This can be run from
* anywhere
*/
class AboutDialog : public QDialog
{
+1 -1
View File
@@ -32,7 +32,7 @@ class ActionSearchList;
* @brief The ActionSearch class
*
* A popup window (accessible through Help > Action Search) that allows users to search for a menu command by typing
* rather than browsing through the menu bar.
* rather than browsing through the menu bar. This can be created from anywhere provided olive::MainWindow is valid.
*/
class ActionSearch : public QDialog
{
+1 -1
View File
@@ -31,7 +31,7 @@
* @brief The AdvancedVideoDialog class
*
* A dialog for interfacing with VideoCodecParams, a struct for more advanced video settings sometimes specific to
* one codec.
* one codec. Primarily a companion to ExportDialog which will provide the VideoCodecParams reference,
*/
class AdvancedVideoDialog : public QDialog {
Q_OBJECT
+222
View File
@@ -0,0 +1,222 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "autocutsilencedialog.h"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QDialogButtonBox>
#include "timeline/sequence.h"
#include "rendering/renderfunctions.h"
#include "panels/panels.h"
#include "panels/timeline.h"
AutoCutSilenceDialog::AutoCutSilenceDialog(QWidget *parent, QVector<Clip*> clips) :
QDialog(parent),
clips_(clips)
{
setWindowTitle(tr("Cut Silence"));
QVBoxLayout* main_layout = new QVBoxLayout(this);
QGridLayout* grid = new QGridLayout();
grid->setSpacing(6);
grid->addWidget(new QLabel(tr("Attack Threshold:"), this), 0, 0);
attack_threshold = new LabelSlider(this);
attack_threshold->SetDecimalPlaces(0);
grid->addWidget(attack_threshold, 0, 1);
grid->addWidget(new QLabel(tr("Attack Time:"), this), 1, 0);
attack_time = new LabelSlider(this);
attack_time->SetDecimalPlaces(0);
grid->addWidget(attack_time, 1, 1);
grid->addWidget(new QLabel(tr("Release Threshold:"), this), 2, 0);
release_threshold = new LabelSlider(this);
release_threshold->SetDecimalPlaces(0);
grid->addWidget(release_threshold, 2, 1);
grid->addWidget(new QLabel(tr("Release Time:"), this), 3, 0);
release_time = new LabelSlider(this);
release_time->SetDecimalPlaces(0);
grid->addWidget(release_time, 3, 1);
main_layout->addLayout(grid);
QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttonBox->setCenterButtons(true);
main_layout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
}
int AutoCutSilenceDialog::exec()
{
default_attack_threshold = 5;
current_attack_threshold = 5;
default_attack_time = 2;
current_attack_time = 2;
default_release_threshold = 2;
current_release_threshold = 2;
default_release_time = 5;
current_release_time = 5;
attack_threshold->SetMinimum(1);
attack_threshold->setEnabled(true);
attack_threshold->SetDefault(default_attack_threshold);
attack_threshold->SetValue(current_attack_threshold);
attack_time->SetMinimum(1);
attack_time->setEnabled(true);
attack_time->SetDefault(default_attack_time);
attack_time->SetValue(current_attack_time);
release_threshold->SetMinimum(1);
release_threshold->setEnabled(true);
release_threshold->SetDefault(default_release_threshold);
release_threshold->SetValue(current_release_threshold);
release_time->SetMinimum(1);
release_time->setEnabled(true);
release_time->SetDefault(default_release_time);
release_time->SetValue(current_release_time);
return QDialog::exec();
}
void AutoCutSilenceDialog::accept() {
current_attack_threshold = attack_threshold->value();
current_attack_time = attack_time->value();
current_release_threshold = release_threshold->value();
current_release_time = release_time->value();
cut_silence();
update_ui(true);
QDialog::accept();
}
void AutoCutSilenceDialog::cut_silence() {
// Loop over clips provided to this dialog
for (int j=0;j<clips_.size();j++) {
Clip* clip = clips_.at(j);
// Check if this clip is an audio footage clip
if (clip->track() >= 0
&& clip->media() != nullptr
&& clip->media_stream()->preview_done) { // TODO provide warning for preview not being done
QVector<long> split_positions;
int clip_start = clip->timeline_in();
const FootageStream* ms = clip->media_stream();
long media_length = clip->media_length();
int preview_size = ms->audio_preview.length();
float chunk_size = (float)preview_size/media_length; // how many audio samples to read for each fotogram
int sample_size = qMax(current_attack_time, current_release_time)+1;
bool attack = false; // status flags
bool release = false;
QVector<qint8> vols;
vols.resize(sample_size);
vols.fill(0);
// loop through the entire sequence
for (long i=clip_start;i<media_length+clip_start;i++) {
long start = ((i-clip_start)*chunk_size); //audio samples are read relative to the clip, not absolute to the timeline
int circular_index = i%sample_size;
// read the current sample into the circular array
qint8 tmp = 0;
for (int k=start; k<start+chunk_size; k++){
tmp = qMax(tmp, qint8(qRound(double(ms->audio_preview.at(k)))));
}
vols[circular_index] = tmp;
//for debug:
//qInfo() << "i:" << i <<" - "<< i/30 <<":"<< i%30 << " - volume:" << vols[circular_index] <<"\n";
int overthreshold = 0;
int cut_idx = 0; //how much to cut (backwards)
// if current volume value is above threshold
if (vols[circular_index] >= current_attack_threshold && !attack){ // if we get one sample over the threshold
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range (avoid false activations)
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
if(vols[back_idx] > current_attack_threshold){
overthreshold++;
cut_idx = k+1;
}
}
// if we reached threshold over the set tolerance
if(overthreshold >= current_attack_time){
split_positions.append(i-cut_idx);
attack = true;
release = false;
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" attack at " << i-cut_idx << "\n\n";
}
overthreshold = 0;
cut_idx = 0;
}else if (vols[circular_index] < current_release_threshold && !release){ // if we get one sample under the threshold
for(int k=0; k<sample_size; k++){ // count how many times this happened before within sample_size range
int back_idx = (((circular_index-k)%sample_size)+sample_size)%sample_size; // positive modulus
if(vols[back_idx] < current_release_threshold)
overthreshold++;
}
// if we reached threshold over the set tolerance
if(overthreshold >= current_release_time){ // must be <= sample_size
attack = false;
release = true;
split_positions.append(i);
//qInfo() << "\n\n Current vol: "<<vols[circular_index]<<" release at " << i << "\n\n";
}
overthreshold = 0;
}
}
ComboAction* ca = new ComboAction();
// NO GOOD VERY BAD TEST CODE
int clip_index = -1;
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
if (olive::ActiveSequence->clips.at(i).get() == clip) {
clip_index = i;
break;
}
}
Q_ASSERT(clip_index > -1);
panel_timeline->split_clip_at_positions(ca, clip_index, split_positions);
olive::UndoStack.push(ca);
}
}
}
+59
View File
@@ -0,0 +1,59 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef SILENCEDIALOG_H
#define SILENCEDIALOG_H
#include <QDialog>
#include <QCheckBox>
#include "timeline/clip.h"
#include "ui/labelslider.h"
class AutoCutSilenceDialog : public QDialog
{
Q_OBJECT
public:
AutoCutSilenceDialog(QWidget* parent, QVector<Clip*> clips);
public slots:
virtual int exec() override;
private slots:
virtual void accept() override;
private:
void cut_silence();
QVector<Clip*> clips_;
LabelSlider* attack_threshold;
LabelSlider* release_threshold;
LabelSlider* attack_time;
LabelSlider* release_time;
int default_attack_threshold;
int current_attack_threshold;
int default_release_threshold;
int current_release_threshold;
int default_attack_time;
int current_attack_time;
int default_release_time;
int current_release_time;
};
#endif // SILENCEDIALOG_H
+2 -1
View File
@@ -30,7 +30,8 @@
/**
* @brief The ClipPropertiesDialog class
*
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties".
* A dialog for setting Clip properties, accessible by right clicking a Clip and clicking "Properties". This can be
* run from anywhere provided it's given a valid array of Clip objects.
*/
class ClipPropertiesDialog : public QDialog {
Q_OBJECT
+5 -1
View File
@@ -27,7 +27,8 @@
/**
* @brief The DebugDialog class
*
* A dialog to display the current debug output.
* A dialog to display the current debug output. This dialog is omnipresent and shown and hidden when the user wants
* to see it. For efficiency, it will not update if it's hidden.
*/
class DebugDialog : public QDialog {
Q_OBJECT
@@ -69,6 +70,9 @@ private:
};
namespace olive {
/**
* @brief Omnipresent instance of DebugDialog to be shown or hidden as the user wants
*/
extern DebugDialog* DebugDialog;
}
+4 -1
View File
@@ -26,7 +26,10 @@
/**
* @brief The DemoNotice class
*
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds).
* Simple dialog shown on startup to introduce Olive as alpha software (in release builds). Can be run from anywhere,
* but there should be no reason to create it outside of the application launch.
*
* To be phased out as Olive gains maturity.
*/
class DemoNotice : public QDialog
{
+3
View File
@@ -569,6 +569,9 @@ void ExportDialog::StartExport() {
connect(export_thread_, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
connect(renderCancel, SIGNAL(clicked(bool)), export_thread_, SLOT(Interrupt()));
// Close all effects in effect controls (prevents UI threading issues)
panel_effect_controls->Clear();
// Close all currently open clips
close_active_clips(olive::ActiveSequence.get());
+3 -1
View File
@@ -35,7 +35,9 @@
/**
* @brief The ExportDialog class
*
* The dialog to initiate an export.
* The dialog to initiate an export. Requires a valid Sequence to be set in olive::ActiveSequence or the result is
* defined (most likely a crash), so you should always do a `nullptr` check on olive::ActiveSequence before constructing
* this dialog.
*/
class ExportDialog : public QDialog
{
+3 -1
View File
@@ -31,7 +31,9 @@
/**
* @brief The LoadDialog class
*
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object.
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object. This dialog should
* generally not be created directly, use OliveGlobal::LoadProject (or its variants) to correctly set up a LoadDialog
* and LoadThread and connect them to each other.
*/
class LoadDialog : public QDialog
{
+2 -1
View File
@@ -34,7 +34,8 @@
/**
* @brief The MediaPropertiesDialog class
*
* A dialog for setting properties on Media.
* A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given
* a valid Media object.
*/
class MediaPropertiesDialog : public QDialog {
Q_OBJECT
+44 -10
View File
@@ -39,19 +39,26 @@
#include "panels/timeline.h"
#include "project/media.h"
#include "rendering/audio.h"
#include "global/config.h"
extern "C" {
#include <libavcodec/avcodec.h>
}
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) :
NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing, Sequence* iexisting_sequence) :
QDialog(parent),
existing_item(existing)
existing_item(existing),
existing_sequence(iexisting_sequence)
{
Q_ASSERT(!(existing != nullptr && iexisting_sequence != nullptr));
setup_ui();
if (existing != nullptr) {
existing_sequence = existing->to_sequence();
existing_sequence = existing->to_sequence().get();
}
if (existing_sequence != nullptr) {
setWindowTitle(tr("Editing \"%1\"").arg(existing_sequence->name));
width_numeric->setValue(existing_sequence->width);
@@ -80,6 +87,12 @@ void NewSequenceDialog::set_sequence_name(const QString& s) {
sequence_name_edit->setText(s);
}
void NewSequenceDialog::SetNameEditable(bool enabled)
{
sequence_name_edit->setVisible(enabled);
sequence_name_label->setVisible(enabled);
}
void NewSequenceDialog::accept() {
if (existing_sequence == nullptr) {
@@ -98,7 +111,7 @@ void NewSequenceDialog::accept() {
panel_project->create_sequence_internal(ca, s, true, nullptr);
olive::UndoStack.push(ca);
} else {
} else if (existing_item != nullptr) {
// The dialog was given an existing Sequence object, so we'll apply the changes to it
@@ -106,7 +119,7 @@ void NewSequenceDialog::accept() {
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_sequence);
EditSequenceCommand* esc = new EditSequenceCommand(existing_item, existing_item->to_sequence());
esc->name = sequence_name_edit->text();
esc->width = width_numeric->value();
esc->height = height_numeric->value();
@@ -123,6 +136,18 @@ void NewSequenceDialog::accept() {
}
olive::UndoStack.push(ca);
} else if (existing_sequence != nullptr) {
// This dialog was given an existing Sequence without a Media wrapper - therefore just directly apply the settings
existing_sequence->name = sequence_name_edit->text();
existing_sequence->width = width_numeric->value();
existing_sequence->height = height_numeric->value();
existing_sequence->frame_rate = frame_rate_combobox->currentData().toDouble();
existing_sequence->audio_frequency = audio_frequency_combobox->currentData().toInt();
existing_sequence->audio_layout = AV_CH_LAYOUT_STEREO;
}
QDialog::accept();
@@ -210,13 +235,13 @@ void NewSequenceDialog::setup_ui() {
videoLayout->addWidget(new QLabel(tr("Width:"), this), 0, 0, 1, 1);
width_numeric = new QSpinBox(videoGroupBox);
width_numeric->setMaximum(9999);
width_numeric->setValue(1920);
width_numeric->setValue(olive::CurrentConfig.default_sequence_width);
videoLayout->addWidget(width_numeric, 0, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Height:"), this), 1, 0, 1, 2);
height_numeric = new QSpinBox(videoGroupBox);
height_numeric->setMaximum(9999);
height_numeric->setValue(1080);
height_numeric->setValue(olive::CurrentConfig.default_sequence_height);
videoLayout->addWidget(height_numeric, 1, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Frame Rate:"), this), 2, 0, 1, 1);
@@ -232,7 +257,11 @@ void NewSequenceDialog::setup_ui() {
frame_rate_combobox->addItem("50 FPS", 50.0);
frame_rate_combobox->addItem("59.94 FPS", 59.94);
frame_rate_combobox->addItem("60 FPS", 60.0);
frame_rate_combobox->setCurrentIndex(6);
for (int i=0;i<frame_rate_combobox->count();i++) {
if (qFuzzyCompare(frame_rate_combobox->itemData(i).toDouble(), olive::CurrentConfig.default_sequence_framerate)) {
frame_rate_combobox->setCurrentIndex(i);
}
}
videoLayout->addWidget(frame_rate_combobox, 2, 2, 1, 2);
videoLayout->addWidget(new QLabel(tr("Pixel Aspect Ratio:"), this), 4, 0, 1, 1);
@@ -258,7 +287,11 @@ void NewSequenceDialog::setup_ui() {
audio_frequency_combobox = new QComboBox(audioGroupBox);
combobox_audio_sample_rates(audio_frequency_combobox);
audio_frequency_combobox->setCurrentIndex(4);
for (int i=0;i<audio_frequency_combobox->count();i++) {
if (audio_frequency_combobox->itemData(i) == olive::CurrentConfig.default_sequence_audio_frequency) {
audio_frequency_combobox->setCurrentIndex(i);
}
}
audioLayout->addWidget(audio_frequency_combobox, 0, 1, 1, 1);
@@ -268,7 +301,8 @@ void NewSequenceDialog::setup_ui() {
QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget);
nameLayout->setContentsMargins(0, 0, 0, 0);
nameLayout->addWidget(new QLabel(tr("Name:"), this));
sequence_name_label = new QLabel(tr("Name:"));
nameLayout->addWidget(sequence_name_label);
sequence_name_edit = new QLineEdit(nameWidget);
+30 -7
View File
@@ -33,7 +33,7 @@
/**
* @brief The NewSequenceDialog class
*
* A dialog that creates a new (or edits an existing) Sequence object.
* A dialog that creates a new (or edits an existing) Sequence object. Can be run from any part of the application.
*/
class NewSequenceDialog : public QDialog
{
@@ -50,8 +50,13 @@ public:
*
* Set this to a Sequence object (wrapped in a Media object) to edit an existing Sequence,
* or leave as nullptr to create a new one.
*
* @param existing_sequence
*
* If your Sequence object is not wrapped in a Media object, use this to reference a raw Sequence pointer. You must
* not use both existing_sequence AND existing - one must be nullptr.
*/
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr);
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr, Sequence* iexisting_sequence = nullptr);
/**
* @brief Set the name for the new Sequence
@@ -68,6 +73,17 @@ public:
*/
void set_sequence_name(const QString& s);
/**
* @brief Set whether the Sequence's name can be edited
*
* This defaults to TRUE.
*
* @param enabled
*
* TRUE to allow the user to edit the Sequence's name. FALSE if not.
*/
void SetNameEditable(bool enabled);
private slots:
/**
* @brief Override accept function to create/edit a Sequence
@@ -86,16 +102,16 @@ private slots:
void preset_changed(int index);
private:
/**
* @brief Internal reference to an existing Sequence (if one was provided to the constructor)
*/
SequencePtr existing_sequence;
/**
* @brief Internal reference to an existing Media wrapper (if one was provided to the constructor)
*/
Media* existing_item;
/**
* @brief Internal reference to an existing Sequence (if one was provided to the constructor)
*/
Sequence* existing_sequence;
/**
* @brief Internal function to create the dialog's UI
*/
@@ -136,6 +152,13 @@ private:
*/
QComboBox* audio_frequency_combobox;
/**
* @brief Label marker for setting the Sequence's name
*
* Primarily a persistent class reference so it can be hidden with SetNameEditable() alongside sequence_name_edit.
*/
QLabel* sequence_name_label;
/**
* @brief Line edit to set the Sequence's name
*/
+121 -28
View File
@@ -50,7 +50,9 @@
#include "rendering/audio.h"
#include "rendering/bitdepths.h"
#include "panels/panels.h"
#include "ui/columnedgridlayout.h"
#include "ui/mainwindow.h"
#include "dialogs/newsequencedialog.h"
KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a)
: QKeySequenceEdit(parent), action(a) {
@@ -59,7 +61,6 @@ KeySequenceEditor::KeySequenceEditor(QWidget* parent, QAction* a)
void KeySequenceEditor::set_action_shortcut() {
action->setShortcut(keySequence());
action->setShortcutContext(Qt::ApplicationShortcut);
}
void KeySequenceEditor::reset_to_default() {
@@ -73,7 +74,7 @@ QString KeySequenceEditor::action_name() {
QString KeySequenceEditor::export_shortcut() {
QString ks = keySequence().toString();
if (ks != action->property("default")) {
return action->property("id").toString() + "\t" + keySequence().toString();
return action->property("id").toString() + "\t" + ks;
}
return nullptr;
}
@@ -82,12 +83,18 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
QDialog(parent)
{
setWindowTitle(tr("Preferences"));
setup_ui();
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats);
setup_kbd_shortcuts(olive::MainWindow->menuBar());
// set up default sequence
default_sequence.name = tr("Default Sequence");
default_sequence.width = olive::CurrentConfig.default_sequence_width;
default_sequence.height = olive::CurrentConfig.default_sequence_height;
default_sequence.frame_rate = olive::CurrentConfig.default_sequence_framerate;
default_sequence.audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
default_sequence.audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout;
}
void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent) {
@@ -235,6 +242,15 @@ void PreferencesDialog::update_ocio_config(const QString &s)
}
#endif
void PreferencesDialog::AddBoolPair(QCheckBox *ui, bool *value, bool restart_required)
{
bool_ui.append(ui);
bool_value.append(value);
bool_restart_required.append(restart_required);
ui->setChecked(*value);
}
void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) {
QList<QAction*> menus = menubar->actions();
@@ -299,13 +315,20 @@ void PreferencesDialog::accept() {
reload_effects = true;
}
bool bool_requires_restart = false;
for (int i=0;i<bool_restart_required.size();i++) {
if (bool_restart_required.at(i)
&& bool_ui.at(i)->isChecked() != *bool_value.at(i)) {
bool_requires_restart = true;
break;
}
}
// Check if any settings will require a restart of Olive
if (olive::CurrentConfig.use_software_fallback != use_software_fallbacks_checkbox->isChecked()
if (bool_requires_restart
|| olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
|| olive::CurrentConfig.waveform_resolution != waveform_res_spinbox->value()
#ifdef Q_OS_WIN32
|| olive::CurrentConfig.use_native_menu_styling != native_menus->isChecked()
#endif
|| olive::CurrentConfig.css_path != custom_css_fn->text()
|| olive::CurrentConfig.style != static_cast<olive::styling::Style>(ui_style->currentData().toInt())) {
// any changes to these settings will require a restart - ask the user if we should do one now or later
@@ -346,25 +369,19 @@ void PreferencesDialog::accept() {
}
// save settings from UI to backend
if (olive::CurrentConfig.css_path != custom_css_fn->text()) {
olive::CurrentConfig.css_path = custom_css_fn->text();
olive::MainWindow->Restyle();
}
olive::CurrentConfig.css_path = custom_css_fn->text();
olive::CurrentConfig.recording_mode = recordingComboBox->currentIndex() + 1;
olive::CurrentConfig.img_seq_formats = imgSeqFormatEdit->text();
olive::CurrentConfig.upcoming_queue_size = upcoming_queue_spinbox->value();
olive::CurrentConfig.upcoming_queue_type = upcoming_queue_type->currentIndex();
olive::CurrentConfig.previous_queue_size = previous_queue_spinbox->value();
olive::CurrentConfig.previous_queue_type = previous_queue_type->currentIndex();
olive::CurrentConfig.add_default_effects_to_clips = add_default_effects_to_clips->isChecked();
olive::CurrentConfig.preferred_audio_output = audio_output_devices->currentData().toString();
olive::CurrentConfig.preferred_audio_input = audio_input_devices->currentData().toString();
olive::CurrentConfig.audio_rate = audio_sample_rate->currentData().toInt();
olive::CurrentConfig.effect_textbox_lines = effect_textbox_lines_field->value();
olive::CurrentConfig.use_software_fallback = use_software_fallbacks_checkbox->isChecked();
olive::CurrentConfig.language_file = language_combobox->currentData().toString();
olive::CurrentConfig.enable_color_management = enable_color_management->isChecked();
@@ -394,10 +411,17 @@ void PreferencesDialog::accept() {
#endif
olive::CurrentConfig.default_sequence_width = default_sequence.width;
olive::CurrentConfig.default_sequence_height = default_sequence.height;
olive::CurrentConfig.default_sequence_framerate = default_sequence.frame_rate;
olive::CurrentConfig.default_sequence_audio_frequency = default_sequence.audio_frequency;
olive::CurrentConfig.default_sequence_audio_channel_layout = default_sequence.audio_layout;
for (int i=0;i<bool_ui.size();i++) {
*bool_value[i] = bool_ui.at(i)->isChecked();
}
olive::CurrentConfig.style = static_cast<olive::styling::Style>(ui_style->currentData().toInt());
#ifdef Q_OS_WIN
olive::CurrentConfig.use_native_menu_styling = native_menus->isChecked();
#endif
// Check if the thumbnail or waveform icon
if (olive::CurrentConfig.thumbnail_resolution != thumbnail_res_spinbox->value()
@@ -615,6 +639,13 @@ void PreferencesDialog::delete_all_previews() {
}
}
void PreferencesDialog::edit_default_sequence_settings()
{
NewSequenceDialog nsd(this, nullptr, &default_sequence);
nsd.SetNameEditable(false);
nsd.exec();
}
void PreferencesDialog::setup_ui() {
QVBoxLayout* verticalLayout = new QVBoxLayout(this);
QTabWidget* tabWidget = new QTabWidget(this);
@@ -696,15 +727,15 @@ void PreferencesDialog::setup_ui() {
row++;
// General -> Use Software Fallbacks When Possible
use_software_fallbacks_checkbox = new QCheckBox(general_tab);
use_software_fallbacks_checkbox->setText(tr("Use Software Fallbacks When Possible"));
use_software_fallbacks_checkbox->setChecked(olive::CurrentConfig.use_software_fallback);
QCheckBox* use_software_fallbacks_checkbox = new QCheckBox(tr("Use Software Fallbacks When Possible"));
AddBoolPair(use_software_fallbacks_checkbox, &olive::CurrentConfig.use_software_fallback, true);
general_layout->addWidget(use_software_fallbacks_checkbox, row, 0, 1, 4);
row++;
// General -> Default Sequence Settings
QPushButton* default_sequence_settings = new QPushButton(tr("Default Sequence Settings"));
connect(default_sequence_settings, SIGNAL(clicked(bool)), this, SLOT(edit_default_sequence_settings()));
general_layout->addWidget(default_sequence_settings);
tabWidget->addTab(general_tab, tr("General"));
@@ -713,11 +744,72 @@ void PreferencesDialog::setup_ui() {
QWidget* behavior_tab = new QWidget(this);
tabWidget->addTab(behavior_tab, tr("Behavior"));
QVBoxLayout* behavior_tab_layout = new QVBoxLayout(behavior_tab);
ColumnedGridLayout* behavior_tab_layout = new ColumnedGridLayout(behavior_tab, 2);
add_default_effects_to_clips = new QCheckBox(tr("Add Default Effects to New Clips"));
add_default_effects_to_clips->setChecked(olive::CurrentConfig.add_default_effects_to_clips);
behavior_tab_layout->addWidget(add_default_effects_to_clips);
QCheckBox* add_default_effects_to_clips = new QCheckBox(tr("Add Default Effects to New Clips"));
AddBoolPair(add_default_effects_to_clips, &olive::CurrentConfig.add_default_effects_to_clips);
behavior_tab_layout->Add(add_default_effects_to_clips);
QCheckBox* auto_seek_to_beginning = new QCheckBox(tr("Automatically Seek to the Beginning When Playing at the End of a Sequence"));
AddBoolPair(auto_seek_to_beginning, &olive::CurrentConfig.auto_seek_to_beginning);
behavior_tab_layout->Add(auto_seek_to_beginning);
QCheckBox* selecting_also_seeks = new QCheckBox(tr("Selecting Also Seeks"));
AddBoolPair(selecting_also_seeks, &olive::CurrentConfig.select_also_seeks);
behavior_tab_layout->Add(selecting_also_seeks);
QCheckBox* edit_tool_also_seeks = new QCheckBox(tr("Edit Tool Also Seeks"));
AddBoolPair(edit_tool_also_seeks, &olive::CurrentConfig.edit_tool_also_seeks);
behavior_tab_layout->Add(edit_tool_also_seeks);
QCheckBox* edit_tool_selects_links = new QCheckBox(tr("Edit Tool Selects Links"));
AddBoolPair(edit_tool_selects_links, &olive::CurrentConfig.edit_tool_selects_links);
behavior_tab_layout->Add(edit_tool_selects_links);
QCheckBox* seek_also_selects = new QCheckBox(tr("Seek Also Selects"));
AddBoolPair(seek_also_selects, &olive::CurrentConfig.seek_also_selects);
behavior_tab_layout->Add(seek_also_selects);
QCheckBox* seek_to_end_of_pastes = new QCheckBox(tr("Seek to the End of Pastes"));
AddBoolPair(seek_to_end_of_pastes, &olive::CurrentConfig.paste_seeks);
behavior_tab_layout->Add(seek_to_end_of_pastes);
QCheckBox* scroll_wheel_zooms = new QCheckBox(tr("Scroll Wheel Zooms"));
scroll_wheel_zooms->setToolTip(tr("Hold CTRL to toggle this setting"));
AddBoolPair(scroll_wheel_zooms, &olive::CurrentConfig.scroll_zooms);
behavior_tab_layout->Add(scroll_wheel_zooms);
QCheckBox* invert_timeline_scroll_axes = new QCheckBox(tr("Invert Timeline Scroll Axes"));
AddBoolPair(invert_timeline_scroll_axes, &olive::CurrentConfig.invert_timeline_scroll_axes);
behavior_tab_layout->Add(invert_timeline_scroll_axes);
QCheckBox* enable_drag_files_to_timeline = new QCheckBox(tr("Enable Drag Files to Timeline"));
AddBoolPair(enable_drag_files_to_timeline, &olive::CurrentConfig.enable_drag_files_to_timeline);
behavior_tab_layout->Add(enable_drag_files_to_timeline);
QCheckBox* autoscale_by_default = new QCheckBox(tr("Auto-Scale By Default"));
AddBoolPair(autoscale_by_default, &olive::CurrentConfig.autoscale_by_default);
behavior_tab_layout->Add(autoscale_by_default);
QCheckBox* enable_seek_to_import = new QCheckBox(tr("Auto-Seek to Imported Clips"));
AddBoolPair(enable_seek_to_import, &olive::CurrentConfig.enable_seek_to_import);
behavior_tab_layout->Add(enable_seek_to_import);
QCheckBox* enable_audio_scrubbing = new QCheckBox(tr("Audio Scrubbing"));
AddBoolPair(enable_audio_scrubbing, &olive::CurrentConfig.enable_audio_scrubbing);
behavior_tab_layout->Add(enable_audio_scrubbing);
QCheckBox* enable_drop_on_media_to_replace = new QCheckBox(tr("Drop Files on Media to Replace"));
AddBoolPair(enable_drop_on_media_to_replace, &olive::CurrentConfig.drop_on_media_to_replace);
behavior_tab_layout->Add(enable_drop_on_media_to_replace);
QCheckBox* enable_hover_focus = new QCheckBox(tr("Enable Hover Focus"));
AddBoolPair(enable_hover_focus, &olive::CurrentConfig.hover_focus);
behavior_tab_layout->Add(enable_hover_focus);
QCheckBox* set_name_and_marker = new QCheckBox(tr("Ask For Name When Setting Marker"));
AddBoolPair(set_name_and_marker, &olive::CurrentConfig.set_name_with_marker);
behavior_tab_layout->Add(set_name_and_marker);
// Appearance
QWidget* appearance_tab = new QWidget(this);
@@ -743,8 +835,8 @@ void PreferencesDialog::setup_ui() {
#ifdef Q_OS_WIN
// Native menu styling is only available on Windows. Environments like Ubuntu and Mac use the native menu system by
// default
native_menus = new QCheckBox(tr("Use Native Menu Styling"));
native_menus->setChecked(olive::CurrentConfig.use_native_menu_styling);
QCheckBox* native_menus = new QCheckBox(tr("Use Native Menu Styling"));
AddBoolPair(native_menus, &olive::CurrentConfig.use_native_menu_styling, true);
appearance_layout->addWidget(native_menus, row, 0, 1, 3);
row++;
@@ -879,6 +971,7 @@ void PreferencesDialog::setup_ui() {
recordingComboBox = new QComboBox(general_tab);
recordingComboBox->addItem(tr("Mono"));
recordingComboBox->addItem(tr("Stereo"));
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
audio_tab_layout->addWidget(recordingComboBox, row, 1);
row++;
+193 -7
View File
@@ -45,7 +45,8 @@ class KeySequenceEditor;
/**
* @brief The PreferencesDialog class
*
* A dialog for the global application settings. Mostly an interface for Config.
* A dialog for the global application settings. Mostly an interface for Config. Can be loaded from any part of the
* application.
*/
class PreferencesDialog : public QDialog
{
@@ -130,6 +131,11 @@ private slots:
void update_ocio_config(const QString&);
#endif
/**
* @brief Shows a NewSequenceDialog attached to default_sequence
*/
void edit_default_sequence_settings();
private:
/**
@@ -186,23 +192,81 @@ private:
void populate_ocio_menus(OCIO::ConstConfigRcPtr config);
#endif
/**
* @brief UI widget for editing the CSS filename
*/
QLineEdit* custom_css_fn;
/**
* @brief UI widget for editing the list of extensions to detect image sequences from
*/
QLineEdit* imgSeqFormatEdit;
/**
* @brief UI widget for editing the recording channels
*/
QComboBox* recordingComboBox;
/**
* @brief UI widget for editing keyboard shortcuts
*/
QTreeWidget* keyboard_tree;
/**
* @brief UI widget for editing the upcoming queue size
*/
QDoubleSpinBox* upcoming_queue_spinbox;
/**
* @brief UI widget for editing the upcoming queue type
*/
QComboBox* upcoming_queue_type;
/**
* @brief UI widget for editing the previous queue size
*/
QDoubleSpinBox* previous_queue_spinbox;
/**
* @brief UI widget for editing the previous queue type
*/
QComboBox* previous_queue_type;
/**
* @brief UI widget for editing the size of textboxes in the EffectControls panel
*/
QSpinBox* effect_textbox_lines_field;
QCheckBox* use_software_fallbacks_checkbox;
/**
* @brief UI widget for selecting the output audio device
*/
QComboBox* audio_output_devices;
/**
* @brief UI widget for selecting the input audio device
*/
QComboBox* audio_input_devices;
/**
* @brief UI widget for selecting the audio sampling rates
*/
QComboBox* audio_sample_rate;
/**
* @brief UI widget for selecting the UI language
*/
QComboBox* language_combobox;
/**
* @brief UI widget for selecting the resolution of the thumbnails to generate
*/
QSpinBox* thumbnail_res_spinbox;
/**
* @brief UI widget for selecting the resolution of the waveforms to generate
*/
QSpinBox* waveform_res_spinbox;
QCheckBox* add_default_effects_to_clips;
QCheckBox* enable_color_management;
QLineEdit* ocio_config_file;
@@ -210,26 +274,148 @@ private:
QComboBox* ocio_view;
QComboBox* ocio_look;
/**
* @brief UI widget for selecting the current UI style
*/
QComboBox* ui_style;
Sequence sequence_settings;
#ifdef Q_OS_WIN
QCheckBox* native_menus;
#endif
/**
* @brief Stored default Sequence object
*
* Default Sequence settings are loaded into an actual Sequence object that can be loaded into NewSequenceDialog
* for the sake of familiarity with the user.
*/
Sequence default_sequence;
/**
* @brief List of keyboard shortcut actions that can be triggered (links with key_shortcut_items and
* key_shortcut_fields)
*/
QVector<QAction*> key_shortcut_actions;
/**
* @brief List of keyboard shortcut items in keyboard_tree corresponding to existing actions (links with
* key_shortcut_actions and key_shortcut_fields)
*/
QVector<QTreeWidgetItem*> key_shortcut_items;
/**
* @brief List of keyboard shortcut editing fields in keyboard_tree corresponding to existing actions (links with
* key_shortcut_actions and key_shortcut_fields)
*/
QVector<KeySequenceEditor*> key_shortcut_fields;
/**
* @brief Add an automated QCheckBox+boolean value pair
*
* Many preferences are simple true/false (or on/off) options. Rather than adding a QCheckBox for each one and
* manually setting its checked value to the configuration setting (and vice versa when saving), this convenience
* function will add it to an automated set of checkboxes, automatically setting the checked state to the current
* setting, and then saving the new checked state back to the setting when the user accepts the changes (clicks OK).
*
* @param ui
*
* A valid QCheckBox item. This function does not take ownership of the QWidget or place it in a layout anywhere.
*
* @param value
*
* A pointer to the Boolean value this QCheckBox should be shared with. The QCheckBox widget's checked state will be
* set to the value of this pointer.
*
* @param restart_required
*
* Defaults to FALSE, set this to TRUE if changing this setting should prompt the user for a restart of Olive before
* the setting change takes effect.
*/
void AddBoolPair(QCheckBox* ui, bool* value, bool restart_required = false);
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<QCheckBox*> bool_ui;
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<bool*> bool_value;
/**
* @brief Internal array managed by AddBoolPair(). Do not access this directly.
*/
QVector<bool> bool_restart_required;
};
/**
* @brief The KeySequenceEditor class
*
* Simple derived class of QKeySequenceEdit that attaches to a QAction and provides functions for transferring
* keyboard shortcuts to and from it.
*/
class KeySequenceEditor : public QKeySequenceEdit {
Q_OBJECT
public:
/**
* @brief KeySequenceEditor Constructor
*
* @param parent
*
* QWidget parent.
*
* @param a
*
* The QAction to link to. This cannot be changed throughout the lifetime of a KeySequenceEditor.
*/
KeySequenceEditor(QWidget *parent, QAction* a);
/**
* @brief Sets the attached QAction's shortcut to the shortcut entered in this field.
*
* This is not done automatically in case the user cancels out of the Preferences dialog, in which case the
* expectation is that the changes made will not be saved. Therefore, this needs to be triggered manually when
* PreferencesDialog saves.
*/
void set_action_shortcut();
/**
* @brief Set this shortcut back to the QAction's default shortcut
*
* Each QAction contains the default shortcut in its `property("default")` and can be used to restore the default
* "hard-coded" shortcut with this function.
*
* This function does not save the default shortcut back into the QAction, it simply loads the default shortcut from
* the QAction into this edit field. To save it into the QAction, it's necessary to call set_action_shortcut() after
* calling this function.
*/
void reset_to_default();
/**
* @brief Return attached QAction's unique ID
*
* Each of Olive's menu actions has a unique string ID (that, unlike the text, is not translated) for matching with
* an external shortcut configuration file. The ID is stored in the QAction's `property("id")`. This function returns
* that ID.
*
* @return
*
* The QAction's unique ID.
*/
QString action_name();
/**
* @brief Serialize this shortcut entry into a string that can be saved to a file
*
* @return
*
* A string serialization of this shortcut. The format is "[ID]\t[SEQUENCE]" where [ID] is the attached QAction's
* unique identifier and [SEQUENCE] is the current keyboard shortcut in the field (NOT necessarily the shortcut in
* the QAction). If the entered shortcut is the same as the QAction's default shortcut, the return value is empty
* because a default shortcut does not need to be saved to a file.
*/
QString export_shortcut();
private:
/**
* @brief Internal reference to the linked QAction
*/
QAction* action;
};
+2 -1
View File
@@ -30,7 +30,8 @@
/**
* @brief The ProxyDialog class
*
* Dialog to set up proxy generation of footage
* Dialog to set up proxy generation of footage. This dialog can be called from anywhere provided it's given a valid
* array of Media and will start all proxy generation.
*/
class ProxyDialog : public QDialog {
Q_OBJECT
+12 -12
View File
@@ -50,16 +50,16 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media
use_same_media_in_points->setChecked(true);
layout->addWidget(use_same_media_in_points);
QHBoxLayout* buttons = new QHBoxLayout();
QHBoxLayout* buttons = new QHBoxLayout();
buttons->addStretch();
QPushButton* replace_button = new QPushButton(tr("Replace"), this);
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(replace()));
connect(replace_button, SIGNAL(clicked(bool)), this, SLOT(accept()));
buttons->addWidget(replace_button);
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(close()));
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(reject()));
buttons->addWidget(cancel_button);
buttons->addStretch();
@@ -69,7 +69,7 @@ ReplaceClipMediaDialog::ReplaceClipMediaDialog(QWidget *parent, Media* old_media
tree->setModel(&olive::project_model);
}
void ReplaceClipMediaDialog::replace() {
void ReplaceClipMediaDialog::accept() {
QModelIndexList selected_items = tree->selectionModel()->selectedRows();
if (selected_items.size() != 1) {
QMessageBox::critical(
@@ -77,23 +77,23 @@ void ReplaceClipMediaDialog::replace() {
tr("No media selected"),
tr("Please select a media to replace with or click 'Cancel'."),
QMessageBox::Ok
);
);
} else {
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
Media* new_item = static_cast<Media*>(selected_items.at(0).internalPointer());
if (media == new_item) {
QMessageBox::critical(
this,
tr("Same media selected"),
tr("You selected the same media that you're replacing. Please select a different one or click 'Cancel'."),
QMessageBox::Ok
);
);
} else if (new_item->get_type() == MEDIA_TYPE_FOLDER) {
QMessageBox::critical(
this,
tr("Folder selected"),
tr("You cannot replace footage with a folder."),
QMessageBox::Ok
);
);
} else {
if (new_item->get_type() == MEDIA_TYPE_SEQUENCE && olive::ActiveSequence == new_item->to_sequence()) {
QMessageBox::critical(
@@ -101,16 +101,16 @@ void ReplaceClipMediaDialog::replace() {
tr("Active sequence selected"),
tr("You cannot insert a sequence into itself."),
QMessageBox::Ok
);
);
} else {
ReplaceClipMediaCommand* rcmc = new ReplaceClipMediaCommand(
media,
new_item,
use_same_media_in_points->isChecked()
);
);
for (int i=0;i<olive::ActiveSequence->clips.size();i++) {
ClipPtr c = olive::ActiveSequence->clips.at(i);
ClipPtr c = olive::ActiveSequence->clips.at(i);
if (c != nullptr && c->media() == media) {
rcmc->clips.append(c);
}
@@ -118,7 +118,7 @@ void ReplaceClipMediaDialog::replace() {
olive::UndoStack.push(rcmc);
close();
QDialog::accept();
}
}
+42 -3
View File
@@ -28,15 +28,54 @@
#include "ui/sourcetable.h"
#include "project/projectelements.h"
/**
* @brief The ReplaceClipMediaDialog class
*
* A dialog to replace all Clips using a certain Media with a different Media. This dialog can be run from anywhere
* provided it's given a valid Media object.
*/
class ReplaceClipMediaDialog : public QDialog {
Q_OBJECT
public:
ReplaceClipMediaDialog(QWidget* parent, Media* old_media);
/**
* @brief ReplaceClipMediaDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Project panel.
*
* @param old_media
*
* A valid Media object which will be used to scan the currently active Sequence for Clips using it.
*/
ReplaceClipMediaDialog(QWidget* parent, Media* old_media);
private slots:
void replace();
/**
* @brief Overrided accept for when the user clicks "Replace"
*
* Checks whether the requested replace is valid using the following criteria:
* * Any Media is selected
* * The selected Media is not the same Media that the user is trying to replace
* * The Media is not a folder
* * The Media is not the currently active Sequence
*/
virtual void accept() override;
private:
Media* media;
/**
* @brief Internal pointer to the Media we're replacing
*/
Media* media;
/**
* @brief Tree widget to show Project's media
*/
QTreeView* tree;
/**
* @brief CheckBox for using the same media in points
*
* When the starting point of a Clip is trimmed (i.e. the Clip no longer starts at 0),
*/
QCheckBox* use_same_media_in_points;
};
+7 -7
View File
@@ -85,16 +85,16 @@ SpeedDialog::SpeedDialog(QWidget *parent, QVector<Clip*> clips) : QDialog(parent
connect(duration, SIGNAL(valueChanged(double)), this, SLOT(duration_update()));
}
void SpeedDialog::run() {
int SpeedDialog::exec() {
bool enable_frame_rate = false;
bool multiple_audio = false;
maintain_pitch->setEnabled(false);
default_frame_rate = qSNaN();
current_frame_rate = qSNaN();
current_percent = qSNaN();
default_length = -1;
current_length = -1;
double default_frame_rate = qSNaN();
double current_frame_rate = qSNaN();
double current_percent = qSNaN();
long default_length = -1;
long current_length = -1;
for (int i=0;i<clips_.size();i++) {
Clip* c = clips_.at(i);
@@ -180,7 +180,7 @@ void SpeedDialog::run() {
duration->SetDefault(default_length);
duration->SetValue((current_length == -1) ? qSNaN() : current_length);
exec();
return QDialog::exec();
}
void SpeedDialog::percent_update() {
+82 -10
View File
@@ -27,34 +27,106 @@
#include "timeline/clip.h"
#include "ui/labelslider.h"
/**
* @brief The SpeedDialog class
*
* A dialog for setting the speed of one or more Clips. This can be run from anywhere provided it's given a valid
* array of Clips.
*
* It's preferable ot
*/
class SpeedDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief SpeedDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Timeline panel.
*
* @param clips
*
* A valid array of Clips to change the speed of.
*/
SpeedDialog(QWidget* parent, QVector<Clip*> clips);
void run();
public slots:
/**
* @brief Override of exec() to set up current Clip speed data just before opening
*
* @return
*
* The result of QDialog::exec(), a DialogCode result.
*/
virtual int exec() override;
private slots:
/**
* @brief Override of accept() to perform the selected changes on the Clips
*/
virtual void accept() override;
/**
* @brief Slot when the speed percentage field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void percent_update();
/**
* @brief Slot when the duration field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void duration_update();
/**
* @brief Slot when the frame rate field is changed by the user
*
* The three fields (percent, duration, and frame rate) all work in tandem to create a speed multipler for the
* Clip. Each has a slot for when one of the fields changes to update the others appropriately so they all have the
* same speed multipler.
*/
void frame_rate_update();
void accept();
private:
/**
* @brief Internal array of Clip objects
*/
QVector<Clip*> clips_;
/**
* @brief Speed percentage field
*/
LabelSlider* percent;
/**
* @brief Duration field
*/
LabelSlider* duration;
/**
* @brief Frame rate field
*/
LabelSlider* frame_rate;
/**
* @brief UI widget for setting the Clip's reverse value
*/
QCheckBox* reverse;
QCheckBox* maintain_pitch;
QCheckBox* ripple;
double default_frame_rate;
double current_frame_rate;
double current_percent;
long default_length;
long current_length;
/**
* @brief UI widget for setting the Clip's maintain pitch value
*/
QCheckBox* maintain_pitch;
/**
* @brief UI widget for setting whether to ripple Clips around these changes or not
*/
QCheckBox* ripple;
};
#endif // SPEEDDIALOG_H
+9 -33
View File
@@ -44,15 +44,6 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
if (rich_text) {
QHBoxLayout* toolbar = new QHBoxLayout();
// Bold Button
/*
bold_button = new QPushButton();
bold_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/bold.svg", false));
bold_button->setCheckable(true);
connect(bold_button, SIGNAL(clicked(bool)), this, SLOT(SetBold(bool)));
toolbar->addWidget(bold_button);
*/
// Italic Button
italic_button = new QPushButton();
italic_button->setIcon(olive::icon::CreateIconFromSVG(":/icons/italic.svg", false));
@@ -156,10 +147,14 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
buttons->setCenterButtons(true);
layout->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), this, SLOT(save()));
connect(buttons, SIGNAL(rejected()), this, SLOT(cancel()));
connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
// Connect the cursor position changing to the rich text toolbar buttons updating (so for example, when italic text
// is selected, the italic button will be pressed)
connect(textEdit, SIGNAL(cursorPositionChanged()), this, SLOT(UpdateUIFromTextCursor()));
// Set the widget's text based on the rich text mode
if (rich_text_) {
textEdit->setHtml(s);
} else {
@@ -167,7 +162,7 @@ TextEditDialog::TextEditDialog(QWidget *parent, const QString &s, bool rich_text
}
// Helps ensure the UI elements update correctly at the beginning - when the cursor is at the start, the UI elements
// show up blank...
// show up blank. Setting it to the end is probably more expected behavior anyway.
textEdit->moveCursor(QTextCursor::End);
}
@@ -175,21 +170,9 @@ const QString& TextEditDialog::get_string() {
return result_str;
}
void TextEditDialog::save() {
void TextEditDialog::accept() {
result_str = rich_text_ ? textEdit->toHtml() : textEdit->toPlainText();
accept();
}
void TextEditDialog::cancel() {
reject();
}
void TextEditDialog::SetBold(bool bold)
{
QFont f = textEdit->currentFont();
f.setBold(bold);
textEdit->setCurrentFont(f);
UpdateUIFromTextCursor();
QDialog::accept();
}
void TextEditDialog::SetFontWeight(int i)
@@ -197,13 +180,6 @@ void TextEditDialog::SetFontWeight(int i)
textEdit->setFontWeight(font_weight->itemData(i).toInt());
}
void TextEditDialog::SetLetterSpacing(qreal spacing)
{
QFont f = textEdit->currentFont();
f.setLetterSpacing(f.letterSpacingType(), spacing);
textEdit->setCurrentFont(f);
}
void TextEditDialog::SetAlignmentFromProperty()
{
textEdit->setAlignment(static_cast<Qt::Alignment>(sender()->property("a").toInt()));
+123 -8
View File
@@ -28,36 +28,151 @@
#include "ui/labelslider.h"
#include "ui/colorbutton.h"
/**
* @brief The TextEditDialog class
*
* A separate window for editing text. This window can be resized arbitrarily and also provides a toolbar for rich text
* editing (if rich text is enabled). This dialog can be run from anywhere. Once the dialog has closed (i.e. returned
* from exec() ), the text entered into it can be retrieved using get_string().
*
* TODO: Add a live signal for updating the calling function.
*/
class TextEditDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief TextEditDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*
* @param s
*
* The starting string when the dialog opens. It'll be read as rich text HTML or plain text based on the `rich_text`
* parameter (which defaults to rich text HTML). It can also be left empty to start blank.
*
* @param rich_text
*
* Set the editing mode of the editor. If TRUE, the dialog will interpret the string in `s` as rich text HTML and also
* return rich text HTML through get_string(). It'll also show a toolbar with rich text options (i.e. font, italic,
* underline, size, etc.) If FALSE, the dialog will run in plain text mode interpreting the string in `s` as plain
* text and returning plain text through get_string(). It also will not show the rich text editing toolbar.
*/
TextEditDialog(QWidget* parent = nullptr, const QString& s = nullptr, bool rich_text = true);
const QString& get_string();
signals:
void cursorPositionChanged();
private slots:
void save();
void cancel();
void SetBold(bool bold);
/**
* @brief Retrieve the current text in the dialog
*
* This function can be called after the user has accepted the dialog (i.e. made changes and clicked OK).
* This will return either plain text or rich text (HTML) depending on the mode it's running in (rich/plain text mode
* is set in the constructor). The value this returns only gets updated when the user clicks OK so it cannot be
* used to retrieve live text updates from the dialog.
*
* @return
*
* The text entered once the user accepted this dialog.
*/
const QString& get_string();
private slots:
/**
* @brief Override of accept() to store the entered text string so it can be retrieved by get_string().
*/
virtual void accept() override;
/**
* @brief Slot for the font_weight combobox to set the font weight based on its data value
*
* @param i
*
* Index of the font_weight to retrieve the desired font weight from
*/
void SetFontWeight(int i);
void SetLetterSpacing(qreal spacing);
/**
* @brief Slot for text alignment buttons to set alignment based on their properties
*
* Intended slot for left_align_button, center_align_button, right_align_button, and justify_align_button. Pulls
* from their property("a") value which should be a member of the Qt::Alignment enum.
*/
void SetAlignmentFromProperty();
/**
* @brief Slot for when the text edit widget's cursor moves so the rich text toolbar can stay up to date
*
* In rich text mode, different parts of a text document can be formatted in different ways. As the user moves
* around the text, the UI buttons should be consistent with whatever text is currently selected. This slot should
* therefore be connected to QTextEdit::cursorPositionChanged() and will change the "checked" state of the formatting
* buttons and current index of the comboboxes to match the currently selected text.
*/
void UpdateUIFromTextCursor();
private:
/**
* @brief Internal rich text mode value
*
* This is set in the constructor and cannot be changed during the lifetime of this dialog.
*/
bool rich_text_;
/**
* @brief Internal storage of text entered, saved when the user clicks OK
*/
QString result_str;
/**
* @brief Main text editing widget
*/
QTextEdit* textEdit;
/**
* @brief Toggle button for setting the italic state of the currently selected text
*/
QPushButton* italic_button;
/**
* @brief Toggle button for setting the underlined state of the currently selected text
*/
QPushButton* underline_button;
/**
* @brief ComboBox for the list of font families that the selected text can be set to
*/
QFontComboBox* font_list;
/**
* @brief ComboBox for the list of font weights that the selected text can be set to
*/
QComboBox* font_weight;
/**
* @brief A slider to set the current font size
*/
LabelSlider* font_size;
/**
* @brief A color selector for setting the current text color
*/
ColorButton* font_color;
/**
* @brief Button for setting the current text row(s) to left alignment
*/
QPushButton* left_align_button;
/**
* @brief Button for setting the current text row(s) to center alignment
*/
QPushButton* center_align_button;
/**
* @brief Button for setting the current text row(s) to right alignment
*/
QPushButton* right_align_button;
/**
* @brief Button for setting the current text row(s) to justified alignment
*/
QPushButton* justify_align_button;
};
+3 -2
View File
@@ -85,9 +85,7 @@ EffectPtr Effect::Create(Clip* c, const EffectMeta* em) {
case EFFECT_INTERNAL_SHAKE: return std::make_shared<ShakeEffect>(c, em);
case EFFECT_INTERNAL_CORNERPIN: return std::make_shared<CornerPinEffect>(c, em);
case EFFECT_INTERNAL_FILLLEFTRIGHT: return std::make_shared<FillLeftRightEffect>(c, em);
#ifndef NOVST
case EFFECT_INTERNAL_VST: return std::make_shared<VSTHost>(c, em);
#endif
case EFFECT_INTERNAL_RICHTEXT: return std::make_shared<RichTextEffect>(c, em);
}
} else if (!em->filename.isEmpty()) {
@@ -597,6 +595,9 @@ void Effect::load(QXmlStreamReader& stream) {
field->keyframes.append(key);
}
}
field->Changed();
}
}
}
-3
View File
@@ -29,7 +29,6 @@
#include "global/path.h"
#include "panels/panels.h"
#include "panels/effectcontrols.h"
#include "global/crossplatformlib.h"
#include "global/config.h"
QMutex olive::effects_loaded;
@@ -55,11 +54,9 @@ void load_internal_effects() {
em.internal = EFFECT_INTERNAL_PAN;
olive::effects.append(em);
#ifndef NOVST
em.name = "VST Plugin 2.x";
em.internal = EFFECT_INTERNAL_VST;
olive::effects.append(em);
#endif
em.name = "Tone";
em.internal = EFFECT_INTERNAL_TONE;
-6
View File
@@ -60,11 +60,6 @@ bool EffectRow::IsKeyframing() {
}
void EffectRow::SetKeyframingInternal(bool b) {
// No need to run this function if the keyframing state isn't actually changing.
if (b == keyframing_) {
return;
}
if (GetParentEffect()->meta->type != EFFECT_TYPE_TRANSITION) {
keyframing_ = b;
emit KeyframingSetChanged(keyframing_);
@@ -127,7 +122,6 @@ void EffectRow::SetKeyframingEnabled(bool enabled) {
} else {
SetKeyframingInternal(true);
}
+49
View File
@@ -32,18 +32,67 @@ class BoolField : public EffectField
{
Q_OBJECT
public:
/**
* @brief See Effect::Effect().
*/
BoolField(EffectRow* parent, const QString& id);
/**
* @brief Get the boolean value at a given timecode
*
* A convenience function, equivalent to GetValueAt(timecode).toBool()
*
* @param timecode
*
* The timecode to retrieve the value at
*
* @return
*
* The boolean value at this timecode
*/
bool GetBoolAt(double timecode);
/**
* @brief See EffectField::CreateWidget()
*/
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
/**
* @brief See EffectField::UpdateWidgetValue()
*/
virtual void UpdateWidgetValue(QWidget* widget, double timecode) override;
/**
* @brief See EffectField::ConvertStringToValue()
*/
virtual QVariant ConvertStringToValue(const QString& s) override;
/**
* @brief See EffectField::ConvertValueToString()
*/
virtual QString ConvertValueToString(const QVariant& v) override;
signals:
/**
* @brief Emitted whenever the UI widget's boolean value has changed
*
* For any QCheckBox created through this field's CreateWidget() function, this signal is emitted any time the
* checkbox value changes (either through user intervention or keyframing). It is mostly useful for
* enabling/disabling/changing other UI elements based on the checked
* state of this field's value (e.g. enabling other fields if this field is checked).
*
* It is NOT a reliable signal that the value has changed at all, as it is only emitted if a widget (created
* from CreateWidget() ) is currently active.
*/
void Toggled(bool);
private slots:
/**
* @brief Internal function connected to any QWidget made from CreateWidget() to update the value based on user input
*
* @param b
*
* The current checked state of the QWidget (QCheckBox in this case). Automatically set when this slot is connected
* to the QCheckBox::toggled() signal.
*/
void UpdateFromWidget(bool b);
};
+60
View File
@@ -23,26 +23,86 @@
#include "../effectfield.h"
/**
* @brief The ButtonField class
*
* A UI-type EffectField. This field is largely an EffectField wrapper around a QPushButton and provides no data that's
* usable in the Effect. It's primarily useful for other UI functions (e.g. showing/hiding a dialog or other UI
* elements). This field is not exposed to the external shader API as it requires raw C++ code to connect it to other
* elements.
*
* As with all widgets created from EffectField::CreateWidget(), you should never interface with the resulting widget
* directly (apart from adding it to a layout and deleting it when it's unnecessary). All signals/slots should pass
* through ButtonField instead to keep consistency with every layer involved.
*/
class ButtonField : public EffectField
{
Q_OBJECT
public:
/**
* @brief See Effect::Effect().
*/
ButtonField(EffectRow* parent, const QString& string);
/**
* @brief Set whether this pushbutton is checkable
*
* This function is mainly a wrapper around QPushButton::setCheckable().
*
* "Checkable" means the button can be toggled between a state of being "normal" and being "pressed". In checkable
* mode this field still cannot be used as a value in an Effect. Instead use BoolField (which uses a QCheckBox
* representation) for passing values to the Effect that can only be true or false.
*
* @param c
*
* TRUE if this button should be checkable or not.
*/
void SetCheckable(bool c);
/**
* @brief See EffectField::CreateWidget()
*/
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
public slots:
/**
* @brief A slot for when a widget's (created and connected from CreateWidget() ) checked state is changed
*
* @param c
*
* The current checked state (automatically filled by the QPushButton::toggled() signal)
*/
void SetChecked(bool c);
signals:
/**
* @brief A signal emitted whenever the field's internal checked state is changed
*
* Primarily used to set any connected widget's checked state to be consistent with the field's.
*/
void CheckedChanged(bool);
/**
* @brief A signal emitted whenever the checked state of a connected widget changes
*
* Any widgets associated with this field will emit this signal when their checked state changes.
*/
void Toggled(bool);
private:
/**
* @brief Internal button text string passed to widgets created by CreateWidget()
*/
bool checkable_;
/**
* @brief Internal checked value passed to and from widgets created by CreateWidget()
*/
bool checked_;
/**
* @brief Internal button text string passed to widgets created by CreateWidget()
*/
QString button_text_;
};
+13 -1
View File
@@ -20,12 +20,15 @@
#include "filefield.h"
#include <QDebug>
#include "ui/embeddedfilechooser.h"
FileField::FileField(EffectRow* parent, const QString &id) :
EffectField(parent, id, EFFECT_FIELD_FILE)
{
// Set default value to an empty string
SetValueAt(0, "");
}
QString FileField::GetFileAt(double timecode)
@@ -43,6 +46,15 @@ QWidget *FileField::CreateWidget(QWidget *existing)
return efc;
}
void FileField::UpdateWidgetValue(QWidget *widget, double timecode)
{
EmbeddedFileChooser* efc = static_cast<EmbeddedFileChooser*>(widget);
efc->blockSignals(true);
efc->setFilename(GetFileAt(timecode));
efc->blockSignals(false);
}
void FileField::UpdateFromWidget(const QString &s)
{
KeyframeDataChange* kdc = new KeyframeDataChange(this);
+1
View File
@@ -32,6 +32,7 @@ public:
QString GetFileAt(double timecode);
virtual QWidget* CreateWidget(QWidget *existing = nullptr) override;
virtual void UpdateWidgetValue(QWidget *widget, double timecode) override;
private slots:
void UpdateFromWidget(const QString &s);
};
+196
View File
@@ -0,0 +1,196 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "frei0reffect.h"
#ifndef NOFREI0R
#include <QMessageBox>
#include <QDir>
#include "timeline/clip.h"
typedef f0r_instance_t (*f0rConstructFunc)(unsigned int width, unsigned int height);
typedef int (*f0rInitFunc) ();
typedef void (*f0rDeinitFunc) ();
typedef void (*f0rUpdateFunc) (f0r_instance_t instance,
double time, const uint32_t* inframe, uint32_t* outframe);
typedef void (*f0rDestructFunc)(f0r_instance_t instance);
typedef void (*f0rGetPluginInfo)(f0r_plugin_info_t* info);
typedef void (*f0rSetParamValue) (f0r_instance_t instance,
f0r_param_t param, int param_index);
Frei0rEffect::Frei0rEffect(Clip* c, const EffectMeta *em) :
Effect(c, em),
open(false)
{
SetFlags(ImageFlag);
// Windows DLL loading routine
QString dll_fn = QDir(em->path).filePath(em->filename);
handle.setFileName(dll_fn);
if (!handle.load()) {
QString dll_error = handle.errorString();
QMessageBox::critical(nullptr, tr("Error loading Frei0r plugin"),
tr("Failed to load Frei0r plugin \"%1\": %2").arg(dll_fn, dll_error));
return;
}
f0rInitFunc init = reinterpret_cast<f0rInitFunc>(handle.resolve("f0r_init"));
init();
construct_module();
f0r_plugin_info_t info;
f0rGetPluginInfo info_func = reinterpret_cast<f0rGetPluginInfo>(handle.resolve("f0r_get_plugin_info"));
info_func(&info);
param_count = info.num_params;
get_param_info = reinterpret_cast<f0rGetParamInfo>(handle.resolve("f0r_get_param_info"));
for (int i=0;i<param_count;i++) {
f0r_param_info_t param_info;
get_param_info(&param_info, i);
if (param_info.type >= 0 && param_info.type <= F0R_PARAM_STRING) {
EffectRow* row = new EffectRow(this, param_info.name);
switch (param_info.type) {
case F0R_PARAM_BOOL:
new BoolField(row, QString::number(i));
break;
case F0R_PARAM_DOUBLE:
{
DoubleField* f = new DoubleField(row, QString::number(i));
f->SetMinimum(0);
f->SetMaximum(100);
}
break;
case F0R_PARAM_COLOR:
new ColorField(row, QString::number(i));
break;
case F0R_PARAM_POSITION:
{
DoubleField* fx = new DoubleField(row, QString("%1X").arg(QString::number(i)));
fx->SetMinimum(0);
fx->SetMaximum(100);
DoubleField* fy = new DoubleField(row, QString("%1Y").arg(QString::number(i)));
fy->SetMinimum(0);
fy->SetMaximum(100);
}
break;
case F0R_PARAM_STRING:
new StringField(row, QString::number(i), false);
break;
}
}
}
}
Frei0rEffect::~Frei0rEffect() {
if (handle.isLoaded()) {
f0rDeinitFunc deinit = reinterpret_cast<f0rDeinitFunc>(handle.resolve("f0r_deinit"));
deinit();
handle.unload();
}
}
void Frei0rEffect::process_image(double timecode, uint8_t *input, uint8_t *output, int) {
f0rUpdateFunc update_func = reinterpret_cast<f0rUpdateFunc>(handle.resolve("f0r_update"));
for (int i=0;i<param_count;i++) {
EffectRow* param_row = row(i);
f0r_param_info_t param_info;
get_param_info(&param_info, i);
f0rSetParamValue set_param = reinterpret_cast<f0rSetParamValue>(handle.resolve("f0r_set_param_value"));
switch (param_info.type) {
case F0R_PARAM_BOOL:
{
double b = param_row->Field(0)->GetValueAt(timecode).toBool();
set_param(instance, &b, i);
}
break;
case F0R_PARAM_DOUBLE:
{
double d = param_row->Field(0)->GetValueAt(timecode).toDouble()*0.01;
set_param(instance, &d, i);
}
break;
case F0R_PARAM_COLOR:
{
QColor qcolor = param_row->Field(0)->GetValueAt(timecode).value<QColor>();
f0r_param_color fcolor;
fcolor.r = float(qcolor.redF());
fcolor.g = float(qcolor.greenF());
fcolor.b = float(qcolor.blueF());
set_param(instance, &fcolor, i);
}
break;
case F0R_PARAM_POSITION:
{
f0r_param_position pos;
pos.x = param_row->Field(0)->GetValueAt(timecode).toDouble();
pos.y = param_row->Field(1)->GetValueAt(timecode).toDouble();
set_param(instance, &pos, i);
}
break;
case F0R_PARAM_STRING:
{
QByteArray bytes = param_row->Field(0)->GetValueAt(timecode).toString().toUtf8();
char* byte_data = bytes.data();
set_param(instance, &byte_data, i);
}
break;
}
}
update_func(instance, timecode, reinterpret_cast<uint32_t*>(input), reinterpret_cast<uint32_t*>(output));
}
void Frei0rEffect::refresh() {
destruct_module();
construct_module();
}
void Frei0rEffect::destruct_module() {
if (open) {
f0rDestructFunc destruct = reinterpret_cast<f0rDestructFunc>(handle.resolve("f0r_destruct"));
destruct(instance);
open = false;
}
}
void Frei0rEffect::construct_module() {
f0rConstructFunc construct = reinterpret_cast<f0rConstructFunc>(handle.resolve("f0r_construct"));
instance = construct(parent_clip->media_width(), parent_clip->media_height());
open = true;
}
#endif
+55
View File
@@ -0,0 +1,55 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef FREI0REFFECT_H
#define FREI0REFFECT_H
#ifndef NOFREI0R
#include <QLibrary>
#include <frei0r.h>
#include "effects/effect.h"
typedef void (*f0rGetParamInfo)(f0r_param_info_t * info,
int param_index );
class Frei0rEffect : public Effect {
Q_OBJECT
public:
Frei0rEffect(Clip* c, const EffectMeta* em);
~Frei0rEffect();
virtual void process_image(double timecode, uint8_t* input, uint8_t* output, int size);
virtual void refresh();
private:
QLibrary handle;
f0r_instance_t instance;
int param_count;
f0rGetParamInfo get_param_info;
void destruct_module();
void construct_module();
bool open;
};
#endif
#endif // FREI0REFFECT_H
+51 -79
View File
@@ -34,7 +34,14 @@
#include "global/global.h"
#include "global/debug.h"
#ifdef __linux__
// Load libraries for retrieving the native window handle. Used for VST plugins that have a separate window
// dedicated to controls.
#if defined(Q_OS_WIN)
#include <Windows.h>
#elif defined(Q_OS_MACOS)
#include <CoreFoundation/CoreFoundation.h>
class NSWindow;
#elif defined(Q_OS_LINUX)
#include <X11/X.h>
#endif
@@ -105,94 +112,53 @@ typedef int32_t (*processEventsFuncPtr)(VstEvents *events);
typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, int32_t sampleFrames);
void VSTHost::loadPlugin() {
QString dll_fn = file_field->GetFileAt(0);
if (dll_fn.isEmpty()) {
return;
}
#if defined(__APPLE__)
bundle = BundleLoad(dll_fn);
// Try to load the plugin
modulePtr.setFileName(dll_fn);
if (!modulePtr.load()) {
if (bundle == NULL) {
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), tr("Failed to create VST reference"));
// Show an error if the plugin fails to load
qCritical() << "Failed to load VST plugin" << dll_fn << "-" << modulePtr.errorString();
QMessageBox::critical(olive::MainWindow,
tr("Error loading VST plugin"),
tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, modulePtr.errorString()));
return;
}
vstPluginFuncPtr mainEntryPoint = NULL;
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("VSTPluginMain"));
// VST plugins previous to the 2.4 SDK used main_macho for the entry point name
if(mainEntryPoint == NULL) {
mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("main_macho"));
// Try to find the VST entry point (first using VSTPluginMain() )
vstPluginFuncPtr mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(modulePtr.resolve("VSTPluginMain"));
if (mainEntryPoint == nullptr) {
// If there's no VSTPluginMain(), the plugin may use main() instead
mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(modulePtr.resolve("main"));
}
if(mainEntryPoint == NULL) {
qCritical() << "Couldn't get a pointer to VST plugin's main()";
BundleClose(bundle);
if (mainEntryPoint == nullptr) {
QMessageBox::critical(olive::MainWindow,
tr("Error loading VST plugin"),
tr("Failed to locate entry point for dynamic library."));
modulePtr.unload();
return;
}
// Instantiate the plugin
plugin = mainEntryPoint(hostCallback);
if(plugin == NULL) {
qCritical() << "Plugin's main() returns null";
BundleClose(bundle);
return;
}
#else
modulePtr = LibLoad(dll_fn);
if(modulePtr == nullptr) {
QString dll_error;
#ifdef _WIN32
DWORD dll_err = GetLastError();
dll_error = QString::number(dll_err);
#elif defined(__linux__) || defined(__HAIKU__)
dll_error = dlerror();
#endif
qCritical() << "Failed to load VST plugin" << dll_fn << "-" << dll_error;
QString msg_err = tr("Failed to load VST plugin \"%1\": %2").arg(dll_fn, dll_error);
#ifdef _WIN32
if (dll_err == 193) {
#ifdef _WIN64
msg_err += "\n\n" + tr("NOTE: You can't load 32-bit VST plugins into a 64-bit build of Olive. Please find a 64-bit version of this plugin or switch to a 32-bit build of Olive.");
#elif _WIN32
msg_err += "\n\n" + tr("NOTE: You can't load 64-bit VST plugins into a 32-bit build of Olive. Please find a 32-bit version of this plugin or switch to a 64-bit build of Olive.");
#endif
}
#endif
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), msg_err);
return;
}
vstPluginFuncPtr mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(LibAddress(modulePtr, "VSTPluginMain"));
if (mainEntryPoint == nullptr) {
// if there's no VSTPluginMain(), fallback to main()
mainEntryPoint = reinterpret_cast<vstPluginFuncPtr>(LibAddress(modulePtr, "main"));
}
if (mainEntryPoint == nullptr) {
QMessageBox::critical(nullptr, tr("Error loading VST plugin"), tr("Failed to locate entry point for dynamic library."));
LibClose(modulePtr);
} else {
// Instantiate the plugin
plugin = mainEntryPoint(hostCallback);
}
#endif
}
void VSTHost::freePlugin() {
if (plugin != nullptr) {
stopPlugin();
#if defined(__APPLE__)
CFBundleUnloadExecutable(bundle);
CFRelease(bundle);
#else
LibClose(modulePtr);
#endif
data_cache.clear();
modulePtr.unload();
plugin = nullptr;
}
}
@@ -266,6 +232,11 @@ void VSTHost::CreateDialogIfNull()
}
}
void VSTHost::send_data_cache_to_plugin()
{
dispatcher(plugin, effSetChunk, 0, int32_t(data_cache.size()), static_cast<void*>(data_cache.data()), 0);
}
VSTHost::VSTHost(Clip* c, const EffectMeta *em) :
Effect(c, em),
plugin(nullptr),
@@ -282,7 +253,7 @@ VSTHost::VSTHost(Clip* c, const EffectMeta *em) :
EffectRow* file_row = new EffectRow(this, tr("Plugin"), true, false);
file_field = new FileField(file_row, "filename");
connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()));
connect(file_field, SIGNAL(Changed()), this, SLOT(change_plugin()), Qt::QueuedConnection);
EffectRow* interface_row = new EffectRow(this, tr("Interface"), false, false);
@@ -344,7 +315,7 @@ void VSTHost::custom_load(QXmlStreamReader &stream) {
stream.readNext();
data_cache = QByteArray::fromBase64(stream.text().toUtf8());
if (plugin != nullptr) {
dispatcher(plugin, effSetChunk, 0, int32_t(data_cache.size()), static_cast<void*>(data_cache.data()), 0);
send_data_cache_to_plugin();
}
}
}
@@ -366,11 +337,11 @@ void VSTHost::show_interface(bool show) {
dialog->setVisible(show);
if (show) {
#if defined(_WIN32)
#if defined(Q_OS_WIN)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<HWND>(dialog->windowHandle()->winId()), 0);
#elif defined(__APPLE__)
#elif defined(Q_OS_MACOS)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<NSWindow*>(dialog->windowHandle()->winId()), 0);
#elif defined(__linux__) || defined(__HAIKU__)
#elif defined(Q_OS_LINUX) || defined(__HAIKU__)
dispatcher(plugin, effEditOpen, 0, 0, reinterpret_cast<void*>(dialog->windowHandle()->winId()), 0);
#endif
} else {
@@ -392,17 +363,18 @@ void VSTHost::change_plugin() {
VSTRect* eRect = nullptr;
plugin->dispatcher(plugin, effEditGetRect, 0, 0, &eRect, 0);
if (!data_cache.isEmpty()) {
send_data_cache_to_plugin();
}
CreateDialogIfNull();
dialog->setFixedSize(eRect->right - eRect->left, eRect->bottom - eRect->top);
} else {
#ifdef __APPLE__
CFBundleUnloadExecutable(bundle);
CFRelease(bundle);
#else
LibClose(modulePtr);
#endif
modulePtr.unload();
plugin = nullptr;
}
}
show_interface_btn->SetEnabled(plugin != nullptr);
+5 -13
View File
@@ -21,19 +21,15 @@
#ifndef VSTHOSTWIN_H
#define VSTHOSTWIN_H
#ifndef NOVST
#include <QDialog>
#include <QLibrary>
#include "effects/effect.h"
#include "global/crossplatformlib.h"
#include "include/vestige.h"
// Plugin's dispatcher function
typedef intptr_t (*dispatcherFuncPtr)(AEffect *effect, int32_t opCode, int32_t index, int32_t value, void *ptr, float opt);
#include <QDialog>
class VSTHost : public Effect {
Q_OBJECT
public:
@@ -68,13 +64,9 @@ private:
QDialog* dialog;
QByteArray data_cache;
#if defined(__APPLE__)
CFBundleRef bundle;
#else
ModulePtr modulePtr;
#endif
void send_data_cache_to_plugin();
QLibrary modulePtr;
};
#endif
#endif // VSTHOSTWIN_H
+33 -3
View File
@@ -57,13 +57,14 @@ Config::Config()
hover_focus(false),
project_view_type(olive::PROJECT_VIEW_TREE),
set_name_with_marker(true),
show_project_toolbar(false),
show_project_toolbar(true),
previous_queue_size(3),
previous_queue_type(olive::FRAME_QUEUE_TYPE_FRAMES),
upcoming_queue_size(0.5),
upcoming_queue_type(olive::FRAME_QUEUE_TYPE_SECONDS),
loop(false),
seek_also_selects(false),
auto_seek_to_beginning(true),
effect_textbox_lines(3),
use_software_fallback(false),
center_timeline_timecodes(true),
@@ -73,7 +74,12 @@ Config::Config()
invert_timeline_scroll_axes(true),
enable_color_management(false),
style(olive::styling::kOliveDefaultDark),
use_native_menu_styling(true)
use_native_menu_styling(true),
default_sequence_width(1920),
default_sequence_height(1080),
default_sequence_framerate(29.97),
default_sequence_audio_frequency(48000),
default_sequence_audio_channel_layout(3)
{}
void Config::load(QString path) {
@@ -180,6 +186,9 @@ void Config::load(QString path) {
} else if (stream.name() == "SeekAlsoSelects") {
stream.readNext();
seek_also_selects = (stream.text() == "1");
} else if (stream.name() == "AutoSeekToBeginning") {
stream.readNext();
auto_seek_to_beginning = (stream.text() == "1");
} else if (stream.name() == "CSSPath") {
stream.readNext();
css_path = stream.text().toString();
@@ -222,6 +231,21 @@ void Config::load(QString path) {
} else if (stream.name() == "NativeMenuStyling") {
stream.readNext();
use_native_menu_styling = (stream.text() == "1");
} else if (stream.name() == "DefaultSequenceWidth") {
stream.readNext();
default_sequence_width = stream.text().toInt();
} else if (stream.name() == "DefaultSequenceHeight") {
stream.readNext();
default_sequence_height = stream.text().toInt();
} else if (stream.name() == "DefaultSequenceFrameRate") {
stream.readNext();
default_sequence_framerate = stream.text().toDouble();
} else if (stream.name() == "DefaultSequenceAudioFrequency") {
stream.readNext();
default_sequence_audio_frequency = stream.text().toInt();
} else if (stream.name() == "DefaultSequenceAudioLayout") {
stream.readNext();
default_sequence_audio_channel_layout = stream.text().toInt();
}
}
}
@@ -271,13 +295,14 @@ void Config::save(QString path) {
stream.writeTextElement("HoverFocus", QString::number(hover_focus));
stream.writeTextElement("ProjectViewType", QString::number(project_view_type));
stream.writeTextElement("SetNameWithMarker", QString::number(set_name_with_marker));
stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->toolbar_widget->isVisible()));
stream.writeTextElement("ShowProjectToolbar", QString::number(panel_project->IsToolbarVisible()));
stream.writeTextElement("PreviousFrameQueueSize", QString::number(previous_queue_size));
stream.writeTextElement("PreviousFrameQueueType", QString::number(previous_queue_type));
stream.writeTextElement("UpcomingFrameQueueSize", QString::number(upcoming_queue_size));
stream.writeTextElement("UpcomingFrameQueueType", QString::number(upcoming_queue_type));
stream.writeTextElement("Loop", QString::number(loop));
stream.writeTextElement("SeekAlsoSelects", QString::number(seek_also_selects));
stream.writeTextElement("AutoSeekToBeginning", QString::number(auto_seek_to_beginning));
stream.writeTextElement("CSSPath", css_path);
stream.writeTextElement("EffectTextboxLines", QString::number(effect_textbox_lines));
stream.writeTextElement("UseSoftwareFallback", QString::number(use_software_fallback));
@@ -292,6 +317,11 @@ void Config::save(QString path) {
stream.writeTextElement("OCIOConfigPath", ocio_config_path);
stream.writeTextElement("Style", QString::number(style));
stream.writeTextElement("NativeMenuStyling", QString::number(use_native_menu_styling));
stream.writeTextElement("DefaultSequenceWidth", QString::number(default_sequence_width));
stream.writeTextElement("DefaultSequenceHeight", QString::number(default_sequence_height));
stream.writeTextElement("DefaultSequenceFrameRate", QString::number(default_sequence_framerate));
stream.writeTextElement("DefaultSequenceAudioFrequency", QString::number(default_sequence_audio_frequency));
stream.writeTextElement("DefaultSequenceAudioLayout", QString::number(default_sequence_audio_channel_layout));
stream.writeEndElement(); // configuration
stream.writeEndDocument(); // doc
+36 -1
View File
@@ -120,7 +120,10 @@ namespace olive {
PROJECT_VIEW_TREE,
/** Display project media in icon browser */
PROJECT_VIEW_ICON
PROJECT_VIEW_ICON,
/** Display project media in list browser */
PROJECT_VIEW_LIST
};
/**
@@ -413,6 +416,13 @@ struct Config {
*/
bool seek_also_selects;
/**
* @brief Automatically seek to the beginning of a sequence if the user plays beyond the end of it
*
* TRUE if this behavior should be enabled.
*/
bool auto_seek_to_beginning;
/**
* @brief CSS Path
*
@@ -558,6 +568,31 @@ struct Config {
*/
bool use_native_menu_styling;
/**
* @brief Default Sequence video width
*/
int default_sequence_width;
/**
* @brief Default Sequence video height
*/
int default_sequence_height;
/**
* @brief Default Sequence video frame rate
*/
double default_sequence_framerate;
/**
* @brief Default Sequence audio frequency
*/
int default_sequence_audio_frequency;
/**
* @brief Default Sequence audio channel layout
*/
int default_sequence_audio_channel_layout;
/**
* @brief Load config from file
*
+85 -42
View File
@@ -39,6 +39,7 @@
#include "dialogs/speeddialog.h"
#include "dialogs/actionsearch.h"
#include "dialogs/loaddialog.h"
#include "dialogs/autocutsilencedialog.h"
#include "project/loadthread.h"
#include "timeline/sequence.h"
#include "ui/mediaiconservice.h"
@@ -91,7 +92,12 @@ void OliveGlobal::check_for_autorecovery_file() {
// detect auto-recovery file
autorecovery_filename = data_dir + "/autorecovery.ove";
if (QFile::exists(autorecovery_filename)) {
if (QMessageBox::question(nullptr, tr("Auto-recovery"), tr("Olive didn't close properly and an autorecovery file was detected. Would you like to open it?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) {
if (QMessageBox::question(nullptr,
tr("Auto-recovery"),
tr("Olive didn't close properly and an autorecovery file "
"was detected. Would you like to open it?"),
QMessageBox::Yes,
QMessageBox::No) == QMessageBox::Yes) {
enable_load_project_on_init = false;
OpenProjectWorker(autorecovery_filename, true);
}
@@ -166,18 +172,19 @@ void OliveGlobal::SetNativeStyling(QWidget *w)
#endif
}
void OliveGlobal::LoadProject(const QString &fn, bool autorecovery, bool clear)
void OliveGlobal::LoadProject(const QString &fn, bool autorecovery)
{
// Normally, the user will be closing the previous project to load a new one, but just in case the user
// is importing a new project
// QSortFilterProxyModels are not thread-safe, and as we'll be loading in another thread, leaving it connected
// can cause glitches in its presentation. Therefore for the duration of the loading process, we disconnect it,
// and reconnect it later once the loading is complete.
if (clear) {
new_project();
}
panel_project->DisconnectFilterToModel();
LoadDialog ld(olive::MainWindow);
LoadThread* lt = new LoadThread(fn, autorecovery, clear);
ld.open();
LoadThread* lt = new LoadThread(fn, autorecovery);
connect(&ld, SIGNAL(cancel()), lt, SLOT(cancel()));
connect(lt, SIGNAL(success()), &ld, SLOT(accept()));
connect(lt, SIGNAL(error()), &ld, SLOT(reject()));
@@ -185,40 +192,46 @@ void OliveGlobal::LoadProject(const QString &fn, bool autorecovery, bool clear)
connect(lt, SIGNAL(report_progress(int)), &ld, SLOT(setValue(int)));
lt->start();
ld.exec();
panel_project->ConnectFilterToModel();
}
void OliveGlobal::ClearProject()
{
// clear graph editor
panel_graph_editor->set_row(nullptr);
// clear effects panel
panel_effect_controls->Clear(true);
// clear existing project
olive::Global->set_sequence(nullptr);
panel_footage_viewer->set_media(nullptr);
// clear project contents (footage, sequences, etc.)
panel_project->clear();
// clear undo stack
olive::UndoStack.clear();
// empty current project filename
update_project_filename("");
// full update of all panels
update_ui(false);
// set to unmodified
olive::Global->set_modified(false);
}
void OliveGlobal::ImportProject(const QString &fn)
{
LoadProject(fn, false, false);
LoadProject(fn, false);
set_modified(true);
}
void OliveGlobal::new_project() {
if (can_close_project()) {
// clear graph editor
panel_graph_editor->set_row(nullptr);
// clear effects panel
panel_effect_controls->Clear(true);
// clear existing project
olive::Global->set_sequence(nullptr);
panel_footage_viewer->set_media(nullptr);
// clear project contents (footage, sequences, etc.)
panel_project->clear();
// clear undo stack
olive::UndoStack.clear();
// empty current project filename
update_project_filename("");
// full update of all panels
update_ui(false);
// set to unmodified
olive::Global->set_modified(false);
ClearProject();
}
}
@@ -289,12 +302,7 @@ bool OliveGlobal::can_close_project() {
}
void OliveGlobal::open_export_dialog() {
if (olive::ActiveSequence == nullptr) {
QMessageBox::information(olive::MainWindow,
tr("No active sequence"),
tr("Please open the sequence you wish to export."),
QMessageBox::Ok);
} else {
if (CheckForActiveSequence()) {
ExportDialog e(olive::MainWindow);
e.exec();
}
@@ -358,12 +366,29 @@ void OliveGlobal::set_sequence(SequencePtr s)
panel_timeline->setFocus();
}
void OliveGlobal::OpenProjectWorker(const QString& fn, bool autorecovery) {
void OliveGlobal::OpenProjectWorker(QString fn, bool autorecovery) {
ClearProject();
update_project_filename(fn);
LoadProject(fn, autorecovery, true);
LoadProject(fn, autorecovery);
olive::UndoStack.clear();
}
bool OliveGlobal::CheckForActiveSequence(bool show_msg)
{
if (olive::ActiveSequence == nullptr) {
if (show_msg) {
QMessageBox::information(olive::MainWindow,
tr("No active sequence"),
tr("Please open the sequence to perform this action."),
QMessageBox::Ok);
}
return false;
}
return true;
}
void OliveGlobal::undo() {
// workaround to prevent crash (and also users should never need to do this)
if (!panel_timeline->importing) {
@@ -413,6 +438,24 @@ void OliveGlobal::open_speed_dialog() {
}
}
void OliveGlobal::open_autocut_silence_dialog() {
if (CheckForActiveSequence()) {
QVector<Clip*> selected_clips = olive::ActiveSequence->SelectedClips();
if (selected_clips.isEmpty()) {
QMessageBox::critical(olive::MainWindow,
tr("No clips selected"),
tr("Select the clips you wish to auto-cut"),
QMessageBox::Ok);
} else {
AutoCutSilenceDialog s(olive::MainWindow, selected_clips);
s.exec();
}
}
}
void OliveGlobal::clear_undo_stack() {
olive::UndoStack.clear();
}
+27 -2
View File
@@ -275,6 +275,11 @@ public slots:
*/
void open_speed_dialog();
/**
* @brief Open the auto-cut silence dialog.
*/
void open_autocut_silence_dialog();
/**
* @brief Open the Action Search overlay.
*/
@@ -338,7 +343,19 @@ private:
* beside the original project file so that it does not overwrite the original and so that the user is not working
* on the autorecovery project in Olive's application data directory.
*/
void OpenProjectWorker(const QString& fn, bool autorecovery);
void OpenProjectWorker(QString fn, bool autorecovery);
/**
* @brief Returns whether a Sequence is currently active or not, and optionally displays a messagebox if not
*
* Checks whether a Sequence is active and can display a messagebox if not to inform users to make one active in
* order to perform said action.
*
* @return
*
* TRUE if there is an active Sequence, FALSE if not.
*/
bool CheckForActiveSequence(bool show_msg = true);
/**
* @brief Create a LoadDialog and start a LoadThread to load data from a project
@@ -366,7 +383,15 @@ private:
* TRUE if the current project should be closed before opening, FALSE if the project should be imported into the
* currently open one.
*/
void LoadProject(const QString& fn, bool autorecovery, bool clear);
void LoadProject(const QString& fn, bool autorecovery);
/**
* @brief Indiscriminately clear the project without prompting the user
*
* Will clear the entire project without prompting to save. This is dangerous, use new_project() instead for
* anything initiated by the user.
*/
void ClearProject();
/**
* @brief File filter used for any file dialogs relating to Olive project files.
+21 -2
View File
@@ -74,9 +74,28 @@ double cubic_t_from_x(double x_target, double a, double b, double c, double d) {
}
double amplitude_to_db(double amplitude) {
return (20.0*(qLn(amplitude)/qLn(10.0)));
return (20.0*(qLn(amplitude)/qLn(10.0)));
}
double db_to_amplitude(double db) {
return qPow(M_E, (db*qLn(10.0))/20.0);
return qPow(M_E, (db*qLn(10.0))/20.0);
}
QRect fit_size_into_rect(const QRect &r, int width, int height)
{
// Get aspect ratio of object we're fitting
double inner_ar = double(width) / double(height);
// Get aspect ratio of rectangle
double rect_ar = double(r.width()) / double(r.height());
if (rect_ar > inner_ar) {
// The rect is wider than the object, so we'll be limiting by height and scaling by width
int new_width = qRound(r.height() * inner_ar);
return QRect(r.x() + (r.width() / 2 - new_width / 2), r.y(), new_width, r.height());
} else {
// The rect is taller than the object, so we'll be limiting by width and scaling by height
int new_height = qRound(r.width() / inner_ar);
return QRect(r.x(), r.y() + (r.height() / 2 - new_height / 2), r.width(), new_height);
}
}
+4
View File
@@ -21,6 +21,8 @@
#ifndef MATH_H
#define MATH_H
#include <QRect>
int lerp(int a, int b, double t);
float float_lerp(float a, float b, float t);
double double_lerp(double a, double b, double t);
@@ -30,6 +32,8 @@ double cubic_from_t(double a, double b, double c, double d, double t);
double cubic_t_from_x(double x_target, double a, double b, double c, double d);
double solveCubicBezier(double p0, double p1, double p2, double p3, double x);
QRect fit_size_into_rect(const QRect& r, int width, int height);
// decibel conversion functions
double amplitude_to_db(double amplitude);
double db_to_amplitude(double db);
+1
View File
@@ -52,5 +52,6 @@
<file>align-right.svg</file>
<file>justify-center.svg</file>
<file>bold.svg</file>
<file>listview.svg</file>
</qresource>
</RCC>
+829
View File
@@ -0,0 +1,829 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="iconview.svg"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:version="0.32"
id="svg2"
height="64"
width="64"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1"
viewBox="0 0 64 64"
inkscape:export-filename="/Users/pablo/olive_pablo/icons/iconview.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs14">
<linearGradient
id="linearGradient3253">
<stop
id="stop3255"
offset="0"
style="stop-color:#89d5f8;stop-opacity:1;" />
<stop
id="stop3257"
offset="1"
style="stop-color:#00899e;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient11979">
<stop
id="stop11981"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop11983"
offset="1"
style="stop-color:#729fcf;stop-opacity:1" />
</linearGradient>
<inkscape:perspective
id="perspective5736"
inkscape:persp3d-origin="24 : 16 : 1"
inkscape:vp_z="48 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 24 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient2846">
<stop
style="stop-color:#8a8a8a;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop2848" />
<stop
style="stop-color:#484848;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop2850" />
</linearGradient>
<linearGradient
id="linearGradient2366">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop2368" />
<stop
id="stop2374"
offset="0.50000000"
style="stop-color:#ffffff;stop-opacity:0.21904762;" />
<stop
style="stop-color:#ffffff;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop2370" />
</linearGradient>
<linearGradient
id="linearGradient4467">
<stop
id="stop4469"
offset="0"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
id="stop4471"
offset="1.0000000"
style="stop-color:#ffffff;stop-opacity:0.24761905;" />
</linearGradient>
<linearGradient
id="linearGradient4454">
<stop
id="stop4456"
offset="0.0000000"
style="stop-color:#729fcf;stop-opacity:0.20784314;" />
<stop
id="stop4458"
offset="1.0000000"
style="stop-color:#729fcf;stop-opacity:0.67619050;" />
</linearGradient>
<linearGradient
id="linearGradient4440">
<stop
id="stop4442"
offset="0"
style="stop-color:#7d7d7d;stop-opacity:1;" />
<stop
style="stop-color:#b1b1b1;stop-opacity:1.0000000;"
offset="0.50000000"
id="stop4448" />
<stop
id="stop4444"
offset="1.0000000"
style="stop-color:#686868;stop-opacity:1.0000000;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="radialGradient2097"
cx="23.070683"
cy="35.127438"
fx="23.070683"
fy="35.127438"
r="10.31934"
gradientTransform="matrix(0.914812,0.01265023,-0.00821502,0.213562,2.253914,27.18889)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="linearGradient7922"
x1="16.874998"
y1="22.851799"
x2="27.900846"
y2="34.976799"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2871"
id="linearGradient7186"
gradientUnits="userSpaceOnUse"
x1="46.834816"
y1="45.264122"
x2="45.380436"
y2="50.939667" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2402"
id="linearGradient7184"
gradientUnits="userSpaceOnUse"
x1="18.935766"
y1="23.667896"
x2="53.588623"
y2="26.649363" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2380"
id="linearGradient7180"
gradientUnits="userSpaceOnUse"
x1="62.513836"
y1="36.061237"
x2="15.984863"
y2="20.60858" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="linearGradient7189"
gradientUnits="userSpaceOnUse"
x1="13.435029"
y1="13.604306"
x2="22.374878"
y2="23.554308"
gradientTransform="rotate(180,23.96967,25.01237)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="linearGradient7185"
x1="13.435029"
y1="13.604306"
x2="22.374878"
y2="23.554308"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1322"
id="linearGradient4975"
x1="34.892849"
y1="36.422989"
x2="45.918697"
y2="48.547989"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-18.01785,-13.57119)" />
<linearGradient
id="linearGradient1322">
<stop
id="stop1324"
offset="0.0000000"
style="stop-color:#729fcf" />
<stop
id="stop1326"
offset="1.0000000"
style="stop-color:#5187d6;stop-opacity:1.0000000;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="linearGradient1491"
gradientUnits="userSpaceOnUse"
x1="5.9649177"
y1="26.048164"
x2="52.854095"
y2="26.048164" />
<linearGradient
id="linearGradient2402">
<stop
style="stop-color:#729fcf;stop-opacity:1;"
offset="0"
id="stop2404" />
<stop
style="stop-color:#528ac5;stop-opacity:1;"
offset="1"
id="stop2406" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient2871">
<stop
style="stop-color:#3465a4;stop-opacity:1;"
offset="0"
id="stop2873" />
<stop
style="stop-color:#3465a4;stop-opacity:1"
offset="1"
id="stop2875" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2690"
id="linearGradient2696"
x1="32.647972"
y1="30.748846"
x2="37.124462"
y2="24.842253"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-48.77039,-5.765705)" />
<linearGradient
inkscape:collect="always"
id="linearGradient2690">
<stop
style="stop-color:#c4d7eb;stop-opacity:1;"
offset="0"
id="stop2692" />
<stop
style="stop-color:#c4d7eb;stop-opacity:0;"
offset="1"
id="stop2694" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2682"
id="linearGradient2688"
x1="36.713837"
y1="31.455952"
x2="37.124462"
y2="24.842253"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-48.77039,-5.765705)" />
<linearGradient
id="linearGradient2682">
<stop
style="stop-color:#3977c3;stop-opacity:1;"
offset="0"
id="stop2684" />
<stop
style="stop-color:#89aedc;stop-opacity:0;"
offset="1"
id="stop2686" />
</linearGradient>
<linearGradient
id="linearGradient2380">
<stop
style="stop-color:#b9cfe7;stop-opacity:1"
offset="0"
id="stop2382" />
<stop
style="stop-color:#729fcf;stop-opacity:1"
offset="1"
id="stop2384" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2831"
id="linearGradient1486"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(-48.30498,-6.043298)"
x1="13.478554"
y1="10.612206"
x2="15.419417"
y2="19.115122" />
<linearGradient
id="linearGradient2831">
<stop
style="stop-color:#3465a4;stop-opacity:1;"
offset="0"
id="stop2833" />
<stop
id="stop2855"
offset="0.33333334"
style="stop-color:#5b86be;stop-opacity:1;" />
<stop
style="stop-color:#83a8d8;stop-opacity:0;"
offset="1"
id="stop2835" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2871"
id="linearGradient1488"
gradientUnits="userSpaceOnUse"
gradientTransform="rotate(180,-0.62124,20.04085)"
x1="37.128052"
y1="29.729605"
x2="37.065414"
y2="26.194071" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="radialGradient1503"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
cx="24.837126"
cy="36.421127"
fx="24.837126"
fy="36.421127"
r="15.644737" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2833" />
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient2571"
gradientUnits="userSpaceOnUse"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3593"
id="radialGradient3352"
gradientUnits="userSpaceOnUse"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<linearGradient
id="linearGradient3593">
<stop
style="stop-color:#c8e0f9;stop-opacity:1;"
offset="0"
id="stop3595" />
<stop
style="stop-color:#637dca;stop-opacity:1;"
offset="1"
id="stop3597" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3593"
id="radialGradient3354"
gradientUnits="userSpaceOnUse"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428"
gradientTransform="translate(-0.1767767,-2.6516504)" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective3372" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3369"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
cx="342.58258"
cy="27.256668"
fx="342.58258"
fy="27.256668"
r="19.571428" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3593"
id="radialGradient3372"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3593"
id="radialGradient3375"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3380"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
cx="320.44025"
cy="113.23357"
fx="320.44025"
fy="113.23357"
r="19.571428" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="linearGradient3914"
x1="6.94525"
y1="36.838673"
x2="48.691113"
y2="36.838673"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="linearGradient3792"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-4.8699606,-2.3863162)"
x1="6.8300767"
y1="34.146042"
x2="48.691113"
y2="36.838673" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3812"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
cx="330.63791"
cy="39.962704"
fx="330.63791"
fy="39.962704"
r="19.571428" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3593"
id="radialGradient3814"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
cx="345.28433"
cy="15.560534"
fx="345.28433"
fy="15.560534"
r="19.571428" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3816"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
cx="320.44025"
cy="113.23357"
fx="320.44025"
fy="113.23357"
r="19.571428" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2734" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="radialGradient3850"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
cx="225.26402"
cy="672.79736"
fx="225.26402"
fy="672.79736"
r="34.345188" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient4467"
id="radialGradient3850-4"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
cx="225.26402"
cy="672.79736"
fx="225.26402"
fy="672.79736"
r="34.345188" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 24 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="48 : 24 : 1"
inkscape:persp3d-origin="24 : 16 : 1"
id="perspective5736-4" />
<radialGradient
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.914812,0.01265023,-0.00821502,0.213562,2.253914,27.18889)"
r="10.31934"
fy="35.127438"
fx="23.070683"
cy="35.127438"
cx="23.070683"
id="radialGradient2097-2"
xlink:href="#linearGradient4467"
inkscape:collect="always" />
<radialGradient
r="15.644737"
fy="36.421127"
fx="24.837126"
cy="36.421127"
cx="24.837126"
gradientTransform="matrix(1,0,0,0.536723,0,16.87306)"
gradientUnits="userSpaceOnUse"
id="radialGradient1503-3"
xlink:href="#linearGradient4467"
inkscape:collect="always" />
<inkscape:perspective
id="perspective2833-5"
inkscape:persp3d-origin="32 : 21.333333 : 1"
inkscape:vp_z="64 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 32 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-215.02413,-170.90186)"
r="19.571428"
fy="27.256668"
fx="342.58258"
cy="27.256668"
cx="342.58258"
gradientUnits="userSpaceOnUse"
id="radialGradient2571-4"
xlink:href="#linearGradient3864"
inkscape:collect="always" />
<radialGradient
gradientTransform="translate(-0.1767767,-2.6516504)"
r="19.571428"
fy="15.560534"
fx="345.28433"
cy="15.560534"
cx="345.28433"
gradientUnits="userSpaceOnUse"
id="radialGradient3352-1"
xlink:href="#linearGradient3593"
inkscape:collect="always" />
<radialGradient
gradientTransform="translate(-0.1767767,-2.6516504)"
r="19.571428"
fy="39.962704"
fx="330.63791"
cy="39.962704"
cx="330.63791"
gradientUnits="userSpaceOnUse"
id="radialGradient3354-1"
xlink:href="#linearGradient3593"
inkscape:collect="always" />
<inkscape:perspective
id="perspective3372-5"
inkscape:persp3d-origin="32 : 21.333333 : 1"
inkscape:vp_z="64 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 32 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
r="19.571428"
fy="27.256668"
fx="342.58258"
cy="27.256668"
cx="342.58258"
gradientTransform="matrix(1.6258409,0.5434973,-8.8819886e-2,0.2656996,-461.81066,-173.06271)"
gradientUnits="userSpaceOnUse"
id="radialGradient3369-3"
xlink:href="#linearGradient3864"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="15.560534"
fx="345.28433"
cy="15.560534"
cx="345.28433"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
gradientUnits="userSpaceOnUse"
id="radialGradient3372-9"
xlink:href="#linearGradient3593"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="39.962704"
fx="330.63791"
cy="39.962704"
cx="330.63791"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
gradientUnits="userSpaceOnUse"
id="radialGradient3375-2"
xlink:href="#linearGradient3593"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="113.23357"
fx="320.44025"
cy="113.23357"
cx="320.44025"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
gradientUnits="userSpaceOnUse"
id="radialGradient3380-0"
xlink:href="#linearGradient3864"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="39.962704"
fx="330.63791"
cy="39.962704"
cx="330.63791"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-287.81791,-28.143054)"
gradientUnits="userSpaceOnUse"
id="radialGradient3812-9"
xlink:href="#linearGradient3864"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="15.560534"
fx="345.28433"
cy="15.560534"
cx="345.28433"
gradientTransform="matrix(1.0012324,0,0,0.9421773,-327.50313,-4.3316646)"
gradientUnits="userSpaceOnUse"
id="radialGradient3814-9"
xlink:href="#linearGradient3593"
inkscape:collect="always" />
<radialGradient
r="19.571428"
fy="113.23357"
fx="320.44025"
cy="113.23357"
cx="320.44025"
gradientTransform="matrix(0.9829174,1.3240854,-1.2330051,0.8105158,-131.04134,-483.74563)"
gradientUnits="userSpaceOnUse"
id="radialGradient3816-2"
xlink:href="#linearGradient3864"
inkscape:collect="always" />
<inkscape:perspective
id="perspective2734-9"
inkscape:persp3d-origin="32 : 21.333333 : 1"
inkscape:vp_z="64 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 32 : 1"
sodipodi:type="inkscape:persp3d" />
<radialGradient
r="34.345188"
fy="672.79736"
fx="225.26402"
cy="672.79736"
cx="225.26402"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
gradientUnits="userSpaceOnUse"
id="radialGradient3850-8"
xlink:href="#linearGradient4467"
inkscape:collect="always" />
<radialGradient
r="34.345188"
fy="672.79736"
fx="225.26402"
cy="672.79736"
cx="225.26402"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
gradientUnits="userSpaceOnUse"
id="radialGradient3850-4-2"
xlink:href="#linearGradient4467"
inkscape:collect="always" />
</defs>
<sodipodi:namedview
stroke="#3465a4"
inkscape:window-y="23"
inkscape:window-x="47"
inkscape:window-height="927"
inkscape:window-width="1633"
inkscape:showpageshadow="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
showgrid="true"
inkscape:current-layer="layer1"
inkscape:cy="30.888147"
inkscape:cx="33.014148"
inkscape:zoom="10.726351"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1"
bordercolor="#ffffff"
pagecolor="#000000"
id="base"
fill="#729fcf"
inkscape:window-maximized="0"
borderlayer="true"
inkscape:snap-global="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="false"
inkscape:bbox-nodes="true"
inkscape:snap-nodes="true"
inkscape:snap-smooth-nodes="true">
<inkscape:grid
type="xygrid"
id="grid0001"
color="#ffffff"
opacity="0.15686275"
empcolor="#ffffff"
empopacity="0.31372549"
dotted="false"
empspacing="8"
spacingx="1"
spacingy="1"
visible="true"
snapvisiblegridlinesonly="true" />
<inkscape:grid
type="xygrid"
id="grid0002"
spacingx="32"
spacingy="32"
empspacing="2"
empcolor="#ffffff"
empopacity="0.47058824"
color="#ffffff"
opacity="0.31372549"
visible="true"
enabled="true" />
</sodipodi:namedview>
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:creator>
<cc:Agent>
<dc:title>Martin Ruskov</dc:title>
</cc:Agent>
</dc:creator>
<dc:source>http://commons.wikimedia.org/wiki/Tango_icon</dc:source>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by-sa/2.0/" />
<dc:title></dc:title>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by-sa/2.0/">
<cc:permits
rdf:resource="http://web.resource.org/cc/Reproduction" />
<cc:permits
rdf:resource="http://web.resource.org/cc/Distribution" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Notice" />
<cc:requires
rdf:resource="http://web.resource.org/cc/Attribution" />
<cc:permits
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
<cc:requires
rdf:resource="http://web.resource.org/cc/ShareAlike" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
inkscape:label="Layer 1"
id="layer1"
transform="translate(0,16)">
<rect
style="opacity:1;fill:#ffffff;fill-opacity:0.86274511;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect14587"
width="56"
height="25"
x="4"
y="-12"
rx="4" />
<rect
style="opacity:1;fill:#ffffff;fill-opacity:0.8627451;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect14591"
width="56"
height="25"
x="4"
y="19"
rx="4"
ry="4" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

+10 -8
View File
@@ -133,7 +133,6 @@ SOURCES += \
ui/viewerwindow.cpp \
project/projectfilter.cpp \
effects/effectloaders.cpp \
global/crossplatformlib.cpp \
effects/internal/vsthost.cpp \
ui/flowlayout.cpp \
dialogs/proxydialog.cpp \
@@ -174,7 +173,10 @@ SOURCES += \
ui/blur.cpp \
ui/menu.cpp \
rendering/qopenglshaderprogramptr.cpp \
rendering/bitdepths.cpp
rendering/bitdepths.cpp \
timeline/mediaimportdata.cpp \
dialogs/autocutsilencedialog.cpp \
ui/columnedgridlayout.cpp
HEADERS += \
ui/mainwindow.h \
@@ -259,7 +261,6 @@ HEADERS += \
ui/viewerwindow.h \
project/projectfilter.h \
effects/effectloaders.h \
global/crossplatformlib.h \
effects/internal/vsthost.h \
ui/flowlayout.h \
dialogs/proxydialog.h \
@@ -302,7 +303,10 @@ HEADERS += \
ui/blur.h \
ui/menu.h \
rendering/qopenglshaderprogramptr.h \
rendering/bitdepths.h
rendering/bitdepths.h \
timeline/mediaimportdata.h \
dialogs/autocutsilencedialog.h \
ui/columnedgridlayout.h
FORMS +=
@@ -316,7 +320,8 @@ TRANSLATIONS += \
ts/olive_ru.ts \
ts/olive_uk.ts \
ts/olive_bs.ts \
ts/olive_sr.ts
ts/olive_sr.ts \
ts/olive_id.ts
win32 {
RC_FILE = packaging/windows/resources.rc
@@ -342,9 +347,6 @@ unix:!mac {
LIBS += -lOpenColorIO
}
}
unix:!mac:!haiku {
LIBS += -ldl
}
RESOURCES += \
icons/icons.qrc \
+1
View File
@@ -86,6 +86,7 @@ void alloc_panels(QWidget* parent) {
panel_sequence_viewer->setObjectName("seq_viewer");
panel_footage_viewer = new Viewer(parent);
panel_footage_viewer->setObjectName("footage_viewer");
panel_footage_viewer->show_videoaudio_buttons(true);
panel_project = new Project(parent);
panel_project->setObjectName("proj_root");
panel_effect_controls = new EffectControls(parent);
+87 -48
View File
@@ -65,20 +65,15 @@ extern "C" {
#include "global/debug.h"
#include "ui/menu.h"
// TODO make these configurable
const int kDefaultSequenceWidth = 1920;
const int kDefaultSequenceHeight = 1080;
const double kDefaultSequenceFrameRate = 29.97;
const int kDefaultSequenceFrequency = 48000;
const int kDefaultSequenceChannelLayout = 3;
#define MAXIMUM_RECENT_PROJECTS 10 // FIXME: should be configurable
QString autorecovery_filename;
QStringList recent_projects;
Project::Project(QWidget *parent) :
Panel(parent)
Panel(parent),
sorter(this),
sources_common(this, sorter)
{
QWidget* dockWidgetContents = new QWidget(this);
@@ -88,10 +83,7 @@ Project::Project(QWidget *parent) :
setWidget(dockWidgetContents);
sources_common = new SourcesCommon(this);
sorter = new ProjectFilter(this);
sorter->setSourceModel(&olive::project_model);
ConnectFilterToModel();
// optional toolbar
toolbar_widget = new QWidget();
@@ -104,57 +96,63 @@ Project::Project(QWidget *parent) :
QPushButton* toolbar_new = new QPushButton();
toolbar_new->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/add-button.svg")));
toolbar_new->setToolTip("New");
toolbar_new->setToolTip(tr("New"));
connect(toolbar_new, SIGNAL(clicked(bool)), this, SLOT(make_new_menu()));
toolbar->addWidget(toolbar_new);
QPushButton* toolbar_open = new QPushButton();
toolbar_open->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/open.svg")));
toolbar_open->setToolTip("Open Project");
toolbar_open->setToolTip(tr("Open Project"));
connect(toolbar_open, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(OpenProject()));
toolbar->addWidget(toolbar_open);
QPushButton* toolbar_save = new QPushButton();
toolbar_save->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/save.svg")));
toolbar_save->setToolTip("Save Project");
toolbar_save->setToolTip(tr("Save Project"));
connect(toolbar_save, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(save_project()));
toolbar->addWidget(toolbar_save);
QPushButton* toolbar_undo = new QPushButton();
toolbar_undo->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/undo.svg")));
toolbar_undo->setToolTip("Undo");
toolbar_undo->setToolTip(tr("Undo"));
connect(toolbar_undo, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(undo()));
toolbar->addWidget(toolbar_undo);
QPushButton* toolbar_redo = new QPushButton();
toolbar_redo->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/redo.svg")));
toolbar_redo->setToolTip("Redo");
toolbar_redo->setToolTip(tr("Redo"));
connect(toolbar_redo, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(redo()));
toolbar->addWidget(toolbar_redo);
toolbar_search = new QLineEdit();
toolbar_search->setClearButtonEnabled(true);
connect(toolbar_search, SIGNAL(textChanged(QString)), sorter, SLOT(update_search_filter(const QString&)));
connect(toolbar_search, SIGNAL(textChanged(QString)), &sorter, SLOT(update_search_filter(const QString&)));
toolbar->addWidget(toolbar_search);
QPushButton* toolbar_tree_view = new QPushButton();
toolbar_tree_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/treeview.svg")));
toolbar_tree_view->setToolTip("Tree View");
toolbar_tree_view->setToolTip(tr("Tree View"));
connect(toolbar_tree_view, SIGNAL(clicked(bool)), this, SLOT(set_tree_view()));
toolbar->addWidget(toolbar_tree_view);
QPushButton* toolbar_icon_view = new QPushButton();
toolbar_icon_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/iconview.svg")));
toolbar_icon_view->setToolTip("Icon View");
toolbar_icon_view->setToolTip(tr("Icon View"));
connect(toolbar_icon_view, SIGNAL(clicked(bool)), this, SLOT(set_icon_view()));
toolbar->addWidget(toolbar_icon_view);
QPushButton* toolbar_list_view = new QPushButton();
toolbar_list_view->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/listview.svg")));
toolbar_list_view->setToolTip(tr("List View"));
connect(toolbar_list_view, SIGNAL(clicked(bool)), this, SLOT(set_list_view()));
toolbar->addWidget(toolbar_list_view);
verticalLayout->addWidget(toolbar_widget);
// tree view
tree_view = new SourceTable();
tree_view = new SourceTable(sources_common);
tree_view->project_parent = this;
tree_view->setModel(sorter);
tree_view->setModel(&sorter);
verticalLayout->addWidget(tree_view);
// Set the first column width
@@ -181,23 +179,23 @@ Project::Project(QWidget *parent) :
icon_view_controls->addStretch();
QSlider* icon_size_slider = new QSlider(Qt::Horizontal);
icon_size_slider = new QSlider(Qt::Horizontal);
icon_size_slider->setMinimum(16);
icon_size_slider->setMaximum(120);
icon_size_slider->setMaximum(256);
icon_view_controls->addWidget(icon_size_slider);
connect(icon_size_slider, SIGNAL(valueChanged(int)), this, SLOT(set_icon_view_size(int)));
icon_view_container_layout->addLayout(icon_view_controls);
icon_view = new SourceIconView();
icon_view = new SourceIconView(sources_common);
icon_view->project_parent = this;
icon_view->setModel(sorter);
icon_view->setIconSize(QSize(100, 100));
icon_view->setModel(&sorter);
icon_view->setGridSize(QSize(100, 100));
icon_view->setViewMode(QListView::IconMode);
icon_view->setUniformItemSizes(true);
icon_view_container_layout->addWidget(icon_view);
icon_size_slider->setValue(icon_view->iconSize().height());
icon_size_slider->setValue(icon_view->gridSize().height());
verticalLayout->addWidget(icon_view_container);
@@ -212,8 +210,14 @@ Project::Project(QWidget *parent) :
Retranslate();
}
Project::~Project() {
delete sorter;
void Project::ConnectFilterToModel()
{
sorter.setSourceModel(&olive::project_model);
}
void Project::DisconnectFilterToModel()
{
sorter.setSourceModel(nullptr);
}
void Project::Retranslate() {
@@ -245,22 +249,22 @@ QString Project::get_next_sequence_name(QString start) {
return name;
}
SequencePtr create_sequence_from_media(QVector<Media*>& media_list) {
SequencePtr create_sequence_from_media(QVector<olive::timeline::MediaImportData>& media_list) {
SequencePtr s(new Sequence());
s->name = panel_project->get_next_sequence_name();
// shitty hardcoded default values
s->width = kDefaultSequenceWidth;
s->height = kDefaultSequenceHeight;
s->frame_rate = kDefaultSequenceFrameRate;
s->audio_frequency = kDefaultSequenceFrequency;
s->audio_layout = kDefaultSequenceChannelLayout;
// Retrieve default Sequence settings from Config
s->width = olive::CurrentConfig.default_sequence_width;
s->height = olive::CurrentConfig.default_sequence_height;
s->frame_rate = olive::CurrentConfig.default_sequence_framerate;
s->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
s->audio_layout = olive::CurrentConfig.default_sequence_audio_channel_layout;
bool got_video_values = false;
bool got_audio_values = false;
for (int i=0;i<media_list.size();i++) {
Media* media = media_list.at(i);
Media* media = media_list.at(i).media();
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
{
@@ -418,10 +422,10 @@ void Project::new_folder() {
QModelIndex index = olive::project_model.create_index(m->row(), 0, m.get());
switch (olive::CurrentConfig.project_view_type) {
case olive::PROJECT_VIEW_TREE:
tree_view->edit(sorter->mapFromSource(index));
tree_view->edit(sorter.mapFromSource(index));
break;
case olive::PROJECT_VIEW_ICON:
icon_view->edit(sorter->mapFromSource(index));
icon_view->edit(sorter.mapFromSource(index));
break;
}
}
@@ -475,7 +479,7 @@ MediaPtr Project::create_folder_internal(QString name) {
}
Media* Project::item_to_media(const QModelIndex &index) {
return static_cast<Media*>(sorter->mapToSource(index).internalPointer());
return static_cast<Media*>(sorter.mapToSource(index).internalPointer());
}
MediaPtr Project::item_to_media_ptr(const QModelIndex &index) {
@@ -503,6 +507,21 @@ void Project::get_all_media_from_table(QList<Media*>& items, QList<Media*>& list
}
}
bool Project::IsToolbarVisible()
{
return toolbar_widget->isVisible();
}
void Project::SetToolbarVisible(bool visible)
{
toolbar_widget->setVisible(visible);
}
bool Project::IsProjectWidget(QObject *child)
{
return (child == tree_view || child == icon_view);
}
bool delete_clips_in_clipboard_with_media(ComboAction* ca, Media* m) {
int delete_count = 0;
if (clipboard_type == CLIPBOARD_TYPE_CLIP) {
@@ -944,7 +963,7 @@ bool Project::reveal_media(Media *media, QModelIndex parent) {
// if m == media, then we found the media object we were looking for
// get sorter proxy item (the item that's "visible")
QModelIndex sorted_index = sorter->mapFromSource(item);
QModelIndex sorted_index = sorter.mapFromSource(item);
// retrieve its parent item
QModelIndex hierarchy = sorted_index.parent();
@@ -959,8 +978,8 @@ bool Project::reveal_media(Media *media, QModelIndex parent) {
// select item (requires a QItemSelection object to select the whole row)
QItemSelection row_select(
sorter->index(sorted_index.row(), 0, sorted_index.parent()),
sorter->index(sorted_index.row(), sorter->columnCount()-1, sorted_index.parent())
sorter.index(sorted_index.row(), 0, sorted_index.parent()),
sorter.index(sorted_index.row(), sorter.columnCount()-1, sorted_index.parent())
);
tree_view->selectionModel()->select(row_select, QItemSelectionModel::Select);
@@ -1307,14 +1326,23 @@ void Project::save_project(bool autorecovery) {
void Project::update_view_type() {
tree_view->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_TREE);
icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON);
icon_view_container->setVisible(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON
|| olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_LIST);
switch (olive::CurrentConfig.project_view_type) {
case olive::PROJECT_VIEW_TREE:
sources_common->view = tree_view;
sources_common.view = tree_view;
break;
case olive::PROJECT_VIEW_ICON:
sources_common->view = icon_view;
case olive::PROJECT_VIEW_LIST:
icon_view->setViewMode(olive::CurrentConfig.project_view_type == olive::PROJECT_VIEW_ICON ?
QListView::IconMode : QListView::ListMode);
// update list/grid size since they use this value slightly differently
set_icon_view_size(icon_size_slider->value());
sources_common.view = icon_view;
break;
}
}
@@ -1324,6 +1352,12 @@ void Project::set_icon_view() {
update_view_type();
}
void Project::set_list_view()
{
olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_LIST;
update_view_type();
}
void Project::set_tree_view() {
olive::CurrentConfig.project_view_type = olive::PROJECT_VIEW_TREE;
update_view_type();
@@ -1352,7 +1386,12 @@ void Project::clear_recent_projects() {
}
void Project::set_icon_view_size(int s) {
icon_view->setIconSize(QSize(s, s));
if (icon_view->viewMode() == QListView::IconMode) {
icon_view->setGridSize(QSize(s, s));
} else {
icon_view->setGridSize(QSize());
icon_view->setIconSize(QSize(s, s));
}
}
void Project::set_up_dir_enabled() {
+18 -9
View File
@@ -35,6 +35,7 @@
#include "project/sourcescommon.h"
#include "ui/panel.h"
#include "ui/sourceiconview.h"
#include "timeline/mediaimportdata.h"
#include "undo/undo.h"
#include "ui/sourcetable.h"
@@ -45,7 +46,7 @@
extern QString autorecovery_filename;
extern QStringList recent_projects;
SequencePtr create_sequence_from_media(QVector<Media *> &media_list);
SequencePtr create_sequence_from_media(QVector<olive::timeline::MediaImportData> &media_list);
QString get_channel_layout_name(int channels, uint64_t layout);
QString get_interlacing_name(int interlacing);
@@ -54,7 +55,9 @@ class Project : public Panel {
Q_OBJECT
public:
explicit Project(QWidget *parent = nullptr);
~Project();
void ConnectFilterToModel();
void DisconnectFilterToModel();
bool is_focused();
void clear();
@@ -77,19 +80,14 @@ public:
QVector<Media*> list_all_project_sequences();
SourceTable* tree_view;
SourceIconView* icon_view;
SourcesCommon* sources_common;
ProjectFilter* sorter;
QVector<Media*> last_imported_media;
QModelIndexList get_current_selected();
void get_all_media_from_table(QList<Media *> &items, QList<Media *> &list, int type = -1);
QWidget* toolbar_widget;
bool IsToolbarVisible();
bool IsProjectWidget(QObject *child);
virtual void Retranslate() override;
protected:
@@ -103,6 +101,8 @@ public slots:
void open_properties();
void new_folder();
void new_sequence();
void SetToolbarVisible(bool visible);
private:
void save_folder(QXmlStreamWriter& stream, int type, bool set_ids_only, const QModelIndex &parent = QModelIndex());
int folder_id;
@@ -112,11 +112,20 @@ private:
QString get_file_name_from_path(const QString &path);
QDir proj_dir;
QWidget* icon_view_container;
QSlider* icon_size_slider;
QPushButton* directory_up;
QLineEdit* toolbar_search;
QWidget* toolbar_widget;
SourceTable* tree_view;
SourceIconView* icon_view;
ProjectFilter sorter;
SourcesCommon sources_common;
private slots:
void update_view_type();
void set_icon_view();
void set_list_view();
void set_tree_view();
void clear_recent_projects();
void set_icon_view_size(int);
+110 -20
View File
@@ -138,6 +138,53 @@ void Timeline::Retranslate() {
UpdateTitle();
}
void Timeline::split_clip_at_positions(ComboAction* ca, int clip_index, QVector<long> positions) {
QVector<int> pre_splits;
// Add the clip and each of its links to the pre_splits array
Clip* clip = olive::ActiveSequence->clips.at(clip_index).get();
pre_splits.append(clip_index);
for (int i=0;i<clip->linked.size();i++) {
pre_splits.append(clip->linked.at(i));
}
std::sort(positions.begin(), positions.end());
// Remove any duplicate positions
for (int i=1;i<positions.size();i++) {
if (positions.at(i-1) == positions.at(i)) {
positions.removeAt(i);
i--;
}
}
for (int i=1;i<positions.size();i++) {
Q_ASSERT(positions.at(i-1) < positions.at(i));
}
QVector< QVector<ClipPtr> > post_splits(positions.size());
for (int i=positions.size()-1;i>=0;i--) {
post_splits[i].resize(pre_splits.size());
for (int j=0;j<pre_splits.size();j++) {
post_splits[i][j] = split_clip(ca, true, pre_splits.at(j), positions.at(i));
if (post_splits[i][j] != nullptr && i + 1 < positions.size()) {
post_splits[i][j]->set_timeline_out(positions.at(i+1));
}
}
}
for (int i=0;i<post_splits.size();i++) {
relink_clips_using_ids(pre_splits, post_splits[i]);
ca->append(new AddClipCommand(olive::ActiveSequence.get(), post_splits[i]));
}
}
void Timeline::previous_cut() {
if (olive::ActiveSequence != nullptr
&& olive::ActiveSequence->playhead > 0) {
@@ -192,14 +239,15 @@ void Timeline::toggle_show_all() {
}
}
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<Media*>& media_list) {
void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector<olive::timeline::MediaImportData>& media_list) {
video_ghosts = false;
audio_ghosts = false;
for (int i=0;i<media_list.size();i++) {
bool can_import = true;
Media* medium = media_list.at(i);
const olive::timeline::MediaImportData import_data = media_list.at(i);
Media* medium = import_data.media();
Footage* m = nullptr;
Sequence* s = nullptr;
long sequence_length = 0;
@@ -212,7 +260,9 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
can_import = m->ready;
if (m->using_inout) {
double source_fr = 30;
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
if (m->video_tracks.size() > 0 && !qIsNull(m->video_tracks.at(0).video_frame_rate)) {
source_fr = m->video_tracks.at(0).video_frame_rate * m->speed;
}
default_clip_in = rescale_frame_number(m->in, source_fr, seq->frame_rate);
default_clip_out = rescale_frame_number(m->out, source_fr, seq->frame_rate);
}
@@ -253,20 +303,27 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
}
}
for (int j=0;j<m->audio_tracks.size();j++) {
if (m->audio_tracks.at(j).enabled) {
g.track = j;
g.media_stream = m->audio_tracks.at(j).file_index;
ghosts.append(g);
audio_ghosts = true;
if (import_data.type() == olive::timeline::kImportAudioOnly
|| import_data.type() == olive::timeline::kImportBoth) {
for (int j=0;j<m->audio_tracks.size();j++) {
if (m->audio_tracks.at(j).enabled) {
g.track = j;
g.media_stream = m->audio_tracks.at(j).file_index;
ghosts.append(g);
audio_ghosts = true;
}
}
}
for (int j=0;j<m->video_tracks.size();j++) {
if (m->video_tracks.at(j).enabled) {
g.track = -1-j;
g.media_stream = m->video_tracks.at(j).file_index;
ghosts.append(g);
video_ghosts = true;
if (import_data.type() == olive::timeline::kImportVideoOnly
|| import_data.type() == olive::timeline::kImportBoth) {
for (int j=0;j<m->video_tracks.size();j++) {
if (m->video_tracks.at(j).enabled) {
g.track = -1-j;
g.media_stream = m->video_tracks.at(j).file_index;
ghosts.append(g);
video_ghosts = true;
}
}
}
break;
@@ -277,10 +334,17 @@ void Timeline::create_ghosts_from_media(Sequence* seq, long entry_point, QVector
g.out -= (sequence_length - default_clip_out);
}
g.track = -1;
ghosts.append(g);
g.track = 0;
ghosts.append(g);
if (import_data.type() == olive::timeline::kImportVideoOnly
|| import_data.type() == olive::timeline::kImportBoth) {
g.track = -1;
ghosts.append(g);
}
if (import_data.type() == olive::timeline::kImportAudioOnly
|| import_data.type() == olive::timeline::kImportBoth) {
g.track = 0;
ghosts.append(g);
}
video_ghosts = true;
audio_ghosts = true;
@@ -446,9 +510,35 @@ void Timeline::nest() {
MediaPtr m = panel_project->create_sequence_internal(ca, s, false, nullptr);
// add nested sequence to active sequence
QVector<Media*> media_list;
QVector<olive::timeline::MediaImportData> media_list;
media_list.append(m.get());
create_ghosts_from_media(olive::ActiveSequence.get(), earliest_point, media_list);
// ensure ghosts won't overlap anything
for (int j=0;j<olive::ActiveSequence->clips.size();j++) {
Clip* c = olive::ActiveSequence->clips.at(j).get();
if (c != nullptr && !selected_clips.contains(j)) {
for (int i=0;i<ghosts.size();i++) {
Ghost& g = ghosts[i];
if (c->track() == g.track
&& !((c->timeline_in() < g.in
&& c->timeline_out() < g.in)
|| (c->timeline_in() > g.out
&& c->timeline_out() > g.out))) {
// There's a clip occupied by the space taken up by this ghost. Move up/down a track, and seek again
if (g.track < 0) {
g.track--;
} else {
g.track++;
}
j = -1;
break;
}
}
}
}
add_clips_from_ghosts(ca, olive::ActiveSequence.get());
panel_graph_editor->set_row(nullptr);
+3 -1
View File
@@ -29,6 +29,7 @@
#include "ui/timelinetools.h"
#include "timeline/selection.h"
#include "timeline/clip.h"
#include "timeline/mediaimportdata.h"
#include "undo/undo.h"
#include "ui/timelineheader.h"
#include "ui/resizablescrollbar.h"
@@ -116,6 +117,7 @@ public:
bool split_selection(ComboAction* ca);
bool split_all_clips_at_point(ComboAction *ca, long point);
bool split_clip_and_relink(ComboAction* ca, int clip, long frame, bool relink);
void split_clip_at_positions(ComboAction* ca, int clip_index, QVector<long> positions);
void clean_up_selections(QVector<Selection>& areas);
void deselect_area(long in, long out, int track);
void delete_areas_and_relink(ComboAction *ca, QVector<Selection>& areas, bool deselect_areas);
@@ -125,7 +127,7 @@ public:
void edit_to_point_internal(bool in, bool ripple);
void delete_in_out_internal(bool ripple);
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<Media *> &media_list);
void create_ghosts_from_media(Sequence *seq, long entry_point, QVector<olive::timeline::MediaImportData> &media_list);
void add_clips_from_ghosts(ComboAction *ca, Sequence *s);
int getTimelineScreenPointFromFrame(long frame);
+133 -46
View File
@@ -20,6 +20,21 @@
#include "viewer.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <QtMath>
#include <QAudioOutput>
#include <QPainter>
#include <QStringList>
#include <QTimer>
#include <QHBoxLayout>
#include <QPushButton>
#include <QDrag>
#include <QMimeData>
#include "rendering/audio.h"
#include "timeline.h"
#include "panels/project.h"
@@ -45,19 +60,6 @@
#define FRAMES_IN_ONE_MINUTE 1798 // 1800 - 2
#define FRAMES_IN_TEN_MINUTES 17978 // (FRAMES_IN_ONE_MINUTE * 10) - 2
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <QtMath>
#include <QAudioOutput>
#include <QPainter>
#include <QStringList>
#include <QTimer>
#include <QHBoxLayout>
#include <QPushButton>
Viewer::Viewer(QWidget *parent) :
Panel(parent),
playing(false),
@@ -120,7 +122,6 @@ bool Viewer::is_main_sequence() {
}
void Viewer::set_main_sequence() {
clean_created_seq();
set_sequence(true, olive::ActiveSequence);
}
@@ -401,7 +402,7 @@ void Viewer::play(bool in_to_out) {
if (!is_recording_cued()
&& playback_speed >= 0
&& (playing_in_to_out
|| seq->playhead >= sequence_end_frame
|| (olive::CurrentConfig.auto_seek_to_beginning && seq->playhead >= sequence_end_frame)
|| (seek_to_in && seq->playhead >= seq->workarea_out))) {
seek(seek_to_in ? seq->workarea_in : 0);
}
@@ -539,6 +540,17 @@ void Viewer::update_viewer() {
update_end_timecode();
}
void Viewer::initiate_drag(olive::timeline::MediaImportType drag_type)
{
// FIXME: This should contain actual metadata rather than fake metadata
QDrag* drag = new QDrag(this);
QMimeData* mimeData = new QMimeData;
mimeData->setText(QString::number(drag_type));
drag->setMimeData(mimeData);
drag->exec();
}
void Viewer::clear_in() {
if (seq != nullptr
&& seq->using_workarea) {
@@ -586,6 +598,12 @@ void Viewer::set_panel_name(const QString &n) {
update_window_title();
}
void Viewer::show_videoaudio_buttons(bool s)
{
video_only_button->setVisible(s);
audio_only_button->setVisible(s);
}
void Viewer::update_window_title() {
QString name;
if (seq == nullptr) {
@@ -659,8 +677,12 @@ void Viewer::setup_ui() {
QHBoxLayout* lower_control_layout = new QHBoxLayout(lower_controls);
lower_control_layout->setMargin(0);
// current time code
QSizePolicy timecode_container_policy(QSizePolicy::Minimum, QSizePolicy::Maximum);
QSizePolicy lower_control_policy(QSizePolicy::Expanding, QSizePolicy::Maximum);
// Current time code container
QWidget* current_timecode_container = new QWidget();
current_timecode_container->setSizePolicy(timecode_container_policy);
QHBoxLayout* current_timecode_container_layout = new QHBoxLayout(current_timecode_container);
current_timecode_container_layout->setSpacing(0);
current_timecode_container_layout->setMargin(0);
@@ -668,11 +690,19 @@ void Viewer::setup_ui() {
current_timecode_container_layout->addWidget(current_timecode_slider);
lower_control_layout->addWidget(current_timecode_container);
// Left controls container
QWidget* left_controls = new QWidget();
left_controls->setSizePolicy(lower_control_policy);
lower_control_layout->addWidget(left_controls);
// Playback controls container
QWidget* playback_controls = new QWidget();
playback_controls->setSizePolicy(lower_control_policy);
QHBoxLayout* playback_control_layout = new QHBoxLayout(playback_controls);
playback_control_layout->setSpacing(0);
playback_control_layout->setMargin(0);
playback_control_layout->addStretch();
go_to_start_button = new QPushButton();
go_to_start_button->setIcon(olive::icon::ViewerGoToStart);
@@ -699,9 +729,40 @@ void Viewer::setup_ui() {
connect(go_to_end_frame, SIGNAL(clicked(bool)), this, SLOT(go_to_out()));
playback_control_layout->addWidget(go_to_end_frame);
playback_control_layout->addStretch();
lower_control_layout->addWidget(playback_controls);
// Right controls container
QWidget* right_controls = new QWidget();
right_controls->setSizePolicy(lower_control_policy);
QHBoxLayout* right_control_layout = new QHBoxLayout(right_controls);
right_control_layout->setSpacing(0);
right_control_layout->setMargin(0);
right_control_layout->addStretch();
video_only_button = new QPushButton();
video_only_button->setToolTip(tr("Drag video only"));
video_only_button->setIcon(olive::icon::MediaVideo);
video_only_button->setVisible(false);
right_control_layout->addWidget(video_only_button);
connect(video_only_button, SIGNAL(pressed()), this, SLOT(drag_video_only()));
audio_only_button = new QPushButton();
audio_only_button->setToolTip(tr("Drag audio only"));
audio_only_button->setIcon(olive::icon::MediaAudio);
audio_only_button->setVisible(false);
right_control_layout->addWidget(audio_only_button);
connect(audio_only_button, SIGNAL(pressed()), this, SLOT(drag_audio_only()));
right_control_layout->addStretch();
lower_control_layout->addWidget(right_controls);
// End time code container
QWidget* end_timecode_container = new QWidget();
end_timecode_container->setSizePolicy(timecode_container_policy);
QHBoxLayout* end_timecode_layout = new QHBoxLayout(end_timecode_container);
end_timecode_layout->setSpacing(0);
@@ -720,7 +781,8 @@ void Viewer::set_media(Media* m) {
main_sequence = false;
media = m;
clean_created_seq();
SequencePtr new_sequence = nullptr;
if (media != nullptr) {
switch (media->get_type()) {
case MEDIA_TYPE_FOOTAGE:
@@ -729,30 +791,31 @@ void Viewer::set_media(Media* m) {
marker_ref = &footage->markers;
seq = std::make_shared<Sequence>();
new_sequence = std::make_shared<Sequence>();
created_sequence = true;
seq->wrapper_sequence = true;
seq->name = footage->name;
new_sequence->wrapper_sequence = true;
new_sequence->name = footage->name;
seq->using_workarea = footage->using_inout;
new_sequence->using_workarea = footage->using_inout;
if (footage->using_inout) {
seq->workarea_in = footage->in;
seq->workarea_out = footage->out;
new_sequence->workarea_in = footage->in;
new_sequence->workarea_out = footage->out;
}
// FIXME: Move this magic number to Config
seq->frame_rate = 30;
new_sequence->frame_rate = olive::CurrentConfig.default_sequence_framerate;
if (footage->video_tracks.size() > 0) {
const FootageStream& video_stream = footage->video_tracks.at(0);
seq->width = video_stream.video_width;
seq->height = video_stream.video_height;
if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) seq->frame_rate = video_stream.video_frame_rate * footage->speed;
new_sequence->width = video_stream.video_width;
new_sequence->height = video_stream.video_height;
if (video_stream.video_frame_rate > 0 && !video_stream.infinite_length) {
new_sequence->frame_rate = video_stream.video_frame_rate * footage->speed;
}
ClipPtr c = std::make_shared<Clip>(seq.get());
ClipPtr c = std::make_shared<Clip>(new_sequence.get());
c->set_media(media, video_stream.file_index);
c->set_timeline_in(0);
c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate));
c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate));
if (c->timeline_out() <= 0) {
// FIXME: Move this magic number to Config
c->set_timeline_out(150);
@@ -760,25 +823,24 @@ void Viewer::set_media(Media* m) {
c->set_track(-1);
c->set_clip_in(0);
c->refresh();
seq->clips.append(c);
new_sequence->clips.append(c);
} else {
// FIXME: Move this magic number to Config
seq->width = 1920;
seq->height = 1080;
new_sequence->width = olive::CurrentConfig.default_sequence_width;
new_sequence->height = olive::CurrentConfig.default_sequence_height;
}
if (footage->audio_tracks.size() > 0) {
const FootageStream& audio_stream = footage->audio_tracks.at(0);
seq->audio_frequency = audio_stream.audio_frequency;
new_sequence->audio_frequency = audio_stream.audio_frequency;
ClipPtr c = std::make_shared<Clip>(seq.get());
ClipPtr c = std::make_shared<Clip>(new_sequence.get());
c->set_media(media, audio_stream.file_index);
c->set_timeline_in(0);
c->set_timeline_out(footage->get_length_in_frames(seq->frame_rate));
c->set_timeline_out(footage->get_length_in_frames(new_sequence->frame_rate));
c->set_track(0);
c->set_clip_in(0);
c->refresh();
seq->clips.append(c);
new_sequence->clips.append(c);
if (footage->video_tracks.size() == 0) {
viewer_widget->waveform = true;
@@ -787,19 +849,19 @@ void Viewer::set_media(Media* m) {
viewer_widget->frame_update();
}
} else {
// FIXME: Move this magic number to Config
seq->audio_frequency = 48000;
new_sequence->audio_frequency = olive::CurrentConfig.default_sequence_audio_frequency;
}
seq->audio_layout = AV_CH_LAYOUT_STEREO;
new_sequence->audio_layout = AV_CH_LAYOUT_STEREO;
}
break;
case MEDIA_TYPE_SEQUENCE:
seq = media->to_sequence();
new_sequence = media->to_sequence();
break;
}
}
set_sequence(false, seq);
set_sequence(false, new_sequence);
}
void Viewer::update_playhead() {
@@ -810,7 +872,11 @@ void Viewer::timer_update() {
previous_playhead = seq->playhead;
seq->playhead = qMax(0, qRound(playhead_start + ((QDateTime::currentMSecsSinceEpoch()-start_msecs) * 0.001 * seq->frame_rate * playback_speed)));
if (olive::CurrentConfig.seek_also_selects) panel_timeline->select_from_playhead();
if (olive::CurrentConfig.seek_also_selects) {
panel_timeline->select_from_playhead();
}
update_parents(olive::CurrentConfig.seek_also_selects);
if (playing) {
@@ -821,7 +887,8 @@ void Viewer::timer_update() {
pause();
}
} else if (playback_speed > 0) {
if (seq->playhead >= seq->getEndFrame()) {
long end_frame = seq->getEndFrame();
if ((olive::CurrentConfig.auto_seek_to_beginning || previous_playhead < end_frame) && seq->playhead >= end_frame) {
pause();
}
if (seq->using_workarea && seq->playhead >= seq->workarea_out) {
@@ -848,6 +915,16 @@ void Viewer::resize_move(double d) {
set_zoom_value(headers->get_zoom()*d);
}
void Viewer::drag_video_only()
{
initiate_drag(olive::timeline::kImportVideoOnly);
}
void Viewer::drag_audio_only()
{
initiate_drag(olive::timeline::kImportAudioOnly);
}
void Viewer::clean_created_seq() {
viewer_widget->waveform = false;
@@ -861,7 +938,9 @@ void Viewer::clean_created_seq() {
}
*/
// Delete the current sequence
seq.reset();
created_sequence = false;
}
}
@@ -871,13 +950,19 @@ void Viewer::set_sequence(bool main, SequencePtr s) {
reset_all_audio();
main_sequence = main;
viewer_widget->wait_until_render_is_paused();
// If we had a current sequence open, close it
if (seq != nullptr) {
close_active_clips(seq.get());
}
clean_created_seq();
main_sequence = main;
seq = (main) ? olive::ActiveSequence : s;
bool null_sequence = (seq == nullptr);
@@ -891,6 +976,8 @@ void Viewer::set_sequence(bool main, SequencePtr s) {
play_button->setEnabled(!null_sequence);
next_frame_button->setEnabled(!null_sequence);
go_to_end_frame->setEnabled(!null_sequence);
video_only_button->setEnabled(!null_sequence);
audio_only_button->setEnabled(!null_sequence);
if (!null_sequence) {
current_timecode_slider->SetFrameRate(seq->frame_rate);
+14
View File
@@ -27,6 +27,7 @@
#include <QPushButton>
#include "timeline/marker.h"
#include "timeline/mediaimportdata.h"
#include "project/media.h"
#include "ui/panel.h"
@@ -64,6 +65,7 @@ public:
void set_out_point();
void set_zoom(bool in);
void set_panel_name(const QString& n);
void show_videoaudio_buttons(bool s);
// playback functions
void seek(long p);
@@ -98,6 +100,10 @@ public:
TimelineHeader* headers;
void initiate_drag(olive::timeline::MediaImportType drag_type);
virtual void Retranslate() override;
protected:
virtual void resizeEvent(QResizeEvent *event) override;
@@ -116,12 +122,17 @@ public slots:
void close_media();
void update_viewer();
private slots:
void update_playhead();
void timer_update();
void recording_flasher_update();
void resize_move(double d);
void drag_video_only();
void drag_audio_only();
private:
void update_window_title();
@@ -155,6 +166,9 @@ private:
QPushButton* next_frame_button;
QPushButton* go_to_end_frame;
QPushButton* video_only_button;
QPushButton* audio_only_button;
bool cue_recording_internal;
QTimer recording_flasher;
-14
View File
@@ -77,17 +77,3 @@ FootageStream* Footage::get_stream_from_file_index(bool video, int index) {
}
return nullptr;
}
void FootageStream::make_square_thumb() {
// generate square version for QListView?
int square_size = qMax(video_preview.width(), video_preview.height());
QPixmap pixmap(square_size, square_size);
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
int diff = (video_preview.width() - video_preview.height())>>1;
int sqx = (diff < 0) ? -diff : 0;
int sqy = (diff > 0) ? diff : 0;
p.drawImage(sqx, sqy, video_preview);
p.end();
video_preview_square = QIcon(pixmap);
}
-2
View File
@@ -62,9 +62,7 @@ struct FootageStream {
// preview thumbnail/waveform
bool preview_done;
QImage video_preview;
QIcon video_preview_square;
QVector<char> audio_preview;
void make_square_thumb();
};
struct Footage {
+5 -10
View File
@@ -34,10 +34,9 @@
#include <QFile>
#include <QTreeWidgetItem>
LoadThread::LoadThread(const QString& filename, bool autorecovery, bool clear) :
LoadThread::LoadThread(const QString& filename, bool autorecovery) :
filename_(filename),
autorecovery_(autorecovery),
clear_(clear),
cancelled_(false)
{
connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
@@ -101,7 +100,7 @@ void LoadThread::load_effect(QXmlStreamReader& stream, Clip* c) {
c->closing_transition = (sharing_clip->opening_transition);
// since this is the closed clip, make this clip the secondary
c->opening_transition->secondary_clip = c;
c->closing_transition->secondary_clip = c;
}
return;
}
@@ -606,8 +605,6 @@ Media* LoadThread::find_loaded_folder_by_id(int id) {
}
void LoadThread::OrganizeFolders(int folder) {
qDebug() << "starting with" << folder;
for (int i=0;i<loaded_folders.size();i++) {
MediaPtr item = loaded_folders.at(i);
int parent_id = item->temp_id2;
@@ -615,7 +612,7 @@ void LoadThread::OrganizeFolders(int folder) {
if (parent_id == folder) {
olive::project_model.appendChild(find_loaded_folder_by_id(parent_id), item);
OrganizeFolders(parent_id);
OrganizeFolders(item->temp_id);
}
}
@@ -776,14 +773,12 @@ void LoadThread::success_func() {
counter++;
}
if (clear_) {
olive::Global->update_project_filename(orig_filename);
}
olive::Global->update_project_filename(orig_filename);
} else {
panel_project->add_recent_project(filename_);
}
olive::Global->set_modified(autorecovery_ || !clear_);
olive::Global->set_modified(autorecovery_);
if (open_seq != nullptr) {
olive::Global->set_sequence(open_seq);
}
+1 -2
View File
@@ -35,7 +35,7 @@ class LoadThread : public QThread
{
Q_OBJECT
public:
LoadThread(const QString& filename, bool autorecovery, bool clear);
LoadThread(const QString& filename, bool autorecovery);
void run();
public slots:
void cancel();
@@ -50,7 +50,6 @@ private slots:
void success_func();
private:
bool autorecovery_;
bool clear_;
QString filename_;
bool load_worker(QFile& f, QXmlStreamReader& stream, int type);
+24 -15
View File
@@ -284,6 +284,24 @@ int Media::columnCount() const {
return 3;
}
QString Media::GetStringDuration() {
if (get_type() == MEDIA_TYPE_SEQUENCE) {
Sequence* s = to_sequence().get();
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* f = to_footage();
double r = 30;
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
r = f->video_tracks.at(0).video_frame_rate * f->speed;
long len = f->get_length_in_frames(r);
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
}
return QString();
}
QVariant Media::data(int column, int role) {
switch (role) {
case Qt::DecorationRole:
@@ -292,7 +310,7 @@ QVariant Media::data(int column, int role) {
Footage* f = to_footage();
if (f->video_tracks.size() > 0
&& f->video_tracks.at(0).preview_done) {
return f->video_tracks.at(0).video_preview_square;
return QIcon(QPixmap::fromImage(f->video_tracks.at(0).video_preview));
}
}
@@ -304,20 +322,7 @@ QVariant Media::data(int column, int role) {
case 0: return (root) ? QCoreApplication::translate("Media", "Name") : get_name();
case 1:
if (root) return QCoreApplication::translate("Media", "Duration");
if (get_type() == MEDIA_TYPE_SEQUENCE) {
Sequence* s = to_sequence().get();
return frame_to_timecode(s->getEndFrame(), olive::CurrentConfig.timecode_view, s->frame_rate);
}
if (get_type() == MEDIA_TYPE_FOOTAGE) {
Footage* f = to_footage();
double r = 30;
if (f->video_tracks.size() > 0 && !qIsNull(f->video_tracks.at(0).video_frame_rate))
r = f->video_tracks.at(0).video_frame_rate * f->speed;
long len = f->get_length_in_frames(r);
if (len > 0) return frame_to_timecode(len, olive::CurrentConfig.timecode_view, r);
}
return GetStringDuration();
break;
case 2:
if (root) return QCoreApplication::translate("Media", "Rate");
@@ -336,6 +341,10 @@ QVariant Media::data(int column, int role) {
break;
case Qt::ToolTipRole:
return tooltip;
case Qt::UserRole:
// User role returns the duration
return GetStringDuration();
}
return QVariant();
}
+2
View File
@@ -87,6 +87,8 @@ private:
int type;
VoidPtr object;
QString GetStringDuration();
// item functions
QList<MediaPtr> children;
Media* parent;
-4
View File
@@ -153,8 +153,6 @@ bool PreviewGenerator::retrieve_preview(const QString& hash) {
QString thumb_path = get_thumbnail_path(hash, ms);
QFile f(thumb_path);
if (f.exists() && ms.video_preview.load(thumb_path)) {
//dout << "loaded thumb" << ms->file_index << "from" << thumb_path;
ms.make_square_thumb();
ms.preview_done = true;
} else {
found = false;
@@ -364,8 +362,6 @@ void PreviewGenerator::generate_waveform() {
&data,
linesize);
s->make_square_thumb();
// is video interlaced?
s->video_auto_interlacing = (temp_frame->interlaced_frame) ? ((temp_frame->top_field_first) ? VIDEO_TOP_FIELD_FIRST : VIDEO_BOTTOM_FIELD_FIRST) : VIDEO_PROGRESSIVE;
s->video_interlacing = s->video_auto_interlacing;
+2 -2
View File
@@ -44,10 +44,10 @@ void ProjectModel::make_root() {
void ProjectModel::destroy_root() {
if (panel_sequence_viewer != nullptr) {
panel_sequence_viewer->viewer_widget->delete_function();
panel_sequence_viewer->set_media(nullptr);
}
if (panel_footage_viewer != nullptr) {
panel_footage_viewer->viewer_widget->delete_function();
panel_footage_viewer->set_media(nullptr);
}
root_item_ = std::make_shared<Media>();
+9 -8
View File
@@ -47,9 +47,10 @@
#include "ui/menu.h"
#include "undo/undostack.h"
SourcesCommon::SourcesCommon(Project* parent) :
SourcesCommon::SourcesCommon(Project* parent, ProjectFilter &sort_filter) :
editing_item(nullptr),
project_parent(parent)
project_parent(parent),
sort_filter_(sort_filter)
{
rename_timer.setInterval(1000);
connect(&rename_timer, SIGNAL(timeout()), this, SLOT(rename_interval()));
@@ -57,7 +58,7 @@ SourcesCommon::SourcesCommon(Project* parent) :
void SourcesCommon::create_seq_from_selected() {
if (!selected_items.isEmpty()) {
QVector<Media*> media_list;
QVector<olive::timeline::MediaImportData> media_list;
for (int i=0;i<selected_items.size();i++) {
media_list.append(project_parent->item_to_media(selected_items.at(i)));
}
@@ -97,13 +98,13 @@ void SourcesCommon::show_context_menu(QWidget* parent, const QModelIndexList& it
QAction* toolbar_action = view_menu->addAction(tr("Show Toolbar"));
toolbar_action->setCheckable(true);
toolbar_action->setChecked(project_parent->toolbar_widget->isVisible());
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent->toolbar_widget, SLOT(setVisible(bool)));
toolbar_action->setChecked(project_parent->IsToolbarVisible());
connect(toolbar_action, SIGNAL(triggered(bool)), project_parent, SLOT(SetToolbarVisible(bool)));
QAction* show_sequences = view_menu->addAction(tr("Show Sequences"));
show_sequences->setCheckable(true);
show_sequences->setChecked(panel_project->sorter->get_show_sequences());
connect(show_sequences, SIGNAL(triggered(bool)), panel_project->sorter, SLOT(set_show_sequences(bool)));
show_sequences->setChecked(sort_filter_.get_show_sequences());
connect(show_sequences, SIGNAL(triggered(bool)), &sort_filter_, SLOT(set_show_sequences(bool)));
if (items.size() > 0) {
if (items.size() == 1) {
@@ -282,7 +283,7 @@ void SourcesCommon::dropEvent(QWidget* parent,
bool replace = false;
if (urls.size() == 1
&& drop_item.isValid()
&& (m != nullptr && m->get_type() == MEDIA_TYPE_FOOTAGE)
&& m->get_type() == MEDIA_TYPE_FOOTAGE
&& !QFileInfo(paths.at(0)).isDir()
&& olive::CurrentConfig.drop_on_media_to_replace
&& QMessageBox::question(
+6 -2
View File
@@ -26,6 +26,7 @@
#include <QVector>
#include "project/footage.h"
#include "project/projectfilter.h"
class Project;
class QMouseEvent;
@@ -36,7 +37,7 @@ class QDropEvent;
class SourcesCommon : public QObject {
Q_OBJECT
public:
SourcesCommon(Project *parent);
SourcesCommon(Project *parent, ProjectFilter& sort_filter);
QAbstractItemView* view;
void show_context_menu(QWidget* parent, const QModelIndexList &items);
@@ -45,6 +46,8 @@ public:
void dropEvent(QWidget *parent, QDropEvent* e, const QModelIndex& drop_item, const QModelIndexList &items);
void item_click(Media* m, const QModelIndex &index);
public slots:
void stop_rename_timer();
private slots:
void create_seq_from_selected();
void reveal_in_browser();
@@ -61,11 +64,12 @@ private:
QModelIndex editing_index;
QModelIndexList selected_items;
Project* project_parent;
void stop_rename_timer();
QTimer rename_timer;
// we cache the selected footage items for open_create_proxy_dialog()
QVector<Media*> cached_selected_footage;
ProjectFilter& sort_filter_;
};
#endif // SOURCESCOMMON_H
+1
View File
@@ -653,6 +653,7 @@ void Cacher::CacheVideoWorker() {
// again, an EOF isn't an "error" but will how we add frames (see below)
qCritical() << "Failed to retrieve frame from buffersink." << retrieve_code;
break;
} else if (decoded_frame->pts != AV_NOPTS_VALUE) {
+26 -15
View File
@@ -20,17 +20,6 @@
#include "exportthread.h"
#include "global/global.h"
#include "timeline/sequence.h"
#include "panels/panels.h"
#include "ui/viewerwidget.h"
#include "rendering/renderthread.h"
#include "rendering/renderfunctions.h"
#include "rendering/audio.h"
#include "ui/mainwindow.h"
#include "global/debug.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
@@ -42,6 +31,17 @@ extern "C" {
#include <QOffscreenSurface>
#include <QOpenGLPaintDevice>
#include <QPainter>
#include <QtMath>
#include "global/global.h"
#include "timeline/sequence.h"
#include "panels/panels.h"
#include "ui/viewerwidget.h"
#include "rendering/renderthread.h"
#include "rendering/renderfunctions.h"
#include "rendering/audio.h"
#include "ui/mainwindow.h"
#include "global/debug.h"
ExportThread::ExportThread(const ExportParams &params,
const VideoCodecParams& vparams,
@@ -91,7 +91,18 @@ bool ExportThread::Encode(AVFormatContext* ofmt_ctx, AVCodecContext* codec_ctx,
}
packet->stream_index = stream->index;
if (rescale) av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base);
if (rescale) {
if (packet->pts != AV_NOPTS_VALUE) {
packet->pts = qRound(packet->pts * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
}
if (packet->dts != AV_NOPTS_VALUE) {
packet->dts = qRound(packet->dts * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
}
if (packet->duration > 0) {
packet->duration = qRound(packet->duration * av_q2d(codec_ctx->time_base) / av_q2d(stream->time_base));
}
//av_packet_rescale_ts(packet, codec_ctx->time_base, stream->time_base);
}
av_interleaved_write_frame(ofmt_ctx, packet);
av_packet_unref(packet);
}
@@ -468,10 +479,10 @@ void ExportThread::Export()
// Convert raw RGBA buffer to format expected by the encoder
sws_scale(sws_ctx, video_frame->data, video_frame->linesize, 0, video_frame->height, sws_frame->data, sws_frame->linesize);
sws_frame->pts = qRound(timecode_secs/av_q2d(video_stream->time_base));
sws_frame->pts = qRound(timecode_secs/av_q2d(vcodec_ctx->time_base));
// Send frame to encoder
if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, false)) {
if (!Encode(fmt_ctx, vcodec_ctx, sws_frame, &video_pkt, video_stream, true)) {
return;
}
@@ -577,7 +588,7 @@ void ExportThread::Export()
// Flush remaining packets out of video and audio encoders
while (continueVideo && continueAudio) {
if (continueVideo) {
continueVideo = Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, false);
continueVideo = Encode(fmt_ctx, vcodec_ctx, nullptr, &video_pkt, video_stream, true);
}
if (continueAudio) {
continueAudio = Encode(fmt_ctx, acodec_ctx, nullptr, &audio_pkt, audio_stream, true);
+4 -2
View File
@@ -696,8 +696,10 @@ GLuint olive::rendering::compose_sequence(ComposeSequenceParams &params) {
params.ctx->functions()->glBindTexture(GL_TEXTURE_2D, textureID);
// set texture filter to bilinear
params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
params.ctx->functions()->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//params.ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
//params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
params.ctx->functions()->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// draw clip on screen according to gl coordinates
params.pipeline->bind();
+16
View File
@@ -370,6 +370,22 @@ void RenderThread::cancel() {
wait();
}
void RenderThread::wait_until_paused()
{
// Wait for thread to finish whatever it's doing before proceeding.
//
// FIXME: This is slow. Perhaps there's a better way...
if (wait_lock_.tryLock()) {
wait_lock_.unlock();
return;
} else {
wait_lock_.lock();
wait_lock_.unlock();
}
}
void RenderThread::delete_buffers() {
composite_buffer.Destroy();
front_buffer_1.Destroy();
+1 -1
View File
@@ -55,7 +55,7 @@ public:
int idivider = 0);
bool did_texture_fail();
void cancel();
void wait_until_paused();
public slots:
// cleanup functions
+17
View File
@@ -0,0 +1,17 @@
#include "mediaimportdata.h"
olive::timeline::MediaImportData::MediaImportData(Media *media, olive::timeline::MediaImportType import_type) :
media_(media),
import_type_(import_type)
{
}
Media *olive::timeline::MediaImportData::media() const
{
return media_;
}
olive::timeline::MediaImportType olive::timeline::MediaImportData::type() const
{
return import_type_;
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef MEDIAIMPORTDATA_H
#define MEDIAIMPORTDATA_H
#include "project/media.h"
namespace olive {
namespace timeline {
enum MediaImportType {
kImportVideoOnly,
kImportAudioOnly,
kImportBoth
};
class MediaImportData {
public:
MediaImportData(Media* media = nullptr, MediaImportType import_type = kImportBoth);
Media* media() const;
MediaImportType type() const;
private:
Media* media_;
MediaImportType import_type_;
};
}
}
#endif // MEDIAIMPORTDATA_H
+3625
View File
File diff suppressed because it is too large Load Diff
+913 -821
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
#include "columnedgridlayout.h"
ColumnedGridLayout::ColumnedGridLayout(QWidget* parent,
int maximum_columns) :
QGridLayout (parent),
maximum_columns_(maximum_columns)
{
}
void ColumnedGridLayout::Add(QWidget *widget)
{
if (maximum_columns_ > 0) {
int row = count() / maximum_columns_;
int column = count() % maximum_columns_;
addWidget(widget, row, column);
} else {
addWidget(widget);
}
}
int ColumnedGridLayout::MaximumColumns() const
{
return maximum_columns_;
}
void ColumnedGridLayout::SetMaximumColumns(int maximum_columns)
{
maximum_columns_ = maximum_columns;
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef COLUMNEDGRIDLAYOUT_H
#define COLUMNEDGRIDLAYOUT_H
#include <QGridLayout>
/**
* @brief The ColumnedGridLayout class
*
* A simple derivative of QGridLayout that provides a automatic row/column layout based on a specified maximum
* column count.
*/
class ColumnedGridLayout : public QGridLayout
{
Q_OBJECT
public:
ColumnedGridLayout(QWidget* parent = nullptr,
int maximum_columns = 0);
void Add(QWidget* widget);
int MaximumColumns() const;
void SetMaximumColumns(int maximum_columns);
private:
int maximum_columns_;
};
#endif // COLUMNEDGRIDLAYOUT_H
+2 -2
View File
@@ -96,8 +96,8 @@ void olive::icon::Initialize()
Diamond = CreateIconFromSVG(":/icons/diamond.svg", false);
Clock = CreateIconFromSVG(":/icons/clock.svg", false);
MediaVideo = CreateIconFromSVG(":/icons/videosource.svg", false);
MediaAudio = CreateIconFromSVG(":/icons/audiosource.svg", false);
MediaVideo = CreateIconFromSVG(":/icons/videosource.svg");
MediaAudio = CreateIconFromSVG(":/icons/audiosource.svg");
MediaImage = CreateIconFromSVG(":/icons/imagesource.svg", false);
MediaError = CreateIconFromSVG(":/icons/error.svg", false);
MediaSequence = CreateIconFromSVG(":/icons/sequence.svg", false);
+17 -92
View File
@@ -358,7 +358,6 @@ void kbd_shortcut_processor(QByteArray& file, QMenu* menu, bool save, bool first
}
}
}
a->setShortcutContext(Qt::ApplicationShortcut);
}
}
}
@@ -408,16 +407,14 @@ bool MainWindow::load_css_from_file(const QString &fn) {
void MainWindow::Restyle()
{
// Set up UI style
if (olive::styling::UseNativeUI()) {
qApp->setStyle(QStyleFactory::create(""));
} else {
if (!olive::styling::UseNativeUI()) {
qApp->setStyle(QStyleFactory::create("Fusion"));
// Set up whether to load custom CSS or default CSS+palette
if (!olive::CurrentConfig.css_path.isEmpty()
&& load_css_from_file(olive::CurrentConfig.css_path)) {
setPalette(QPalette());
qApp->setPalette(qApp->style()->standardPalette());
} else {
@@ -476,7 +473,17 @@ void MainWindow::Restyle()
palette.setColor(QPalette::HighlightedText, Qt::white);
// set default CSS
setStyleSheet("QPushButton::checked { background: rgb(25, 25, 25); }");
QString stylesheet = "QPushButton::checked { background: rgb(25, 25, 25); }";
// Windows menus have the option of being native, so we may not need this CSS
#ifdef Q_OS_WIN
if (!olive::CurrentConfig.use_native_menu_styling) {
#endif
stylesheet.append("QMenu::separator { background: #404040; }");
#ifdef Q_OS_WIN
}
#endif
setStyleSheet(stylesheet);
}
@@ -779,61 +786,7 @@ void MainWindow::setup_menus() {
tools_menu->addSeparator();
selecting_also_seeks = MenuHelper::create_menu_action(tools_menu, "selectingalsoseeks", &olive::MenuHelper, SLOT(toggle_bool_action()));
selecting_also_seeks->setCheckable(true);
selecting_also_seeks->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.select_also_seeks));
edit_tool_also_seeks = MenuHelper::create_menu_action(tools_menu, "editalsoseeks", &olive::MenuHelper, SLOT(toggle_bool_action()));
edit_tool_also_seeks->setCheckable(true);
edit_tool_also_seeks->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.edit_tool_also_seeks));
edit_tool_selects_links = MenuHelper::create_menu_action(tools_menu, "editselectslinks", &olive::MenuHelper, SLOT(toggle_bool_action()));
edit_tool_selects_links->setCheckable(true);
edit_tool_selects_links->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.edit_tool_selects_links));
seek_also_selects = MenuHelper::create_menu_action(tools_menu, "seekalsoselects", &olive::MenuHelper, SLOT(toggle_bool_action()));
seek_also_selects->setCheckable(true);
seek_also_selects->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.seek_also_selects));
seek_to_end_of_pastes = MenuHelper::create_menu_action(tools_menu, "seektoendofpastes", &olive::MenuHelper, SLOT(toggle_bool_action()));
seek_to_end_of_pastes->setCheckable(true);
seek_to_end_of_pastes->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.paste_seeks));
scroll_wheel_zooms = MenuHelper::create_menu_action(tools_menu, "scrollwheelzooms", &olive::MenuHelper, SLOT(toggle_bool_action()));
scroll_wheel_zooms->setCheckable(true);
scroll_wheel_zooms->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.scroll_zooms));
invert_timeline_scroll_axes = MenuHelper::create_menu_action(tools_menu, "inverttimelinescrollaxes", &olive::MenuHelper, SLOT(toggle_bool_action()));
invert_timeline_scroll_axes->setCheckable(true);
invert_timeline_scroll_axes->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.invert_timeline_scroll_axes));
enable_drag_files_to_timeline = MenuHelper::create_menu_action(tools_menu, "enabledragfilestotimeline", &olive::MenuHelper, SLOT(toggle_bool_action()));
enable_drag_files_to_timeline->setCheckable(true);
enable_drag_files_to_timeline->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_drag_files_to_timeline));
autoscale_by_default = MenuHelper::create_menu_action(tools_menu, "autoscalebydefault", &olive::MenuHelper, SLOT(toggle_bool_action()));
autoscale_by_default->setCheckable(true);
autoscale_by_default->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.autoscale_by_default));
enable_seek_to_import = MenuHelper::create_menu_action(tools_menu, "enableseektoimport", &olive::MenuHelper, SLOT(toggle_bool_action()));
enable_seek_to_import->setCheckable(true);
enable_seek_to_import->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_seek_to_import));
enable_audio_scrubbing = MenuHelper::create_menu_action(tools_menu, "audioscrubbing", &olive::MenuHelper, SLOT(toggle_bool_action()));
enable_audio_scrubbing->setCheckable(true);
enable_audio_scrubbing->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.enable_audio_scrubbing));
enable_drop_on_media_to_replace = MenuHelper::create_menu_action(tools_menu, "enabledropmediareplace", &olive::MenuHelper, SLOT(toggle_bool_action()));
enable_drop_on_media_to_replace->setCheckable(true);
enable_drop_on_media_to_replace->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.drop_on_media_to_replace));
enable_hover_focus = MenuHelper::create_menu_action(tools_menu, "hoverfocus", &olive::MenuHelper, SLOT(toggle_bool_action()));
enable_hover_focus->setCheckable(true);
enable_hover_focus->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.hover_focus));
set_name_and_marker = MenuHelper::create_menu_action(tools_menu, "asknamemarkerset", &olive::MenuHelper, SLOT(toggle_bool_action()));
set_name_and_marker->setCheckable(true);
set_name_and_marker->setData(reinterpret_cast<quintptr>(&olive::CurrentConfig.set_name_with_marker));
autocut_silence_ = MenuHelper::create_menu_action(tools_menu, "autocutsilence", olive::Global.get(), SLOT(open_autocut_silence_dialog()));
tools_menu->addSeparator();
@@ -971,21 +924,7 @@ void MainWindow::Retranslate()
hand_tool_action->setText(tr("Hand Tool"));
transition_tool_action->setText(tr("Transition Tool"));
snap_toggle->setText(tr("Enable Snapping"));
selecting_also_seeks->setText(tr("Selecting Also Seeks"));
edit_tool_also_seeks->setText(tr("Edit Tool Also Seeks"));
edit_tool_selects_links->setText(tr("Edit Tool Selects Links"));
seek_also_selects->setText(tr("Seek Also Selects"));
seek_to_end_of_pastes->setText(tr("Seek to the End of Pastes"));
scroll_wheel_zooms->setText(tr("Scroll Wheel Zooms"));
scroll_wheel_zooms->setToolTip(tr("Hold CTRL to toggle this setting"));
invert_timeline_scroll_axes->setText(tr("Invert Timeline Scroll Axes"));
enable_drag_files_to_timeline->setText(tr("Enable Drag Files to Timeline"));
autoscale_by_default->setText(tr("Auto-Scale By Default"));
enable_seek_to_import->setText(tr("Enable Seek to Import"));
enable_audio_scrubbing->setText(tr("Audio Scrubbing"));
enable_drop_on_media_to_replace->setText(tr("Enable Drop on Media to Replace"));
enable_hover_focus->setText(tr("Enable Hover Focus"));
set_name_and_marker->setText(tr("Ask For Name When Setting Marker"));
autocut_silence_->setText(tr("Auto-Cut Silence"));
no_autoscroll->setText(tr("No Auto-Scroll"));
page_autoscroll->setText(tr("Page Auto-Scroll"));
@@ -1194,6 +1133,8 @@ void MainWindow::playbackMenu_About_To_Be_Shown() {
void MainWindow::viewMenu_About_To_Be_Shown() {
olive::MenuHelper.set_bool_action_checked(track_lines);
olive::MenuHelper.set_bool_action_checked(rectified_waveforms);
olive::MenuHelper.set_int_action_checked(frames_action, olive::CurrentConfig.timecode_view);
olive::MenuHelper.set_int_action_checked(drop_frame_action, olive::CurrentConfig.timecode_view);
olive::MenuHelper.set_int_action_checked(nondrop_frame_action, olive::CurrentConfig.timecode_view);
@@ -1229,22 +1170,6 @@ void MainWindow::toolMenu_About_To_Be_Shown() {
olive::MenuHelper.set_button_action_checked(transition_tool_action);
olive::MenuHelper.set_button_action_checked(snap_toggle);
olive::MenuHelper.set_bool_action_checked(selecting_also_seeks);
olive::MenuHelper.set_bool_action_checked(edit_tool_also_seeks);
olive::MenuHelper.set_bool_action_checked(edit_tool_selects_links);
olive::MenuHelper.set_bool_action_checked(seek_to_end_of_pastes);
olive::MenuHelper.set_bool_action_checked(scroll_wheel_zooms);
olive::MenuHelper.set_bool_action_checked(invert_timeline_scroll_axes);
olive::MenuHelper.set_bool_action_checked(rectified_waveforms);
olive::MenuHelper.set_bool_action_checked(enable_drag_files_to_timeline);
olive::MenuHelper.set_bool_action_checked(autoscale_by_default);
olive::MenuHelper.set_bool_action_checked(enable_seek_to_import);
olive::MenuHelper.set_bool_action_checked(enable_audio_scrubbing);
olive::MenuHelper.set_bool_action_checked(enable_drop_on_media_to_replace);
olive::MenuHelper.set_bool_action_checked(enable_hover_focus);
olive::MenuHelper.set_bool_action_checked(set_name_and_marker);
olive::MenuHelper.set_bool_action_checked(seek_also_selects);
olive::MenuHelper.set_int_action_checked(no_autoscroll, olive::CurrentConfig.autoscroll);
olive::MenuHelper.set_int_action_checked(page_autoscroll, olive::CurrentConfig.autoscroll);
olive::MenuHelper.set_int_action_checked(smooth_autoscroll, olive::CurrentConfig.autoscroll);
+1 -14
View File
@@ -328,21 +328,8 @@ private:
QAction* hand_tool_action;
QAction* transition_tool_action;
QAction* snap_toggle;
QAction* selecting_also_seeks;
QAction* edit_tool_also_seeks;
QAction* edit_tool_selects_links;
QAction* seek_to_end_of_pastes;
QAction* scroll_wheel_zooms;
QAction* invert_timeline_scroll_axes;
QAction* rectified_waveforms;
QAction* enable_drag_files_to_timeline;
QAction* autoscale_by_default;
QAction* enable_seek_to_import;
QAction* enable_audio_scrubbing;
QAction* enable_drop_on_media_to_replace;
QAction* enable_hover_focus;
QAction* set_name_and_marker;
QAction* seek_also_selects;
QAction* autocut_silence_;
QAction* preferences_action_;
QAction* clear_undo_action_;
+135 -9
View File
@@ -21,32 +21,38 @@
#include "sourceiconview.h"
#include <QMimeData>
#include <QImage>
#include "panels/project.h"
#include "project/media.h"
#include "project/sourcescommon.h"
#include "global/debug.h"
#include "global/math.h"
SourceIconView::SourceIconView(QWidget *parent) : QListView(parent) {
SourceIconView::SourceIconView(SourcesCommon &commons) :
commons_(commons)
{
setMovement(QListView::Free);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setResizeMode(QListView::Adjust);
setContextMenuPolicy(Qt::CustomContextMenu);
setItemDelegate(&delegate_);
connect(this, SIGNAL(clicked(const QModelIndex&)), this, SLOT(item_click(const QModelIndex&)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(show_context_menu()));
}
void SourceIconView::show_context_menu() {
project_parent->sources_common->show_context_menu(this, selectedIndexes());
commons_.show_context_menu(this, selectedIndexes());
}
void SourceIconView::item_click(const QModelIndex& index) {
if (selectedIndexes().size() == 1 && index.column() == 0) {
project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
commons_.item_click(project_parent->item_to_media(index), index);
}
}
void SourceIconView::mousePressEvent(QMouseEvent* event) {
project_parent->sources_common->mousePressEvent(event);
commons_.mousePressEvent(event);
if (!indexAt(event->pos()).isValid()) selectionModel()->clear();
QListView::mousePressEvent(event);
}
@@ -70,20 +76,140 @@ void SourceIconView::dragMoveEvent(QDragMoveEvent *event) {
void SourceIconView::dropEvent(QDropEvent* event) {
QModelIndex drop_item = indexAt(event->pos());
if (!drop_item.isValid()) drop_item = rootIndex();
project_parent->sources_common->dropEvent(this, event, drop_item, selectedIndexes());
commons_.dropEvent(this, event, drop_item, selectedIndexes());
}
void SourceIconView::mouseDoubleClickEvent(QMouseEvent *) {
bool default_behavior = true;
if (selectedIndexes().size() == 1) {
Media* m = project_parent->item_to_media(selectedIndexes().at(0));
if (m->get_type() == MEDIA_TYPE_FOLDER) {
default_behavior = false;
setRootIndex(selectedIndexes().at(0));
emit changed_root();
return;
}
}
if (default_behavior) {
project_parent->sources_common->mouseDoubleClickEvent(selectedIndexes());
// Double click was not a folder, so we perform the default behavior (sending the double click to SourcesCommon)
commons_.mouseDoubleClickEvent(selectedIndexes());
}
SourceIconDelegate::SourceIconDelegate(QObject *parent) :
QStyledItemDelegate (parent)
{
}
QSize SourceIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (option.decorationPosition == QStyleOptionViewItem::Top) { // Icon Mode
return QSize(256, 256);
} else {
return QSize(option.decorationSize.height(), option.decorationSize.height());
}
}
void SourceIconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QFontMetrics fm = painter->fontMetrics();
QRect img_rect = option.rect;
if (option.decorationPosition == QStyleOptionViewItem::Top) { // Icon Mode
// Draw Text
if (fm.height() < option.rect.height() / 2) {
img_rect.setHeight(img_rect.height()-fm.height());
QRect text_rect = option.rect;
text_rect.setTop(text_rect.top() + option.rect.height() - fm.height());
QColor text_bgcolor;
QColor text_fgcolor;
if (option.state & QStyle::State_Selected) {
text_bgcolor = option.palette.highlight().color();
text_fgcolor = option.palette.highlightedText().color();
} else {
text_bgcolor = Qt::white;
text_fgcolor = Qt::black;
}
painter->fillRect(text_rect, text_bgcolor);
painter->setPen(text_fgcolor);
QString duration_str = index.data(Qt::UserRole).toString();
int timecode_width = fm.width(duration_str);
int max_name_width = option.rect.width();
if (timecode_width < option.rect.width() / 2) {
painter->drawText(text_rect, Qt::AlignBottom | Qt::AlignRight, index.data(Qt::UserRole).toString());
max_name_width -= timecode_width;
}
painter->drawText(text_rect,
Qt::AlignBottom | Qt::AlignLeft,
fm.elidedText(index.data(Qt::DisplayRole).toString(), Qt::ElideRight, max_name_width));
}
// Draw image
QIcon ico = index.data(Qt::DecorationRole).value<QIcon>();
QSize icon_size = ico.actualSize(img_rect.size());
img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
icon_size.width(),
icon_size.height());
painter->drawPixmap(img_rect, ico.pixmap(icon_size));
if (option.state & QStyle::State_Selected) {
QColor highlight_color = option.palette.highlight().color();
highlight_color.setAlphaF(0.5);
painter->setCompositionMode(QPainter::CompositionMode_SourceAtop);
painter->fillRect(img_rect, highlight_color);
}
} else if (option.decorationPosition == QStyleOptionViewItem::Left) { // List Mode
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
img_rect.setWidth(qMin(img_rect.width(), img_rect.height()));
QIcon ico = index.data(Qt::DecorationRole).value<QIcon>();
QSize icon_size = ico.actualSize(img_rect.size());
img_rect = QRect(img_rect.x() + (img_rect.width() / 2 - icon_size.width() / 2),
img_rect.y() + (img_rect.height() / 2 - icon_size.height() / 2),
icon_size.width(),
icon_size.height());
painter->drawPixmap(img_rect, ico.pixmap(icon_size));
QRect text_rect = option.rect;
text_rect.setLeft(text_rect.left() + option.rect.height());
int maximum_line_count = qMax(1, option.rect.height() / fm.height() - 1);
QString text;
if (maximum_line_count == 1) {
text = index.data(Qt::DisplayRole).toString();
} else {
text = index.data(Qt::ToolTipRole).toString();
if (text.isEmpty()) {
text = index.data(Qt::DisplayRole).toString();
} else {
QStringList strings = text.split("\n");
while (strings.size() > maximum_line_count) {
strings.removeLast();
}
text = strings.join("\n");
}
}
painter->setPen(option.state & QStyle::State_Selected ?
option.palette.highlightedText().color() : option.palette.text().color());
painter->drawText(text_rect, Qt::AlignLeft | Qt::AlignVCenter, text);
}
}
+26 -11
View File
@@ -22,25 +22,40 @@
#define SOURCEICONVIEW_H
#include <QListView>
#include <QDropEvent>
#include <QStyledItemDelegate>
#include "project/sourcescommon.h"
class Project;
class SourceIconDelegate;
class SourceIconDelegate : public QStyledItemDelegate {
public:
SourceIconDelegate(QObject *parent = nullptr);
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
};
class SourceIconView : public QListView {
Q_OBJECT
Q_OBJECT
public:
SourceIconView(QWidget* parent = 0);
Project* project_parent;
SourceIconView(SourcesCommon& commons);
Project* project_parent;
void mousePressEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseDoubleClickEvent(QMouseEvent *event);
void dragEnterEvent(QDragEnterEvent *event);
void dragMoveEvent(QDragMoveEvent *event);
void dropEvent(QDropEvent* event);
signals:
void changed_root();
void changed_root();
private slots:
void show_context_menu();
void item_click(const QModelIndex& index);
void show_context_menu();
void item_click(const QModelIndex& index);
private:
SourcesCommon& commons_;
SourceIconDelegate delegate_;
};
#endif // SOURCEICONVIEW_H
+6 -6
View File
@@ -45,7 +45,7 @@
#include <QDir>
#include <QProcess>
SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
SourceTable::SourceTable(SourcesCommon& commons) : commons_(commons) {
setSortingEnabled(true);
setAcceptDrops(true);
sortByColumn(0, Qt::AscendingOrder);
@@ -58,22 +58,22 @@ SourceTable::SourceTable(QWidget* parent) : QTreeView(parent) {
}
void SourceTable::show_context_menu() {
project_parent->sources_common->show_context_menu(this, selectionModel()->selectedRows());
commons_.show_context_menu(this, selectionModel()->selectedRows());
}
void SourceTable::item_click(const QModelIndex& index) {
if (selectionModel()->selectedRows().size() == 1 && index.column() == 0) {
project_parent->sources_common->item_click(project_parent->item_to_media(index), index);
commons_.item_click(project_parent->item_to_media(index), index);
}
}
void SourceTable::mousePressEvent(QMouseEvent* event) {
project_parent->sources_common->mousePressEvent(event);
commons_.mousePressEvent(event);
QTreeView::mousePressEvent(event);
}
void SourceTable::mouseDoubleClickEvent(QMouseEvent* ) {
project_parent->sources_common->mouseDoubleClickEvent(selectionModel()->selectedRows());
commons_.mouseDoubleClickEvent(selectionModel()->selectedRows());
}
void SourceTable::dragEnterEvent(QDragEnterEvent *event) {
@@ -93,5 +93,5 @@ void SourceTable::dragMoveEvent(QDragMoveEvent *event) {
}
void SourceTable::dropEvent(QDropEvent* event) {
project_parent->sources_common->dropEvent(this, event, indexAt(event->pos()), selectionModel()->selectedRows());
commons_.dropEvent(this, event, indexAt(event->pos()), selectionModel()->selectedRows());
}
+5 -1
View File
@@ -25,6 +25,8 @@
#include <QTimer>
#include <QUndoCommand>
#include "project/sourcescommon.h"
class Project;
class Media;
@@ -32,7 +34,7 @@ class SourceTable : public QTreeView
{
Q_OBJECT
public:
SourceTable(QWidget* parent = 0);
SourceTable(SourcesCommon& commons);
Project* project_parent;
protected:
void mousePressEvent(QMouseEvent*);
@@ -43,6 +45,8 @@ protected:
private slots:
void item_click(const QModelIndex& index);
void show_context_menu();
private:
SourcesCommon& commons_;
};
#endif // SOURCETABLE_H
+23 -5
View File
@@ -118,11 +118,26 @@ void TimelineWidget::show_context_menu(const QPoint& pos) {
if (!selected_clips.isEmpty()) {
bool video_clips_are_selected = false;
bool audio_clips_are_selected = false;
for (int i=0;i<selected_clips.size();i++) {
if (selected_clips.at(i)->track() < 0) {
video_clips_are_selected = true;
} else {
audio_clips_are_selected = true;
}
}
menu.addSeparator();
menu.addAction(tr("&Speed/Duration"), olive::Global.get(), SLOT(open_speed_dialog()));
QAction* autoscaleAction = menu.addAction(tr("Auto-s&cale"), this, SLOT(toggle_autoscale()));
if (audio_clips_are_selected) {
menu.addAction(tr("Auto-Cut Silence"), olive::Global.get(), SLOT(open_autocut_silence_dialog()));
}
QAction* autoscaleAction = menu.addAction(tr("Auto-S&cale"), this, SLOT(toggle_autoscale()));
autoscaleAction->setCheckable(true);
// set autoscale to the first selected clip
autoscaleAction->setChecked(selected_clips.at(0)->autoscaled());
@@ -237,10 +252,10 @@ bool same_sign(int a, int b) {
void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
bool import_init = false;
QVector<Media*> media_list;
QVector<olive::timeline::MediaImportData> media_list;
panel_timeline->importing_files = false;
if (event->source() == panel_project->tree_view || event->source() == panel_project->icon_view) {
if (panel_project->IsProjectWidget(event->source())) {
QModelIndexList items = panel_project->get_current_selected();
media_list.resize(items.size());
for (int i=0;i<items.size();i++) {
@@ -249,10 +264,13 @@ void TimelineWidget::dragEnterEvent(QDragEnterEvent *event) {
import_init = true;
}
if (event->source() == panel_footage_viewer->viewer_widget) {
if (event->source() == panel_footage_viewer) {
if (panel_footage_viewer->seq != olive::ActiveSequence) { // don't allow nesting the same sequence
media_list.append(panel_footage_viewer->media);
media_list.append(olive::timeline::MediaImportData(panel_footage_viewer->media,
static_cast<olive::timeline::MediaImportType>(event->mimeData()->text().toInt())));
import_init = true;
}
}
+6 -9
View File
@@ -90,10 +90,6 @@ ViewerWidget::~ViewerWidget() {
renderer.cancel();
}
void ViewerWidget::delete_function() {
close_active_clips(viewer->seq.get());
}
void ViewerWidget::set_waveform_scroll(int s) {
if (waveform) {
waveform_scroll = s;
@@ -383,11 +379,7 @@ void ViewerWidget::mouseMoveEvent(QMouseEvent* event) {
container->dragScrollMove(event->pos()*container->zoom);
} else if (event->buttons() & Qt::LeftButton) {
if (gizmos == nullptr) {
QDrag* drag = new QDrag(this);
QMimeData* mimeData = new QMimeData;
mimeData->setText("h"); // QMimeData will fail without some kind of data
drag->setMimeData(mimeData);
drag->exec();
viewer->initiate_drag(olive::timeline::kImportBoth);
dragging = false;
} else {
move_gizmos(event, false);
@@ -421,6 +413,11 @@ void ViewerWidget::close_window() {
window->hide();
}
void ViewerWidget::wait_until_render_is_paused()
{
renderer.wait_until_paused();
}
void ViewerWidget::draw_waveform_func() {
QPainter p(this);
if (viewer->seq->using_workarea) {
+1 -1
View File
@@ -50,8 +50,8 @@ public:
ViewerWidget(QWidget *parent = nullptr);
~ViewerWidget();
void delete_function();
void close_window();
void wait_until_render_is_paused();
void paintGL();
void initializeGL();
+36
View File
@@ -31,6 +31,7 @@
#include <QDebug>
#include "rendering/renderfunctions.h"
#include "ui/mainwindow.h"
ViewerWindow::ViewerWindow(QWidget *parent) :
QOpenGLWidget(parent, Qt::Window),
@@ -51,6 +52,41 @@ void ViewerWindow::set_texture(GLuint t, double iar, QMutex* imutex) {
update();
}
void ViewerWindow::shortcut_copier(QVector<QShortcut*>& shortcuts, QMenu* menu) {
QList<QAction*> menu_action = menu->actions();
for (int i=0;i<menu_action.size();i++) {
if (menu_action.at(i)->menu() != nullptr) {
shortcut_copier(shortcuts, menu_action.at(i)->menu());
} else if (!menu_action.at(i)->isSeparator() && !menu_action.at(i)->shortcut().isEmpty()) {
QShortcut* sc = new QShortcut(this);
sc->setKey(menu_action.at(i)->shortcut());
connect(sc, SIGNAL(activated()), menu_action.at(i), SLOT(trigger()));
shortcuts.append(sc);
}
}
}
void ViewerWindow::showEvent(QShowEvent *)
{
// Here, we copy all shortcuts from the MainWindow to this window. I don't like this solution, but messing around
// with Qt's event system proved fruitless. Also setting the shortcuts to ApplicationShortcut rather than
// WindowShortcut caused issues elsewhere (shortcuts being picked up in comboboxes and dialog boxes - we only
// want the shortcuts to be shared to this window). Therefore, this and shortcut_copier() are so far the best
// solutions I can find.
// Clear any existing shortcuts in case they've changed since the last showing
for (int i=0;i<shortcuts_.size();i++) {
delete shortcuts_.at(i);
}
shortcuts_.clear();
// Recursively copy all shortcuts from MainWindow to this window
QList<QAction*> menubar_actions = olive::MainWindow->menuBar()->actions();
for (int i=0;i<menubar_actions.size();i++) {
shortcut_copier(shortcuts_, menubar_actions.at(i)->menu());
}
}
void ViewerWindow::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Escape) {
hide();
+6
View File
@@ -24,6 +24,7 @@
#include <QOpenGLWidget>
#include <QTimer>
#include <QMutex>
#include <QMenu>
#include "rendering/qopenglshaderprogramptr.h"
@@ -33,6 +34,7 @@ public:
ViewerWindow(QWidget *parent);
void set_texture(GLuint t, double iar, QMutex *imutex);
protected:
virtual void showEvent(QShowEvent*) override;
virtual void keyPressEvent(QKeyEvent*) override;
virtual void mousePressEvent(QMouseEvent*) override;
virtual void mouseMoveEvent(QMouseEvent*) override;
@@ -45,6 +47,10 @@ private:
QMutex* mutex_;
QOpenGLShaderProgramPtr pipeline_;
// shortcuts
void shortcut_copier(QVector<QShortcut*>& shortcuts, QMenu* menu);
QVector<QShortcut*> shortcuts_;
// exit full screen message
QTimer fullscreen_msg_timer_;
bool show_fullscreen_msg_;