diff --git a/app/core.cpp b/app/core.cpp index c9d47386e..f0d7d2f7f 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -46,6 +46,7 @@ #ifdef USE_OTIO #include "dialog/otioproperties/otiopropertiesdialog.h" #endif +#include "dialog/projectproperties/projectproperties.h" #include "dialog/sequence/sequence.h" #include "dialog/task/task.h" #include "dialog/preferences/preferences.h" @@ -367,6 +368,21 @@ void Core::DialogPreferencesShow() pd.exec(); } +void Core::DialogProjectPropertiesShow() +{ + Project *proj = GetActiveProject(); + + if (proj) { + ProjectPropertiesDialog ppd(proj, main_window_); + ppd.exec(); + } else { + QMessageBox::critical(main_window_, + tr("No Active Project"), + tr("No project is currently open to set the properties for"), + QMessageBox::Ok); + } +} + void Core::DialogExportShow() { ViewerOutput* viewer = GetSequenceToExport(); diff --git a/app/core.h b/app/core.h index de7ac21f3..653769af6 100644 --- a/app/core.h +++ b/app/core.h @@ -391,6 +391,11 @@ public slots: */ void DialogPreferencesShow(); + /** + * @brief Show Project Properties dialog + */ + void DialogProjectPropertiesShow(); + /** * @brief Show Export dialog */ diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index 7692472ec..a7ecf0f78 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -29,6 +29,7 @@ if(OpenTimelineIO_FOUND) endif() add_subdirectory(preferences) add_subdirectory(progress) +add_subdirectory(projectproperties) add_subdirectory(rendercancel) add_subdirectory(sequence) add_subdirectory(speedduration) diff --git a/app/dialog/projectproperties/CMakeLists.txt b/app/dialog/projectproperties/CMakeLists.txt new file mode 100644 index 000000000..b45092bf6 --- /dev/null +++ b/app/dialog/projectproperties/CMakeLists.txt @@ -0,0 +1,22 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2020 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/projectproperties/projectproperties.h + dialog/projectproperties/projectproperties.cpp + PARENT_SCOPE +) diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp new file mode 100644 index 000000000..2c75edc9e --- /dev/null +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -0,0 +1,255 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 . + +***/ + +#include "projectproperties.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common/filefunctions.h" +#include "common/ocioutils.h" +#include "config/config.h" +#include "core.h" +#include "node/color/colormanager/colormanager.h" +#include "render/diskmanager.h" + +namespace olive { + +#define super QDialog + +ProjectPropertiesDialog::ProjectPropertiesDialog(Project* p, QWidget *parent) : + super(parent), + working_project_(p), + ocio_config_is_valid_(true) +{ + QVBoxLayout* layout = new QVBoxLayout(this); + + setWindowTitle(tr("Project Properties for '%1'").arg(working_project_->name())); + + QTabWidget* tabs = new QTabWidget; + layout->addWidget(tabs); + + { + // Color management group + QWidget* color_group = new QWidget(); + + QVBoxLayout* color_outer_layout = new QVBoxLayout(color_group); + + QGridLayout* color_layout = new QGridLayout(); + color_outer_layout->addLayout(color_layout); + + int row = 0; + + color_layout->addWidget(new QLabel(tr("OpenColorIO Configuration:")), row, 0); + + ocio_filename_ = new QLineEdit(); + ocio_filename_->setPlaceholderText(tr("(default)")); + color_layout->addWidget(ocio_filename_, row, 1); + + row++; + + color_layout->addWidget(new QLabel(tr("Default Input Color Space:")), row, 0); + + default_input_colorspace_ = new QComboBox(); + color_layout->addWidget(default_input_colorspace_, row, 1, 1, 2); + + row++; + + QPushButton* browse_btn = new QPushButton(tr("Browse")); + color_layout->addWidget(browse_btn, 0, 2); + connect(browse_btn, &QPushButton::clicked, this, &ProjectPropertiesDialog::BrowseForOCIOConfig); + + ocio_filename_->setText(working_project_->color_manager()->GetConfigFilename()); + + connect(ocio_filename_, &QLineEdit::textChanged, this, &ProjectPropertiesDialog::OCIOFilenameUpdated); + OCIOFilenameUpdated(); + + tabs->addTab(color_group, tr("Color Management")); + + color_outer_layout->addStretch(); + } + + { + // Cache group + QWidget* cache_group = new QWidget(); + + QVBoxLayout* cache_layout = new QVBoxLayout(cache_group); + + QButtonGroup* disk_cache_btn_group = new QButtonGroup(); + + // Create radio buttons and add to widget and button group + disk_cache_radios_[ProjectSettingsNode::kCacheUseDefaultLocation] = new QRadioButton(tr("Use Default Location")); + disk_cache_radios_[ProjectSettingsNode::kCacheStoreAlongsideProject] = new QRadioButton(tr("Store Alongside Project")); + disk_cache_radios_[ProjectSettingsNode::kCacheCustomPath] = new QRadioButton(tr("Use Custom Location:")); + for (int i=0; iaddButton(disk_cache_radios_[i]); + cache_layout->addWidget(disk_cache_radios_[i]); + } + + // Create custom cache path widget + custom_cache_path_ = new PathWidget(working_project_->settings()->GetCustomCachePath(), this); + custom_cache_path_->setEnabled(false); + cache_layout->addWidget(custom_cache_path_); + + // Ensure custom cache path "enabled" is tied to the radio button being checked + connect(disk_cache_radios_[ProjectSettingsNode::kCacheCustomPath], &QRadioButton::toggled, custom_cache_path_, &PathWidget::setEnabled); + + // Check the radio button that should currently be active + disk_cache_radios_[working_project_->settings()->GetCacheSetting()]->setChecked(true); + + // Add disk cache settings button + QPushButton* disk_cache_settings_btn = new QPushButton(tr("Disk Cache Settings")); + connect(disk_cache_settings_btn, &QPushButton::clicked, this, &ProjectPropertiesDialog::OpenDiskCacheSettings); + cache_layout->addWidget(disk_cache_settings_btn); + + tabs->addTab(cache_group, tr("Disk Cache")); + } + + QDialogButtonBox* dialog_btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal); + layout->addWidget(dialog_btns); + connect(dialog_btns, &QDialogButtonBox::accepted, this, &ProjectPropertiesDialog::accept); + connect(dialog_btns, &QDialogButtonBox::rejected, this, &ProjectPropertiesDialog::reject); +} + +void ProjectPropertiesDialog::accept() +{ + if (!ocio_config_is_valid_) { + QMessageBox mb(this); + mb.setWindowModality(Qt::WindowModal); + mb.setIcon(QMessageBox::Critical); + mb.setWindowTitle(tr("OpenColorIO Config Error")); + mb.setText(tr("Failed to set OpenColorIO configuration: %1").arg(ocio_config_error_)); + mb.addButton(QMessageBox::Ok); + mb.exec(); + return; + } + + if (disk_cache_radios_[ProjectSettingsNode::kCacheUseDefaultLocation]->isChecked()) { + // Keep new cache path empty, which means default + } else if (disk_cache_radios_[ProjectSettingsNode::kCacheStoreAlongsideProject]->isChecked()) { + // Ensure alongside project path is valid + if (!VerifyPathAndWarnIfBad(working_project_->get_cache_alongside_project_path())) { + return; + } + } else { + // Ensure custom path is valid + if (!VerifyPathAndWarnIfBad(custom_cache_path_->text())) { + return; + } + } + + if (custom_cache_path_->text() != working_project_->settings()->GetCustomCachePath()) { + // Check if the user is okay with invalidating the current cache + if (!DiskManager::ShowDiskCacheChangeConfirmationDialog(this)) { + return; + } + + working_project_->settings()->SetCustomCachePath(custom_cache_path_->text()); + + emit DiskManager::instance()->InvalidateProject(working_project_); + } + + // This should ripple changes throughout the graph/cache that the color config has changed, and + // therefore should be done after the cache path is changed + if (working_project_->color_manager()->GetConfigFilename() != ocio_filename_->text()) { + working_project_->color_manager()->SetConfigFilename(ocio_filename_->text()); + } + if (working_project_->color_manager()->GetDefaultInputColorSpace() != default_input_colorspace_->currentText()) { + working_project_->color_manager()->SetDefaultInputColorSpace(default_input_colorspace_->currentText()); + } + + super::accept(); +} + +bool ProjectPropertiesDialog::VerifyPathAndWarnIfBad(const QString &path) +{ + if (!FileFunctions::DirectoryIsValid(path, true)) { + QMessageBox mb(this); + mb.setWindowModality(Qt::WindowModal); + mb.setIcon(QMessageBox::Critical); + mb.setWindowTitle(tr("Invalid path")); + mb.setText(tr("The custom cache path is invalid. Please check it and try again.")); + mb.addButton(QMessageBox::Ok); + mb.exec(); + return false; + } + + return true; +} + +void ProjectPropertiesDialog::BrowseForOCIOConfig() +{ + QString fn = QFileDialog::getOpenFileName(this, tr("Browse for OpenColorIO configuration")); + if (!fn.isEmpty()) { + ocio_filename_->setText(fn); + } +} + +void ProjectPropertiesDialog::OCIOFilenameUpdated() +{ + default_input_colorspace_->clear(); + + try { + OCIO::ConstConfigRcPtr c; + + if (ocio_filename_->text().isEmpty()) { + c = ColorManager::GetDefaultConfig(); + } else { + c = ColorManager::CreateConfigFromFile(ocio_filename_->text()); + } + + ocio_filename_->setStyleSheet(QString()); + ocio_config_is_valid_ = true; + + // List input color spaces + QStringList input_cs = ColorManager::ListAvailableColorspaces(c); + + foreach (QString cs, input_cs) { + default_input_colorspace_->addItem(cs); + + if (cs == working_project_->color_manager()->GetDefaultInputColorSpace()) { + default_input_colorspace_->setCurrentIndex(default_input_colorspace_->count()-1); + } + } + } catch (OCIO::Exception& e) { + ocio_config_is_valid_ = false; + ocio_filename_->setStyleSheet(QStringLiteral("QLineEdit {color: red;}")); + ocio_config_error_ = e.what(); + } +} + +void ProjectPropertiesDialog::OpenDiskCacheSettings() +{ + if (disk_cache_radios_[ProjectSettingsNode::kCacheUseDefaultLocation]->isChecked()) { + DiskManager::instance()->ShowDiskCacheSettingsDialog(DiskManager::instance()->GetDefaultCacheFolder(), this); + } else if (disk_cache_radios_[ProjectSettingsNode::kCacheStoreAlongsideProject]->isChecked()) { + DiskManager::instance()->ShowDiskCacheSettingsDialog(working_project_->get_cache_alongside_project_path(), this); + } else { + DiskManager::instance()->ShowDiskCacheSettingsDialog(custom_cache_path_->text(), this); + } +} + +} diff --git a/app/dialog/projectproperties/projectproperties.h b/app/dialog/projectproperties/projectproperties.h new file mode 100644 index 000000000..e6963e541 --- /dev/null +++ b/app/dialog/projectproperties/projectproperties.h @@ -0,0 +1,74 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2020 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 . + +***/ + +#ifndef PROJECTPROPERTIESDIALOG_H +#define PROJECTPROPERTIESDIALOG_H + +#include +#include +#include +#include +#include +#include + +#include "node/project/project.h" +#include "widget/path/pathwidget.h" + +namespace olive { + +class ProjectPropertiesDialog : public QDialog +{ + Q_OBJECT +public: + ProjectPropertiesDialog(Project *p, QWidget* parent); + +public slots: + virtual void accept() override; + +private: + bool VerifyPathAndWarnIfBad(const QString &path); + + Project* working_project_; + + QLineEdit* ocio_filename_; + + QComboBox* default_input_colorspace_; + + bool ocio_config_is_valid_; + + QString ocio_config_error_; + + PathWidget* custom_cache_path_; + + static const int kDiskCacheRadioCount = 3; + QRadioButton *disk_cache_radios_[kDiskCacheRadioCount]; + +private slots: + void BrowseForOCIOConfig(); + + void OCIOFilenameUpdated(); + + void OpenDiskCacheSettings(); + +}; + +} + +#endif // PROJECTPROPERTIESDIALOG_H diff --git a/app/node/project/project.cpp b/app/node/project/project.cpp index 9641669cd..d244b41ed 100644 --- a/app/node/project/project.cpp +++ b/app/node/project/project.cpp @@ -320,6 +320,15 @@ bool Project::is_new() const return !is_modified_ && filename_.isEmpty(); } +QString Project::get_cache_alongside_project_path() const +{ + if (!filename_.isEmpty()) { + // Non-translated string so the path doesn't change if the language does + return QFileInfo(filename_).dir().filePath(QStringLiteral("cache")); + } + return QString(); +} + QString Project::cache_path() const { ProjectSettingsNode::CacheSetting setting = settings_->GetCacheSetting(); @@ -330,7 +339,6 @@ QString Project::cache_path() const case ProjectSettingsNode::kCacheCustomPath: { QString cache_path = settings_->GetCustomCachePath(); - if (cache_path.isEmpty()) { return cache_path; } @@ -338,8 +346,9 @@ QString Project::cache_path() const } case ProjectSettingsNode::kCacheStoreAlongsideProject: { - if (!filename_.isEmpty()) { - return QFileInfo(filename_).path(); + QString alongside = get_cache_alongside_project_path(); + if (!alongside.isEmpty()) { + return alongside; } break; } diff --git a/app/node/project/project.h b/app/node/project/project.h index 2565e3697..11f31ee41 100644 --- a/app/node/project/project.h +++ b/app/node/project/project.h @@ -73,6 +73,7 @@ public: bool is_new() const; + QString get_cache_alongside_project_path() const; QString cache_path() const; const QUuid& GetUuid() const diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index cb3322f67..3492f9442 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -69,6 +69,8 @@ MainMenu::MainMenu(MainWindow *parent) : file_close_all_projects_item_ = file_menu_->AddItem("closeallproj", Core::instance(), static_cast(&Core::CloseAllProjects)); file_close_all_except_item_ = file_menu_->AddItem("closeallexcept", Core::instance(), &Core::CloseAllExceptActiveProject); file_menu_->addSeparator(); + file_project_properties_item_ = file_menu_->AddItem("projectproperties", Core::instance(), &Core::DialogProjectPropertiesShow, tr("Shift+F10")); + file_menu_->addSeparator(); file_exit_item_ = file_menu_->AddItem("exit", parent, &MainWindow::close); // @@ -696,6 +698,7 @@ void MainMenu::Retranslate() file_export_menu_->setTitle(tr("&Export")); file_export_media_item_->setText(tr("&Media...")); file_close_all_projects_item_->setText(tr("Close All Projects")); + file_project_properties_item_->setText(tr("Project Properties")); file_exit_item_->setText(tr("E&xit")); // Edit menu diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index ff038e64a..748e7159f 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -210,6 +210,7 @@ private: QAction* file_close_project_item_; QAction* file_close_all_projects_item_; QAction* file_close_all_except_item_; + QAction* file_project_properties_item_; QAction* file_exit_item_; Menu* edit_menu_;