diff --git a/app/core.cpp b/app/core.cpp index 9fc916734..25bc8a58f 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -33,6 +33,7 @@ #include "config/config.h" #include "dialog/about/about.h" #include "dialog/export/export.h" +#include "dialog/loadsave/loadsave.h" #include "dialog/sequence/sequence.h" #include "dialog/preferences/preferences.h" #include "dialog/projectproperties/projectproperties.h" @@ -40,6 +41,7 @@ #include "panel/panelmanager.h" #include "panel/project/project.h" #include "panel/viewer/viewer.h" +#include "project/projectsavemanager.h" #include "project/item/footage/footage.h" #include "project/item/sequence/sequence.h" #include "render/colormanager.h" @@ -404,6 +406,36 @@ void Core::StartGUI(bool full_screen) autorecovery_timer_.start(); } +void Core::SaveProjectInternal(Project *project) +{ + // Create save dialog + LoadSaveDialog* lsd = new LoadSaveDialog(tr("Saving '%1'").arg(project->filename()), tr("Save Project"), main_window_); + lsd->open(); + + // Create save manager + ProjectSaveManager* psm = new ProjectSaveManager(project); + + // Create a separate thread to save in + QThread* save_thread = new QThread(); + save_thread->start(); + + // Move the save manager to this thread + psm->moveToThread(save_thread); + + // Connect the save manager progress signal to the progress bar update on the dialog + connect(psm, &ProjectSaveManager::ProgressChanged, lsd, &LoadSaveDialog::SetProgress, Qt::QueuedConnection); + + // Connect cleanup functions (ensure everything new'd in this function is deleteLater'd) + connect(psm, &ProjectSaveManager::Finished, lsd, &LoadSaveDialog::accept, Qt::QueuedConnection); + connect(psm, &ProjectSaveManager::Finished, lsd, &LoadSaveDialog::deleteLater, Qt::QueuedConnection); + connect(psm, &ProjectSaveManager::Finished, psm, &ProjectSaveManager::deleteLater, Qt::QueuedConnection); + connect(psm, &ProjectSaveManager::Finished, save_thread, &QThread::quit, Qt::QueuedConnection); + connect(psm, &ProjectSaveManager::Finished, save_thread, &QThread::deleteLater, Qt::QueuedConnection); + + // Start the save process + QMetaObject::invokeMethod(psm, "Start", Qt::QueuedConnection); +} + void Core::SaveAutorecovery() { if (queue_autorecovery_) { @@ -439,6 +471,41 @@ void Core::SetAutorecoveryInterval(int minutes) autorecovery_timer_.setInterval(minutes * 60000); } +void Core::SaveActiveProject() +{ + Project* active_project = GetActiveProject(); + + if (!active_project) { + return; + } + + if (active_project->filename().isEmpty()) { + SaveActiveProjectAs(); + } else { + SaveProjectInternal(active_project); + } +} + +void Core::SaveActiveProjectAs() +{ + Project* active_project = GetActiveProject(); + + if (!active_project) { + return; + } + + QString fn = QFileDialog::getSaveFileName(main_window_, + tr("Save Project As"), + QString(), + tr("Olive Project (*.ove)")); + + if (!fn.isEmpty()) { + active_project->set_filename(fn); + + SaveProjectInternal(active_project); + } +} + QList Core::SupportedFrameRates() { QList frame_rates; diff --git a/app/core.h b/app/core.h index 9cb06a0e4..076ff02cf 100644 --- a/app/core.h +++ b/app/core.h @@ -174,6 +174,18 @@ public: static QString ChannelLayoutToString(const uint64_t &layout); public slots: + /** + * @brief Save the currently active project + * + * If the project hasn't been saved before, this will be equivalent to calling SaveActiveProjectAs(). + */ + void SaveActiveProject(); + + /** + * @brief Save the currently active project with a new filename + */ + void SaveActiveProjectAs(); + /** * @brief Set the current application-wide tool * @@ -263,6 +275,11 @@ private: */ void StartGUI(bool full_screen); + /** + * @brief Internal function for saving a project to a file + */ + void SaveProjectInternal(Project* project); + /** * @brief Internal main window object */ diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index f5683a0e3..39a7c201a 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(actionsearch) add_subdirectory(export) add_subdirectory(footageproperties) add_subdirectory(keyframeproperties) +add_subdirectory(loadsave) add_subdirectory(preferences) add_subdirectory(projectproperties) add_subdirectory(sequence) diff --git a/app/dialog/loadsave/CMakeLists.txt b/app/dialog/loadsave/CMakeLists.txt new file mode 100644 index 000000000..a7f7e0ea0 --- /dev/null +++ b/app/dialog/loadsave/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2019 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/loadsave/loadsave.h + dialog/loadsave/loadsave.cpp + PARENT_SCOPE +) diff --git a/app/dialog/loadsave/loadsave.cpp b/app/dialog/loadsave/loadsave.cpp new file mode 100644 index 000000000..b207c87bb --- /dev/null +++ b/app/dialog/loadsave/loadsave.cpp @@ -0,0 +1,41 @@ +#include "loadsave.h" + +#include +#include +#include + +LoadSaveDialog::LoadSaveDialog(const QString& message, const QString& title, QWidget *parent) : + QDialog(parent) +{ + if (!title.isEmpty()) { + setWindowTitle(title); + } + + QVBoxLayout* layout = new QVBoxLayout(this); + + layout->addWidget(new QLabel(message)); + + bar_ = new QProgressBar(); + bar_->setMinimum(0); + bar_->setValue(0); + bar_->setMaximum(100); + layout->addWidget(bar_); + + QHBoxLayout* cancel_layout = new QHBoxLayout(); + layout->addLayout(cancel_layout); + cancel_layout->setMargin(0); + cancel_layout->setSpacing(0); + cancel_layout->addStretch(); + + QPushButton* cancel_btn = new QPushButton(tr("Cancel")); + connect(cancel_btn, &QPushButton::clicked, this, &LoadSaveDialog::Cancelled); + connect(cancel_btn, &QPushButton::clicked, this, &LoadSaveDialog::reject); + cancel_layout->addWidget(cancel_btn); + + cancel_layout->addStretch(); +} + +void LoadSaveDialog::SetProgress(int value) +{ + bar_->setValue(value); +} diff --git a/app/dialog/loadsave/loadsave.h b/app/dialog/loadsave/loadsave.h new file mode 100644 index 000000000..6b640fe68 --- /dev/null +++ b/app/dialog/loadsave/loadsave.h @@ -0,0 +1,23 @@ +#ifndef LOADSAVEDIALOG_H +#define LOADSAVEDIALOG_H + +#include +#include + +class LoadSaveDialog : public QDialog +{ + Q_OBJECT +public: + LoadSaveDialog(const QString &message, const QString &title, QWidget* parent = nullptr); + +public slots: + void SetProgress(int value); + +signals: + void Cancelled(); + +private: + QProgressBar* bar_; +}; + +#endif // LOADSAVEDIALOG_H diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index 813a8e38b..8c711a4b9 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -75,9 +75,17 @@ Project *ProjectPanel::project() void ProjectPanel::set_project(Project *p) { + if (project()) { + disconnect(project(), &Project::NameChanged, this, &ProjectPanel::ProjectNameChanged); + } + explorer_->set_project(p); - Retranslate(); + if (project()) { + connect(project(), &Project::NameChanged, this, &ProjectPanel::ProjectNameChanged); + } + + ProjectNameChanged(); } QList ProjectPanel::SelectedItems() @@ -112,11 +120,7 @@ void ProjectPanel::Retranslate() { SetTitle(tr("Project")); - if (project() == nullptr) { - SetSubtitle(tr("(none)")); - } else { - SetSubtitle(project()->name()); - } + ProjectNameChanged(); } void ProjectPanel::ItemDoubleClickSlot(Item *item) @@ -141,3 +145,12 @@ void ProjectPanel::ShowNewMenu() new_menu.exec(QCursor::pos()); } + +void ProjectPanel::ProjectNameChanged() +{ + if (project() == nullptr) { + SetSubtitle(tr("(none)")); + } else { + SetSubtitle(project()->name()); + } +} diff --git a/app/panel/project/project.h b/app/panel/project/project.h index 07d99312f..c0e3fc480 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -58,6 +58,8 @@ private slots: void ItemDoubleClickSlot(Item* item); void ShowNewMenu(); + + void ProjectNameChanged(); }; #endif // PROJECT_PANEL_H diff --git a/app/project/CMakeLists.txt b/app/project/CMakeLists.txt index 1b9763aba..a39e09ff3 100644 --- a/app/project/CMakeLists.txt +++ b/app/project/CMakeLists.txt @@ -20,6 +20,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} project/project.h project/project.cpp + project/projectsavemanager.h + project/projectsavemanager.cpp project/projectviewmodel.h project/projectviewmodel.cpp PARENT_SCOPE diff --git a/app/project/project.cpp b/app/project/project.cpp index 6ef62fb6e..6f26ff194 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -20,9 +20,10 @@ #include "project.h" +#include + Project::Project() { - name_ = tr("(untitled)"); root_.set_project(this); } @@ -31,17 +32,28 @@ Folder *Project::root() return &root_; } -const QString &Project::name() +QString Project::name() const { - return name_; + if (filename_.isEmpty()) { + return tr("(untitled)"); + } else { + return QFileInfo(filename_).baseName(); + } } -void Project::set_name(const QString &s) +const QString &Project::filename() const { - name_ = s; + return filename_; } -const QString &Project::ocio_config() +void Project::set_filename(const QString &s) +{ + filename_ = s; + + emit NameChanged(); +} + +const QString &Project::ocio_config() const { return ocio_config_; } @@ -51,7 +63,7 @@ void Project::set_ocio_config(const QString &ocio_config) ocio_config_ = ocio_config; } -const QString &Project::default_input_colorspace() +const QString &Project::default_input_colorspace() const { return default_input_colorspace_; } diff --git a/app/project/project.h b/app/project/project.h index 241759045..b6d993e2b 100644 --- a/app/project/project.h +++ b/app/project/project.h @@ -46,21 +46,26 @@ public: Folder* root(); - const QString& name(); - void set_name(const QString& s); + QString name() const; - const QString& ocio_config(); + const QString& filename() const; + void set_filename(const QString& s); + + const QString& ocio_config() const; void set_ocio_config(const QString& ocio_config); - const QString& default_input_colorspace(); + const QString& default_input_colorspace() const; void set_default_input_colorspace(const QString& colorspace); ColorManager* color_manager(); +signals: + void NameChanged(); + private: Folder root_; - QString name_; + QString filename_; QString ocio_config_; QString default_input_colorspace_; diff --git a/app/project/projectsavemanager.cpp b/app/project/projectsavemanager.cpp new file mode 100644 index 000000000..37c28ea42 --- /dev/null +++ b/app/project/projectsavemanager.cpp @@ -0,0 +1,22 @@ +#include "projectsavemanager.h" + +ProjectSaveManager::ProjectSaveManager(Project *project) : + project_(project) +{ + +} + +void ProjectSaveManager::Start() +{ + int prog = 0; + + do { + prog += 10; + + emit ProgressChanged(prog); + + Sleep(200); + } while (prog < 100); + + emit Finished(); +} diff --git a/app/project/projectsavemanager.h b/app/project/projectsavemanager.h new file mode 100644 index 000000000..e5767b9dd --- /dev/null +++ b/app/project/projectsavemanager.h @@ -0,0 +1,27 @@ +#ifndef PROJECTSAVEMANAGER_H +#define PROJECTSAVEMANAGER_H + +#include + +#include "project.h" + +class ProjectSaveManager : public QObject +{ + Q_OBJECT +public: + ProjectSaveManager(Project* project); + +public slots: + void Start(); + +signals: + void ProgressChanged(int); + + void Finished(); + +private: + Project* project_; + +}; + +#endif // PROJECTSAVEMANAGER_H diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 2da6030a4..bc5d43df8 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -44,8 +44,8 @@ MainMenu::MainMenu(QMainWindow *parent) : file_open_item_ = file_menu_->AddItem("openproj", nullptr, nullptr, "Ctrl+O"); file_open_recent_menu_ = new Menu(file_menu_); file_open_recent_clear_item_ = file_open_recent_menu_->AddItem("clearopenrecent", nullptr, nullptr); - file_save_item_ = file_menu_->AddItem("saveproj", nullptr, nullptr, "Ctrl+S"); - file_save_as_item_ = file_menu_->AddItem("saveprojas", nullptr, nullptr, "Ctrl+Shift+S"); + file_save_item_ = file_menu_->AddItem("saveproj", Core::instance(), SLOT(SaveActiveProject()), "Ctrl+S"); + file_save_as_item_ = file_menu_->AddItem("saveprojas", Core::instance(), SLOT(SaveActiveProjectAs()), "Ctrl+Shift+S"); file_menu_->addSeparator(); file_import_item_ = file_menu_->AddItem("import", Core::instance(), SLOT(DialogImportShow()), "Ctrl+I"); file_menu_->addSeparator();