merged master and fixed superimpose effect

This commit is contained in:
itsmattkc
2019-03-22 02:48:59 +11:00
31 changed files with 761 additions and 183 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ extern "C" {
}
AdvancedVideoDialog::AdvancedVideoDialog(QWidget *parent,
int encoding_codec,
AVCodecID encoding_codec,
VideoCodecParams &iparams) :
QDialog(parent),
params_(iparams)
+19 -1
View File
@@ -36,11 +36,29 @@
class AdvancedVideoDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief AdvancedVideoDialog Constructor
*
* @param parent
*
* QWidget parent. Usually ExportDialog.
*
* @param encoding_codec
*
* The AVCodecID of the selected export codec.
*
* @param iparams
*
* A VideoCodecParams struct containing the extra codec data.
*/
AdvancedVideoDialog(QWidget* parent,
int encoding_codec,
AVCodecID encoding_codec,
VideoCodecParams& iparams);
public slots:
/**
* @brief Overrided accept for saving the UI data into the provided VideoCodecParams struct.
*/
virtual void accept() override;
private:
/**
+11 -11
View File
@@ -330,16 +330,16 @@ void ExportDialog::format_changed(int index) {
audioGroupbox->setEnabled(audio_enabled);
}
void ExportDialog::render_thread_finished() {
void ExportDialog::export_thread_finished() {
// Determine if the export succeeded
bool succeeded = (progressBar->value() == 100);
// If it failed and we didn't cancel it, it must have errored out. Show an error message.
if (!succeeded && !et->WasInterrupted()) {
if (!succeeded && !export_thread_->WasInterrupted()) {
QMessageBox::critical(
this,
tr("Export Failed"),
tr("Export failed - %1").arg(et->GetError()),
tr("Export failed - %1").arg(export_thread_->GetError()),
QMessageBox::Ok
);
}
@@ -358,10 +358,10 @@ void ExportDialog::render_thread_finished() {
update_ui(false);
// Disconnect cancel button from export thread
disconnect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
disconnect(renderCancel, SIGNAL(clicked(bool)), export_thread_, SLOT(Interrupt()));
// Free the export thread
et->deleteLater();
export_thread_->deleteLater();
// If the export succeeded, close the dialog
if (succeeded) {
@@ -562,12 +562,12 @@ void ExportDialog::StartExport() {
}
// Create export thread
et = new ExportThread(params, vcodec_params, this);
export_thread_ = new ExportThread(params, vcodec_params, this);
// Connect export thread signals/slots
connect(et, SIGNAL(finished()), this, SLOT(render_thread_finished()));
connect(et, SIGNAL(ProgressChanged(int, qint64)), this, SLOT(update_progress_bar(int, qint64)));
connect(renderCancel, SIGNAL(clicked(bool)), et, SLOT(Interrupt()));
connect(export_thread_, SIGNAL(finished()), this, SLOT(export_thread_finished()));
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 currently open clips
close_active_clips(olive::ActiveSequence.get());
@@ -580,7 +580,7 @@ void ExportDialog::StartExport() {
total_export_time_start = QDateTime::currentMSecsSinceEpoch();
et->start();
export_thread_->start();
}
}
@@ -667,7 +667,7 @@ void ExportDialog::comp_type_changed(int) {
}
void ExportDialog::open_advanced_video_dialog() {
AdvancedVideoDialog avd(this, vcodecCombobox->currentData().toInt(), vcodec_params);
AdvancedVideoDialog avd(this, static_cast<AVCodecID>(vcodecCombobox->currentData().toInt()), vcodec_params);
avd.exec();
}
+167 -4
View File
@@ -68,42 +68,205 @@ private slots:
* Asks the user for the file to save to.
*/
void StartExport();
/**
* @brief Slot for the export thread to update the progress bar's value
*
* @param value
*
* An value between 0 - 100. A percentage of the Sequence that has been exported so far.
*
* @param remaining_ms
*
* The estimated time in milliseconds that it will take to complete the rest of the Sequence.
*/
void update_progress_bar(int value, qint64 remaining_ms);
void render_thread_finished();
/**
* @brief Slot for the export thread completing (both succeeding and failing)
*
* Runs whenever the thread has finished. Determines whether the thread succeeded or not (and shows an error message
* if not), cleans up the ExportThread object, sets the UI state back to normal.
*
* Connect to ExportThread::finished().
*/
void export_thread_finished();
/**
* @brief Slot for when the video codec changes
*
* Some video codecs require different settings. In the case of that, this function sorts through those.
*
* @param index
*
* Current vcodecCombobox index - its item data contains the AVCodecID.
*/
void vcodec_changed(int index);
/**
* @brief Slot for when the compression type changes
*
* Different UI objects should be displayed for different compression types.
*
* @param index
*
* Unused.
*/
void comp_type_changed(int index);
/**
* @brief Slot to open the Advanced Video Dialog
*
* Opens a dialog for setting more advanced video settings and passes a reference to vcodec_params to it.
*/
void open_advanced_video_dialog();
private:
/**
* @brief Function to create UI objects.
*/
void setup_ui();
/**
* @brief Enables/disables certain UI objects based on the exporting state.
*
* Some UI controls don't need to be set while exporting. This function enables/disables them appropriately.
*
* @param r
*
* TRUE if we're exporting, FALSE if we finished.
*/
void prep_ui_for_render(bool r);
QVector<QString> format_strings;
ExportThread* et;
/**
* @brief Retrieves the human-readable name of an AVCodecID and adds it to a QComboBox
*
* Also sets that item's data to the AVCodecID so it can be retrieved directly from the QComboBox.
*
* @param box
*
* The QComboBox to add the item to.
*
* @param codec
*
* The codec to add to the QComboBox.
*/
void add_codec_to_combobox(QComboBox* box, enum AVCodecID codec);
/**
* @brief Internal array of human-readable names corresponding to enum ExportFormats
*/
QVector<QString> format_strings;
/**
* @brief Pointer to an ExportThread
*
* Set when exporting starts, and deleted by export_thread_finished() when the thread is complete.
*/
ExportThread* export_thread_;
/**
* @brief Struct for advanced video codec parameters.
*
* More advanced video encoding parameters to be sent to the ExportThread. These variables are not directly editable
* in this dialog, instead calling open_advanced_video_dialog() will open an AdvancedVideoDialog for setting these
* values directly. vcodec_changed() should also set these to the defaults for that codec where appropriate.
*/
VideoCodecParams vcodec_params;
/**
* @brief ComboBox for selecting the time range of the Sequence to export
*/
QComboBox* rangeCombobox;
/**
* @brief SpinBox for the exported video's width
*/
QSpinBox* widthSpinbox;
/**
* @brief SpinBox for the exported video's bitrate
*/
QDoubleSpinBox* videobitrateSpinbox;
/**
* @brief Label for the exported video's bitrate - changes depending on the compression type
*/
QLabel* videoBitrateLabel;
/**
* @brief SpinBox for the exported video's frame rate
*/
QDoubleSpinBox* framerateSpinbox;
/**
* @brief ComboBox for the exported video codec
*/
QComboBox* vcodecCombobox;
/**
* @brief ComboBox for the exported audio's codec
*/
QComboBox* acodecCombobox;
/**
* @brief SpinBox for the exported audio's sample rate
*/
QSpinBox* samplingRateSpinbox;
/**
* @brief SpinBox for the exported audio's bitrate
*/
QSpinBox* audiobitrateSpinbox;
/**
* @brief Progress bar for visually showing the export progress
*/
QProgressBar* progressBar;
/**
* @brief ComboBox for the exported video's format
*/
QComboBox* formatCombobox;
/**
* @brief SpinBox for the exported video's height
*/
QSpinBox* heightSpinbox;
/**
* @brief Export button to trigger the start of an export
*/
QPushButton* export_button;
/**
* @brief Dialog cancel button to close this dialog
*/
QPushButton* cancel_button;
/**
* @brief Cancel button to abort the export before completion
*/
QPushButton* renderCancel;
/**
* @brief GroupBox containing all video-related UI objects
*/
QGroupBox* videoGroupbox;
/**
* @brief GroupBox containing all audio-related UI objects
*/
QGroupBox* audioGroupbox;
/**
* @brief ComboBox for the exported video compression type
*/
QComboBox* compressionTypeCombobox;
/**
* @brief Time value set when exporting begins to determine the total duration of the export
*/
qint64 total_export_time_start;
};
+8 -25
View File
@@ -31,7 +31,7 @@
#include "ui/sourcetable.h"
#include "ui/mainwindow.h"
LoadDialog::LoadDialog(QWidget *parent, const QString& fn, bool autorecovery, bool clear) :
LoadDialog::LoadDialog(QWidget *parent) :
QDialog(parent)
{
setWindowTitle(tr("Loading..."));
@@ -45,36 +45,19 @@ LoadDialog::LoadDialog(QWidget *parent, const QString& fn, bool autorecovery, bo
bar->setValue(0);
layout->addWidget(bar);
cancel_button = new QPushButton(tr("Cancel"), this);
connect(cancel_button, SIGNAL(clicked(bool)), this, SLOT(cancel()));
QPushButton* cancel_button = new QPushButton(tr("Cancel"), this);
connect(cancel_button, SIGNAL(clicked(bool)), this, SIGNAL(cancel()));
hboxLayout = new QHBoxLayout();
// Wrap cancel button in a horizontal layout so it can be centered
QHBoxLayout* hboxLayout = new QHBoxLayout();
hboxLayout->addStretch();
hboxLayout->addWidget(cancel_button);
hboxLayout->addStretch();
layout->addLayout(hboxLayout);
update();
lt = new LoadThread(fn, autorecovery, clear);
QObject::connect(lt, SIGNAL(success()), this, SLOT(thread_done()));
QObject::connect(lt, SIGNAL(error()), this, SLOT(die()));
QObject::connect(lt, SIGNAL(report_progress(int)), bar, SLOT(setValue(int)));
lt->start();
}
void LoadDialog::cancel() {
lt->cancel();
lt->wait();
die();
}
void LoadDialog::die() {
olive::Global->new_project();
reject();
}
void LoadDialog::thread_done() {
accept();
void LoadDialog::setValue(int i)
{
bar->setValue(i);
}
+34 -7
View File
@@ -28,20 +28,47 @@
#include "project/projectelements.h"
#include "project/loadthread.h"
/**
* @brief The LoadDialog class
*
* Shows a modal dialog for loading a project. Designed to be connected to a LoadThread object.
*/
class LoadDialog : public QDialog
{
Q_OBJECT
public:
LoadDialog(QWidget* parent, const QString& filename, bool autorecovery, bool clear);
private slots:
/**
* @brief LoadDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
LoadDialog(QWidget* parent);
public slots:
/**
* @brief Set the progress bar value
*
* Ideally, connect this to LoadThread::report_progress().
*
* @param i
*
* Should be a value between 0-100.
*/
void setValue(int i);
signals:
/**
* @brief Signal emitted when the cancel button is clicked.
*
* Ideally, connect this to LoadThread::cancel();
*/
void cancel();
void die();
void thread_done();
private:
/**
* @brief Progress bar widget
*/
QProgressBar* bar;
QPushButton* cancel_button;
QHBoxLayout* hboxLayout;
LoadThread* lt;
};
#endif // LOADDIALOG_H
+43
View File
@@ -31,19 +31,62 @@
#include "project/footage.h"
#include "project/media.h"
/**
* @brief The MediaPropertiesDialog class
*
* A dialog for setting properties on Media.
*/
class MediaPropertiesDialog : public QDialog {
Q_OBJECT
public:
/**
* @brief MediaPropertiesDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow or Project panel.
*
* @param i
*
* Media object to set properties for.
*/
MediaPropertiesDialog(QWidget *parent, Media* i);
private:
/**
* @brief ComboBox for interlacing setting
*/
QComboBox* interlacing_box;
/**
* @brief Media name text field
*/
QLineEdit* name_box;
/**
* @brief Internal pointer to Media object (set in constructor)
*/
Media* item;
/**
* @brief A list widget for listing the tracks in Media
*/
QListWidget* track_list;
/**
* @brief Frame rate to conform to
*/
QDoubleSpinBox* conform_fr;
/**
* @brief Setting for associated/premultiplied alpha
*/
QCheckBox* premultiply_alpha_setting;
QComboBox* color_management;
private slots:
/**
* @brief Overrided accept function for saving the properties back to the Media class
*/
void accept();
};
+10 -6
View File
@@ -76,15 +76,15 @@ NewSequenceDialog::NewSequenceDialog(QWidget *parent, Media *existing) :
}
}
NewSequenceDialog::~NewSequenceDialog()
{}
void NewSequenceDialog::set_sequence_name(const QString& s) {
sequence_name_edit->setText(s);
}
void NewSequenceDialog::create() {
void NewSequenceDialog::accept() {
if (existing_sequence == nullptr) {
// The dialog wasn't given an existing Sequence object, so we'll make a new one
SequencePtr s = std::make_shared<Sequence>();
s->name = sequence_name_edit->text();
@@ -97,7 +97,11 @@ void NewSequenceDialog::create() {
ComboAction* ca = new ComboAction();
panel_project->create_sequence_internal(ca, s, true, nullptr);
olive::UndoStack.push(ca);
} else {
// The dialog was given an existing Sequence object, so we'll apply the changes to it
ComboAction* ca = new ComboAction();
double multiplier = frame_rate_combobox->currentData().toDouble() / existing_sequence->frame_rate;
@@ -121,7 +125,7 @@ void NewSequenceDialog::create() {
olive::UndoStack.push(ca);
}
accept();
QDialog::accept();
}
void NewSequenceDialog::preset_changed(int index) {
@@ -280,6 +284,6 @@ void NewSequenceDialog::setup_ui() {
verticalLayout->addWidget(buttonBox);
connect(preset_combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(preset_changed(int)));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(create()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
+85 -3
View File
@@ -30,33 +30,115 @@
#include "project/media.h"
#include "timeline/sequence.h"
/**
* @brief The NewSequenceDialog class
*
* A dialog that creates a new (or edits an existing) Sequence object.
*/
class NewSequenceDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief NewSequenceDialog constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*
* @param existing
*
* 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.
*/
explicit NewSequenceDialog(QWidget *parent = nullptr, Media* existing = nullptr);
~NewSequenceDialog();
/**
* @brief Set the name for the new Sequence
*
* If creating a new Sequence, use this function before calling exec() to set what the new Sequence's
* name will be.
*
* The primary use of this is to set a unique default name (i.e. one that doesn't exist
* in the Sequence already) which is done by Project panel. This is usually "Sequence" followed by a number.
*
* @param s
*
* The name to set the new Sequence.
*/
void set_sequence_name(const QString& s);
private slots:
void create();
/**
* @brief Override accept function to create/edit a Sequence
*/
virtual void accept() override;
/**
* @brief Slot when the user changes the preset
*
* Sets all values according to the preset chosen.
*
* @param index
*
* Currently selected index of preset_combobox;
*/
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 function to create the dialog's UI
*/
void setup_ui();
/**
* @brief ComboBox to set the preset
*/
QComboBox* preset_combobox;
/**
* @brief SpinBox to set the Sequence height
*/
QSpinBox* height_numeric;
/**
* @brief SpinBox to set the Sequence width
*/
QSpinBox* width_numeric;
/**
* @brief ComboBox to set the pixel aspect ratio
*/
QComboBox* par_combobox;
/**
* @brief ComboBox to set the interlacing mode
*/
QComboBox* interlacing_combobox;
/**
* @brief ComboBox to set the frame rate
*/
QComboBox* frame_rate_combobox;
/**
* @brief ComboBox to set the audio frequence
*/
QComboBox* audio_frequency_combobox;
/**
* @brief Line edit to set the Sequence's name
*/
QLineEdit* sequence_name_edit;
};
+8 -5
View File
@@ -83,9 +83,12 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
{
setWindowTitle(tr("Preferences"));
setup_ui();
}
PreferencesDialog::~PreferencesDialog() {}
recordingComboBox->setCurrentIndex(olive::CurrentConfig.recording_mode - 1);
imgSeqFormatEdit->setText(olive::CurrentConfig.img_seq_formats);
setup_kbd_shortcuts(olive::MainWindow->menuBar());
}
void PreferencesDialog::setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent) {
QList<QAction*> actions = menu->actions();
@@ -255,7 +258,7 @@ void PreferencesDialog::setup_kbd_shortcuts(QMenuBar* menubar) {
}
}
void PreferencesDialog::save() {
void PreferencesDialog::accept() {
bool restart_after_saving = false;
bool reinit_audio = false;
bool reload_language = false;
@@ -448,7 +451,7 @@ void PreferencesDialog::save() {
olive::Global->load_translation_from_config();
}
accept();
QDialog::accept();
if (restart_after_saving) {
// since we already ran can_close_project(), bypass checking again by running set_modified(false)
@@ -1025,6 +1028,6 @@ void PreferencesDialog::setup_ui() {
verticalLayout->addWidget(buttonBox);
connect(buttonBox, SIGNAL(accepted()), this, SLOT(save()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
+106 -16
View File
@@ -40,38 +40,86 @@ namespace OCIO = OCIO_NAMESPACE::v1;
#include "timeline/sequence.h"
class KeySequenceEditor : public QKeySequenceEdit {
Q_OBJECT
public:
KeySequenceEditor(QWidget *parent, QAction* a);
void set_action_shortcut();
void reset_to_default();
QString action_name();
QString export_shortcut();
private:
QAction* action;
};
class KeySequenceEditor;
/**
* @brief The PreferencesDialog class
*
* A dialog for the global application settings. Mostly an interface for Config.
*/
class PreferencesDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief PreferencesDialog Constructor
*
* @param parent
*
* QWidget parent. Usually MainWindow.
*/
explicit PreferencesDialog(QWidget *parent = nullptr);
~PreferencesDialog();
void setup_kbd_shortcuts(QMenuBar* menu);
private slots:
void save();
/**
* @brief Override of accept to save preferences to Config.
*/
virtual void accept() override;
/**
* @brief Reset all selected shortcuts in keyboard_tree to their defaults
*/
void reset_default_shortcut();
/**
* @brief Reset all shortcuts indiscriminately to their defaults
*
* This is safe to call directly as it'll ask the user if they wish to do so before it resets.
*/
void reset_all_shortcuts();
bool refine_shortcut_list(const QString &, QTreeWidgetItem* parent = nullptr);
/**
* @brief Shows/hides shortcut entries according to a shortcut query.
*
* This function can be directly connected to QLineEdit::textChanged() for simplicity.
*
* @param s
*
* The search query to compare shortcut names to.
*
* @param parent
*
* This is used as the function calls itself recursively to traverse the menu item hierarchy. This should be left as
* nullptr when called externally.
*
* @return
*
* Value used as function calls itself recursively to determine if a menu parent has any children that are not hidden.
* If so, TRUE is returned so the parent is shown too (even if it doesn't match the search query). If not, FALSE is
* returned so the parent is hidden.
*/
bool refine_shortcut_list(const QString &s, QTreeWidgetItem* parent = nullptr);
/**
* @brief Show a file dialog to load an external shortcut preset from file
*/
void load_shortcut_file();
/**
* @brief Show a file dialog to save an external shortcut preset from file
*/
void save_shortcut_file();
/**
* @brief Delete all previews (waveform and thumbnail cache)
*/
void delete_all_previews();
// Browse for file functionns
/**
* @brief Show a file dialog to browse for an external CSS file to load for styling the application.
*/
void browse_css_file();
void browse_ocio_config();
@@ -83,7 +131,37 @@ private slots:
#endif
private:
/**
* @brief Create and arrange all UI widgets
*/
void setup_ui();
/**
* @brief Populate keyboard shortcut panel with keyboard shortcuts from the menu bar
*
* @param menu
*
* A reference to the main application's menu bar. Usually MainWindow::menuBar().
*/
void setup_kbd_shortcuts(QMenuBar* menu);
/**
* @brief Internal function called by setup_kbd_shortcuts() to traverse down the menu bar's hierarchy and populate the
* shortcut panel.
*
* This function will call itself recursively as it finds submenus belong to the menu provided. It will also create
* QTreeWidgetItems as children of the parent item provided, either using them as parents themselves for submenus
* or attaching a KeySequenceEditor to them for shortcut editing.
*
* @param menu
*
* The current menu to traverse down.
*
* @param parent
*
* The parent item to add QTreeWidgetItems to.
*/
void setup_kbd_shortcut_worker(QMenu* menu, QTreeWidgetItem* parent);
enum PreviewDeleteTypes {
@@ -143,4 +221,16 @@ private:
QVector<KeySequenceEditor*> key_shortcut_fields;
};
class KeySequenceEditor : public QKeySequenceEdit {
Q_OBJECT
public:
KeySequenceEditor(QWidget *parent, QAction* a);
void set_action_shortcut();
void reset_to_default();
QString action_name();
QString export_shortcut();
private:
QAction* action;
};
#endif // PREFERENCESDIALOG_H
+27 -1
View File
@@ -122,7 +122,8 @@ Effect::Effect(Clip* c, const EffectMeta *em) :
isOpen(false),
bound(false),
iterations(1),
enabled_(true)
enabled_(true),
expanded_(true)
{
if (em != nullptr) {
// set up UI from effect file
@@ -507,6 +508,16 @@ bool Effect::IsEnabled() {
return enabled_;
}
bool Effect::IsExpanded()
{
return expanded_;
}
void Effect::SetExpanded(bool e)
{
expanded_ = e;
}
void Effect::SetEnabled(bool b) {
enabled_ = b;
emit EnabledChanged(b);
@@ -895,15 +906,30 @@ GLuint Effect::process_superimpose(double timecode) {
tex_width_ = img.width();
tex_height_ = img.height();
// create texture object
f->glGenTextures(1, &texture);
f->glBindTexture(GL_TEXTURE_2D, texture);
// set texture filtering to bilinear
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA8, tex_width_, tex_height_, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits()
);
f->glBindTexture(GL_TEXTURE_2D, 0);
redrew_image = false;
}
if (redrew_image) {
f->glBindTexture(GL_TEXTURE_2D, texture);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, tex_width_, tex_height_, GL_RGBA, GL_UNSIGNED_BYTE, img.constBits());
f->glBindTexture(GL_TEXTURE_2D, 0);
}
return texture;
+3
View File
@@ -153,6 +153,7 @@ public:
int gizmo_count();
bool IsEnabled();
bool IsExpanded();
virtual void refresh();
@@ -212,6 +213,7 @@ public:
public slots:
void FieldChanged();
void SetEnabled(bool b);
void SetExpanded(bool e);
signals:
void EnabledChanged(bool);
private slots:
@@ -243,6 +245,7 @@ private:
int iterations;
bool enabled_;
bool expanded_;
int flags_;
+44 -9
View File
@@ -38,6 +38,8 @@
#include "dialogs/aboutdialog.h"
#include "dialogs/speeddialog.h"
#include "dialogs/actionsearch.h"
#include "dialogs/loaddialog.h"
#include "project/loadthread.h"
#include "timeline/sequence.h"
#include "ui/mediaiconservice.h"
#include "ui/mainwindow.h"
@@ -91,7 +93,7 @@ void OliveGlobal::check_for_autorecovery_file() {
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) {
enable_load_project_on_init = false;
open_project_worker(autorecovery_filename, true);
OpenProjectWorker(autorecovery_filename, true);
}
}
autorecovery_timer.setInterval(60000);
@@ -164,6 +166,33 @@ void OliveGlobal::SetNativeStyling(QWidget *w)
#endif
}
void OliveGlobal::LoadProject(const QString &fn, bool autorecovery, bool clear)
{
// 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
if (clear) {
new_project();
}
LoadDialog ld(olive::MainWindow);
LoadThread* lt = new LoadThread(fn, autorecovery, clear);
connect(&ld, SIGNAL(cancel()), lt, SLOT(cancel()));
connect(lt, SIGNAL(success()), &ld, SLOT(accept()));
connect(lt, SIGNAL(error()), &ld, SLOT(reject()));
connect(lt, SIGNAL(error()), this, SLOT(new_project()));
connect(lt, SIGNAL(report_progress(int)), &ld, SLOT(setValue(int)));
lt->start();
ld.exec();
}
void OliveGlobal::ImportProject(const QString &fn)
{
LoadProject(fn, false, false);
}
void OliveGlobal::new_project() {
if (can_close_project()) {
// clear graph editor
@@ -172,8 +201,12 @@ void OliveGlobal::new_project() {
// 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->new_project();
panel_project->clear();
// clear undo stack
olive::UndoStack.clear();
@@ -183,13 +216,16 @@ void OliveGlobal::new_project() {
// full update of all panels
update_ui(false);
// set to unmodified
olive::Global->set_modified(false);
}
}
void OliveGlobal::open_project() {
void OliveGlobal::OpenProject() {
QString fn = QFileDialog::getOpenFileName(olive::MainWindow, tr("Open Project..."), "", project_file_filter);
if (!fn.isEmpty() && can_close_project()) {
open_project_worker(fn, false);
OpenProjectWorker(fn, false);
}
}
@@ -205,7 +241,7 @@ void OliveGlobal::open_recent(int index) {
panel_project->save_recent_projects();
}
} else if (can_close_project()) {
open_project_worker(recent_url, false);
OpenProjectWorker(recent_url, false);
}
}
@@ -269,7 +305,7 @@ void OliveGlobal::finished_initialize() {
// if a project was set as a command line argument, we load it here
if (QFileInfo::exists(olive::ActiveProjectFilename)) {
open_project_worker(olive::ActiveProjectFilename, false);
OpenProjectWorker(olive::ActiveProjectFilename, false);
} else {
QMessageBox::critical(olive::MainWindow,
tr("Missing Project File"),
@@ -308,7 +344,6 @@ void OliveGlobal::open_preferences() {
panel_footage_viewer->pause();
PreferencesDialog pd(olive::MainWindow);
pd.setup_kbd_shortcuts(olive::MainWindow->menuBar());
pd.exec();
}
@@ -323,9 +358,9 @@ void OliveGlobal::set_sequence(SequencePtr s)
panel_timeline->setFocus();
}
void OliveGlobal::open_project_worker(const QString& fn, bool autorecovery) {
void OliveGlobal::OpenProjectWorker(const QString& fn, bool autorecovery) {
update_project_filename(fn);
panel_project->load_project(fn, autorecovery, true);
LoadProject(fn, autorecovery, true);
olive::UndoStack.clear();
}
+41 -2
View File
@@ -193,7 +193,18 @@ public slots:
* Confirms whether the current project can be closed, and if so, shows an open file dialog to allow the user to
* select a project file and then triggers a project load with it.
*/
void open_project();
void OpenProject();
/**
* @brief Import project from file
*
* Imports an Olive project into the current project, effectively merging them.
*
* @param fn
*
* The filename of the project to import.
*/
void ImportProject(const QString& fn);
/**
* @brief Open recent project from list
@@ -327,7 +338,35 @@ 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 open_project_worker(const QString& fn, bool autorecovery);
void OpenProjectWorker(const QString& fn, bool autorecovery);
/**
* @brief Create a LoadDialog and start a LoadThread to load data from a project
*
* Loads data from an Olive project file creating a LoadDialog to show visual information and a LoadThread to load
* outside of the main/GUI thread.
*
* All project loading functions eventually lead to this one and there's no reason to use it directly. Instead use
* one of the following functions:
*
* * OpenProject() - to check if the current project can be closed and prompt the user for the new project file
* * OpenProjectWorker() - if you already have the filename and wish to close the current project and open it
* * ImportProject() - to import a project file into this one, effectively merging them both
*
* @param fn
*
* The URL of the project file to open
*
* @param autorecovery
*
* TRUE if this file is an autorecovery file, in which case it's loaded slightly differently
*
* @param clear
*
* 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);
/**
* @brief File filter used for any file dialogs relating to Olive project files.
+3 -2
View File
@@ -46,6 +46,7 @@
#include "panels/grapheditor.h"
#include "ui/viewerwidget.h"
#include "ui/menuhelper.h"
#include "ui/icons.h"
#include "project/clipboard.h"
#include "global/config.h"
#include "ui/timelineheader.h"
@@ -374,8 +375,8 @@ void EffectControls::setup_ui() {
veHeaderLayout->setSpacing(0);
veHeaderLayout->setMargin(0);
QIcon add_effect_icon(":/icons/add-effect.svg");
QIcon add_transition_icon(":/icons/add-transition.svg");
QIcon add_effect_icon = olive::icon::CreateIconFromSVG(":/icons/add-effect.svg", false);
QIcon add_transition_icon = olive::icon::CreateIconFromSVG(":/icons/add-transition.svg", false);
btnAddVideoEffect = new QPushButton();
btnAddVideoEffect->setIcon(add_effect_icon);
+13 -31
View File
@@ -111,7 +111,7 @@ Project::Project(QWidget *parent) :
QPushButton* toolbar_open = new QPushButton();
toolbar_open->setIcon(olive::icon::CreateIconFromSVG(QStringLiteral(":/icons/open.svg")));
toolbar_open->setToolTip("Open Project");
connect(toolbar_open, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(open_project()));
connect(toolbar_open, SIGNAL(clicked(bool)), olive::Global.get(), SLOT(OpenProject()));
toolbar->addWidget(toolbar_open);
QPushButton* toolbar_save = new QPushButton();
@@ -433,27 +433,30 @@ void Project::new_sequence() {
}
MediaPtr Project::create_sequence_internal(ComboAction *ca, SequencePtr s, bool open, Media* parent) {
if (parent == nullptr) {
parent = olive::project_model.get_root();
}
MediaPtr item = std::make_shared<Media>(parent);
MediaPtr item = std::make_shared<Media>();
item->set_sequence(s);
if (ca != nullptr) {
ca->append(new AddMediaCommand(item, parent));
if (open) {
ca->append(new ChangeSequenceAction(s));
}
} else {
olive::project_model.appendChild(parent, item);
if (open) {
olive::Global->set_sequence(s);
}
}
return item;
}
QString Project::get_file_name_from_path(const QString& path) {
@@ -700,14 +703,14 @@ void Project::process_file_list(QStringList& files, bool recursive, MediaPtr rep
subdir_filenames.append(subdir_files.at(j).filePath());
}
process_file_list(subdir_filenames, true, nullptr, folder.get());
if (create_undo_action) {
ca->append(new AddMediaCommand(folder, parent));
} else {
olive::project_model.appendChild(parent, folder);
}
process_file_list(subdir_filenames, true, nullptr, folder.get());
imported = true;
} else if (!files.at(i).isEmpty()) {
@@ -724,7 +727,7 @@ void Project::process_file_list(QStringList& files, bool recursive, MediaPtr rep
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
// load the project without clearing the current one
load_project(file, false, false);
olive::Global->ImportProject(file);
}
@@ -868,7 +871,7 @@ void Project::process_file_list(QStringList& files, bool recursive, MediaPtr rep
if (replace != nullptr) {
item = replace;
} else {
item = std::make_shared<Media>(parent);
item = std::make_shared<Media>();
}
m = std::make_shared<Footage>();
@@ -891,7 +894,7 @@ void Project::process_file_list(QStringList& files, bool recursive, MediaPtr rep
if (create_undo_action) {
ca->append(new AddMediaCommand(item, parent));
} else {
parent->appendChild(item);
olive::project_model.appendChild(parent, item);
}
}
@@ -1046,27 +1049,6 @@ void Project::clear() {
tree_view->update();
}
void Project::new_project() {
// clear existing project
olive::Global->set_sequence(nullptr);
panel_footage_viewer->set_media(nullptr);
clear();
olive::Global->set_modified(false);
}
void Project::load_project(const QString& filename, bool autorecovery, bool clear) {
// 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
if (clear) {
new_project();
}
LoadDialog ld(this, filename, autorecovery, clear);
ld.exec();
}
void save_marker(QXmlStreamWriter& stream, const Marker& m) {
stream.writeStartElement("marker");
stream.writeAttribute("frame", QString::number(m.frame));
-2
View File
@@ -66,8 +66,6 @@ public:
bool reveal_media(Media *media, QModelIndex parent = QModelIndex());
void add_recent_project(QString url);
void new_project();
void load_project(const QString &filename, bool autorecovery, bool clear);
void save_project(bool autorecovery);
MediaPtr create_folder_internal(QString name);
+18 -6
View File
@@ -605,6 +605,22 @@ Media* LoadThread::find_loaded_folder_by_id(int id) {
return nullptr;
}
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;
if (parent_id == folder) {
olive::project_model.appendChild(find_loaded_folder_by_id(parent_id), item);
OrganizeFolders(parent_id);
}
}
}
void LoadThread::run() {
mutex.lock();
@@ -657,15 +673,11 @@ void LoadThread::run() {
cont = load_worker(file, stream, MEDIA_TYPE_FOLDER);
}
// load media
if (cont) {
// since folders loaded correctly, organize them appropriately
for (int i=0;i<loaded_folders.size();i++) {
MediaPtr folder = loaded_folders.at(i);
int parent = folder->temp_id2;
olive::project_model.appendChild(find_loaded_folder_by_id(parent), folder);
}
OrganizeFolders();
// load media
cont = load_worker(file, stream, MEDIA_TYPE_FOOTAGE);
}
+2
View File
@@ -37,6 +37,7 @@ class LoadThread : public QThread
public:
LoadThread(const QString& filename, bool autorecovery, bool clear);
void run();
public slots:
void cancel();
signals:
void start_question(const QString &title, const QString &text, int buttons);
@@ -75,6 +76,7 @@ private:
QVector<ClipPtr> loaded_clips;
QVector<Media*> loaded_sequences;
Media* find_loaded_folder_by_id(int id);
void OrganizeFolders(int folder = 0);
int current_element_count;
int total_element_count;
+5 -5
View File
@@ -61,10 +61,10 @@ QString get_channel_layout_name(int channels, uint64_t layout) {
}
}
Media::Media(Media* iparent) {
parent = iparent;
root = false;
type = -1;
Media::Media() :
root(false),
type(-1)
{
}
Footage* Media::to_footage() {
@@ -341,7 +341,7 @@ QVariant Media::data(int column, int role) {
}
int Media::row() const {
if (parent) {
if (parent != nullptr) {
for (int i=0;i<parent->children.size();i++) {
if (parent->children.at(i).get() == this) {
return i;
+1 -1
View File
@@ -45,7 +45,7 @@ using MediaPtr = std::shared_ptr<Media>;
class Media
{
public:
Media(Media* iparent = nullptr);
Media();
Footage *to_footage();
SequencePtr to_sequence();
+9 -9
View File
@@ -28,29 +28,29 @@ class ProjectFilter : public QSortFilterProxyModel {
public:
ProjectFilter(QObject *parent = nullptr);
// are sequences visible
// are sequences visible
bool get_show_sequences();
public slots:
// set whether sequences are visible
// set whether sequences are visible
void set_show_sequences(bool b);
// update search filter
void update_search_filter(const QString& s);
// update search filter
void update_search_filter(const QString& s);
protected:
// function that filters whether rows are displayed or not
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
// function that filters whether rows are displayed or not
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
private:
// internal variable for whether to show sequences
// internal variable for whether to show sequences
bool show_sequences;
// search filter variable
QString search_filter;
// search filter variable
QString search_filter;
};
+20 -5
View File
@@ -43,8 +43,12 @@ void ProjectModel::make_root() {
}
void ProjectModel::destroy_root() {
if (panel_sequence_viewer != nullptr) panel_sequence_viewer->viewer_widget->delete_function();
if (panel_footage_viewer != nullptr) panel_footage_viewer->viewer_widget->delete_function();
if (panel_sequence_viewer != nullptr) {
panel_sequence_viewer->viewer_widget->delete_function();
}
if (panel_footage_viewer != nullptr) {
panel_footage_viewer->viewer_widget->delete_function();
}
root_item_ = std::make_shared<Media>();
}
@@ -64,7 +68,9 @@ QVariant ProjectModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
return static_cast<Media*>(index.internalPointer())->data(index.column(), role);
Media* media = static_cast<Media*>(index.internalPointer());
return media->data(index.column(), role);
}
Qt::ItemFlags ProjectModel::flags(const QModelIndex &index) const {
@@ -191,11 +197,20 @@ void ProjectModel::set_icon(Media* m, const QIcon &ico) {
}
void ProjectModel::appendChild(Media* parent, MediaPtr child) {
QModelIndex row_start;
if (parent == nullptr) {
parent = get_root();
row_start = QModelIndex();
} else {
row_start = createIndex(parent->row(), 0, parent);
}
beginInsertRows(parent == get_root() ?
QModelIndex() : createIndex(parent->row(), 0, parent), parent->childCount(), parent->childCount());
beginInsertRows(row_start, parent->childCount(), parent->childCount());
parent->appendChild(child);
endInsertRows();
}
+1 -4
View File
@@ -271,9 +271,6 @@ void SourcesCommon::dropEvent(QWidget* parent,
const QModelIndexList& items) {
const QMimeData* mimeData = event->mimeData();
MediaPtr m = project_parent->item_to_media_ptr(drop_item);
if (m == nullptr) {
return;
}
if (mimeData->hasUrls()) {
// drag files in from outside
QList<QUrl> urls = mimeData->urls();
@@ -285,7 +282,7 @@ void SourcesCommon::dropEvent(QWidget* parent,
bool replace = false;
if (urls.size() == 1
&& drop_item.isValid()
&& m->get_type() == MEDIA_TYPE_FOOTAGE
&& (m != nullptr && m->get_type() == MEDIA_TYPE_FOOTAGE)
&& !QFileInfo(paths.at(0)).isDir()
&& olive::CurrentConfig.drop_on_media_to_replace
&& QMessageBox::question(
+8 -3
View File
@@ -87,6 +87,13 @@ bool CollapsibleWidget::IsExpanded() {
return contents->isVisible();
}
void CollapsibleWidget::SetExpanded(bool s)
{
contents->setVisible(s);
set_button_icon(s);
emit visibleChanged(s);
}
bool CollapsibleWidget::IsSelected()
{
return selected;
@@ -116,9 +123,7 @@ void CollapsibleWidget::SetTitle(const QString &s) {
}
void CollapsibleWidget::on_visible_change() {
contents->setVisible(!contents->isVisible());
set_button_icon(contents->isVisible());
emit visibleChanged();
SetExpanded(!IsExpanded());
}
CollapsibleWidgetHeader::CollapsibleWidgetHeader(QWidget* parent) : QWidget(parent), selected(false) {
+2 -1
View File
@@ -52,6 +52,7 @@ public:
void SetTitle(const QString &);
bool IsFocused();
bool IsExpanded();
void SetExpanded(bool s);
bool IsSelected();
protected:
QCheckBox* enabled_check;
@@ -68,7 +69,7 @@ private:
signals:
void deselect_others(QWidget*);
void visibleChanged();
void visibleChanged(bool);
private slots:
void on_visible_change();
+3
View File
@@ -88,6 +88,9 @@ EffectUI::EffectUI(Effect* e) :
QWidget* ui = new QWidget(this);
SetContents(ui);
SetExpanded(e->IsExpanded());
connect(this, SIGNAL(visibleChanged(bool)), e, SLOT(SetExpanded(bool)));
layout_ = new QGridLayout(ui);
layout_->setSpacing(4);
+60 -21
View File
@@ -61,8 +61,6 @@
MainWindow* olive::MainWindow;
#define DEFAULT_CSS "QPushButton::checked { background: rgb(25, 25, 25); }"
void MainWindow::setup_layout(bool reset) {
// load panels from file
if (!reset) {
@@ -424,25 +422,66 @@ void MainWindow::Restyle()
} else {
// set default palette
QPalette darkPalette;
darkPalette.setColor(QPalette::Window, QColor(53,53,53));
darkPalette.setColor(QPalette::WindowText, Qt::white);
darkPalette.setColor(QPalette::Base, QColor(25,25,25));
darkPalette.setColor(QPalette::AlternateBase, QColor(53,53,53));
darkPalette.setColor(QPalette::ToolTipBase, QColor(25,25,25));
darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Button, QColor(53,53,53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(128, 128, 128));
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218));
darkPalette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(darkPalette);
QPalette palette;
if (olive::CurrentConfig.style == olive::styling::kOliveDefaultLight) {
palette.setColor(QPalette::Window, QColor(208, 208, 208));
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Base, QColor(240, 240, 240));
palette.setColor(QPalette::AlternateBase, QColor(208, 208, 208));
palette.setColor(QPalette::ToolTipBase, QColor(255, 255, 255));
palette.setColor(QPalette::ToolTipText, Qt::black);
palette.setColor(QPalette::Text, Qt::black);
palette.setColor(QPalette::Button, QColor(208, 208, 208));
palette.setColor(QPalette::ButtonText, Qt::black);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(208, 208, 208));
palette.setColor(QPalette::Link, QColor(42, 130, 218));
palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
palette.setColor(QPalette::HighlightedText, Qt::white);
/* Olive Mid
palette.setColor(QPalette::Window, QColor(128, 128, 128));
palette.setColor(QPalette::WindowText, Qt::black);
palette.setColor(QPalette::Base, QColor(192, 192, 192));
palette.setColor(QPalette::AlternateBase, QColor(128, 128, 128));
palette.setColor(QPalette::ToolTipBase, QColor(192, 192, 192));
palette.setColor(QPalette::ToolTipText, Qt::black);
palette.setColor(QPalette::Text, Qt::black);
palette.setColor(QPalette::Button, QColor(128, 128, 128));
palette.setColor(QPalette::ButtonText, Qt::black);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(128, 128, 128));
palette.setColor(QPalette::Link, QColor(42, 130, 218));
palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
palette.setColor(QPalette::HighlightedText, Qt::black);
*/
} else {
palette.setColor(QPalette::Window, QColor(53,53,53));
palette.setColor(QPalette::WindowText, Qt::white);
palette.setColor(QPalette::Base, QColor(25,25,25));
palette.setColor(QPalette::AlternateBase, QColor(53,53,53));
palette.setColor(QPalette::ToolTipBase, QColor(25,25,25));
palette.setColor(QPalette::ToolTipText, Qt::white);
palette.setColor(QPalette::Text, Qt::white);
palette.setColor(QPalette::Button, QColor(53,53,53));
palette.setColor(QPalette::ButtonText, Qt::white);
palette.setColor(QPalette::BrightText, Qt::red);
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(128, 128, 128));
palette.setColor(QPalette::Link, QColor(42, 130, 218));
palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
palette.setColor(QPalette::HighlightedText, Qt::white);
// set default CSS
setStyleSheet("QPushButton::checked { background: rgb(25, 25, 25); }");
}
qApp->setPalette(palette);
// set default CSS
setStyleSheet(DEFAULT_CSS);
}
}
}
@@ -470,7 +509,7 @@ void MainWindow::setup_menus() {
new_menu = MenuHelper::create_submenu(file_menu);
olive::MenuHelper.make_new_menu(new_menu);
open_project = MenuHelper::create_menu_action(file_menu, "openproj", olive::Global.get(), SLOT(open_project()), QKeySequence("Ctrl+O"));
open_project = MenuHelper::create_menu_action(file_menu, "openproj", olive::Global.get(), SLOT(OpenProject()), QKeySequence("Ctrl+O"));
open_recent = MenuHelper::create_submenu(file_menu);
+8 -2
View File
@@ -316,16 +316,22 @@ void DeleteTransitionCommand::doRedo() {
AddMediaCommand::AddMediaCommand(MediaPtr iitem, Media *iparent) :
item(iitem),
parent(iparent)
parent(iparent),
done_(false)
{
doRedo();
}
void AddMediaCommand::doUndo() {
olive::project_model.removeChild(parent, item.get());
done_ = false;
}
void AddMediaCommand::doRedo() {
olive::project_model.appendChild(parent, item);
if (!done_) {
olive::project_model.appendChild(parent, item);
done_ = true;
}
}
DeleteMediaCommand::DeleteMediaCommand(MediaPtr i) :
+1
View File
@@ -213,6 +213,7 @@ public:
private:
MediaPtr item;
Media* parent;
bool done_;
};
class DeleteMediaCommand : public OliveAction {