From f41dfc8dbaafa39185f07ba17ced74c50b98fa36 Mon Sep 17 00:00:00 2001 From: shun-iwasawa Date: Thu, 18 Feb 2021 17:24:29 +0900 Subject: [PATCH] clean assets feature --- toonz/sources/include/toonz/preferences.h | 3 + .../include/toonz/preferencesitemids.h | 1 + toonz/sources/include/toonz/toonzscene.h | 1 + toonz/sources/toonz/iocommand.cpp | 46 +++++++ toonz/sources/toonz/levelcommand.cpp | 125 +++++++++++------- toonz/sources/toonz/levelcommand.h | 15 ++- toonz/sources/toonz/levelcreatepopup.cpp | 46 +++++-- toonz/sources/toonz/levelsettingspopup.cpp | 15 +++ toonz/sources/toonz/preferencespopup.cpp | 5 +- toonz/sources/toonzlib/preferences.cpp | 3 + 10 files changed, 200 insertions(+), 60 deletions(-) diff --git a/toonz/sources/include/toonz/preferences.h b/toonz/sources/include/toonz/preferences.h index f90e1f2e..41ac3a69 100644 --- a/toonz/sources/include/toonz/preferences.h +++ b/toonz/sources/include/toonz/preferences.h @@ -269,6 +269,9 @@ public: int matchLevelFormat(const TFilePath &fp) const; //!< Returns the \a nonnegative index of the first level format //! matching the specified file path, or \p -1 if none. + bool isAutoRemoveUnusedLevelsEnabled() const { + return isAutoExposeEnabled() && getBoolValue(autoRemoveUnusedLevels); + } // Saving tab TPixel getRasterBackgroundColor() const { diff --git a/toonz/sources/include/toonz/preferencesitemids.h b/toonz/sources/include/toonz/preferencesitemids.h index e9c726d3..7684ce2b 100644 --- a/toonz/sources/include/toonz/preferencesitemids.h +++ b/toonz/sources/include/toonz/preferencesitemids.h @@ -62,6 +62,7 @@ enum PreferencesItemId { initialLoadTlvCachingBehavior, columnIconLoadingPolicy, levelFormats, // need to be handle separately + autoRemoveUnusedLevels, //---------- // Saving diff --git a/toonz/sources/include/toonz/toonzscene.h b/toonz/sources/include/toonz/toonzscene.h index 10c3897a..55e92ad1 100644 --- a/toonz/sources/include/toonz/toonzscene.h +++ b/toonz/sources/include/toonz/toonzscene.h @@ -286,6 +286,7 @@ private: ToonzScene(const ToonzScene &); ToonzScene &operator=(const ToonzScene &); +public: // if the option is set in the preferences, // remove the scene numbers("c####_") from the file name std::wstring getLevelNameWithoutSceneNumber(std::wstring orgName); diff --git a/toonz/sources/toonz/iocommand.cpp b/toonz/sources/toonz/iocommand.cpp index 0b8162c9..907bfc24 100644 --- a/toonz/sources/toonz/iocommand.cpp +++ b/toonz/sources/toonz/iocommand.cpp @@ -21,6 +21,7 @@ #include "cachefxcommand.h" #include "xdtsio.h" #include "expressionreferencemanager.h" +#include "levelcommand.h" // TnzTools includes #include "tools/toolhandle.h" @@ -356,6 +357,37 @@ int getLevelType(const TFilePath &actualPath) { return UNKNOWN_XSHLEVEL; } +//=========================================================================== +// removeSameNamedUnusedLevel(scene, actualPath, levelName); +//--------------------------------------------------------------------------- + +void removeSameNamedUnusedLevel(ToonzScene *scene, const TFilePath actualPath, + std::wstring levelName) { + if (QString::fromStdWString(levelName).isEmpty()) { + // if the option is set in the preferences, + // remove the scene numbers("c####_") from the file name + levelName = scene->getLevelNameWithoutSceneNumber(actualPath.getWideName()); + } + + TLevelSet *levelSet = scene->getLevelSet(); + NameModifier nm(levelName); + levelName = nm.getNext(); + while (1) { + TXshLevel *existingLevel = levelSet->getLevel(levelName); + // if the level name is not used in the cast, nothing to do + if (!existingLevel) return; + // try if the existing level is unused in the xsheet and remove from the + // cast + else if (LevelCmd::removeLevelFromCast(existingLevel, scene, false)) { + DVGui::info(QObject::tr("Removed unused level %1 from the scene cast. " + "(This behavior can be disabled in Preferences.)") + .arg(QString::fromStdWString(levelName))); + return; + } + levelName = nm.getNext(); + } +} + //=========================================================================== // class LoadLevelUndo //--------------------------------------------------------------------------- @@ -876,6 +908,11 @@ TXshLevel *loadLevel(ToonzScene *scene, bool isFirstTime = !xl; std::wstring name = actualPath.getWideName(); + if (isFirstTime && expose && + Preferences::instance()->isAutoRemoveUnusedLevelsEnabled()) { + removeSameNamedUnusedLevel(scene, actualPath, levelName); + } + IoCmd::ConvertingPopup *convertingPopup = new IoCmd::ConvertingPopup( TApp::instance()->getMainWindow(), QString::fromStdWString(name) + @@ -1404,6 +1441,15 @@ bool IoCmd::saveScene(const TFilePath &path, int flags) { TXsheet *xsheet = 0; if (saveSubxsheet) xsheet = TApp::instance()->getCurrentXsheet()->getXsheet(); + // Automatically remove unused levels + if (!saveSubxsheet && + Preferences::instance()->isAutoRemoveUnusedLevelsEnabled()) { + if (LevelCmd::removeUnusedLevelsFromCast(false)) + DVGui::info( + QObject::tr("Removed unused levels from the scene cast. (This " + "behavior can be disabled in Preferences.)")); + } + // If the scene will be saved in the different folder, check out the scene // cast. // if the cast contains the level specified with $scenefolder alias, diff --git a/toonz/sources/toonz/levelcommand.cpp b/toonz/sources/toonz/levelcommand.cpp index 7093153b..bc7c0666 100644 --- a/toonz/sources/toonz/levelcommand.cpp +++ b/toonz/sources/toonz/levelcommand.cpp @@ -63,6 +63,61 @@ public: } // namespace +//----------------------------------------------------------------------------- + +bool LevelCmd::removeUnusedLevelsFromCast(bool showMessage) { + TApp *app = TApp::instance(); + ToonzScene *scene = app->getCurrentScene()->getScene(); + + TLevelSet *levelSet = scene->getLevelSet(); + + std::set usedLevels; + scene->getTopXsheet()->getUsedLevels(usedLevels); + + std::vector unused; + + for (int i = 0; i < levelSet->getLevelCount(); i++) { + TXshLevel *xl = levelSet->getLevel(i); + if (usedLevels.count(xl) == 0) unused.push_back(xl); + } + if (unused.empty()) { + if (showMessage) DVGui::error(QObject::tr("No unused levels")); + return false; + } else { + TUndoManager *um = TUndoManager::manager(); + um->beginBlock(); + for (int i = 0; i < (int)unused.size(); i++) { + TXshLevel *xl = unused[i]; + um->add(new DeleteLevelUndo(xl)); + scene->getLevelSet()->removeLevel(xl); + } + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); + TApp::instance()->getCurrentScene()->notifyCastChange(); + + um->endBlock(); + } + return true; +} + +bool LevelCmd::removeLevelFromCast(TXshLevel *level, ToonzScene *scene, + bool showMessage) { + if (!scene) scene = TApp::instance()->getCurrentScene()->getScene(); + if (scene->getChildStack()->getTopXsheet()->isLevelUsed(level)) { + if (showMessage) { + DVGui::error( + QObject::tr("It is not possible to delete the used level %1.") + .arg(QString::fromStdWString( + level->getName()))); //"E_CantDeleteUsedLevel_%1" + } + return false; + } else { + TUndoManager *um = TUndoManager::manager(); + um->add(new DeleteLevelUndo(level)); + scene->getLevelSet()->removeLevel(level); + } + return true; +} + //============================================================================= // RemoveUnusedLevelCommand //----------------------------------------------------------------------------- @@ -71,38 +126,7 @@ class RemoveUnusedLevelsCommand final : public MenuItemHandler { public: RemoveUnusedLevelsCommand() : MenuItemHandler(MI_RemoveUnused) {} - void execute() override { - TApp *app = TApp::instance(); - ToonzScene *scene = app->getCurrentScene()->getScene(); - - TLevelSet *levelSet = scene->getLevelSet(); - - std::set usedLevels; - scene->getTopXsheet()->getUsedLevels(usedLevels); - - std::vector unused; - - for (int i = 0; i < levelSet->getLevelCount(); i++) { - TXshLevel *xl = levelSet->getLevel(i); - if (usedLevels.count(xl) == 0) unused.push_back(xl); - } - if (unused.empty()) { - DVGui::error(QObject::tr("No unused levels")); - return; - } else { - TUndoManager *um = TUndoManager::manager(); - um->beginBlock(); - for (int i = 0; i < (int)unused.size(); i++) { - TXshLevel *xl = unused[i]; - um->add(new DeleteLevelUndo(xl)); - scene->getLevelSet()->removeLevel(xl); - } - TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); - TApp::instance()->getCurrentScene()->notifyCastChange(); - - um->endBlock(); - } - } + void execute() override { LevelCmd::removeUnusedLevelsFromCast(); } } removeUnusedLevelsCommand; //============================================================================= @@ -113,22 +137,6 @@ class RemoveLevelCommand final : public MenuItemHandler { public: RemoveLevelCommand() : MenuItemHandler(MI_RemoveLevel) {} - bool removeLevel(TXshLevel *level) { - TApp *app = TApp::instance(); - ToonzScene *scene = app->getCurrentScene()->getScene(); - if (scene->getChildStack()->getTopXsheet()->isLevelUsed(level)) - DVGui::error( - QObject::tr("It is not possible to delete the used level %1.") - .arg(QString::fromStdWString( - level->getName()))); //"E_CantDeleteUsedLevel_%1" - else { - TUndoManager *um = TUndoManager::manager(); - um->add(new DeleteLevelUndo(level)); - scene->getLevelSet()->removeLevel(level); - } - return true; - } - void execute() override { TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet(); CastSelection *castSelection = @@ -143,7 +151,7 @@ public: } int count = 0; for (int i = 0; i < (int)levels.size(); i++) - if (removeLevel(levels[i])) count++; + if (LevelCmd::removeLevelFromCast(levels[i])) count++; if (count == 0) return; TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); TApp::instance()->getCurrentScene()->notifyCastChange(); @@ -523,7 +531,24 @@ void LevelCmd::addMissingLevelsToCast(std::set &levels) { std::wstring oldName = levelName; NameModifier nm(levelName); levelName = nm.getNext(); - while (levelSet->hasLevel(levelName)) levelName = nm.getNext(); + while (1) { + TXshLevel *existingLevel = levelSet->getLevel(levelName); + // if the level name is not used in the cast, nothing to do + if (!existingLevel) break; + // try if the existing level is unused in the xsheet and remove from the + // cast + else if (Preferences::instance()->isAutoRemoveUnusedLevelsEnabled() && + LevelCmd::removeLevelFromCast( + existingLevel, + TApp::instance()->getCurrentScene()->getScene(), false)) { + DVGui::info( + QObject::tr("Removed unused level %1 from the scene cast. (This " + "behavior can be disabled in Preferences.)") + .arg(QString::fromStdWString(levelName))); + break; + } + levelName = nm.getNext(); + } addLevelToCastUndo *undo = new addLevelToCastUndo(level, levelName, oldName); undo->m_isLastInRedoBlock = false; // prevent to emit signal diff --git a/toonz/sources/toonz/levelcommand.h b/toonz/sources/toonz/levelcommand.h index d14132fb..bbcbb229 100644 --- a/toonz/sources/toonz/levelcommand.h +++ b/toonz/sources/toonz/levelcommand.h @@ -9,10 +9,23 @@ #include class TXshLevel; +class ToonzScene; namespace LevelCmd { void addMissingLevelsToCast(const QList& columns); void addMissingLevelsToCast(std::set& levels); + +// Remove all unused level from the scene cast. +// When there is no unused level, show an error message if showmessage==true. +// Return true if something is removed. +bool removeUnusedLevelsFromCast(bool showMessage = true); + +// Remove the level from the scene cast if it is not used in the xsheet. +// Return true if the level is unused and removed. +// When the level is used, an show error message if showMessage==true and +// returns false. +bool removeLevelFromCast(TXshLevel* level, ToonzScene* scene = nullptr, + bool showMessage = true); } // namespace LevelCmd -#endif +#endif \ No newline at end of file diff --git a/toonz/sources/toonz/levelcreatepopup.cpp b/toonz/sources/toonz/levelcreatepopup.cpp index 39afb062..1e6c08a7 100644 --- a/toonz/sources/toonz/levelcreatepopup.cpp +++ b/toonz/sources/toonz/levelcreatepopup.cpp @@ -5,6 +5,7 @@ // Tnz6 includes #include "menubarcommandids.h" #include "tapp.h" +#include "levelcommand.h" // TnzTools includes #include "tools/toolhandle.h" @@ -34,6 +35,7 @@ #include "toonz/palettecontroller.h" #include "toonz/tproject.h" #include "toonz/namebuilder.h" +#include "toonz/childstack.h" // TnzCore includes #include "tsystem.h" @@ -327,9 +329,16 @@ bool LevelCreatePopup::levelExists(std::wstring levelName) { .withParentDir(parentDir); actualFp = scene->decodeFilePath(fp); - if (levelSet->getLevel(levelName) != 0 || - TSystem::doesExistFileOrLevel(actualFp)) { + if (TSystem::doesExistFileOrLevel(actualFp)) return true; + else if (TXshLevel *level = levelSet->getLevel(levelName)) { + // even if the level exists in the scene cast, it can be replaced if it is + // unused + if (Preferences::instance()->isAutoRemoveUnusedLevelsEnabled() && + !scene->getChildStack()->getTopXsheet()->isLevelUsed(level)) + return false; + else + return true; } else return false; } @@ -469,12 +478,18 @@ bool LevelCreatePopup::apply() { int numFrames = step * (((to - from) / inc) + 1); - if (scene->getLevelSet()->getLevel(levelName)) { - error( - tr("The level name specified is already used: please choose a " - "different level name")); - m_nameFld->selectAll(); - return false; + TXshLevel *existingLevel = scene->getLevelSet()->getLevel(levelName); + if (existingLevel) { + // check if the existing level can be removed + if (!Preferences::instance()->isAutoRemoveUnusedLevelsEnabled() || + scene->getChildStack()->getTopXsheet()->isLevelUsed(existingLevel)) { + error( + tr("The level name specified is already used: please choose a " + "different level name")); + m_nameFld->selectAll(); + return false; + } + // if the exitingLevel is not null, it will be removed afterwards } TFilePath parentDir(m_pathFld->getPath().toStdWString()); @@ -525,6 +540,18 @@ bool LevelCreatePopup::apply() { } } + TUndoManager::manager()->beginBlock(); + + // existingLevel is not nullptr only if the level is unused AND + // the preference option AutoRemoveUnusedLevels is ON + if (existingLevel) { + bool ok = LevelCmd::removeLevelFromCast(existingLevel, scene, false); + assert(ok); + DVGui::info(QObject::tr("Removed unused level %1 from the scene cast. " + "(This behavior can be disabled in Preferences.)") + .arg(QString::fromStdWString(levelName))); + } + /*-- これからLevelを配置しようとしているセルが空いているかどうかのチェック * --*/ bool areColumnsShifted = false; @@ -560,6 +587,7 @@ bool LevelCreatePopup::apply() { TXshLevel *level = scene->createNewLevel(lType, levelName, TDimension(), 0, fp); TXshSimpleLevel *sl = dynamic_cast(level); + assert(sl); // sl->setPath(fp, true); if (lType == TZP_XSHLEVEL || lType == OVL_XSHLEVEL) { @@ -603,6 +631,8 @@ bool LevelCreatePopup::apply() { undo->onAdd(sl); + TUndoManager::manager()->endBlock(); + app->getCurrentScene()->notifySceneChanged(); app->getCurrentScene()->notifyCastChange(); app->getCurrentXsheet()->notifyXsheetChanged(); diff --git a/toonz/sources/toonz/levelsettingspopup.cpp b/toonz/sources/toonz/levelsettingspopup.cpp index 92a5603a..df5a3dc3 100644 --- a/toonz/sources/toonz/levelsettingspopup.cpp +++ b/toonz/sources/toonz/levelsettingspopup.cpp @@ -10,6 +10,7 @@ #include "castselection.h" #include "fileselection.h" #include "columnselection.h" +#include "levelcommand.h" // TnzQt includes #include "toonzqt/menubarcommand.h" @@ -964,6 +965,18 @@ void LevelSettingsPopup::onNameChanged() { TLevelSet *levelSet = TApp::instance()->getCurrentScene()->getScene()->getLevelSet(); + TUndoManager::manager()->beginBlock(); + + // remove existing & unused level + if (Preferences::instance()->isAutoRemoveUnusedLevelsEnabled()) { + TXshLevel *existingLevel = levelSet->getLevel(text.toStdWString()); + if (existingLevel && + LevelCmd::removeLevelFromCast(existingLevel, nullptr, false)) + DVGui::info(QObject::tr("Removed unused level %1 from the scene cast. " + "(This behavior can be disabled in Preferences.)") + .arg(text)); + } + if (!levelSet->renameLevel(level, text.toStdWString())) { error("The name " + text + " you entered for the level is already used.\nPlease enter a " @@ -975,6 +988,8 @@ void LevelSettingsPopup::onNameChanged() { TUndoManager::manager()->add( new LevelSettingsUndo(level, LevelSettingsUndo::Name, oldName, text)); + TUndoManager::manager()->endBlock(); + TApp::instance()->getCurrentXsheet()->notifyXsheetChanged(); TApp::instance()->getCurrentScene()->notifyCastChange(); } diff --git a/toonz/sources/toonz/preferencespopup.cpp b/toonz/sources/toonz/preferencespopup.cpp index a989e08d..11dd2fbd 100644 --- a/toonz/sources/toonz/preferencespopup.cpp +++ b/toonz/sources/toonz/preferencespopup.cpp @@ -1027,6 +1027,8 @@ QString PreferencesPopup::getUIString(PreferencesItemId id) { // Loading {importPolicy, tr("Default File Import Behavior:")}, {autoExposeEnabled, tr("Expose Loaded Levels in the Scene")}, + {autoRemoveUnusedLevels, + tr("Automatically Remove Unused Levels From Scene Cast")}, {subsceneFolderEnabled, tr("Create Sub-folder when Importing Sub-Scene")}, {removeSceneNumberFromLoadedLevelName, tr("Automatically Remove Scene Number from Loaded Level Name")}, @@ -1535,7 +1537,8 @@ QWidget* PreferencesPopup::createLoadingPage() { setupLayout(lay); insertUI(importPolicy, lay, getComboItemList(importPolicy)); - insertUI(autoExposeEnabled, lay); + QGridLayout* autoExposeLay = insertGroupBoxUI(autoExposeEnabled, lay); + { insertUI(autoRemoveUnusedLevels, autoExposeLay); } insertUI(subsceneFolderEnabled, lay); insertUI(removeSceneNumberFromLoadedLevelName, lay); // insertUI(IgnoreImageDpi, lay); diff --git a/toonz/sources/toonzlib/preferences.cpp b/toonz/sources/toonzlib/preferences.cpp index 82289898..adfe0a98 100644 --- a/toonz/sources/toonzlib/preferences.cpp +++ b/toonz/sources/toonzlib/preferences.cpp @@ -456,6 +456,9 @@ void Preferences::definePreferenceItems() { QMetaType::Int, 0); // On Demand define(columnIconLoadingPolicy, "columnIconLoadingPolicy", QMetaType::Int, (int)LoadAtOnce); + define(autoRemoveUnusedLevels, "autoRemoveUnusedLevels", QMetaType::Bool, + false); + //"levelFormats" need to be handle separately // Saving