From bb63d765ba8d5011195cd5547d563f62b09bbb97 Mon Sep 17 00:00:00 2001 From: Jeremy Bullock Date: Sun, 8 Nov 2020 23:55:47 -0700 Subject: [PATCH] Recent Projects and Default Project Location (#415) * Recent Projects and Default Project Location * Add to load scene * Added shortening of long paths and hover effects * The ComboBox is back baby. * Browse option --- toonz/sources/include/toonz/preferences.h | 3 + .../include/toonz/preferencesitemids.h | 1 + toonz/sources/include/toonzqt/filefield.h | 1 + toonz/sources/toonz/filebrowser.cpp | 9 +- toonz/sources/toonz/filebrowser.h | 2 +- toonz/sources/toonz/filebrowsermodel.cpp | 66 +++--- toonz/sources/toonz/filebrowsermodel.h | 4 +- toonz/sources/toonz/filebrowserpopup.cpp | 2 +- toonz/sources/toonz/iocommand.cpp | 19 +- toonz/sources/toonz/main.cpp | 2 +- toonz/sources/toonz/mainwindow.cpp | 48 ++++- toonz/sources/toonz/mainwindow.h | 6 +- toonz/sources/toonz/menubar.cpp | 2 + toonz/sources/toonz/menubarcommandids.h | 1 + toonz/sources/toonz/preferencespopup.cpp | 11 +- toonz/sources/toonz/projectpopup.cpp | 128 ++++++++++-- toonz/sources/toonz/projectpopup.h | 30 +++ toonz/sources/toonz/startuppopup.cpp | 197 ++++++++++++++---- toonz/sources/toonz/startuppopup.h | 4 +- toonz/sources/toonzlib/preferences.cpp | 6 + toonz/sources/toonzqt/filefield.cpp | 21 ++ 21 files changed, 458 insertions(+), 105 deletions(-) diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index 2684c5f8..3d8898e7 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -272,6 +272,9 @@ public: TPixel getRasterBackgroundColor() const { return getColorValue(rasterBackgroundColor); } + QString getDefaultProjectPath() const { + return getStringValue(defaultProjectPath); + } // Import Export Tab QString getFfmpegPath() const { return getStringValue(ffmpegPath); } diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index 53c5e587..16f81a76 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -67,6 +67,7 @@ enum PreferencesItemId { // Saving rasterBackgroundColor, resetUndoOnSavingLevel, + defaultProjectPath, //---------- // Import / Export diff --git a/toonz/sources/include/toonzqt/filefield.h b/toonz/sources/include/toonzqt/filefield.h index cffa7ceb..4e1ea966 100644 --- a/toonz/sources/include/toonzqt/filefield.h +++ b/toonz/sources/include/toonzqt/filefield.h @@ -96,6 +96,7 @@ public: static void setBrowserPopupController(BrowserPopupController *controller); static BrowserPopupController *getBrowserPopupController(); + void forceOpenBrowser(); protected slots: /*! Open a static file dialog popup to browse and choose directories. If a diff --git a/toonz/sources/toonz/filebrowser.cpp b/toonz/sources/toonz/filebrowser.cpp index c33caf74..561d11b4 100644 --- a/toonz/sources/toonz/filebrowser.cpp +++ b/toonz/sources/toonz/filebrowser.cpp @@ -145,9 +145,8 @@ QMutex levelFileMutex; } // namespace inline bool isMultipleFrameType(std::string type) { - return (type == "tlv" || type == "tzl" || type == "pli" || - type == "avi" || type == "gif" || type == "mp4" || - type == "webm"); + return (type == "tlv" || type == "tzl" || type == "pli" || type == "avi" || + type == "gif" || type == "mp4" || type == "webm"); } //============================================================================= @@ -686,7 +685,7 @@ void FileBrowser::refreshCurrentFolderItems() { //----------------------------------------------------------------------------- void FileBrowser::setFolder(const TFilePath &fp, bool expandNode, - bool forceUpdate) { + bool forceUpdate, bool collapseAll) { if (fp == m_folder && !forceUpdate) return; // set the current folder path @@ -699,7 +698,7 @@ void FileBrowser::setFolder(const TFilePath &fp, bool expandNode, m_folderName->setText(toQString(fp)); refreshCurrentFolderItems(); - + if (collapseAll) m_folderTreeView->collapseAll(); if (!TFileStatus(fp).isLink()) m_folderTreeView->setCurrentNode(fp, expandNode); } diff --git a/toonz/sources/toonz/filebrowser.h b/toonz/sources/toonz/filebrowser.h index bace0810..55cf1be1 100644 --- a/toonz/sources/toonz/filebrowser.h +++ b/toonz/sources/toonz/filebrowser.h @@ -99,7 +99,7 @@ types to be displayed in the file browser. void removeFilterType(const QString &type); void setFolder(const TFilePath &fp, bool expandNode = false, - bool forceUpdate = false); + bool forceUpdate = false, bool collapseAll = false); // process when inputting the folder which is not regitered in the folder tree // (e.g. UNC path in Windows) void setUnregisteredFolder(const TFilePath &fp); diff --git a/toonz/sources/toonz/filebrowsermodel.cpp b/toonz/sources/toonz/filebrowsermodel.cpp index e6cd8b7f..808dc138 100644 --- a/toonz/sources/toonz/filebrowsermodel.cpp +++ b/toonz/sources/toonz/filebrowsermodel.cpp @@ -15,6 +15,7 @@ #include "tapp.h" #include "toonz/tscenehandle.h" +#include "mainwindow.h" #include #include @@ -774,8 +775,10 @@ void DvDirModelProjectNode::makeCurrent() { TProjectManager *pm = TProjectManager::instance(); TFilePath projectPath = getProjectPath(); if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project"))) return; - + TFilePath projectFolder = getPath(); pm->setCurrentProjectPath(projectPath); + RecentFiles::instance()->addFilePath(projectFolder.getQString(), + RecentFiles::Project); IoCmd::newScene(); } @@ -1029,6 +1032,7 @@ DvDirModelRootNode::DvDirModelRootNode() : DvDirModelNode(0, L"Root") , m_myComputerNode(0) , m_networkNode(0) + , m_currentProjectNode(0) , m_sandboxProjectNode(0) { m_nodeType = "Root"; } @@ -1077,24 +1081,10 @@ void DvDirModelRootNode::refreshChildren() { TProjectManager *pm = TProjectManager::instance(); TFilePath sandboxProjectPath = pm->getSandboxProjectFolder(); + m_projectPaths.insert(sandboxProjectPath); m_sandboxProjectNode = new DvDirModelProjectNode(this, sandboxProjectPath); addChild(m_sandboxProjectNode); - - TFilePath projectPath = pm->getCurrentProjectPath().getParentDir(); - if (projectPath != pm->getSandboxProjectFolder()) { - m_currentProjectNode = new DvDirModelProjectNode(this, projectPath); - m_projectPaths.insert(projectPath); - addChild(m_currentProjectNode); - } - - if (m_projectPaths.size() > 1) { - for (auto i : m_projectPaths) { - if (i == projectPath) continue; - DvDirModelProjectNode *addedProjectNode = - new DvDirModelProjectNode(this, projectPath); - addChild(addedProjectNode); - } - } + m_projectNodes.push_back(m_sandboxProjectNode); // SVN Repositories QList repositories = @@ -1118,10 +1108,25 @@ void DvDirModelRootNode::refreshChildren() { new DvDirModelSceneFolderNode(this, L"Scene Folder", TFilePath()); m_sceneFolderNode->setPixmap(QPixmap(":Resources/clapboard.png")); } else { - TProjectManager *pm = TProjectManager::instance(); + RecentFiles *recent = RecentFiles::instance(); + QList recentFiles = recent->getFilesNameList(RecentFiles::Project); + int recentCount = recentFiles.size(); + TProjectManager *pm = TProjectManager::instance(); + for (auto path : recentFiles) { + TFilePath projectPath(path); + if (TSystem::doesExistFileOrLevel(projectPath) && + m_projectPaths.find(projectPath) == m_projectPaths.end() && + pm->isProject(projectPath)) { + DvDirModelProjectNode *addedProjectNode = + new DvDirModelProjectNode(this, projectPath); + m_projectPaths.insert(projectPath); + addChild(addedProjectNode); + m_projectNodes.push_back(addedProjectNode); + } + } + TFilePath projectPath = pm->getCurrentProjectPath().getParentDir(); - if (projectPath != pm->getSandboxProjectFolder() && - (m_projectPaths.find(projectPath) == m_projectPaths.end())) { + if (m_projectPaths.find(projectPath) == m_projectPaths.end()) { std::string rootString = projectPath.getQString().toStdString(); m_currentProjectNode = new DvDirModelProjectNode(this, projectPath); m_projectPaths.insert(projectPath); @@ -1150,15 +1155,14 @@ DvDirModelNode *DvDirModelRootNode::getNodeByPath(const TFilePath &path) { } // path could be a project, under some project root - for (i = 0; i < (int)m_projectRootNodes.size(); i++) { - node = m_projectRootNodes[i]->getNodeByPath(path); - if (node) return node; + for (i = 0; i < (int)m_projectNodes.size(); i++) { + node = m_projectNodes[i]; + DvDirModelProjectNode *projectNode = + dynamic_cast(node); // search in the project folders - for (int j = 0; j < m_projectRootNodes[i]->getChildCount(); j++) { - DvDirModelProjectNode *projectNode = - dynamic_cast( - m_projectRootNodes[i]->getChild(j)); - if (projectNode) { + if (projectNode) { + if (projectNode->getPath() == path) return node; + for (int j = 0; j < m_projectNodes[i]->getChildCount(); j++) { // for the normal folder in the project folder node = projectNode->getNodeByPath(path); if (node) return node; @@ -1170,10 +1174,6 @@ DvDirModelNode *DvDirModelRootNode::getNodeByPath(const TFilePath &path) { if (node) return node; } } - } else // for the normal folder in the project root - { - node = m_projectRootNodes[i]->getChild(j)->getNodeByPath(path); - if (node) return node; } } } @@ -1341,6 +1341,8 @@ void DvDirModel::refreshFolderChild(const QModelIndex &i) { for (r = 0; r < count; r++) refreshFolderChild(index(r, 0, i)); } +void DvDirModel::forceRefresh() { onSceneSwitched(); } + //----------------------------------------------------------------------------- DvDirModelNode *DvDirModel::getNode(const QModelIndex &index) const { diff --git a/toonz/sources/toonz/filebrowsermodel.h b/toonz/sources/toonz/filebrowsermodel.h index 8a5e8a7b..383c0c71 100644 --- a/toonz/sources/toonz/filebrowsermodel.h +++ b/toonz/sources/toonz/filebrowsermodel.h @@ -305,8 +305,8 @@ public: //----------------------------------------------------------------------------- class DvDirModelRootNode final : public DvDirModelNode { - std::vector m_projectRootNodes; std::vector m_versionControlNodes; + std::vector m_projectNodes; DvDirModelMyComputerNode *m_myComputerNode; DvDirModelNetworkNode *m_networkNode; DvDirModelProjectNode *m_sandboxProjectNode; @@ -371,6 +371,8 @@ public: void refreshFolder(const TFilePath &folderPath, const QModelIndex &i = QModelIndex()); void refreshFolderChild(const QModelIndex &i = QModelIndex()); + // Only used when first launching the program to update recent projects + void forceRefresh(); bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; diff --git a/toonz/sources/toonz/filebrowserpopup.cpp b/toonz/sources/toonz/filebrowserpopup.cpp index 8e4c3b91..9d2f6a02 100644 --- a/toonz/sources/toonz/filebrowserpopup.cpp +++ b/toonz/sources/toonz/filebrowserpopup.cpp @@ -204,7 +204,7 @@ void FileBrowserPopup::setFileMode(bool isDirectoryOnly) { //----------------------------------------------------------------------------- void FileBrowserPopup::setFolder(const TFilePath &folderPath) { - m_browser->setFolder(folderPath, true); + m_browser->setFolder(folderPath, true, true, true); } //----------------------------------------------------------------------------- diff --git a/toonz/sources/toonz/iocommand.cpp b/toonz/sources/toonz/iocommand.cpp index ea192215..b4ade176 100644 --- a/toonz/sources/toonz/iocommand.cpp +++ b/toonz/sources/toonz/iocommand.cpp @@ -1939,10 +1939,16 @@ bool IoCmd::loadScene(const TFilePath &path, bool updateRecentFile, // set dirty for xdts files since converted tnz is not yet saved TApp::instance()->getCurrentScene()->setDirtyFlag(isXdts); History::instance()->addItem(scenePath); - if (updateRecentFile) + if (updateRecentFile) { RecentFiles::instance()->addFilePath( toQString(scenePath), RecentFiles::Scene, QString::fromStdString(scene->getProject()->getName().getName())); + } + RecentFiles::instance()->addFilePath(TProjectManager::instance() + ->getCurrentProjectPath() + .getParentDir() + .getQString(), + RecentFiles::Project); QApplication::restoreOverrideCursor(); int forbiddenLevelCount = 0; @@ -2909,6 +2915,17 @@ public: //----------------------------------------------------------------------------- +class ClearRecentProjectListCommandHandler final : public MenuItemHandler { +public: + ClearRecentProjectListCommandHandler() + : MenuItemHandler(MI_ClearRecentProject) {} + void execute() override { + RecentFiles::instance()->clearRecentFilesList(RecentFiles::Project); + } +} clearRecentProjectListCommandHandler; + +//----------------------------------------------------------------------------- + class RevertScene final : public MenuItemHandler { public: RevertScene() : MenuItemHandler(MI_RevertScene) {} diff --git a/toonz/sources/toonz/main.cpp b/toonz/sources/toonz/main.cpp index 7a1e5c79..76c810a6 100644 --- a/toonz/sources/toonz/main.cpp +++ b/toonz/sources/toonz/main.cpp @@ -730,7 +730,7 @@ int main(int argc, char *argv[]) { // a.connect(&a, SIGNAL(lastWindowClosed()), &a, SLOT(quit())); if (Preferences::instance()->isLatestVersionCheckEnabled()) w.checkForUpdates(); - + DvDirModel::instance()->forceRefresh(); w.show(); // Show floating panels only after the main window has been shown diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index bf111467..cde32e48 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -1692,6 +1692,9 @@ void MainWindow::defineActions() { "", tr("Remove everything from the recent scene list.")); createMenuFileAction(MI_ClearRecentLevel, tr("&Clear Recent level File List"), "", tr("Remove everything from the recent level list.")); + createMenuFileAction(MI_ClearRecentProject, tr("&Clear Recent Project List"), + "", + tr("Remove everything from the recent project list.")); menuAct = createMenuLevelAction(MI_NewLevel, tr("&New Level..."), "Alt+N", tr("Create a new drawing layer.")); @@ -3751,7 +3754,10 @@ void MainWindow::onQuit() { close(); } //============================================================================= RecentFiles::RecentFiles() - : m_recentScenes(), m_recentSceneProjects(), m_recentLevels() {} + : m_recentScenes() + , m_recentSceneProjects() + , m_recentLevels() + , m_recentProjects() {} //----------------------------------------------------------------------------- @@ -3771,7 +3777,9 @@ void RecentFiles::addFilePath(QString path, FileType fileType, QList files = (fileType == Scene) ? m_recentScenes : (fileType == Level) ? m_recentLevels - : m_recentFlipbookImages; + : (fileType == Project) + ? m_recentProjects + : m_recentFlipbookImages; int i; for (i = 0; i < files.size(); i++) if (files.at(i) == path) { @@ -3790,6 +3798,8 @@ void RecentFiles::addFilePath(QString path, FileType fileType, m_recentScenes = files; else if (fileType == Level) m_recentLevels = files; + else if (fileType == Project) + m_recentProjects = files; else m_recentFlipbookImages = files; @@ -3805,6 +3815,8 @@ void RecentFiles::moveFilePath(int fromIndex, int toIndex, FileType fileType) { m_recentSceneProjects.move(fromIndex, toIndex); } else if (fileType == Level) m_recentLevels.move(fromIndex, toIndex); + else if (fileType == Project) + m_recentProjects.move(fromIndex, toIndex); else m_recentFlipbookImages.move(fromIndex, toIndex); saveRecentFiles(); @@ -3818,6 +3830,8 @@ void RecentFiles::removeFilePath(int index, FileType fileType) { m_recentSceneProjects.removeAt(index); } else if (fileType == Level) m_recentLevels.removeAt(index); + else if (fileType == Project) + m_recentProjects.removeAt(index); saveRecentFiles(); } @@ -3826,8 +3840,10 @@ void RecentFiles::removeFilePath(int index, FileType fileType) { QString RecentFiles::getFilePath(int index, FileType fileType) const { return (fileType == Scene) ? m_recentScenes[index] - : (fileType == Level) ? m_recentLevels[index] - : m_recentFlipbookImages[index]; + : (fileType == Level) + ? m_recentLevels[index] + : (fileType == Project) ? m_recentProjects[index] + : m_recentFlipbookImages[index]; } //----------------------------------------------------------------------------- @@ -3853,6 +3869,8 @@ void RecentFiles::clearRecentFilesList(FileType fileType) { m_recentSceneProjects.clear(); } else if (fileType == Level) m_recentLevels.clear(); + else if (fileType == Project) + m_recentProjects.clear(); else m_recentFlipbookImages.clear(); @@ -3902,6 +3920,15 @@ void RecentFiles::loadRecentFiles() { if (!level.isEmpty()) m_recentLevels.append(level); } + QList projects = settings.value(QString("Projects")).toList(); + if (!projects.isEmpty()) { + for (i = 0; i < projects.size(); i++) + m_recentProjects.append(projects.at(i).toString()); + } else { + QString project = settings.value(QString("Projects")).toString(); + if (!project.isEmpty()) m_recentProjects.append(project); + } + QList flipImages = settings.value(QString("FlipbookImages")).toList(); if (!flipImages.isEmpty()) { @@ -3915,6 +3942,7 @@ void RecentFiles::loadRecentFiles() { refreshRecentFilesMenu(Scene); refreshRecentFilesMenu(Level); refreshRecentFilesMenu(Flip); + refreshRecentFilesMenu(Project); } //----------------------------------------------------------------------------- @@ -3925,6 +3953,7 @@ void RecentFiles::saveRecentFiles() { settings.setValue(QString("Scenes"), QVariant(m_recentScenes)); settings.setValue(QString("SceneProjects"), QVariant(m_recentSceneProjects)); settings.setValue(QString("Levels"), QVariant(m_recentLevels)); + settings.setValue(QString("Projects"), QVariant(m_recentProjects)); settings.setValue(QString("FlipbookImages"), QVariant(m_recentFlipbookImages)); } @@ -3935,14 +3964,19 @@ QList RecentFiles::getFilesNameList(FileType fileType) { QList files = (fileType == Scene) ? m_recentScenes : (fileType == Level) ? m_recentLevels - : m_recentFlipbookImages; + : (fileType == Project) + ? m_recentProjects + : m_recentFlipbookImages; QList names; int i; for (i = 0; i < files.size(); i++) { TFilePath path(files.at(i).toStdWString()); QString str, number; - names.append(number.number(i + 1) + QString(". ") + - str.fromStdWString(path.getWideString())); + if (fileType != Project) + names.append(number.number(i + 1) + QString(". ") + + str.fromStdWString(path.getWideString())); + else + names.append(str.fromStdWString(path.getWideString())); } return names; } diff --git a/toonz/sources/toonz/mainwindow.h b/toonz/sources/toonz/mainwindow.h index d042760a..96f2ee8f 100644 --- a/toonz/sources/toonz/mainwindow.h +++ b/toonz/sources/toonz/mainwindow.h @@ -338,15 +338,19 @@ signals: class RecentFiles { friend class StartupPopup; + friend class DvDirModelRootNode; + friend class ProjectPopup; + friend class ProjectSettingsPopup; QList m_recentScenes; QList m_recentSceneProjects; QList m_recentLevels; QList m_recentFlipbookImages; + QList m_recentProjects; RecentFiles(); public: - enum FileType { Scene, Level, Flip, None }; + enum FileType { Scene, Level, Flip, Project, None }; static RecentFiles *instance(); ~RecentFiles(); diff --git a/toonz/sources/toonz/menubar.cpp b/toonz/sources/toonz/menubar.cpp index 44efa8af..b8074a1b 100644 --- a/toonz/sources/toonz/menubar.cpp +++ b/toonz/sources/toonz/menubar.cpp @@ -249,6 +249,8 @@ void TopBar::loadMenubar() { addMenuItem(projectManagementMenu, MI_ProjectSettings); projectManagementMenu->addSeparator(); addMenuItem(projectManagementMenu, MI_SaveDefaultSettings); + projectManagementMenu->addSeparator(); + addMenuItem(projectManagementMenu, MI_ClearRecentProject); } fileMenu->addSeparator(); QMenu *importMenu = fileMenu->addMenu(tr("Import")); diff --git a/toonz/sources/toonz/menubarcommandids.h b/toonz/sources/toonz/menubarcommandids.h index 27e42e84..630fee7f 100644 --- a/toonz/sources/toonz/menubarcommandids.h +++ b/toonz/sources/toonz/menubarcommandids.h @@ -24,6 +24,7 @@ #define MI_OpenRecentLevel "MI_OpenRecentLevel" #define MI_ClearRecentScene "MI_ClearRecentScene" #define MI_ClearRecentLevel "MI_ClearRecentLevel" +#define MI_ClearRecentProject "MI_ClearRecentProject" #define MI_PrintXsheet "MI_PrintXsheet" #define MI_NewLevel "MI_NewLevel" #define MI_NewVectorLevel "MI_NewVectorLevel" diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index edac1372..74eec225 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -470,9 +470,9 @@ void PreferencesPopup::beforeRoomChoiceChanged() { //----------------------------------------------------------------------------- void PreferencesPopup::onColorCalibrationChanged() { - LutManager::instance()->update(); - TApp::instance()->getCurrentScene()->notifyPreferenceChanged( - "ColorCalibration"); + LutManager::instance()->update(); + TApp::instance()->getCurrentScene()->notifyPreferenceChanged( + "ColorCalibration"); } //----------------------------------------------------------------------------- @@ -1028,6 +1028,7 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { {rasterBackgroundColor, tr("Matte color:")}, {resetUndoOnSavingLevel, tr("Clear Undo History when Saving Levels")}, {doNotShowPopupSaveScene, tr("Do not show Save Scene popup warning")}, + {defaultProjectPath, tr("Default Project Path:")}, // Import / Export {ffmpegPath, tr("FFmpeg Path:")}, @@ -1320,7 +1321,7 @@ QWidget* PreferencesPopup::createGeneralPage() { QWidget* widget = new QWidget(this); QGridLayout* lay = new QGridLayout(); setupLayout(lay); - + insertUI(defaultProjectPath, lay); insertUI(defaultViewerEnabled, lay); insertUI(rasterOptimizedMemory, lay); insertUI(startupPopupEnabled, lay); @@ -1471,7 +1472,7 @@ QWidget* PreferencesPopup::createInterfacePage() { // m_preEditedFuncMap.insert(CurrentRoomChoice, // &PreferencesPopup::beforeRoomChoiceChanged); m_onEditedFuncMap.insert(colorCalibrationEnabled, - &PreferencesPopup::onColorCalibrationChanged); + &PreferencesPopup::onColorCalibrationChanged); return widget; } diff --git a/toonz/sources/toonz/projectpopup.cpp b/toonz/sources/toonz/projectpopup.cpp index 17a1214e..92c5bb16 100644 --- a/toonz/sources/toonz/projectpopup.cpp +++ b/toonz/sources/toonz/projectpopup.cpp @@ -9,6 +9,7 @@ #include "iocommand.h" #include "filebrowsermodel.h" #include "dvdirtreeview.h" +#include "mainwindow.h" // TnzQt includes #include "toonzqt/menubarcommand.h" @@ -23,15 +24,16 @@ #include "tapp.h" #include "toonz/tscenehandle.h" #include "toonz/toonzscene.h" +#include "toonz/preferences.h" // Qt includes #include #include #include -#include #include #include #include +#include using namespace DVGui; @@ -50,17 +52,24 @@ TFilePath getDocumentsPath() { ProjectPopup::ProjectPopup(bool isModal) : Dialog(TApp::instance()->getMainWindow(), isModal, false, "Project") { TProjectManager *pm = TProjectManager::instance(); - m_mainFrame->setFixedHeight(100); + // m_mainFrame->setFixedHeight(100); m_mainFrame->setMinimumWidth(400); this->layout()->setSizeConstraint(QLayout::SetFixedSize); - m_choosePrjLabel = new QLabel(tr("Project:"), this); - m_prjNameLabel = new QLabel(tr("Project Name:"), this); - m_pathFieldLabel = new QLabel(tr("Create Project In:"), this); - m_nameFld = new LineEdit(); - + m_choosePrjLabel = new QLabel(tr("Project:"), this); + m_prjNameLabel = new QLabel(tr("Project Name:"), this); + m_pathFieldLabel = new QLabel(tr("Create Project In:"), this); + m_nameFld = new LineEdit(); + m_recentProjectLayout = new QGridLayout(this); + m_recentProjectLayout->setSpacing(2); + m_recentProjectLayout->setMargin(4); m_projectLocationFld = new DVGui::FileField(this, getDocumentsPath().getQString()); + QString defaultProjectLocation = + Preferences::instance()->getDefaultProjectPath(); + if (TSystem::doesExistFileOrLevel(TFilePath(defaultProjectLocation))) { + m_projectLocationFld->setPath(defaultProjectLocation); + } m_nameFld->setMaximumHeight(WidgetHeight); @@ -114,6 +123,11 @@ ProjectPopup::ProjectPopup(bool isModal) cb->hide(); } m_topLayout->addLayout(upperLayout); + m_projectGB = new QGroupBox(tr("Recent Projects"), this); + m_projectGB->setLayout(m_recentProjectLayout); + m_projectGB->setAlignment(Qt::AlignLeft); + m_topLayout->addWidget(m_projectGB); + m_projectGB->hide(); } pm->addListener(this); @@ -271,7 +285,8 @@ void ProjectSettingsPopup::projectChanged() { if (projectP->getProjectPath() != path) { projectP->save(); } - + RecentFiles::instance()->addFilePath(path.getParentDir().getQString(), + RecentFiles::Project); updateFieldsFromProject(projectP); IoCmd::newScene(); accept(); @@ -326,6 +341,64 @@ void ProjectSettingsPopup::showEvent(QShowEvent *) { TProjectP currentProject = TProjectManager::instance()->getCurrentProject(); updateFieldsFromProject(currentProject.getPointer()); + if (m_recentProjectLayout) { + while (m_recentProjectLayout->count() > 0) { + QLayoutItem *item = m_recentProjectLayout->takeAt(0); + QWidget *widget = item->widget(); + if (widget) delete widget; + delete item; + } + } + + RecentFiles *recent = RecentFiles::instance(); + QList recentProjects = + recent->getFilesNameList(RecentFiles::Project); + + TProjectManager *pm = TProjectManager::instance(); + static QPixmap closeProjectPixmap( + svgToPixmap(getIconThemePath("actions/18/folder_project.svg"))); + // setPixmap(closeProjectPixmap); + int i = 0; + for (auto path : recentProjects) { + TFilePath projectPath(path); + if (TSystem::doesExistFileOrLevel(projectPath) && + pm->isProject(projectPath)) { + QLabel *folderLabel = new QLabel(this); + folderLabel->setPixmap(closeProjectPixmap); + folderLabel->setFixedWidth(20); + folderLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + m_recentProjectLayout->addWidget(folderLabel, i, 0, Qt::AlignRight); + // int slashCount = path.count("\\"); + // QString lookFor = slashCount > 0 ? "\\" : "//"; + QString labelText = QString::fromStdString(projectPath.getName()); + // if (labelText.count(lookFor) > 4) { + // while (labelText.count(lookFor) > 4) { + // int firstIndex = labelText.indexOf(lookFor, 0); + // labelText.remove(0, firstIndex + 1); + // } + // labelText.insert(0, "..." + lookFor); + //} + ClickableProjectLabel *projectLabel = + new ClickableProjectLabel(labelText, this); + projectLabel->setPath(path); + projectLabel->setToolTip(path); + m_recentProjectLayout->addWidget(projectLabel, i, 1, Qt::AlignLeft); + connect(projectLabel, &ClickableProjectLabel::onMouseRelease, [=]() { + m_projectLocationFld->blockSignals(true); + m_projectLocationFld->setPath(projectLabel->getPath()); + m_projectLocationFld->blockSignals(false); + projectChanged(); + }); + i++; + } + } + if (recentProjects.size() > 0) { + m_projectGB->show(); + m_recentProjectLayout->setColumnStretch(0, 0); + m_recentProjectLayout->setColumnStretch(1, 10); + } else + m_projectGB->hide(); + m_nameFld->setText(""); m_projectLocationFld->blockSignals(true); @@ -336,13 +409,10 @@ void ProjectSettingsPopup::showEvent(QShowEvent *) { m_oldPath = TFilePath(m_projectLocationFld->getPath()); m_projectLocationFld->blockSignals(false); - resize(600, 75); QSizePolicy sizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); setSizePolicy(sizePolicy); - setMinimumSize(QSize(600, 75)); - setMaximumSize(QSize(600, 75)); setFixedSize(width(), height()); setSizeGripEnabled(false); } @@ -467,7 +537,11 @@ void ProjectCreatePopup::showEvent(QShowEvent *) { cb->setChecked(false); cb->blockSignals(signalesAlreadyBlocked); } - + QString defaultProjectLocation = + Preferences::instance()->getDefaultProjectPath(); + if (TSystem::doesExistFileOrLevel(TFilePath(defaultProjectLocation))) { + m_projectLocationFld->setPath(defaultProjectLocation); + } m_nameFld->setText(""); resize(600, 150); @@ -485,6 +559,36 @@ void ProjectCreatePopup::setPath(QString path) { m_projectLocationFld->setPath(path); } +//============================================================================= + +ClickableProjectLabel::ClickableProjectLabel(const QString &text, + QWidget *parent, Qt::WindowFlags f) + : QLabel(text, parent, f) { + m_path = text; +} + +//----------------------------------------------------------------------------- + +ClickableProjectLabel::~ClickableProjectLabel() {} + +//----------------------------------------------------------------------------- + +void ClickableProjectLabel::mouseReleaseEvent(QMouseEvent *event) { + emit onMouseRelease(event); +} + +//----------------------------------------------------------------------------- + +void ClickableProjectLabel::enterEvent(QEvent *) { + setStyleSheet("text-decoration: underline;"); +} + +//----------------------------------------------------------------------------- + +void ClickableProjectLabel::leaveEvent(QEvent *) { + setStyleSheet("text-decoration: none;"); +} + //----------------------------------------------------------------------------- OpenPopupCommandHandler openProjectCreatePopup( diff --git a/toonz/sources/toonz/projectpopup.h b/toonz/sources/toonz/projectpopup.h index dc718800..e892a189 100644 --- a/toonz/sources/toonz/projectpopup.h +++ b/toonz/sources/toonz/projectpopup.h @@ -7,6 +7,8 @@ #include "toonz/tproject.h" #include "filebrowsermodel.h" #include +#include +#include // forward declaration namespace DVGui { @@ -16,6 +18,8 @@ class CheckBox; } class QComboBox; +class QGridLayout; +class QGroupBox; //============================================================================= // ProjectPopup @@ -35,6 +39,9 @@ protected: DVGui::FileField *m_projectLocationFld; + QGridLayout *m_recentProjectLayout; + QGroupBox *m_projectGB; + public: ProjectPopup(bool isModal); // da TProjectManager::Listener @@ -89,4 +96,27 @@ protected: void showEvent(QShowEvent *) override; }; +// The ClickableLabel class is used to allow click and dragging +// on a label to change the value of a linked field +class ClickableProjectLabel : public QLabel { + Q_OBJECT + + QString m_path; + +protected: + void mouseReleaseEvent(QMouseEvent *) override; + void enterEvent(QEvent *) override; + void leaveEvent(QEvent *) override; + +public: + ClickableProjectLabel(const QString &text, QWidget *parent = nullptr, + Qt::WindowFlags f = Qt::WindowFlags()); + ~ClickableProjectLabel(); + void setPath(QString path) { m_path = path; } + QString getPath() { return m_path; } + +signals: + void onMouseRelease(QMouseEvent *event); +}; + #endif // PROJECTPOPUP_H diff --git a/toonz/sources/toonz/startuppopup.cpp b/toonz/sources/toonz/startuppopup.cpp index 49999ea8..4aee83da 100644 --- a/toonz/sources/toonz/startuppopup.cpp +++ b/toonz/sources/toonz/startuppopup.cpp @@ -109,6 +109,7 @@ StartupPopup::StartupPopup() .getParentDir() .getQString(); m_projectLocationFld->setPath(currProjectPath); + m_projectsCB = new QComboBox(this); m_sceneNameLabel = new QLabel(tr("Scene Name:")); m_widthLabel = new QLabel(tr("Width:"), this); m_widthFld = new MeasuredDoubleLineEdit(this); @@ -200,7 +201,9 @@ StartupPopup::StartupPopup() projectLay->setMargin(8); { projectLay->addWidget(m_projectLocationFld, 1); + projectLay->addWidget(m_projectsCB, 1); projectLay->addWidget(newProjectButton, 0); + m_projectLocationFld->hide(); } m_projectBox->setLayout(projectLay); guiLay->addWidget(m_projectBox, 1, 0, 1, 1, Qt::AlignCenter); @@ -214,9 +217,6 @@ StartupPopup::StartupPopup() Qt::AlignRight | Qt::AlignVCenter); newSceneLay->addWidget(m_nameFld, 0, 1, 1, 5); - // Save In - // newSceneLay->addWidget(new QLabel(tr("Save In:")), 1, 0, - // Qt::AlignRight | Qt::AlignVCenter); newSceneLay->addWidget(m_pathFld, 1, 1, 1, 5); m_pathFld->hide(); newSceneLay->addWidget(new QLabel(tr("Preset:")), 2, 0, @@ -249,9 +249,7 @@ StartupPopup::StartupPopup() newSceneLay->addWidget(m_resXFld, 4, 1); newSceneLay->addWidget(m_resXLabel, 4, 2, 1, 1, Qt::AlignCenter); newSceneLay->addWidget(m_resYFld, 4, 3); - // newSceneLay->addWidget(m_fpsLabel, 5, 0, - // Qt::AlignRight | Qt::AlignVCenter); - // newSceneLay->addWidget(m_fpsFld, 5, 1, 1, 1); + newSceneLay->addWidget(createButton, 6, 1, 1, 3, Qt::AlignLeft); newSceneLay->setColumnStretch(4, 1); } @@ -320,16 +318,16 @@ StartupPopup::StartupPopup() SLOT(onAutoSaveOnChanged(int))); ret = ret && connect(m_autoSaveTimeFld, SIGNAL(editingFinished()), this, SLOT(onAutoSaveTimeChanged())); - ret = ret && connect(m_projectLocationFld, SIGNAL(pathChanged()), this, - SLOT(checkProject())); + ret = ret && connect(m_projectsCB, SIGNAL(currentIndexChanged(int)), this, + SLOT(onProjectComboChanged(int))); assert(ret); - checkProject(); } //----------------------------------------------------------------------------- void StartupPopup::showEvent(QShowEvent *) { loadPresetList(); + updateProjectCB(); m_nameFld->setFocus(); m_pathFld->setPath(TApp::instance() ->getCurrentScene() @@ -443,21 +441,93 @@ void StartupPopup::refreshRecentScenes() { //----------------------------------------------------------------------------- -void StartupPopup::onCreateButton() { +void StartupPopup::updateProjectCB() { + m_updating = true; + m_projectPaths.clear(); + m_projectsCB->clear(); + TProjectManager *pm = TProjectManager::instance(); - TFilePath projectFolder = TFilePath(m_projectLocationFld->getPath()); - TFilePath projectPath = pm->projectFolderToProjectPath(projectFolder); - if (!checkProject()) { - DVGui::warning( - tr("The project needs to be a valid project.\n" - "Please select a valid project or create a new project.")); - m_projectLocationFld->setFocus(); + QString currentProjectPath = + pm->getCurrentProjectPath().getParentDir().getQString(); + RecentFiles::instance()->addFilePath(currentProjectPath, + RecentFiles::Project); + + TFilePath sandboxFp = pm->getSandboxProjectFolder() + "sandbox_otprj.xml"; + m_projectPaths.push_back(sandboxFp); + m_projectsCB->addItem("sandbox"); + m_projectsCB->setItemData(0, pm->getSandboxProjectFolder().getQString(), + Qt::ToolTipRole); + + QList recentProjects = + RecentFiles::instance()->getFilesNameList(RecentFiles::Project); + int j = 1; + for (int i = 0; i < recentProjects.size(); i++) { + TFilePath fp(recentProjects.at(i)); + if (pm->isProject(fp) && + recentProjects.at(i) != pm->getSandboxProjectFolder().getQString()) { + m_projectPaths.push_back(pm->projectFolderToProjectPath(fp)); + TFilePath prjFile = pm->getProjectPathByProjectFolder(fp); + m_projectsCB->addItem(QString::fromStdString(prjFile.getName())); + m_projectsCB->setItemData(j, recentProjects.at(i), Qt::ToolTipRole); + j++; + } + } + + m_projectsCB->addItem(tr("Browse...")); + m_projectsCB->setItemData(j, tr("Open a different project."), + Qt::ToolTipRole); + + int i; + for (i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_pathFld->setPath(TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProject() + ->getScenesPath() + .getQString()); + m_updating = false; +} + +//----------------------------------------------------------------------------- + +void StartupPopup::onProjectComboChanged(int index) { + if (m_updating) return; + TProjectManager *pm = TProjectManager::instance(); + + // The last index is Browse. . . + if (index == m_projectsCB->count() - 1) { + m_projectsCB->blockSignals(true); + for (int i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_projectsCB->blockSignals(false); + m_projectLocationFld->setPath( + Preferences::instance()->getDefaultProjectPath()); + m_projectLocationFld->forceOpenBrowser(); return; } - assert(TFileStatus(projectPath).doesExist()); - pm->setCurrentProjectPath(projectPath); + TFilePath projectFp = m_projectPaths[index]; + + pm->setCurrentProjectPath(projectFp); + + TProjectP currentProject = pm->getCurrentProject(); + + // In case the project file was upgraded to current version, save it now + if (currentProject->getProjectPath() != projectFp) { + m_projectPaths[index] = currentProject->getProjectPath(); + currentProject->save(); + } + IoCmd::newScene(); m_pathFld->setPath(TApp::instance() ->getCurrentScene() @@ -465,6 +535,36 @@ void StartupPopup::onCreateButton() { ->getProject() ->getScenesPath() .getQString()); + m_fpsFld->setValue(TApp::instance() + ->getCurrentScene() + ->getScene() + ->getProperties() + ->getOutputProperties() + ->getFrameRate()); + TDimension res = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getCurrentCamera() + ->getRes(); + m_xRes = res.lx; + m_yRes = res.ly; + m_resXFld->setValue(m_xRes); + m_resYFld->setValue(m_yRes); + TDimensionD size = TApp::instance() + ->getCurrentScene() + ->getScene() + ->getCurrentCamera() + ->getSize(); + m_widthFld->setValue(size.lx); + m_heightFld->setValue(size.ly); + m_dpi = 120.0; + RecentFiles::instance()->addFilePath(projectFp.getParentDir().getQString(), + RecentFiles::Project); +} + +//----------------------------------------------------------------------------- + +void StartupPopup::onCreateButton() { if (m_nameFld->text().trimmed() == "") { DVGui::warning(tr("The name cannot be empty.")); m_nameFld->setFocus(); @@ -555,7 +655,15 @@ void StartupPopup::onProjectLocationChanged() { if (!TSystem::doesExistFileOrLevel(path)) { DVGui::warning(tr( "This is not a valid folder. Please choose an existing location.")); - checkProject(); + // checkProject(); + m_projectsCB->blockSignals(true); + for (int i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_projectsCB->blockSignals(false); return; } } @@ -573,13 +681,38 @@ void StartupPopup::onProjectLocationChanged() { ->getProject() ->getProjectFolder() .getQString()); + m_projectsCB->blockSignals(true); + for (int i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_projectsCB->blockSignals(false); } else { + // Put the combo box back in case of cancelling + m_projectsCB->blockSignals(true); + for (int i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_projectsCB->blockSignals(false); ProjectCreatePopup *popup = new ProjectCreatePopup(); popup->setPath(path.getQString()); popup->exec(); } } else { if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change Project"))) { + m_projectsCB->blockSignals(true); + for (int i = 0; i < m_projectPaths.size(); i++) { + if (pm->getCurrentProjectPath() == m_projectPaths[i]) { + m_projectsCB->setCurrentIndex(i); + break; + } + } + m_projectsCB->blockSignals(false); m_projectLocationFld->blockSignals(true); m_projectLocationFld->setPath(TApp::instance() ->getCurrentScene() @@ -600,7 +733,8 @@ void StartupPopup::onProjectLocationChanged() { if (projectP->getProjectPath() != projectPath) { projectP->save(); } - + RecentFiles::instance()->addFilePath( + projectPath.getParentDir().getQString(), RecentFiles::Project); IoCmd::newScene(); } } @@ -897,6 +1031,7 @@ void StartupPopup::onSceneChanged() { ->getProjectFolder(); std::string pathStr = path.getQString().toStdString(); m_projectLocationFld->setPath(path.getQString()); + updateProjectCB(); } } @@ -957,6 +1092,10 @@ void StartupPopup::onRecentSceneClicked(int index) { } else RecentFiles::instance()->moveFilePath(index, 0, RecentFiles::Scene); RecentFiles::instance()->refreshRecentFilesMenu(RecentFiles::Scene); + TFilePath projectPath = + TProjectManager::instance()->getCurrentProjectPath(); + RecentFiles::instance()->addFilePath( + projectPath.getParentDir().getQString(), RecentFiles::Project); hide(); } } @@ -1059,22 +1198,6 @@ void StartupPopup::updateSize() { //----------------------------------------------------------------------------- -bool StartupPopup::checkProject() { - TFilePath currPath = TFilePath(m_projectLocationFld->getPath()); - bool isProject = TProjectManager::instance()->isProject(currPath); - if (isProject) { - m_projectLocationFld->getField()->setStyleSheet( - m_pathFld->getField()->styleSheet()); - m_projectLocationFld->setToolTip(tr("")); - } else { - m_projectLocationFld->getField()->setStyleSheet("color: red;"); - m_projectLocationFld->setToolTip(tr("Not a valid project location")); - } - return isProject; -} - -//----------------------------------------------------------------------------- - StartupLabel::StartupLabel(const QString &text, QWidget *parent, int index) : QLabel(parent), m_index(index) { setText(text); diff --git a/toonz/sources/toonz/startuppopup.h b/toonz/sources/toonz/startuppopup.h index 7eaa451b..ffa920a1 100644 --- a/toonz/sources/toonz/startuppopup.h +++ b/toonz/sources/toonz/startuppopup.h @@ -50,6 +50,7 @@ class StartupPopup final : public DVGui::Dialog { QPushButton *m_loadOtherSceneButton; QPushButton *m_newProjectButton; QComboBox *m_presetCombo; + QComboBox *m_projectsCB; QPushButton *m_addPresetBtn, *m_removePresetBtn; CameraSettingsWidget *m_cameraSettingsWidget; double m_dpi; @@ -76,9 +77,11 @@ protected: bool parsePresetString(const QString &str, QString &name, int &xres, int &yres, double &fx, double &fy, QString &xoffset, QString &yoffset, double &ar, bool forCleanup = false); + void updateProjectCB(); public slots: void onRecentSceneClicked(int index); + void onProjectComboChanged(int index); void onCreateButton(); void onShowAtStartChanged(int index); void onProjectChanged(int index); @@ -94,7 +97,6 @@ public slots: void onCameraUnitChanged(int index); void onAutoSaveOnChanged(int index); void onAutoSaveTimeChanged(); - bool checkProject(); void onProjectLocationChanged(); }; diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 3ad686c7..4c57f0f3 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // boost includes #include @@ -436,6 +437,11 @@ void Preferences::definePreferenceItems() { setCallBack(rasterBackgroundColor, &Preferences::setRasterBackgroundColor); + QString documentsPath = + QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)[0]; + define(defaultProjectPath, "defaultProjectPath", QMetaType::QString, + documentsPath); + // Import / Export define(ffmpegPath, "ffmpegPath", QMetaType::QString, ""); define(ffmpegTimeout, "ffmpegTimeout", QMetaType::Int, 600, 1, diff --git a/toonz/sources/toonzqt/filefield.cpp b/toonz/sources/toonzqt/filefield.cpp index 5b3875f9..9e172608 100644 --- a/toonz/sources/toonzqt/filefield.cpp +++ b/toonz/sources/toonzqt/filefield.cpp @@ -90,6 +90,27 @@ FileField::BrowserPopupController *FileField::getBrowserPopupController() { //----------------------------------------------------------------------------- +void FileField::forceOpenBrowser() { + QString directory = QString(); + + if (!m_browserPopupController) return; + m_browserPopupController->openPopup( + m_filters, (m_fileMode == QFileDialog::DirectoryOnly), + (m_lastSelectedPath == m_descriptionText) ? "" : m_lastSelectedPath, + this); + if (m_browserPopupController->isExecute()) + directory = m_browserPopupController->getPath(m_codePath); + + if (!directory.isEmpty()) { + setPath(directory); + m_lastSelectedPath = directory; + emit pathChanged(); + return; + } +} + +//----------------------------------------------------------------------------- + void FileField::browseDirectory() { if (!m_fileBrowseButton->hasFocus()) return; QString directory = QString();