diff --git a/app/core.cpp b/app/core.cpp index b14e4d761..d18f421b5 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -67,8 +67,7 @@ Core::Core() : main_window_(nullptr), tool_(Tool::kPointer), addable_object_(Tool::kAddableEmpty), - snapping_(true), - queue_autorecovery_(false) + snapping_(true) { } @@ -157,7 +156,7 @@ void Core::Start() if (startup_project_.isEmpty()) { // If no load project is set, create a new one on open - AddOpenProject(std::make_shared()); + CreateNewProject(); } else { OpenProjectInternal(startup_project_); } @@ -252,6 +251,19 @@ void Core::ClearOpenRecentList() recent_projects_.clear(); } +void Core::CreateNewProject() +{ + // If we already have an empty/new project, switch to it + foreach (ProjectPtr already_open, open_projects_) { + if (already_open->is_new()) { + AddOpenProject(already_open); + return; + } + } + + AddOpenProject(std::make_shared()); +} + const bool &Core::snapping() const { return snapping_; @@ -316,7 +328,7 @@ void Core::DialogPreferencesShow() void Core::DialogProjectPropertiesShow() { - ProjectPropertiesDialog ppd(main_window_); + ProjectPropertiesDialog ppd(GetActiveProject(), main_window_); ppd.exec(); } @@ -406,6 +418,21 @@ void Core::CreateNewSequence() void Core::AddOpenProject(ProjectPtr p) { + // Ensure project is not open at the moment + foreach (ProjectPtr already_open, open_projects_) { + if (already_open == p) { + // Signal UI to switch to this project + emit ProjectOpened(p.get()); + return; + } + } + + // If we currently have an empty project, close it first + if (!open_projects_.isEmpty() && open_projects_.last()->is_new()) { + CloseProject(open_projects_.last().get(), false); + } + + connect(p.get(), &Project::ModifiedChanged, this, &Core::ProjectWasModified); open_projects_.append(p); PushRecentlyOpenedProject(p->filename()); @@ -433,6 +460,27 @@ bool Core::ConfirmImageSequence(const QString& filename) return (mb.exec() == QMessageBox::Yes); } +void Core::ProjectWasModified(bool e) +{ + //Project* p = static_cast(sender()); + + if (e) { + // If this project is modified, we know for sure the window should show a "modified" flag (the * in the titlebar) + main_window_->setWindowModified(true); + } else { + // If we just set this project to "not modified", see if all projects are not modified in which case we can hide + // the modified flag + foreach (ProjectPtr open, open_projects_) { + if (open->is_modified()) { + main_window_->setWindowModified(true); + return; + } + } + + main_window_->setWindowModified(false); + } +} + void Core::DeclareTypesForQt() { qRegisterMetaType(); @@ -490,6 +538,7 @@ void Core::StartGUI(bool full_screen) // When a new project is opened, update the mainwindow connect(this, &Core::ProjectOpened, main_window_, &MainWindow::ProjectOpen); + connect(this, &Core::ProjectClosed, main_window_, &MainWindow::ProjectClose); // Start autorecovery timer using the config value as its interval SetAutorecoveryInterval(Config::Current()["AutorecoveryInterval"].toInt()); @@ -501,7 +550,7 @@ void Core::SaveProjectInternal(Project *project) // Create save manager ProjectSaveManager* psm = new ProjectSaveManager(project); - connect(psm, &Task::Succeeded, this, &Core::ProjectSaveSucceeded); + connect(psm, &ProjectSaveManager::ProjectSaveSucceeded, this, &Core::ProjectSaveSucceeded); TaskDialog* task_dialog = new TaskDialog(psm, tr("Save Project"), main_window()); task_dialog->open(); @@ -509,21 +558,22 @@ void Core::SaveProjectInternal(Project *project) void Core::SaveAutorecovery() { - if (queue_autorecovery_) { - // FIXME: Save autorecovery of projects open - - queue_autorecovery_ = false; + foreach (ProjectPtr p, open_projects_) { + if (!p->has_autorecovery_been_saved()) { + // FIXME: SAVE AN AUTORECOVERY PROJECT + p->set_autorecovery_saved(true); + } } } -void Core::ProjectSaveSucceeded() +void Core::ProjectSaveSucceeded(Project* p) { - PushRecentlyOpenedProject(GetActiveProject()->filename()); + PushRecentlyOpenedProject(p->filename()); - SetProjectModified(false); + p->set_modified(false); } -Project *Core::GetActiveProject() +Project *Core::GetActiveProject() const { ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused(); @@ -534,7 +584,7 @@ Project *Core::GetActiveProject() } } -ProjectViewModel *Core::GetActiveProjectModel() +ProjectViewModel *Core::GetActiveProjectModel() const { ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused(); @@ -545,7 +595,7 @@ ProjectViewModel *Core::GetActiveProjectModel() } } -Folder *Core::GetSelectedFolderInActiveProject() +Folder *Core::GetSelectedFolderInActiveProject() const { ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused(); @@ -568,17 +618,6 @@ void Core::SetTimecodeDisplay(Timecode::Display d) emit TimecodeDisplayChanged(d); } -void Core::SetProjectModified(bool e) -{ - main_window()->setWindowModified(e); - queue_autorecovery_ = e; -} - -bool Core::IsProjectModified() const -{ - return main_window_->isWindowModified(); -} - void Core::SetAutorecoveryInterval(int minutes) { // Convert minutes to milliseconds @@ -599,38 +638,19 @@ bool Core::SaveActiveProject() { Project* active_project = GetActiveProject(); - if (!active_project) { - return false; + if (active_project) { + return SaveProject(active_project); } - if (active_project->filename().isEmpty()) { - return SaveActiveProjectAs(); - } else { - SaveProjectInternal(active_project); - - return true; - } + return false; } bool Core::SaveActiveProjectAs() { Project* active_project = GetActiveProject(); - if (!active_project) { - return false; - } - - QString fn = QFileDialog::getSaveFileName(main_window_, - tr("Save Project As"), - QString(), - GetProjectFilter()); - - if (!fn.isEmpty()) { - active_project->set_filename(fn); - - SaveProjectInternal(active_project); - - return true; + if (active_project) { + return SaveProjectAs(active_project); } return false; @@ -722,6 +742,35 @@ QString Core::GetRecentProjectsFilePath() return QDir(GetConfigurationLocation()).filePath(QStringLiteral("recent")); } +bool Core::SaveProject(Project *p) +{ + if (p->filename().isEmpty()) { + return SaveProjectAs(p); + } else { + SaveProjectInternal(p); + + return true; + } +} + +bool Core::SaveProjectAs(Project *p) +{ + QString fn = QFileDialog::getSaveFileName(main_window_, + tr("Save Project As"), + QString(), + GetProjectFilter()); + + if (!fn.isEmpty()) { + p->set_filename(fn); + + SaveProjectInternal(p); + + return true; + } + + return false; +} + void Core::PushRecentlyOpenedProject(const QString& s) { if (s.isEmpty()) { @@ -739,6 +788,15 @@ void Core::PushRecentlyOpenedProject(const QString& s) void Core::OpenProjectInternal(const QString &filename) { + // See if this project is open already + foreach (ProjectPtr p, open_projects_) { + if (p->filename() == filename) { + // This project is already open + AddOpenProject(p); + return; + } + } + ProjectLoadManager* plm = new ProjectLoadManager(filename); // We use a blocking queued connection here because we want to ensure we have this project instance before the @@ -818,6 +876,60 @@ void Core::OpenProjectFromRecentList(int index) } } +bool Core::CloseProject(Project *p, bool auto_open_new) +{ + for (int i=0;iis_modified()) { + QMessageBox mb(main_window_); + + mb.setIcon(QMessageBox::Question); + mb.setWindowTitle(tr("Unsaved Changes")); + mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?") + .arg(p->name())); + + QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole); + mb.addButton(tr("Don't Save"), QMessageBox::NoRole); + QPushButton* cancel_btn = mb.addButton(QMessageBox::Cancel); + + mb.exec(); + + if (mb.clickedButton() == cancel_btn + || (mb.clickedButton() == yes_btn && !SaveActiveProject())) { + // Don't close if the user clicked cancel on this messagebox OR they cancelled a save as operation + return false; + } + } + + disconnect(p, &Project::ModifiedChanged, this, &Core::ProjectWasModified); + emit ProjectClosed(p); + open_projects_.removeAt(i); + break; + } + } + + // Ensure a project is always active + if (auto_open_new && open_projects_.isEmpty()) { + CreateNewProject(); + } + + return true; +} + +bool Core::CloseAllProjects(bool auto_open_new) +{ + QList copy = open_projects_; + + foreach (ProjectPtr p, copy) { + if (!CloseProject(p.get(), auto_open_new)) { + return false; + } + } + + return true; +} + void Core::OpenProject() { QString file = QFileDialog::getOpenFileName(main_window_, diff --git a/app/core.h b/app/core.h index 2725682a2..b45c85755 100644 --- a/app/core.h +++ b/app/core.h @@ -134,9 +134,9 @@ public: * * The active Project file, or nullptr if the heuristic couldn't find one. */ - Project* GetActiveProject(); - ProjectViewModel* GetActiveProjectModel(); - Folder* GetSelectedFolderInActiveProject(); + Project* GetActiveProject() const; + ProjectViewModel* GetActiveProjectModel() const; + Folder* GetSelectedFolderInActiveProject() const; /** * @brief Gets current timecode display mode @@ -148,16 +148,6 @@ public: */ void SetTimecodeDisplay(Timecode::Display d); - /** - * @brief Sets state to "modified" so that the GUI will prompt the user to save before closing - * - * Call this function whenever a change is made to a currently active project. Saving the project will automatically - * unset this. - */ - void SetProjectModified(bool e); - - bool IsProjectModified() const; - /** * @brief Set how frequently an autorecovery should be saved (if the project has changed, see SetProjectModified()) */ @@ -216,6 +206,16 @@ public: */ void OpenProjectFromRecentList(int index); + /** + * @brief Closes a project + */ + bool CloseProject(Project* p, bool auto_open_new); + + /** + * @brief Closes all open projects + */ + bool CloseAllProjects(bool auto_open_new); + public slots: /** * @brief Starts an open file dialog to load a project from file @@ -291,6 +291,11 @@ public slots: */ void ClearOpenRecentList(); + /** + * @brief Creates a new empty project and opens it + */ + void CreateNewProject(); + signals: /** * @brief Signal emitted when a project is opened @@ -301,6 +306,11 @@ signals: */ void ProjectOpened(Project* p); + /** + * @brief Signal emitted when a project is closed + */ + void ProjectClosed(Project* p); + /** * @brief Signal emitted when the tool is changed from somewhere */ @@ -327,6 +337,16 @@ private: */ static QString GetRecentProjectsFilePath(); + /** + * @brief Saves a specific project + */ + bool SaveProject(Project* p); + + /** + * @brief Performs a "save as" on a specific project + */ + bool SaveProjectAs(Project* p); + /** * @brief Adds a filename to the top of the recently opened projects list (or moves it if it already exists) */ @@ -391,14 +411,6 @@ private: */ bool snapping_; - /** - * @brief Internal value for whether to make an autorecovery next interval - * - * True if the project has changed since the last autorecovery and we should save next time. False if the project has - * not changed and saving another autorecovery would be a waste. - */ - bool queue_autorecovery_; - /** * @brief Internal timer for saving autorecovery files */ @@ -422,7 +434,7 @@ private: private slots: void SaveAutorecovery(); - void ProjectSaveSucceeded(); + void ProjectSaveSucceeded(Project *p); /** * @brief Adds a project to the "open projects" list @@ -433,6 +445,8 @@ private slots: bool ConfirmImageSequence(const QString &filename); + void ProjectWasModified(bool e); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/dialog/footageproperties/footageproperties.cpp b/app/dialog/footageproperties/footageproperties.cpp index 7e935d120..2134b3666 100644 --- a/app/dialog/footageproperties/footageproperties.cpp +++ b/app/dialog/footageproperties/footageproperties.cpp @@ -143,6 +143,11 @@ FootagePropertiesDialog::FootageChangeCommand::FootageChangeCommand(Footage *foo { } +Project *FootagePropertiesDialog::FootageChangeCommand::GetRelevantProject() const +{ + return footage_->project(); +} + void FootagePropertiesDialog::FootageChangeCommand::redo_internal() { old_name_ = footage_->name(); @@ -163,6 +168,11 @@ FootagePropertiesDialog::StreamEnableChangeCommand::StreamEnableChangeCommand(St { } +Project *FootagePropertiesDialog::StreamEnableChangeCommand::GetRelevantProject() const +{ + return stream_->footage()->project(); +} + void FootagePropertiesDialog::StreamEnableChangeCommand::redo_internal() { stream_->set_enabled(new_enabled_); diff --git a/app/dialog/footageproperties/footageproperties.h b/app/dialog/footageproperties/footageproperties.h index 9aeaf6e22..d2018699f 100644 --- a/app/dialog/footageproperties/footageproperties.h +++ b/app/dialog/footageproperties/footageproperties.h @@ -62,6 +62,8 @@ private: const QString& name, QUndoCommand *command = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -79,6 +81,8 @@ private: bool enabled, QUndoCommand* command = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index a3a6ae53a..47317e0fc 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -158,6 +158,11 @@ VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(ImageS { } +Project *VideoStreamProperties::VideoStreamChangeCommand::GetRelevantProject() const +{ + return stream_->footage()->project(); +} + void VideoStreamProperties::VideoStreamChangeCommand::redo_internal() { old_premultiplied_ = stream_->premultiplied_alpha(); @@ -181,6 +186,11 @@ VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(Vi { } +Project *VideoStreamProperties::ImageSequenceChangeCommand::GetRelevantProject() const +{ + return video_stream_->footage()->project(); +} + void VideoStreamProperties::ImageSequenceChangeCommand::redo_internal() { old_start_index_ = video_stream_->start_time(); diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h index 6472c6958..6bd164ab8 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -75,6 +75,8 @@ private: QString colorspace, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -97,6 +99,8 @@ private: int64_t duration, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp index 751720f8b..c4dfadf9f 100644 --- a/app/dialog/projectproperties/projectproperties.cpp +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -36,9 +36,9 @@ namespace OCIO = OCIO_NAMESPACE::v1; OLIVE_NAMESPACE_ENTER -ProjectPropertiesDialog::ProjectPropertiesDialog(QWidget *parent) : +ProjectPropertiesDialog::ProjectPropertiesDialog(Project* p, QWidget *parent) : QDialog(parent), - working_project_(Core::instance()->GetActiveProject()) + working_project_(p) { QVBoxLayout* layout = new QVBoxLayout(this); diff --git a/app/dialog/projectproperties/projectproperties.h b/app/dialog/projectproperties/projectproperties.h index 5fa2fe0d9..8df9dd281 100644 --- a/app/dialog/projectproperties/projectproperties.h +++ b/app/dialog/projectproperties/projectproperties.h @@ -33,7 +33,7 @@ class ProjectPropertiesDialog : public QDialog { Q_OBJECT public: - ProjectPropertiesDialog(QWidget* parent); + ProjectPropertiesDialog(Project *p, QWidget* parent); public slots: virtual void accept() override; diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index 059e9e503..b4b637bbf 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -211,6 +211,11 @@ SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence* s, { } +Project *SequenceDialog::SequenceParamCommand::GetRelevantProject() const +{ + return sequence_->project(); +} + void SequenceDialog::SequenceParamCommand::redo_internal() { sequence_->set_video_params(new_video_params_); diff --git a/app/dialog/sequence/sequence.h b/app/dialog/sequence/sequence.h index a7903ba70..228b87a13 100644 --- a/app/dialog/sequence/sequence.h +++ b/app/dialog/sequence/sequence.h @@ -122,6 +122,8 @@ private: const QString& name, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/dialog/speedduration/speedduration.cpp b/app/dialog/speedduration/speedduration.cpp index 7ae37e71f..4c6fc4ac9 100644 --- a/app/dialog/speedduration/speedduration.cpp +++ b/app/dialog/speedduration/speedduration.cpp @@ -360,6 +360,11 @@ BlockReverseCommand::BlockReverseCommand(Block *block, QUndoCommand *parent) : { } +Project *BlockReverseCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockReverseCommand::redo_internal() { block_->set_media_in(block_->media_out()); diff --git a/app/dialog/speedduration/speedduration.h b/app/dialog/speedduration/speedduration.h index 21b17e775..28a6badc1 100644 --- a/app/dialog/speedduration/speedduration.h +++ b/app/dialog/speedduration/speedduration.h @@ -70,6 +70,8 @@ class BlockReverseCommand : public UndoCommand { public: BlockReverseCommand(Block* block, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/node/output/track/tracklist.h b/app/node/output/track/tracklist.h index 6cde374f8..283394909 100644 --- a/app/node/output/track/tracklist.h +++ b/app/node/output/track/tracklist.h @@ -50,6 +50,8 @@ public: int TrackCount() const; + NodeGraph* GetParentGraph() const; + signals: void BlockAdded(Block* block, int index); @@ -66,8 +68,6 @@ signals: void TrackHeightChanged(int index, int height); private: - NodeGraph* GetParentGraph() const; - /** * @brief A cache of connected Tracks */ diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index e17e181e6..cbf552c5f 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -82,6 +82,7 @@ void ProjectPanel::set_project(Project *p) if (project()) { disconnect(project(), &Project::NameChanged, this, &ProjectPanel::UpdateSubtitle); disconnect(project(), &Project::NameChanged, this, &ProjectPanel::ProjectNameChanged); + disconnect(project(), &Project::ModifiedChanged, this, &ProjectPanel::setWindowModified); } explorer_->set_project(p); @@ -89,9 +90,16 @@ void ProjectPanel::set_project(Project *p) if (project()) { connect(project(), &Project::NameChanged, this, &ProjectPanel::UpdateSubtitle); connect(project(), &Project::NameChanged, this, &ProjectPanel::ProjectNameChanged); + connect(project(), &Project::ModifiedChanged, this, &ProjectPanel::setWindowModified); } UpdateSubtitle(); + + if (p) { + setWindowModified(p->is_modified()); + } else { + setWindowModified(false); + } } QList ProjectPanel::SelectedItems() @@ -182,7 +190,7 @@ void ProjectPanel::UpdateSubtitle() if (project() == nullptr) { SetSubtitle(tr("(none)")); } else { - SetSubtitle(project()->name()); + SetSubtitle(QStringLiteral("[*]%1").arg(project()->name())); } } diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index f6c77a1eb..f701d3267 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -26,8 +26,7 @@ OLIVE_NAMESPACE_ENTER TimelinePanel::TimelinePanel(QWidget *parent) : - TimeBasedPanel(parent), - signal_instead_of_close_(false) + TimeBasedPanel(parent) { // FIXME: This won't work if there's ever more than one of this panel setObjectName("TimelinePanel"); @@ -168,11 +167,6 @@ void TimelinePanel::OverwriteFootageAtPlayhead(const QList &footage) static_cast(GetTimeBasedWidget())->OverwriteFootageAtPlayhead(footage); } -void TimelinePanel::SetSignalInsteadOfClose(bool e) -{ - signal_instead_of_close_ = e; -} - void TimelinePanel::Retranslate() { TimeBasedPanel::Retranslate(); @@ -180,14 +174,4 @@ void TimelinePanel::Retranslate() SetTitle(tr("Timeline")); } -void TimelinePanel::closeEvent(QCloseEvent *event) -{ - if (signal_instead_of_close_) { - event->ignore(); - emit CloseRequested(); - } else { - PanelWidget::closeEvent(event); - } -} - OLIVE_NAMESPACE_EXIT diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index 83a53954d..8739b6eb5 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -83,21 +83,12 @@ public: void OverwriteFootageAtPlayhead(const QList &footage); - void SetSignalInsteadOfClose(bool e); - protected: virtual void Retranslate() override; - virtual void closeEvent(QCloseEvent* event) override; - signals: void SelectionChanged(const QList& selected_blocks); - void CloseRequested(); - -private: - bool signal_instead_of_close_; - }; OLIVE_NAMESPACE_EXIT diff --git a/app/project/item/item.cpp b/app/project/item/item.cpp index 734fd2a99..cfb5bd915 100644 --- a/app/project/item/item.cpp +++ b/app/project/item/item.cpp @@ -132,7 +132,7 @@ const Item *Item::root() const { const Item* item = this; - while (item->parent() != nullptr) { + while (item->parent()) { item = item->parent(); } diff --git a/app/project/project.cpp b/app/project/project.cpp index 15d899401..794ee37a2 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -30,7 +30,9 @@ OLIVE_NAMESPACE_ENTER -Project::Project() +Project::Project() : + is_modified_(false), + autorecovery_saved_(true) { root_.set_project(this); } @@ -156,4 +158,32 @@ QList Project::get_items_of_type(Item::Type type) const return root_.get_children_of_type(type, true); } +bool Project::is_modified() const +{ + return is_modified_; +} + +void Project::set_modified(bool e) +{ + is_modified_ = e; + autorecovery_saved_ = !e; + + emit ModifiedChanged(is_modified_); +} + +bool Project::has_autorecovery_been_saved() const +{ + return autorecovery_saved_; +} + +void Project::set_autorecovery_saved(bool e) +{ + autorecovery_saved_ = e; +} + +bool Project::is_new() const +{ + return !is_modified_ && filename_.isEmpty(); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/project/project.h b/app/project/project.h index 6be15699f..b172cef18 100644 --- a/app/project/project.h +++ b/app/project/project.h @@ -68,9 +68,19 @@ public: QList get_items_of_type(Item::Type type) const; + bool is_modified() const; + void set_modified(bool e); + + bool has_autorecovery_been_saved() const; + void set_autorecovery_saved(bool e); + + bool is_new() const; + signals: void NameChanged(); + void ModifiedChanged(bool e); + private: Folder root_; @@ -80,6 +90,10 @@ private: ColorManager color_manager_; + bool is_modified_; + + bool autorecovery_saved_; + }; using ProjectPtr = std::shared_ptr; diff --git a/app/project/projectsavemanager.cpp b/app/project/projectsavemanager.cpp index fb7bc017b..b6ebb12d8 100644 --- a/app/project/projectsavemanager.cpp +++ b/app/project/projectsavemanager.cpp @@ -55,6 +55,7 @@ void ProjectSaveManager::Action() } emit Succeeded(); + emit ProjectSaveSucceeded(project_); } OLIVE_NAMESPACE_EXIT diff --git a/app/project/projectsavemanager.h b/app/project/projectsavemanager.h index 4eab63f8a..4e0560ee2 100644 --- a/app/project/projectsavemanager.h +++ b/app/project/projectsavemanager.h @@ -32,6 +32,9 @@ class ProjectSaveManager : public Task public: ProjectSaveManager(Project* project); +signals: + void ProjectSaveSucceeded(Project* p); + protected: virtual void Action() override; diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index 0f8307608..2dee64116 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -511,6 +511,11 @@ ProjectViewModel::MoveItemCommand::MoveItemCommand(ProjectViewModel *model, setText(tr("Move Item")); } +Project *ProjectViewModel::MoveItemCommand::GetRelevantProject() const +{ + return model_->project(); +} + void ProjectViewModel::MoveItemCommand::redo_internal() { model_->MoveItemInternal(item_, destination_); @@ -532,6 +537,11 @@ ProjectViewModel::RenameItemCommand::RenameItemCommand(ProjectViewModel* model, setText(tr("Rename Item")); } +Project *ProjectViewModel::RenameItemCommand::GetRelevantProject() const +{ + return model_->project(); +} + void ProjectViewModel::RenameItemCommand::redo_internal() { model_->RenameChild(item_, new_name_); @@ -551,6 +561,11 @@ ProjectViewModel::AddItemCommand::AddItemCommand(ProjectViewModel* model, Item* { } +Project *ProjectViewModel::AddItemCommand::GetRelevantProject() const +{ + return model_->project(); +} + void ProjectViewModel::AddItemCommand::redo_internal() { model_->AddChild(parent_, child_); @@ -572,6 +587,11 @@ ProjectViewModel::RemoveItemCommand::RemoveItemCommand(ProjectViewModel *model, { } +Project *ProjectViewModel::RemoveItemCommand::GetRelevantProject() const +{ + return model_->project(); +} + void ProjectViewModel::RemoveItemCommand::redo_internal() { parent_ = item_->parent(); diff --git a/app/project/projectviewmodel.h b/app/project/projectviewmodel.h index 6042f7e5c..4e300e7ad 100644 --- a/app/project/projectviewmodel.h +++ b/app/project/projectviewmodel.h @@ -114,6 +114,8 @@ public: public: MoveItemCommand(ProjectViewModel* model, Item* item, Folder* destination, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; @@ -134,6 +136,8 @@ public: public: RenameItemCommand(ProjectViewModel* model, Item* item, const QString& name, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; @@ -153,6 +157,8 @@ public: public: AddItemCommand(ProjectViewModel* model, Item* folder, ItemPtr child, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; @@ -172,6 +178,8 @@ public: public: RemoveItemCommand(ProjectViewModel* model, ItemPtr item, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; diff --git a/app/ui/style/style.cpp b/app/ui/style/style.cpp index c3273b70b..b85ee80f1 100644 --- a/app/ui/style/style.cpp +++ b/app/ui/style/style.cpp @@ -42,14 +42,14 @@ QList StyleManager::ListInternal() return style_list; } +#ifdef Q_OS_WINDOWS void StyleManager::UseNativeWindowsStyling(QWidget *widget) { -#ifdef Q_OS_WINDOWS QStyle* s = QStyleFactory::create(QStringLiteral("windowsvista")); widget->setStyle(s); widget->setPalette(s->standardPalette()); -#endif } +#endif QPalette StyleManager::ParsePalette(const QString& ini_path) { diff --git a/app/ui/style/style.h b/app/ui/style/style.h index b82452b3d..7b146886d 100644 --- a/app/ui/style/style.h +++ b/app/ui/style/style.h @@ -52,7 +52,9 @@ public: static QList ListInternal(); +#ifdef Q_OS_WINDOWS static void UseNativeWindowsStyling(QWidget* widget); +#endif private: static QPalette ParsePalette(const QString& ini_path); diff --git a/app/undo/undocommand.cpp b/app/undo/undocommand.cpp index a380ea6d6..38c81a851 100644 --- a/app/undo/undocommand.cpp +++ b/app/undo/undocommand.cpp @@ -33,15 +33,15 @@ void UndoCommand::redo() { redo_internal(); - modified_ = Core::instance()->IsProjectModified(); - Core::instance()->SetProjectModified(true); + modified_ = GetRelevantProject()->is_modified(); + GetRelevantProject()->set_modified(true); } void UndoCommand::undo() { undo_internal(); - Core::instance()->SetProjectModified(modified_); + GetRelevantProject()->set_modified(modified_); } void UndoCommand::redo_internal() diff --git a/app/undo/undocommand.h b/app/undo/undocommand.h index 762e79e59..2da3b0c4f 100644 --- a/app/undo/undocommand.h +++ b/app/undo/undocommand.h @@ -24,6 +24,7 @@ #include #include "common/define.h" +#include "project/project.h" OLIVE_NAMESPACE_ENTER @@ -35,6 +36,8 @@ public: virtual void redo() override; virtual void undo() override; + virtual Project* GetRelevantProject() const = 0; + protected: virtual void redo_internal(); virtual void undo_internal(); diff --git a/app/widget/keyframeview/keyframeviewundo.cpp b/app/widget/keyframeview/keyframeviewundo.cpp index d08b1d44d..2ad37e0de 100644 --- a/app/widget/keyframeview/keyframeviewundo.cpp +++ b/app/widget/keyframeview/keyframeviewundo.cpp @@ -20,6 +20,10 @@ #include "keyframeviewundo.h" +#include "node/input.h" +#include "node/node.h" +#include "project/item/sequence/sequence.h" + OLIVE_NAMESPACE_ENTER KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand *parent) : @@ -30,6 +34,11 @@ KeyframeSetTypeCommand::KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe { } +Project *KeyframeSetTypeCommand::GetRelevantProject() const +{ + return static_cast(key_->parent()->parentNode()->parent())->project(); +} + void KeyframeSetTypeCommand::redo_internal() { key_->set_type(new_type_); @@ -58,6 +67,11 @@ KeyframeSetBezierControlPoint::KeyframeSetBezierControlPoint(NodeKeyframePtr key { } +Project *KeyframeSetBezierControlPoint::GetRelevantProject() const +{ + return static_cast(key_->parent()->parentNode()->parent())->project(); +} + void KeyframeSetBezierControlPoint::redo_internal() { key_->set_bezier_control(mode_, new_point_); diff --git a/app/widget/keyframeview/keyframeviewundo.h b/app/widget/keyframeview/keyframeviewundo.h index 54d021a5a..6c7610866 100644 --- a/app/widget/keyframeview/keyframeviewundo.h +++ b/app/widget/keyframeview/keyframeviewundo.h @@ -30,6 +30,8 @@ class KeyframeSetTypeCommand : public UndoCommand { public: KeyframeSetTypeCommand(NodeKeyframePtr key, NodeKeyframe::Type type, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -48,6 +50,8 @@ public: KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& point, QUndoCommand* parent = nullptr); KeyframeSetBezierControlPoint(NodeKeyframePtr key, NodeKeyframe::BezierType mode, const QPointF& new_point, const QPointF& old_point, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/widget/menu/menu.cpp b/app/widget/menu/menu.cpp index 8ff9aa77b..fd4b71a6e 100644 --- a/app/widget/menu/menu.cpp +++ b/app/widget/menu/menu.cpp @@ -41,13 +41,13 @@ Menu::Menu(Menu *menu) Menu::Menu(QWidget *parent) : QMenu(parent) { - StyleManager::UseNativeWindowsStyling(this); + Init(); } Menu::Menu(const QString &s, QWidget *parent) : QMenu(s, parent) { - StyleManager::UseNativeWindowsStyling(this); + Init(); } QAction* Menu::InsertAlphabetically(const QString &s) @@ -101,7 +101,9 @@ void Menu::SetBooleanAction(QAction *a, bool* boolean) void Menu::Init() { +#ifdef Q_OS_WINDOWS StyleManager::UseNativeWindowsStyling(this); +#endif } OLIVE_NAMESPACE_EXIT diff --git a/app/widget/menu/menushared.cpp b/app/widget/menu/menushared.cpp index c493dc853..96015d2b8 100644 --- a/app/widget/menu/menushared.cpp +++ b/app/widget/menu/menushared.cpp @@ -31,7 +31,7 @@ MenuShared* MenuShared::instance_ = nullptr; MenuShared::MenuShared() { // "New" menu shared items - new_project_item_ = Menu::CreateItem(this, "newproj", this, &MenuShared::NewProjectTriggered, "Ctrl+N"); + new_project_item_ = Menu::CreateItem(this, "newproj", Core::instance(), &Core::CreateNewProject, "Ctrl+N"); new_sequence_item_ = Menu::CreateItem(this, "newseq", Core::instance(), &Core::CreateNewSequence, "Ctrl+Shift+N"); new_folder_item_ = Menu::CreateItem(this, "newfolder", Core::instance(), &Core::CreateNewFolder); @@ -114,11 +114,6 @@ MenuShared *MenuShared::instance() return instance_; } -void MenuShared::NewProjectTriggered() -{ - qDebug() << "FIXME: Stub"; -} - void MenuShared::SplitAtPlayheadTriggered() { TimelinePanel* timeline = PanelManager::instance()->MostRecentlyFocused(); diff --git a/app/widget/menu/menushared.h b/app/widget/menu/menushared.h index 12af66511..81848b3db 100644 --- a/app/widget/menu/menushared.h +++ b/app/widget/menu/menushared.h @@ -77,8 +77,6 @@ private: static MenuShared* instance_; private slots: - void NewProjectTriggered(); - void SplitAtPlayheadTriggered(); void DeleteSelectedTriggered(); diff --git a/app/widget/nodeparamview/nodeparamviewundo.cpp b/app/widget/nodeparamview/nodeparamviewundo.cpp index 606dfaac2..ad55dd256 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.cpp +++ b/app/widget/nodeparamview/nodeparamviewundo.cpp @@ -20,6 +20,9 @@ #include "nodeparamviewundo.h" +#include "node/node.h" +#include "project/item/sequence/sequence.h" + OLIVE_NAMESPACE_ENTER NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, bool setting, QUndoCommand *parent) : @@ -30,6 +33,11 @@ NodeParamSetKeyframingCommand::NodeParamSetKeyframingCommand(NodeInput *input, b Q_ASSERT(setting != input_->is_keyframing()); } +Project *NodeParamSetKeyframingCommand::GetRelevantProject() const +{ + return static_cast(input_->parentNode()->parent())->project(); +} + void NodeParamSetKeyframingCommand::redo_internal() { input_->set_is_keyframing(setting_); @@ -57,6 +65,11 @@ NodeParamSetKeyframeValueCommand::NodeParamSetKeyframeValueCommand(NodeKeyframeP } +Project *NodeParamSetKeyframeValueCommand::GetRelevantProject() const +{ + return static_cast(key_->parent()->parentNode()->parent())->project(); +} + void NodeParamSetKeyframeValueCommand::redo_internal() { key_->set_value(new_value_); @@ -83,6 +96,11 @@ NodeParamInsertKeyframeCommand::NodeParamInsertKeyframeCommand(NodeInput *input, { } +Project *NodeParamInsertKeyframeCommand::GetRelevantProject() const +{ + return static_cast(input_->parentNode()->parent())->project(); +} + void NodeParamInsertKeyframeCommand::redo_internal() { if (!done_) { @@ -103,6 +121,11 @@ NodeParamRemoveKeyframeCommand::NodeParamRemoveKeyframeCommand(NodeInput *input, { } +Project *NodeParamRemoveKeyframeCommand::GetRelevantProject() const +{ + return static_cast(input_->parentNode()->parent())->project(); +} + void NodeParamRemoveKeyframeCommand::redo_internal() { input_->remove_keyframe(keyframe_); @@ -129,6 +152,11 @@ NodeParamSetKeyframeTimeCommand::NodeParamSetKeyframeTimeCommand(NodeKeyframePtr { } +Project *NodeParamSetKeyframeTimeCommand::GetRelevantProject() const +{ + return static_cast(key_->parent()->parentNode()->parent())->project(); +} + void NodeParamSetKeyframeTimeCommand::redo_internal() { key_->set_time(new_time_); @@ -157,6 +185,11 @@ NodeParamSetStandardValueCommand::NodeParamSetStandardValueCommand(NodeInput *in { } +Project *NodeParamSetStandardValueCommand::GetRelevantProject() const +{ + return static_cast(input_->parentNode()->parent())->project(); +} + void NodeParamSetStandardValueCommand::redo_internal() { input_->set_standard_value(new_value_, track_); diff --git a/app/widget/nodeparamview/nodeparamviewundo.h b/app/widget/nodeparamview/nodeparamviewundo.h index 9190d896a..2ce5b5150 100644 --- a/app/widget/nodeparamview/nodeparamviewundo.h +++ b/app/widget/nodeparamview/nodeparamviewundo.h @@ -30,6 +30,8 @@ class NodeParamSetKeyframingCommand : public UndoCommand { public: NodeParamSetKeyframingCommand(NodeInput* input, bool setting, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -44,6 +46,8 @@ public: NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr); NodeParamInsertKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, bool already_done, QUndoCommand *parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -61,6 +65,8 @@ class NodeParamRemoveKeyframeCommand : public UndoCommand { public: NodeParamRemoveKeyframeCommand(NodeInput* input, NodeKeyframePtr keyframe, QUndoCommand *parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -77,6 +83,8 @@ public: NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& time, QUndoCommand* parent = nullptr); NodeParamSetKeyframeTimeCommand(NodeKeyframePtr key, const rational& new_time, const rational& old_time, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -94,6 +102,8 @@ public: NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& value, QUndoCommand* parent = nullptr); NodeParamSetKeyframeValueCommand(NodeKeyframePtr key, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -111,6 +121,8 @@ public: NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& value, QUndoCommand* parent = nullptr); NodeParamSetStandardValueCommand(NodeInput* input, int track, const QVariant& new_value, const QVariant& old_value, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/widget/nodeview/nodeviewundo.cpp b/app/widget/nodeview/nodeviewundo.cpp index 54e0c352d..b3eef2300 100644 --- a/app/widget/nodeview/nodeviewundo.cpp +++ b/app/widget/nodeview/nodeviewundo.cpp @@ -20,6 +20,8 @@ #include "nodeviewundo.h" +#include "project/item/sequence/sequence.h" + OLIVE_NAMESPACE_ENTER NodeEdgeAddCommand::NodeEdgeAddCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : @@ -59,6 +61,11 @@ void NodeEdgeAddCommand::undo_internal() done_ = false; } +Project *NodeEdgeAddCommand::GetRelevantProject() const +{ + return static_cast(output_->parentNode()->parent())->project(); +} + NodeEdgeRemoveCommand::NodeEdgeRemoveCommand(NodeOutput *output, NodeInput *input, QUndoCommand *parent) : UndoCommand(parent), output_(output), @@ -95,6 +102,11 @@ void NodeEdgeRemoveCommand::undo_internal() done_ = false; } +Project *NodeEdgeRemoveCommand::GetRelevantProject() const +{ + return static_cast(output_->parentNode()->parent())->project(); +} + NodeAddCommand::NodeAddCommand(NodeGraph *graph, Node *node, QUndoCommand *parent) : UndoCommand(parent), graph_(graph), @@ -114,6 +126,11 @@ void NodeAddCommand::undo_internal() graph_->TakeNode(node_, &memory_manager_); } +Project *NodeAddCommand::GetRelevantProject() const +{ + return static_cast(graph_)->project(); +} + NodeRemoveCommand::NodeRemoveCommand(NodeGraph *graph, const QList &nodes, QUndoCommand *parent) : UndoCommand(parent), graph_(graph), @@ -156,6 +173,11 @@ void NodeRemoveCommand::undo_internal() edges_.clear(); } +Project *NodeRemoveCommand::GetRelevantProject() const +{ + return static_cast(graph_)->project(); +} + NodeRemoveWithExclusiveDeps::NodeRemoveWithExclusiveDeps(NodeGraph *graph, Node *node, QUndoCommand *parent) : UndoCommand(parent) { @@ -166,8 +188,13 @@ NodeRemoveWithExclusiveDeps::NodeRemoveWithExclusiveDeps(NodeGraph *graph, Node remove_command_ = new NodeRemoveCommand(graph, node_and_its_deps, this); } +Project *NodeRemoveWithExclusiveDeps::GetRelevantProject() const +{ + return remove_command_->GetRelevantProject(); +} + NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include_connections, QUndoCommand *parent) : - UndoCommand(parent), + QUndoCommand(parent), src_(src), dest_(dest), include_connections_(include_connections) @@ -175,14 +202,14 @@ NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, bool include } NodeCopyInputsCommand::NodeCopyInputsCommand(Node *src, Node *dest, QUndoCommand *parent) : - UndoCommand(parent), + QUndoCommand(parent), src_(src), dest_(dest), include_connections_(true) { } -void NodeCopyInputsCommand::redo_internal() +void NodeCopyInputsCommand::redo() { Node::CopyInputs(src_, dest_, include_connections_); } diff --git a/app/widget/nodeview/nodeviewundo.h b/app/widget/nodeview/nodeviewundo.h index 48910fdcc..4a9faefd4 100644 --- a/app/widget/nodeview/nodeviewundo.h +++ b/app/widget/nodeview/nodeviewundo.h @@ -39,6 +39,8 @@ class NodeEdgeAddCommand : public UndoCommand { public: NodeEdgeAddCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -62,6 +64,8 @@ public: NodeEdgeRemoveCommand(NodeOutput* output, NodeInput* input, QUndoCommand* parent = nullptr); NodeEdgeRemoveCommand(NodeEdgePtr edge, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -77,6 +81,8 @@ class NodeAddCommand : public UndoCommand { public: NodeAddCommand(NodeGraph* graph, Node* node, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -94,6 +100,8 @@ public: const QList& nodes, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -112,11 +120,13 @@ public: Node* node, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + private: NodeRemoveCommand* remove_command_; }; -class NodeCopyInputsCommand : public UndoCommand { +class NodeCopyInputsCommand : public QUndoCommand { public: NodeCopyInputsCommand(Node* src, Node* dest, @@ -128,7 +138,7 @@ public: QUndoCommand* parent = nullptr); protected: - virtual void redo_internal() override; + virtual void redo() override; private: Node* src_; diff --git a/app/widget/panel/panel.cpp b/app/widget/panel/panel.cpp index b557e8b54..d73dc02db 100644 --- a/app/widget/panel/panel.cpp +++ b/app/widget/panel/panel.cpp @@ -20,6 +20,8 @@ #include "panel.h" +#include +#include #include #include #include @@ -29,9 +31,12 @@ OLIVE_NAMESPACE_ENTER PanelWidget::PanelWidget(QWidget *parent) : QDockWidget(parent), - border_visible_(false) + border_visible_(false), + signal_instead_of_close_(false) { setFocusPolicy(Qt::ClickFocus); + + connect(this, &PanelWidget::visibilityChanged, this, &PanelWidget::PanelVisibilityChanged); } void PanelWidget::SetMovementLocked(bool locked) @@ -104,6 +109,28 @@ void PanelWidget::UpdateTitle() } } +void PanelWidget::PanelVisibilityChanged(bool e) +{ + if (e) { + setFocus(); + } +} + +void PanelWidget::SetSignalInsteadOfClose(bool e) +{ + signal_instead_of_close_ = e; +} + +void PanelWidget::closeEvent(QCloseEvent *event) +{ + if (signal_instead_of_close_) { + event->ignore(); + emit CloseRequested(); + } else { + QDockWidget::closeEvent(event); + } +} + void PanelWidget::changeEvent(QEvent *e) { if (e->type() == QEvent::LanguageChange) { diff --git a/app/widget/panel/panel.h b/app/widget/panel/panel.h index 5e8ef8f16..93409bf69 100644 --- a/app/widget/panel/panel.h +++ b/app/widget/panel/panel.h @@ -56,6 +56,13 @@ public: */ void SetBorderVisible(bool enabled); + /** + * @brief If enabled, sends signal CloseRequested() when the user closes instead of closing + * + * Defaults to FALSE. Use this to override default panel closing functionality. + */ + void SetSignalInsteadOfClose(bool e); + /** * @brief Called whenever this panel is focused and user uses "Zoom In" (either in menus or as a keyboard shortcut) * @@ -156,6 +163,9 @@ public: virtual void ToggleSelectedEnabled(){} +signals: + void CloseRequested(); + protected: /** * @brief paintEvent @@ -165,6 +175,8 @@ protected: virtual void changeEvent(QEvent* e) override; + virtual void closeEvent(QCloseEvent* event) override; + virtual void Retranslate(); protected slots: @@ -204,20 +216,17 @@ private: */ void UpdateTitle(); - /** - * @brief Internal title string - */ QString title_; - /** - * @brief Internal subtitle string - */ QString subtitle_; - /** - * @brief Internal border visibility value - */ bool border_visible_; + + bool signal_instead_of_close_; + +private slots: + void PanelVisibilityChanged(bool e); + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/timebased/timebased.cpp b/app/widget/timebased/timebased.cpp index 356ccf766..d51bab8c9 100644 --- a/app/widget/timebased/timebased.cpp +++ b/app/widget/timebased/timebased.cpp @@ -189,6 +189,11 @@ TimelinePoints *TimeBasedWidget::ConnectTimelinePoints() return static_cast(viewer_node_->parent()); } +Project *TimeBasedWidget::GetTimelinePointsProject() +{ + return static_cast(viewer_node_->parent())->project(); +} + TimelinePoints *TimeBasedWidget::GetConnectedTimelinePoints() const { return points_; @@ -339,7 +344,7 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time) // Enable workarea if it isn't already enabled if (!points_->workarea()->enabled()) { - new WorkareaSetEnabledCommand(points_, true, command); + new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, true, command); } // Determine our new range @@ -364,7 +369,7 @@ void TimeBasedWidget::SetPoint(Timeline::MovementMode m, const rational& time) } // Set workarea - new WorkareaSetRangeCommand(points_, TimeRange(in_point, out_point), command); + new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, TimeRange(in_point, out_point), command); Core::instance()->undo_stack()->push(command); } @@ -383,7 +388,7 @@ void TimeBasedWidget::ResetPoint(Timeline::MovementMode m) r.set_out(TimelineWorkArea::kResetOut); } - Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(points_, r)); + Core::instance()->undo_stack()->push(new WorkareaSetRangeCommand(GetTimelinePointsProject(), points_, r)); } void TimeBasedWidget::SetInAtPlayhead() @@ -413,7 +418,7 @@ void TimeBasedWidget::ClearInOutPoints() } - Core::instance()->undo_stack()->push(new WorkareaSetEnabledCommand(points_, false)); + Core::instance()->undo_stack()->push(new WorkareaSetEnabledCommand(GetTimelinePointsProject(), points_, false)); } void TimeBasedWidget::SetMarker() diff --git a/app/widget/timebased/timebased.h b/app/widget/timebased/timebased.h index 08457286f..596f6ffe6 100644 --- a/app/widget/timebased/timebased.h +++ b/app/widget/timebased/timebased.h @@ -116,6 +116,8 @@ protected: virtual TimelinePoints* ConnectTimelinePoints(); + virtual Project* GetTimelinePointsProject(); + TimelinePoints* GetConnectedTimelinePoints() const; void ConnectTimelineView(TimelineViewBase* base); diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 1ea795671..f1c3b717b 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -156,16 +156,13 @@ TimelineWidget::~TimelineWidget() void TimelineWidget::Clear() { - SetTimebase(0); - - QMap::iterator iterator = block_items_.begin(); - - while (iterator != block_items_.end()) { + QMap::const_iterator iterator; + for (iterator=block_items_.begin(); iterator!=block_items_.end(); iterator++) { delete iterator.value(); - iterator = block_items_.erase(iterator); } - block_items_.clear(); + + SetTimebase(0); } void TimelineWidget::TimebaseChangedEvent(const rational &timebase) @@ -725,7 +722,7 @@ void TimelineWidget::DeleteInToOut(bool ripple) } // Clear workarea after this - new WorkareaSetEnabledCommand(GetConnectedTimelinePoints(), false, command); + new WorkareaSetEnabledCommand(GetTimelinePointsProject(), GetConnectedTimelinePoints(), false, command); Core::instance()->undo_stack()->push(command); } diff --git a/app/widget/timelinewidget/undo/undo.cpp b/app/widget/timelinewidget/undo/undo.cpp index 826682f39..8dd71617a 100644 --- a/app/widget/timelinewidget/undo/undo.cpp +++ b/app/widget/timelinewidget/undo/undo.cpp @@ -42,6 +42,11 @@ BlockResizeCommand::BlockResizeCommand(Block *block, rational new_length, QUndoC { } +Project *BlockResizeCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockResizeCommand::redo_internal() { block_->set_length_and_media_out(new_length_); @@ -60,6 +65,11 @@ BlockResizeWithMediaInCommand::BlockResizeWithMediaInCommand(Block *block, ratio { } +Project *BlockResizeWithMediaInCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockResizeWithMediaInCommand::redo_internal() { block_->set_length_and_media_in(new_length_); @@ -78,6 +88,11 @@ BlockSetMediaInCommand::BlockSetMediaInCommand(Block *block, rational new_media_ { } +Project *BlockSetMediaInCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockSetMediaInCommand::redo_internal() { block_->set_media_in(new_media_in_); @@ -95,6 +110,11 @@ TrackRippleRemoveBlockCommand::TrackRippleRemoveBlockCommand(TrackOutput *track, { } +Project *TrackRippleRemoveBlockCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void TrackRippleRemoveBlockCommand::redo_internal() { before_ = block_->previous(); @@ -111,9 +131,9 @@ void TrackRippleRemoveBlockCommand::undo_internal() } TrackInsertBlockAfterCommand::TrackInsertBlockAfterCommand(TrackOutput *track, - Block *block, - Block *before, - QUndoCommand *parent) : + Block *block, + Block *before, + QUndoCommand *parent) : UndoCommand(parent), track_(track), block_(block), @@ -121,6 +141,11 @@ TrackInsertBlockAfterCommand::TrackInsertBlockAfterCommand(TrackOutput *track, { } +Project *TrackInsertBlockAfterCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void TrackInsertBlockAfterCommand::redo_internal() { track_->InsertBlockAfter(block_, before_); @@ -143,6 +168,11 @@ TrackRippleRemoveAreaCommand::TrackRippleRemoveAreaCommand(TrackOutput *track, r { } +Project *TrackRippleRemoveAreaCommand::GetRelevantProject() const +{ + return static_cast(track_->parent())->project(); +} + void TrackRippleRemoveAreaCommand::SetInsert(Block *insert) { insert_ = insert; @@ -386,10 +416,15 @@ BlockSplitCommand::BlockSplitCommand(TrackOutput* track, Block *block, rational } } +Project *BlockSplitCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockSplitCommand::redo_internal() { // FIXME: Reintroduce this optimization when block waveforms update automatically -// track_->BlockInvalidateCache(); + // track_->BlockInvalidateCache(); static_cast(block_->parent())->AddNode(new_block_); Node::CopyInputs(block_, new_block_); @@ -407,12 +442,12 @@ void BlockSplitCommand::redo_internal() NodeParam::ConnectEdge(new_block_->output(), transition); } -// track_->UnblockInvalidateCache(); + // track_->UnblockInvalidateCache(); } void BlockSplitCommand::undo_internal() { -// track_->BlockInvalidateCache(); + // track_->BlockInvalidateCache(); block_->set_length_and_media_out(old_length_); track_->RippleRemoveBlock(new_block_); @@ -424,7 +459,7 @@ void BlockSplitCommand::undo_internal() NodeParam::ConnectEdge(block_->output(), transition); } -// track_->UnblockInvalidateCache(); + // track_->UnblockInvalidateCache(); } Block *BlockSplitCommand::new_block() @@ -433,7 +468,8 @@ Block *BlockSplitCommand::new_block() } TrackSplitAtTimeCommand::TrackSplitAtTimeCommand(TrackOutput *track, rational point, QUndoCommand *parent) : - UndoCommand(parent) + UndoCommand(parent), + track_(track) { // Find Block that contains this time foreach (Block* b, track->Blocks()) { @@ -442,12 +478,17 @@ TrackSplitAtTimeCommand::TrackSplitAtTimeCommand(TrackOutput *track, rational po return; } else if (b->in() < point && b->out() > point) { // We found the Block, split it - new BlockSplitCommand(track, b, point, this); + new BlockSplitCommand(track_, b, point, this); return; } } } +Project *TrackSplitAtTimeCommand::GetRelevantProject() const +{ + return static_cast(track_->parent())->project(); +} + TrackReplaceBlockCommand::TrackReplaceBlockCommand(TrackOutput* track, Block *old, Block *replace, QUndoCommand *parent) : UndoCommand(parent), track_(track), @@ -456,6 +497,11 @@ TrackReplaceBlockCommand::TrackReplaceBlockCommand(TrackOutput* track, Block *ol { } +Project *TrackReplaceBlockCommand::GetRelevantProject() const +{ + return static_cast(track_->parent())->project(); +} + void TrackReplaceBlockCommand::redo_internal() { track_->ReplaceBlock(old_, replace_); @@ -473,6 +519,11 @@ TrackPrependBlockCommand::TrackPrependBlockCommand(TrackOutput *track, Block *bl { } +Project *TrackPrependBlockCommand::GetRelevantProject() const +{ + return static_cast(track_->parent())->project(); +} + void TrackPrependBlockCommand::redo_internal() { track_->PrependBlock(block_); @@ -535,6 +586,11 @@ BlockSplitPreservingLinksCommand::BlockSplitPreservingLinksCommand(const QVector } } +Project *BlockSplitPreservingLinksCommand::GetRelevantProject() const +{ + return static_cast(blocks_.first()->parent())->project(); +} + TrackCleanGapsCommand::TrackCleanGapsCommand(TrackList *track_list, int index, QUndoCommand *parent) : UndoCommand(parent), track_list_(track_list), @@ -542,6 +598,11 @@ TrackCleanGapsCommand::TrackCleanGapsCommand(TrackList *track_list, int index, Q { } +Project *TrackCleanGapsCommand::GetRelevantProject() const +{ + return static_cast(track_list_->GetParentGraph())->project(); +} + void TrackCleanGapsCommand::redo_internal() { GapBlock* on_gap = nullptr; @@ -632,6 +693,11 @@ BlockSetSpeedCommand::BlockSetSpeedCommand(Block *block, const rational &new_spe { } +Project *BlockSetSpeedCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockSetSpeedCommand::redo_internal() { block_->set_speed(new_speed_); @@ -649,6 +715,11 @@ TimelineRippleDeleteGapsAtRegionsCommand::TimelineRippleDeleteGapsAtRegionsComma { } +Project *TimelineRippleDeleteGapsAtRegionsCommand::GetRelevantProject() const +{ + return static_cast(timeline_->parent())->project(); +} + void TimelineRippleDeleteGapsAtRegionsCommand::redo_internal() { foreach (const TimeRange& range, regions_) { @@ -699,14 +770,20 @@ void TimelineRippleDeleteGapsAtRegionsCommand::undo_internal() commands_.empty(); } -WorkareaSetEnabledCommand::WorkareaSetEnabledCommand(TimelinePoints *points, bool enabled, QUndoCommand *parent) : +WorkareaSetEnabledCommand::WorkareaSetEnabledCommand(Project* project, TimelinePoints *points, bool enabled, QUndoCommand *parent) : UndoCommand(parent), + project_(project), points_(points), old_enabled_(points_->workarea()->enabled()), new_enabled_(enabled) { } +Project *WorkareaSetEnabledCommand::GetRelevantProject() const +{ + return project_; +} + void WorkareaSetEnabledCommand::redo_internal() { points_->workarea()->set_enabled(new_enabled_); @@ -717,14 +794,20 @@ void WorkareaSetEnabledCommand::undo_internal() points_->workarea()->set_enabled(old_enabled_); } -WorkareaSetRangeCommand::WorkareaSetRangeCommand(TimelinePoints *points, const TimeRange &range, QUndoCommand *parent) : +WorkareaSetRangeCommand::WorkareaSetRangeCommand(Project* project, TimelinePoints *points, const TimeRange &range, QUndoCommand *parent) : UndoCommand(parent), + project_(project), points_(points), old_range_(points_->workarea()->range()), new_range_(range) { } +Project *WorkareaSetRangeCommand::GetRelevantProject() const +{ + return project_; +} + void WorkareaSetRangeCommand::redo_internal() { points_->workarea()->set_range(new_range_); @@ -743,6 +826,11 @@ BlockLinkCommand::BlockLinkCommand(Block *a, Block *b, bool link, QUndoCommand * { } +Project *BlockLinkCommand::GetRelevantProject() const +{ + return static_cast(a_->parent())->project(); +} + void BlockLinkCommand::redo_internal() { if (link_) { @@ -769,6 +857,11 @@ BlockUnlinkAllCommand::BlockUnlinkAllCommand(Block *block, QUndoCommand *parent) { } +Project *BlockUnlinkAllCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockUnlinkAllCommand::redo_internal() { unlinked_ = block_->linked_clips(); @@ -788,10 +881,11 @@ void BlockUnlinkAllCommand::undo_internal() } BlockLinkManyCommand::BlockLinkManyCommand(const QList blocks, bool link, QUndoCommand *parent) : - UndoCommand(parent) + UndoCommand(parent), + blocks_(blocks) { - foreach (Block* a, blocks) { - foreach (Block* b, blocks) { + foreach (Block* a, blocks_) { + foreach (Block* b, blocks_) { if (a != b) { new BlockLinkCommand(a, b, link, this); } @@ -799,6 +893,11 @@ BlockLinkManyCommand::BlockLinkManyCommand(const QList blocks, bool lin } } +Project *BlockLinkManyCommand::GetRelevantProject() const +{ + return static_cast(blocks_.first()->parent())->project(); +} + BlockEnableDisableCommand::BlockEnableDisableCommand(Block *block, bool enabled, QUndoCommand *parent) : UndoCommand(parent), block_(block), @@ -807,6 +906,11 @@ BlockEnableDisableCommand::BlockEnableDisableCommand(Block *block, bool enabled, { } +Project *BlockEnableDisableCommand::GetRelevantProject() const +{ + return static_cast(block_->parent())->project(); +} + void BlockEnableDisableCommand::redo_internal() { block_->set_enabled(new_enabled_); diff --git a/app/widget/timelinewidget/undo/undo.h b/app/widget/timelinewidget/undo/undo.h index 3098863c7..d763d007c 100644 --- a/app/widget/timelinewidget/undo/undo.h +++ b/app/widget/timelinewidget/undo/undo.h @@ -36,6 +36,8 @@ class BlockResizeCommand : public UndoCommand { public: BlockResizeCommand(Block* block, rational new_length, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -50,6 +52,8 @@ class BlockResizeWithMediaInCommand : public UndoCommand { public: BlockResizeWithMediaInCommand(Block* block, rational new_length, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -64,6 +68,8 @@ class BlockSetMediaInCommand : public UndoCommand { public: BlockSetMediaInCommand(Block* block, rational new_media_in, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -78,6 +84,8 @@ class BlockSetSpeedCommand : public UndoCommand { public: BlockSetSpeedCommand(Block* block, const rational& new_speed, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -93,6 +101,8 @@ class TrackRippleRemoveBlockCommand : public UndoCommand { public: TrackRippleRemoveBlockCommand(TrackOutput* track, Block* block, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -109,6 +119,8 @@ class TrackPrependBlockCommand : public UndoCommand { public: TrackPrependBlockCommand(TrackOutput* track, Block* block, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -122,6 +134,8 @@ class TrackInsertBlockAfterCommand : public UndoCommand { public: TrackInsertBlockAfterCommand(TrackOutput* track, Block* block, Block* before, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -145,6 +159,8 @@ class TrackRippleRemoveAreaCommand : public UndoCommand { public: TrackRippleRemoveAreaCommand(TrackOutput* track, rational in, rational out, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + void SetInsert(Block* insert); protected: @@ -203,6 +219,8 @@ class BlockSplitCommand : public UndoCommand { public: BlockSplitCommand(TrackOutput* track, Block* block, rational point, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + Block* new_block(); protected: @@ -228,12 +246,20 @@ private: class TrackSplitAtTimeCommand : public UndoCommand { public: TrackSplitAtTimeCommand(TrackOutput* track, rational point, QUndoCommand* parent = nullptr); + + virtual Project* GetRelevantProject() const override; + +private: + TrackOutput* track_; + }; class BlockSplitPreservingLinksCommand : public UndoCommand { public: BlockSplitPreservingLinksCommand(const QVector &blocks, const QList& times, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + private: QVector blocks_; @@ -249,6 +275,8 @@ class TrackReplaceBlockCommand : public UndoCommand { public: TrackReplaceBlockCommand(TrackOutput* track, Block* old, Block* replace, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -263,6 +291,8 @@ class TrackCleanGapsCommand : public UndoCommand { public: TrackCleanGapsCommand(TrackList* track_list, int index, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -290,6 +320,8 @@ class TimelineRippleDeleteGapsAtRegionsCommand : public UndoCommand { public: TimelineRippleDeleteGapsAtRegionsCommand(ViewerOutput* vo, const TimeRangeList& regions, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -304,13 +336,17 @@ private: class WorkareaSetEnabledCommand : public UndoCommand { public: - WorkareaSetEnabledCommand(TimelinePoints* points, bool enabled, QUndoCommand* parent = nullptr); + WorkareaSetEnabledCommand(Project *project, TimelinePoints* points, bool enabled, QUndoCommand* parent = nullptr); + + virtual Project* GetRelevantProject() const override; protected: virtual void redo_internal() override; virtual void undo_internal() override; private: + Project* project_; + TimelinePoints* points_; bool old_enabled_; @@ -321,13 +357,17 @@ private: class WorkareaSetRangeCommand : public UndoCommand { public: - WorkareaSetRangeCommand(TimelinePoints* points, const TimeRange& range, QUndoCommand* parent = nullptr); + WorkareaSetRangeCommand(Project *project, TimelinePoints* points, const TimeRange& range, QUndoCommand* parent = nullptr); + + virtual Project* GetRelevantProject() const override; protected: virtual void redo_internal() override; virtual void undo_internal() override; private: + Project* project_; + TimelinePoints* points_; TimeRange old_range_; @@ -339,12 +379,20 @@ private: class BlockLinkManyCommand : public UndoCommand { public: BlockLinkManyCommand(const QList blocks, bool link, QUndoCommand* parent = nullptr); + + virtual Project* GetRelevantProject() const override; + +private: + QList blocks_; + }; class BlockLinkCommand : public UndoCommand { public: BlockLinkCommand(Block* a, Block* b, bool link, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -364,6 +412,8 @@ class BlockUnlinkAllCommand : public UndoCommand { public: BlockUnlinkAllCommand(Block* block, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; @@ -379,6 +429,8 @@ class BlockEnableDisableCommand : public UndoCommand { public: BlockEnableDisableCommand(Block* block, bool enabled, QUndoCommand* parent = nullptr); + virtual Project* GetRelevantProject() const override; + protected: virtual void redo_internal() override; virtual void undo_internal() override; diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 4cfd170bc..08f1c4b71 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -98,6 +98,11 @@ TimelinePoints *FootageViewerWidget::ConnectTimelinePoints() return footage_; } +Project *FootageViewerWidget::GetTimelinePointsProject() +{ + return footage_->project(); +} + void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enable_audio) { if (!GetFootage()) { diff --git a/app/widget/viewer/footageviewer.h b/app/widget/viewer/footageviewer.h index fa76ae57c..6043b01f2 100644 --- a/app/widget/viewer/footageviewer.h +++ b/app/widget/viewer/footageviewer.h @@ -40,6 +40,8 @@ public: protected: virtual TimelinePoints* ConnectTimelinePoints() override; + virtual Project* GetTimelinePointsProject() override; + private: void StartFootageDragInternal(bool enable_video, bool enable_audio); diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 4a07bed58..f279c3a0c 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -38,7 +38,9 @@ OLIVE_NAMESPACE_ENTER MainMenu::MainMenu(MainWindow *parent) : QMenuBar(parent) { +#ifdef Q_OS_WINDOWS StyleManager::UseNativeWindowsStyling(this); +#endif // // FILE MENU @@ -300,7 +302,7 @@ void MainMenu::TimecodeDisplayTriggered() void MainMenu::FileMenuAboutToShow() { - file_project_properties_item_->setEnabled(Core::instance()->GetActiveProject() != nullptr); + file_project_properties_item_->setEnabled(Core::instance()->GetActiveProject()); } void MainMenu::ViewMenuAboutToShow() diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index a119c29bd..626c7474e 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -70,7 +70,7 @@ MainWindow::MainWindow(QWidget *parent) : param_panel_ = PanelManager::instance()->CreatePanel(this); sequence_viewer_panel_ = PanelManager::instance()->CreatePanel(this); pixel_sampler_panel_ = PanelManager::instance()->CreatePanel(this); - project_panel_ = PanelManager::instance()->CreatePanel(this); + AppendProjectPanel(); tool_panel_ = PanelManager::instance()->CreatePanel(this); task_man_panel_ = PanelManager::instance()->CreatePanel(this); curve_panel_ = PanelManager::instance()->CreatePanel(this); @@ -98,8 +98,9 @@ MainWindow::MainWindow(QWidget *parent) : footage_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); sequence_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_); - connect(project_panel_, &ProjectPanel::ProjectNameChanged, this, &MainWindow::UpdateTitle); UpdateTitle(); + + QMetaObject::invokeMethod(this, "SetDefaultLayout", Qt::QueuedConnection); } MainWindow::~MainWindow() @@ -114,9 +115,7 @@ MainWindow::~MainWindow() void MainWindow::OpenSequence(Sequence *sequence) { // See if this sequence is already open, and switch to it if so - for (int i=0;iGetConnectedViewer() == sequence->viewer_output()) { tl->raise(); return; @@ -222,42 +221,54 @@ void MainWindow::ToggleMaximizedPanel() void MainWindow::ProjectOpen(Project* p) { - // FIXME Use settings data to create panels and restore state if they exist - project_panel_->set_project(p); - - UpdateTitle(); - - SetDefaultLayout(); -} - -void MainWindow::closeEvent(QCloseEvent *e) -{ - if (isWindowModified()) { - QMessageBox mb(this); - - mb.setIcon(QMessageBox::Question); - mb.setWindowTitle(tr("Unsaved Changes")); - mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?") - .arg(Core::instance()->GetActiveProject()->name())); - - QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole); - mb.addButton(tr("Don't Save"), QMessageBox::NoRole); - QPushButton* cancel_btn = mb.addButton(QMessageBox::Cancel); - - mb.exec(); - - if (mb.clickedButton() == cancel_btn - || (mb.clickedButton() == yes_btn && !Core::instance()->SaveActiveProject())) { - // Don't close if the user clicked cancel on this messagebox OR they cancelled a save as operation - e->ignore(); + // See if this project is already open, and switch to it if so + foreach (ProjectPanel* pl, project_panels_) { + if (pl->project() == p) { + pl->raise(); return; } } - // Close viewers first since we don't want to delete any nodes while they might be mid-render - QList viewers = PanelManager::instance()->GetPanelsOfType(); - foreach (ViewerPanelBase* viewer, viewers) { - viewer->ConnectViewerNode(nullptr); + ProjectPanel* panel; + + if (!project_panels_.first()->project()) { + panel = project_panels_.first(); + } else { + panel = AppendProjectPanel(); + } + + panel->set_project(p); + + // FIXME Use settings data to create panels and restore state if they exist +} + +void MainWindow::ProjectClose(Project *p) +{ + // Close project from project panel + foreach (ProjectPanel* panel, project_panels_) { + if (panel->project() == p) { + RemoveProjectPanel(panel); + } + } + + // Close any open sequences from project + QList open_sequences = p->get_items_of_type(Item::kSequence); + + foreach (ItemPtr item, open_sequences) { + Sequence* seq = static_cast(item.get()); + + if (IsSequenceOpen(seq)) { + CloseSequence(seq); + } + } +} + +void MainWindow::closeEvent(QCloseEvent *e) +{ + // Try to close all projects (this will return false if the user chooses not to close) + if (!Core::instance()->CloseAllProjects(false)) { + e->ignore(); + return; } PanelManager::instance()->DeleteAllPanels(); @@ -292,10 +303,10 @@ bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *r void MainWindow::UpdateTitle() { - if (project_panel_->project()) { + if (Core::instance()->GetActiveProject()) { setWindowTitle(QStringLiteral("%1 %2 - [*]%3").arg(QApplication::applicationName(), QApplication::applicationVersion(), - project_panel_->project()->pretty_filename())); + Core::instance()->GetActiveProject()->pretty_filename())); } else { setWindowTitle(QStringLiteral("%1 %2").arg(QApplication::applicationName(), QApplication::applicationVersion())); @@ -307,24 +318,19 @@ void MainWindow::TimelineCloseRequested() RemoveTimelinePanel(static_cast(sender())); } +void MainWindow::ProjectCloseRequested() +{ + ProjectPanel* panel = static_cast(sender()); + Project* p = panel->project(); + + Core::instance()->CloseProject(p, true); +} + TimelinePanel* MainWindow::AppendTimelinePanel() { - TimelinePanel* panel = PanelManager::instance()->CreatePanel(this);; - - if (!timeline_panels_.isEmpty()) { - tabifyDockWidget(timeline_panels_.last(), panel); - - // For some reason raise() on its own doesn't do anything, we need both - panel->show(); - panel->raise(); - } - - timeline_panels_.append(panel); - - // Let us handle the panel closing rather than the panel itself - panel->SetSignalInsteadOfClose(true); - connect(panel, &TimelinePanel::CloseRequested, this, &MainWindow::TimelineCloseRequested); + TimelinePanel* panel = AppendPanelInternal(timeline_panels_); + connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::TimelineCloseRequested); connect(panel, &TimelinePanel::TimeChanged, param_panel_, &ParamPanel::SetTime); connect(panel, &TimelinePanel::TimeChanged, sequence_viewer_panel_, &SequenceViewerPanel::SetTime); connect(panel, &TimelinePanel::TimeChanged, curve_panel_, &CurvePanel::SetTime); @@ -340,6 +346,16 @@ TimelinePanel* MainWindow::AppendTimelinePanel() return panel; } +ProjectPanel *MainWindow::AppendProjectPanel() +{ + ProjectPanel* panel = AppendPanelInternal(project_panels_); + + connect(panel, &PanelWidget::CloseRequested, this, &MainWindow::ProjectCloseRequested); + connect(panel, &ProjectPanel::ProjectNameChanged, this, &MainWindow::UpdateTitle); + + return panel; +} + void MainWindow::RemoveTimelinePanel(TimelinePanel *panel) { // Stop showing this timeline in the viewer @@ -354,6 +370,16 @@ void MainWindow::RemoveTimelinePanel(TimelinePanel *panel) } } +void MainWindow::RemoveProjectPanel(ProjectPanel *panel) +{ + if (project_panels_.size() == 1) { + panel->set_project(nullptr); + } else { + project_panels_.removeOne(panel); + panel->deleteLater(); + } +} + void MainWindow::TimelineFocused(ViewerOutput* viewer) { sequence_viewer_panel_->ConnectViewerNode(viewer); @@ -373,6 +399,14 @@ void MainWindow::FocusedPanelChanged(PanelWidget *panel) if (timeline) { TimelineFocused(timeline->GetConnectedViewer()); + return; + } + + ProjectPanel* project = dynamic_cast(panel); + + if (project) { + UpdateTitle(); + return; } } @@ -395,8 +429,8 @@ void MainWindow::SetDefaultLayout() pixel_sampler_panel_->setFloating(true); addDockWidget(Qt::TopDockWidgetArea, pixel_sampler_panel_); - project_panel_->show(); - addDockWidget(Qt::BottomDockWidgetArea, project_panel_); + project_panels_.first()->show(); + addDockWidget(Qt::BottomDockWidgetArea, project_panels_.first()); tool_panel_->show(); addDockWidget(Qt::BottomDockWidgetArea, tool_panel_); @@ -419,13 +453,34 @@ void MainWindow::SetDefaultLayout() {width()/3, width()/3, width()/3}, Qt::Horizontal); - resizeDocks({project_panel_, tool_panel_, timeline_panels_.first(), audio_monitor_panel_}, + resizeDocks({project_panels_.first(), tool_panel_, timeline_panels_.first(), audio_monitor_panel_}, {width()/4, 1, width(), 1}, Qt::Horizontal); - resizeDocks({node_panel_, project_panel_}, + resizeDocks({node_panel_, project_panels_.first()}, {height()/2, height()/2}, Qt::Vertical); } +template +T *MainWindow::AppendPanelInternal(QList& list) +{ + T* panel = PanelManager::instance()->CreatePanel(this); + + if (!list.isEmpty()) { + tabifyDockWidget(list.last(), panel); + } + + // For some reason raise() on its own doesn't do anything, we need both + panel->show(); + panel->raise(); + + list.append(panel); + + // Let us handle the panel closing rather than the panel itself + panel->SetSignalInsteadOfClose(true); + + return panel; +} + OLIVE_NAMESPACE_EXIT diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index 716f17a56..e14d49dd5 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -67,7 +67,11 @@ public: public slots: void ProjectOpen(Project *p); + + void ProjectClose(Project* p); + void SetFullscreen(bool fullscreen); + void ToggleMaximizedPanel(); void SetDefaultLayout(); @@ -82,8 +86,15 @@ protected: private: TimelinePanel* AppendTimelinePanel(); + ProjectPanel* AppendProjectPanel(); + + template + T* AppendPanelInternal(QList& list); + void RemoveTimelinePanel(TimelinePanel *panel); + void RemoveProjectPanel(ProjectPanel* panel); + void TimelineFocused(ViewerOutput *viewer); QByteArray premaximized_state_; @@ -93,7 +104,7 @@ private: ParamPanel* param_panel_; SequenceViewerPanel* sequence_viewer_panel_; FootageViewerPanel* footage_viewer_panel_; - ProjectPanel* project_panel_; + QList project_panels_; ToolPanel* tool_panel_; QList timeline_panels_; AudioMonitorPanel* audio_monitor_panel_; @@ -114,6 +125,8 @@ private slots: void TimelineCloseRequested(); + void ProjectCloseRequested(); + }; OLIVE_NAMESPACE_EXIT