1 #include <memory>
2 
3 #include "iocommand.h"
4 
5 // Toonz includes
6 #include "menubarcommandids.h"
7 #include "filebrowserpopup.h"
8 #include "tapp.h"
9 #include "history.h"
10 #include "fileselection.h"
11 #include "previewer.h"
12 #include "previewfxmanager.h"
13 #include "tcontenthistory.h"
14 #include "filebrowsermodel.h"
15 #include "mainwindow.h"
16 #include "overwritepopup.h"
17 #include "cleanupsettingspopup.h"
18 #include "psdsettingspopup.h"
19 #include "filebrowser.h"
20 #include "versioncontrol.h"
21 #include "cachefxcommand.h"
22 #include "xdtsio.h"
23 #include "expressionreferencemanager.h"
24 
25 // TnzTools includes
26 #include "tools/toolhandle.h"
27 
28 // ToonzQt includes
29 #include "toonzqt/gutil.h"
30 #include "toonzqt/icongenerator.h"
31 #include "toonzqt/swatchviewer.h"
32 #include "toonzqt/tselectionhandle.h"
33 #include "toonzqt/dvdialog.h"
34 
35 // ToonzLib includes
36 #include "toonz/palettecontroller.h"
37 #include "toonz/tscenehandle.h"
38 #include "toonz/tobjecthandle.h"
39 #include "toonz/tcolumnhandle.h"
40 #include "toonz/tframehandle.h"
41 #include "toonz/txsheethandle.h"
42 #include "toonz/txshlevelhandle.h"
43 #include "toonz/tpalettehandle.h"
44 #include "toonz/toonzscene.h"
45 #include "toonz/tproject.h"
46 #include "toonz/txshsimplelevel.h"
47 #include "toonz/txshchildlevel.h"
48 #include "toonz/sceneproperties.h"
49 #include "toonz/levelproperties.h"
50 #include "toonz/stage2.h"
51 #include "toonz/imagemanager.h"
52 #include "toonz/sceneresources.h"
53 #include "toonz/txshsoundlevel.h"
54 #include "toonz/txshpalettecolumn.h"
55 #include "toonz/txshpalettelevel.h"
56 #include "toonz/txshleveltypes.h"
57 #include "toonz/txshsoundtextcolumn.h"
58 #include "toonz/tstageobjecttree.h"
59 #include "toonz/levelset.h"
60 #include "toonz/namebuilder.h"
61 #include "toonz/fullcolorpalette.h"
62 #include "toonz/palettecmd.h"
63 #include "toonz/toonzimageutils.h"
64 #include "toonz/imagestyles.h"
65 #include "toutputproperties.h"
66 #include "toonz/studiopalette.h"
67 
68 // TnzCore includes
69 #include "tofflinegl.h"
70 #include "tvectorimage.h"
71 #include "tvectorrenderdata.h"
72 #include "tropcm.h"
73 #include "tfiletype.h"
74 #include "tunit.h"
75 #include "tproperty.h"
76 #include "tlevel.h"
77 #include "tlevel_io.h"
78 
79 // Qt includes
80 #include <QLabel>
81 #include <QApplication>
82 #include <QClipboard>
83 
84 // boost includes
85 #include <boost/optional.hpp>
86 #include <boost/utility/in_place_factory.hpp>
87 
88 //#define USE_SQLITE_HDPOOL
89 
90 using namespace DVGui;
91 
92 //-----------------------------------------------------------------------------
93 namespace {
94 //-----------------------------------------------------------------------------
95 
96 TXshLevel *getLevelByPath(ToonzScene *scene, const TFilePath &actualPath);
97 
98 // forward declaration
99 class RenderingSuspender;
100 
101 //===========================================================================
102 // class ResourceImportDialog
103 //---------------------------------------------------------------------------
104 
105 class ResourceImportDialog final : public ResourceImportStrategy {
106   OverwriteDialog *m_dialog;
107   bool m_isLastResource;
108   bool m_importQuestionAsked;
109   bool m_aborted;
110   TFilePath m_dstFolder;
111   bool m_importEnabled;
112   std::map<TFilePath, TFilePath> m_importedFiles;
113 
114 public:
115   enum Resolution { A_IMPORT, A_LOAD, A_CANCEL };
116 
117 public:
ResourceImportDialog()118   ResourceImportDialog()
119       : ResourceImportStrategy(ResourceImportStrategy::IMPORT_AND_RENAME)
120       , m_dialog(0)
121       , m_isLastResource(false)
122       , m_importQuestionAsked(false)
123       , m_aborted(false)
124       , m_importEnabled(false) {
125     m_dialog = new OverwriteDialog;
126   }
127 
~ResourceImportDialog()128   ~ResourceImportDialog() {
129     if (m_dialog) m_dialog->deleteLater();
130   }
131 
isImportEnabled() const132   bool isImportEnabled() const { return m_importEnabled; }
setImportEnabled(bool enabled)133   void setImportEnabled(bool enabled) {
134     m_importQuestionAsked = true, m_importEnabled = enabled;
135   }
136 
setDstFolder(const TFilePath & dstFolder)137   void setDstFolder(const TFilePath &dstFolder) { m_dstFolder = dstFolder; }
getDstFolder() const138   TFilePath getDstFolder() const { return m_dstFolder; }
139 
askImportQuestion(const TFilePath & path)140   int askImportQuestion(const TFilePath &path) {
141     if (m_importQuestionAsked)
142       return isImportEnabled() ? A_IMPORT : A_LOAD;
143     else {
144       m_importQuestionAsked = true;
145 
146       QString label =
147           QObject::tr(
148               "File %1 doesn't belong to the current project.\n"
149               "Do you want to import it or load it from its original location?")
150               .arg(QString::fromStdWString(path.getWideString()));
151       QString checkBoxLabel =
152           QObject::tr("Always do this action.")
153               .arg(QString::fromStdWString(path.getWideString()));
154       QStringList buttons;
155       buttons << QObject::tr("Import") << QObject::tr("Load")
156               << QObject::tr("Cancel");
157       DVGui::MessageAndCheckboxDialog *importDialog =
158           DVGui::createMsgandCheckbox(DVGui::QUESTION, label, checkBoxLabel,
159                                       buttons, 0, Qt::Unchecked);
160       int ret     = importDialog->exec();
161       int checked = importDialog->getChecked();
162       importDialog->deleteLater();
163 
164       if (ret == 0 || ret == 3) {
165         m_aborted = true;
166         return A_CANCEL;
167       }
168       if (ret == 1 && checked > 0) {
169         Preferences::instance()->setValue(importPolicy, 1);
170         TApp::instance()->getCurrentScene()->notifyImportPolicyChanged(1);
171       } else if (ret == 2 && checked > 0) {
172         Preferences::instance()->setValue(importPolicy, 2);
173         TApp::instance()->getCurrentScene()->notifyImportPolicyChanged(2);
174       }
175       m_importEnabled = (ret == 1);
176       return ret == 1 ? A_IMPORT : A_LOAD;
177     }
178   }
179 
setIsLastResource(bool isLastResource)180   void setIsLastResource(bool isLastResource) {
181     m_isLastResource = isLastResource;
182   }
183 
aborted() const184   bool aborted() const { return m_aborted || m_dialog->cancelPressed(); }
185 
186   //
187   // process the file 'srcPath' (possibly copying it)
188   // scrPath can be a coded path (related to srcScene); srcScene can be 0
189   // the method returns the processed codedPath (related to scene)
190   //
process(ToonzScene * scene,ToonzScene * srcScene,TFilePath srcPath)191   TFilePath process(ToonzScene *scene, ToonzScene *srcScene,
192                     TFilePath srcPath) override {
193     TFilePath actualSrcPath = srcPath;
194     if (srcScene) actualSrcPath = srcScene->decodeFilePath(srcPath);
195 
196     if (!isImportEnabled()) {
197       TFilePath path = scene->codeFilePath(actualSrcPath);
198       return path;
199     }
200     if ((!scene->isExternPath(actualSrcPath) && m_dstFolder.isEmpty()) ||
201         m_dialog->cancelPressed())
202       return srcPath;
203 
204     // find the proper dstPath (coded)
205     TFilePath dstPath;
206     if (srcPath.getWideString().find(L'+') == 0) {
207       dstPath = srcPath;
208       // override the folder
209       if (m_dstFolder != TFilePath()) {
210         // +drawings/oldfolder/level.pli =>
211         // +drawings/xsheetfolder/oldfolder/level.pli
212         std::wstring head;
213         TFilePath tail;
214         dstPath.split(head, tail);
215         dstPath = TFilePath(head) + m_dstFolder + tail;
216       }
217     } else {
218       dstPath = scene->getImportedLevelPath(srcPath);
219       // override the folder
220       if (m_dstFolder != TFilePath())
221         dstPath = dstPath.withParentDir(dstPath.getParentDir() + m_dstFolder);
222     }
223 
224     // actual path
225     TFilePath actualDstPath = scene->decodeFilePath(dstPath);
226     assert(actualDstPath != TFilePath());
227 
228     std::map<TFilePath, TFilePath>::iterator it =
229         m_importedFiles.find(actualDstPath);
230     if (it != m_importedFiles.end()) return it->second;
231     m_importedFiles[actualDstPath] = dstPath;
232 
233     // possibly, a level already exists
234     bool overwritten = false;
235     if (TSystem::doesExistFileOrLevel(actualDstPath)) {
236       std::wstring newName =
237           m_dialog->execute(scene, dstPath, m_isLastResource == false);
238       if (m_dialog->cancelPressed()) return srcPath;
239       int importMode = m_dialog->getChoice();
240       if (importMode == OverwriteDialog::KEEP_OLD)
241         return dstPath;
242       else if (importMode == OverwriteDialog::OVERWRITE)
243         overwritten = true;
244       else {
245         dstPath                        = dstPath.withName(newName);
246         m_importedFiles[actualDstPath] = dstPath;
247         actualDstPath                  = actualDstPath.withName(newName);
248       }
249     }
250 
251     // copy resources
252     try {
253       if (TSystem::doesExistFileOrLevel(actualDstPath))
254         TSystem::removeFileOrLevel(actualDstPath);
255       if (TSystem::doesExistFileOrLevel(actualSrcPath))
256         TXshSimpleLevel::copyFiles(actualDstPath, actualSrcPath);
257     } catch (TException &e) {
258       DVGui::Dialog *errorDialog = DVGui::createMsgBox(
259           DVGui::WARNING,
260           "Can't copy resources: " + QString::fromStdWString(e.getMessage()),
261           QStringList("OK"), 0);
262 
263       errorDialog->exec();
264       errorDialog->deleteLater();
265     }
266     // notify
267     FileBrowser::refreshFolder(actualDstPath.getParentDir());
268     IconGenerator::instance()->invalidate(actualDstPath);
269 
270     // refresh icons and level path
271     if (overwritten) {
272       TXshLevel *xl       = getLevelByPath(scene, actualDstPath);
273       TXshSimpleLevel *sl = 0;
274       if (xl && 0 != (sl = xl->getSimpleLevel())) {
275         std::vector<TFrameId> fids;
276         sl->getFids(fids);
277         sl->setPath(sl->getPath(), false);
278         for (int i = 0; i < (int)fids.size(); i++)
279           IconGenerator::instance()->invalidate(sl, fids[i]);
280       }
281     }
282     return dstPath;
283   }
284 };
285 
286 //===========================================================================
287 // getLevelByPath(scene, actualPath)
288 //---------------------------------------------------------------------------
289 
getLevelByPath(ToonzScene * scene,const TFilePath & actualPath)290 TXshLevel *getLevelByPath(ToonzScene *scene, const TFilePath &actualPath) {
291   TLevelSet *levelSet = scene->getLevelSet();
292   for (int i = 0; i < levelSet->getLevelCount(); i++) {
293     TXshLevel *xl = levelSet->getLevel(i);
294     if (!xl) continue;
295     TFilePath fp = scene->decodeFilePath(xl->getPath());
296     if (fp == actualPath) return xl;
297   }
298   return 0;
299 }
300 
301 //===========================================================================
302 // getSimpleLevelByPath(scene, actualPath)
303 //---------------------------------------------------------------------------
304 
getSimpleLevelByPath(ToonzScene * scene,const TFilePath & actualPath)305 TXshSimpleLevel *getSimpleLevelByPath(ToonzScene *scene,
306                                       const TFilePath &actualPath) {
307   TXshLevel *xl = getLevelByPath(scene, actualPath);
308   if (!xl) return 0;
309   TXshSimpleLevel *sl = xl->getSimpleLevel();
310   return sl;
311 }
312 
313 //===========================================================================
314 // beforeCellsInsert(xsh, row, col, rowCount)
315 //---------------------------------------------------------------------------
316 
beforeCellsInsert(TXsheet * xsh,int row,int & col,int rowCount,int newLevelColumnType)317 bool beforeCellsInsert(TXsheet *xsh, int row, int &col, int rowCount,
318                        int newLevelColumnType) {
319   bool shiftColumn   = false;
320   int i              = 0;
321   TXshColumn *column = xsh->getColumn(col);
322 
323   for (i = 0; i < rowCount && xsh->getCell(row + i, col).isEmpty(); i++) {
324   }
325   int type = column ? column->getColumnType() : newLevelColumnType;
326   // If some used cells in range or column type mismatch must insert a column.
327   if (col < 0 || i < rowCount || newLevelColumnType != type) {
328     col += 1;
329     TApp::instance()->getCurrentColumn()->setColumnIndex(col);
330     shiftColumn = true;
331     xsh->insertColumn(col);
332   } else {
333     // don't overlap
334     xsh->removeCells(row, col, rowCount);
335   }
336   return shiftColumn;
337 }
338 
339 //===========================================================================
340 // getLevelType(actualLevelPath)
341 //---------------------------------------------------------------------------
342 
getLevelType(const TFilePath & actualPath)343 int getLevelType(const TFilePath &actualPath) {
344   TFileType::Type type = TFileType::getInfo(actualPath);
345   if (type == TFileType::RASTER_IMAGE || type == TFileType::RASTER_LEVEL ||
346       type == TFileType::CMAPPED_LEVEL) {
347     std::string ext = actualPath.getType();
348     if (ext == "tzp" || ext == "tzu" || ext == "tzl" || ext == "tlv")
349       return TZP_XSHLEVEL;
350     else
351       return OVL_XSHLEVEL;
352   } else if (type == TFileType::VECTOR_LEVEL) {
353     return PLI_XSHLEVEL;
354   } else
355     return UNKNOWN_XSHLEVEL;
356 }
357 
358 //===========================================================================
359 // class LoadLevelUndo
360 //---------------------------------------------------------------------------
361 
362 class LoadLevelUndo final : public TUndo {
363   TXshLevelP m_level;
364   TFilePath m_levelSetFolder;
365   int m_row, m_col, m_rowCount;
366   bool m_columnInserted;
367   std::vector<TXshCell> m_cells;
368   bool m_isFirstTime;
369 
370 public:
LoadLevelUndo()371   LoadLevelUndo()
372       : m_level()
373       , m_levelSetFolder()
374       , m_row(0)
375       , m_col(0)
376       , m_rowCount(0)
377       , m_columnInserted(false)
378       , m_isFirstTime(true) {}
setLevel(TXshLevel * xl)379   void setLevel(TXshLevel *xl) { m_level = xl; }
setLevelSetFolder(const TFilePath & levelSetFolder)380   void setLevelSetFolder(const TFilePath &levelSetFolder) {
381     m_levelSetFolder = levelSetFolder;
382   }
setIsFirstTime(bool flag)383   void setIsFirstTime(bool flag) { m_isFirstTime = flag; }
setCells(TXsheet * xsh,int row,int col,int rowCount)384   void setCells(TXsheet *xsh, int row, int col, int rowCount) {
385     m_row      = row;
386     m_col      = col;
387     m_rowCount = rowCount;
388     if (rowCount > 0) {
389       m_cells.resize(rowCount);
390       xsh->getCells(row, col, rowCount, &m_cells[0]);
391     } else
392       m_cells.clear();
393   }
setColumnInserted(bool columnInserted)394   void setColumnInserted(bool columnInserted) {
395     m_columnInserted = columnInserted;
396   }
397 
undo() const398   void undo() const override {
399     TApp *app = TApp::instance();
400     /*- 最初にシーンに読み込んだ操作のUndoのとき、Castから除く -*/
401     if (m_level && m_isFirstTime)
402       app->getCurrentScene()->getScene()->getLevelSet()->removeLevel(
403           m_level.getPointer());
404     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
405     if (m_columnInserted)
406       xsh->removeColumn(m_col);
407     else {
408       xsh->removeCells(m_row, m_col, m_rowCount);
409       xsh->insertCells(m_row, m_col, m_rowCount);  // insert empty cells
410       // Remove sound or palette column if it is empty.
411       if (xsh->getColumn(m_col) && xsh->getColumn(m_col)->isEmpty()) {
412         TXshColumn::ColumnType columnType =
413             xsh->getColumn(m_col)->getColumnType();
414         if (columnType != TXshColumn::eLevelType) {
415           TStageObjectTree *stageObjectTree = xsh->getStageObjectTree();
416           int lastIndex = stageObjectTree->getStageObjectCount();
417           stageObjectTree->getStageObject(TStageObjectId::ColumnId(lastIndex),
418                                           true);
419           stageObjectTree->swapColumns(m_col, lastIndex);
420           xsh->removeColumn(m_col);
421           xsh->insertColumn(m_col);
422           stageObjectTree->swapColumns(lastIndex, m_col);
423         }
424       }
425     }
426     app->getCurrentXsheet()->notifyXsheetChanged();
427     if (app->getCurrentFrame()->isEditingLevel() &&
428         app->getCurrentLevel()->getLevel() == m_level.getPointer())
429       app->getCurrentLevel()->setLevel(0);
430     app->getCurrentScene()->notifyCastChange();
431   }
redo() const432   void redo() const override {
433     TApp *app = TApp::instance();
434     if (m_level)
435       app->getCurrentScene()->getScene()->getLevelSet()->insertLevel(
436           m_level.getPointer());
437     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
438     if (m_columnInserted) xsh->insertColumn(m_col);
439     if (m_rowCount > 0) {
440       xsh->removeCells(m_row, m_col, m_rowCount);
441       xsh->insertCells(m_row, m_col, m_rowCount);
442       xsh->setCells(m_row, m_col, m_rowCount, &m_cells[0]);
443     }
444     app->getCurrentXsheet()->notifyXsheetChanged();
445     if (app->getCurrentFrame()->isEditingLevel() &&
446         app->getCurrentLevel()->getLevel() == 0)
447       app->getCurrentLevel()->setLevel(m_level.getPointer());
448     app->getCurrentScene()->notifyCastChange();
449   }
getSize() const450   int getSize() const override {
451     return sizeof(*this) + sizeof(TXshCell) * m_cells.size() +
452            sizeof(TXshLevel);
453   }
getHistoryString()454   QString getHistoryString() override {
455     return QObject::tr("Load Level  %1")
456         .arg(QString::fromStdWString(m_level->getName()));
457   }
458 };
459 
460 //===========================================================================
461 // class LoadLevelAndReplaceUndo
462 //---------------------------------------------------------------------------
463 
464 class LoadAndReplaceLevelUndo final : public TUndo {
465   TXshSimpleLevelP m_level;
466   QMap<QPair<int, int>, QPair<TXshSimpleLevelP, TFrameId>> m_oldLevels;
467   int m_row0, m_col0, m_row1, m_col1;
468 
469 public:
LoadAndReplaceLevelUndo(const TXshSimpleLevelP & level,int row0,int col0,int row1,int col1)470   LoadAndReplaceLevelUndo(const TXshSimpleLevelP &level, int row0, int col0,
471                           int row1, int col1)
472       : m_level(level), m_row0(row0), m_row1(row1), m_col0(col0), m_col1(col1) {
473     int c, r;
474     TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
475     for (c = m_col0; c <= m_col1; c++)
476       for (r = m_row0; r <= m_row1; r++) {
477         TXshSimpleLevel *oldLevel = xsh->getCell(r, c).getSimpleLevel();
478         TFrameId fid              = xsh->getCell(r, c).getFrameId();
479         QPair<int, int> cellId(r, c);
480         m_oldLevels[cellId] = QPair<TXshSimpleLevelP, TFrameId>(oldLevel, fid);
481       }
482   }
483 
undo() const484   void undo() const override {
485     TApp *app    = TApp::instance();
486     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
487     int c, r;
488     for (c = m_col0; c <= m_col1; c++)
489       for (r = m_row0; r <= m_row1; r++) {
490         QPair<int, int> cellId(r, c);
491         TXshSimpleLevel *oldLevel = m_oldLevels[cellId].first.getPointer();
492         TFrameId fid              = m_oldLevels[cellId].second;
493         TXshCell cell             = xsh->getCell(r, c);
494         cell.m_level              = oldLevel;
495         cell.m_frameId            = fid;
496         xsh->setCell(r, c, cell);
497       }
498     app->getCurrentXsheet()->notifyXsheetChanged();
499     app->getCurrentScene()->notifyCastChange();
500   }
501 
redo() const502   void redo() const override {
503     TApp *app    = TApp::instance();
504     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
505     int c, r;
506     for (c = m_col0; c <= m_col1; c++)
507       for (r = m_row0; r <= m_row1; r++) {
508         QPair<int, int> cellId(r, c);
509         TFrameId fid   = m_oldLevels[cellId].second;
510         TXshCell cell  = xsh->getCell(r, c);
511         cell.m_level   = m_level.getPointer();
512         cell.m_frameId = fid;
513         xsh->setCell(r, c, cell);
514       }
515     app->getCurrentXsheet()->notifyXsheetChanged();
516     app->getCurrentScene()->notifyCastChange();
517   }
518 
getSize() const519   int getSize() const override { return sizeof(*this); }
getHistoryString()520   QString getHistoryString() override {
521     return QObject::tr("Load and Replace Level  %1")
522         .arg(QString::fromStdWString(m_level->getName()));
523   }
524 };
525 
526 //===========================================================================
527 // loadPalette(scene, actualPath, row, col)
528 //---------------------------------------------------------------------------
529 
loadPalette(ToonzScene * scene,TFilePath actualPath,TFilePath castFolder,int row,int & col)530 TXshLevel *loadPalette(ToonzScene *scene, TFilePath actualPath,
531                        TFilePath castFolder, int row, int &col) {
532   TFilePath palettePath     = actualPath;
533   TXsheet *xsh              = scene->getXsheet();
534   TXshPaletteColumn *column = new TXshPaletteColumn;
535   xsh->insertColumn(col, column);
536   TXshLevel *level =
537       scene->createNewLevel(PLT_XSHLEVEL, palettePath.getWideName());
538   level->getPaletteLevel()->setPath(scene->codeFilePath(palettePath));
539   level->getPaletteLevel()->load();
540   TXshCell cell;
541   cell.m_level   = level;
542   cell.m_frameId = TFrameId(1);
543   xsh->setCell(row, col, cell);
544   xsh->updateFrameCount();
545   // Undo
546   LoadLevelUndo *undo = new LoadLevelUndo();
547   undo->setLevel(level);
548   undo->setLevelSetFolder(castFolder);
549   undo->setCells(scene->getXsheet(), row, col, 1);
550   undo->setColumnInserted(true);
551   TUndoManager::manager()->add(undo);
552   return level;
553 }
554 
555 //===========================================================================
556 // substituteLevel() ; TODO: move to a different file
557 //---------------------------------------------------------------------------
558 
substituteLevel(TXsheet * xsh,TXshLevel * srcLevel,TXshLevel * dstLevel)559 void substituteLevel(TXsheet *xsh, TXshLevel *srcLevel, TXshLevel *dstLevel) {
560   std::set<TXshChildLevel *> substitutedSubs;
561 
562   for (int c = 0; c < xsh->getColumnCount(); c++) {
563     int r0 = 0, r1 = -1;
564     xsh->getCellRange(c, r0, r1);
565     if (r0 > r1) continue;
566     int rowCount = r1 - r0 + 1;
567     std::vector<TXshCell> cells(rowCount);
568     xsh->getCells(r0, c, rowCount, &cells[0]);
569     bool changed = false;
570     for (int i = 0; i < rowCount; i++) {
571       if (!cells[i].isEmpty()) {
572         if (cells[i].m_level.getPointer() == srcLevel) {
573           cells[i].m_level = dstLevel;
574           changed          = true;
575         } else {
576           // Recursive on sub-xsheets
577           TXshChildLevel *childLevel = cells[i].m_level->getChildLevel();
578           if (childLevel)
579             if (substitutedSubs.find(childLevel) == substitutedSubs.end()) {
580               substituteLevel(childLevel->getXsheet(), srcLevel, dstLevel);
581               substitutedSubs.insert(childLevel);
582             }
583         }
584       }
585     }
586     if (changed) xsh->setCells(r0, c, rowCount, &cells[0]);
587   }
588 }
589 
590 //===========================================================================
591 // class ChildLevelResourceImporter
592 //---------------------------------------------------------------------------
593 
594 class ChildLevelResourceImporter final : public ResourceProcessor {
595   ToonzScene *m_parentScene;
596   ToonzScene *m_childScene;
597 
598   ResourceImportStrategy &m_importStrategy;
599   TFilePath m_levelSetFolder;
600 
601 public:
602   ChildLevelResourceImporter(ToonzScene *parentScene, ToonzScene *childScene,
603                              ResourceImportStrategy &importStrategy);
604 
setLevelSetFolder(const TFilePath & levelSetFolder)605   void setLevelSetFolder(const TFilePath &levelSetFolder) {
606     m_levelSetFolder = levelSetFolder;
607   }
608 
609   void process(TXshSimpleLevel *sl) override;
610   void process(TXshPaletteLevel *sl) override;
611   void process(TXshSoundLevel *sl) override;
aborted() const612   bool aborted() const override { return m_importStrategy.aborted(); }
613 };
614 
615 //---------------------------------------------------------------------------
616 
ChildLevelResourceImporter(ToonzScene * parentScene,ToonzScene * childScene,ResourceImportStrategy & importStrategy)617 ChildLevelResourceImporter::ChildLevelResourceImporter(
618     ToonzScene *parentScene, ToonzScene *childScene,
619     ResourceImportStrategy &importStrategy)
620     : m_parentScene(parentScene)
621     , m_childScene(childScene)
622     , m_importStrategy(importStrategy)
623     , m_levelSetFolder() {}
624 
625 //---------------------------------------------------------------------------
626 
process(TXshSimpleLevel * sl)627 void ChildLevelResourceImporter::process(TXshSimpleLevel *sl) {
628   TLevelSet *parentLevelSet = m_parentScene->getLevelSet();
629   if (parentLevelSet->hasLevel(sl->getName())) {
630     TXshSimpleLevel *other =
631         parentLevelSet->getLevel(sl->getName())->getSimpleLevel();
632     if (other)  // in case that the retrieved level from scene cast is
633                 // subXSheetLevel
634     {
635       TFilePath otherActualPath =
636           m_parentScene->decodeFilePath(other->getPath());
637       TFilePath slActualPath = m_childScene->decodeFilePath(sl->getPath());
638       if (otherActualPath == slActualPath &&
639           other->getProperties()->options() == sl->getProperties()->options()) {
640         substituteLevel(m_childScene->getXsheet(), sl, other);
641         return;
642       }
643     }
644   }
645 
646   TFilePath slPath   = sl->getPath();
647   std::string suffix = ResourceImporter::extractPsdSuffix(slPath);
648 
649   TFilePath path = m_importStrategy.process(m_parentScene, m_childScene,
650                                             slPath);  // actualPath);
651   if (suffix != "") path = ResourceImporter::buildPsd(path, suffix);
652 
653   sl->setPath(path, false);  // m_parentScene->codeFilePath(actualPath), false);
654   NameModifier nm(sl->getName());
655   std::wstring levelName;
656   for (;;) {
657     levelName = nm.getNext();
658     if (!parentLevelSet->hasLevel(levelName)) break;
659   }
660   assert(sl->getRefCount() > 0);
661   sl->addRef();
662   m_childScene->getLevelSet()->removeLevel(sl);
663   sl->setName(levelName);
664   bool ret = parentLevelSet->insertLevel(sl);
665   assert(ret);
666   if (m_levelSetFolder != TFilePath())
667     parentLevelSet->moveLevelToFolder(m_levelSetFolder, sl);
668   sl->setScene(m_parentScene);
669   try {
670     sl->load();
671   } catch (...) {
672   }
673 
674   // Check if the scene saved with the previous version AND the premultiply
675   // option is set to PNG level setting
676   if (m_childScene->getVersionNumber() <
677       VersionNumber(71, 1)) {  // V1.4 = 71.0 , V1.5 = 71.1
678     if (!path.isEmpty() && path.getType() == "png" &&
679         sl->getProperties()->doPremultiply())
680       sl->getProperties()->setDoPremultiply(false);
681   }
682 
683   sl->release();
684 }
685 
686 //---------------------------------------------------------------------------
687 
process(TXshPaletteLevel * pl)688 void ChildLevelResourceImporter::process(TXshPaletteLevel *pl) {
689   TLevelSet *parentLevelSet = m_parentScene->getLevelSet();
690 
691   TFilePath plPath = pl->getPath();
692 
693   TFilePath path =
694       m_importStrategy.process(m_parentScene, m_childScene, plPath);
695 
696   pl->setPath(path);
697   NameModifier nm(pl->getName());
698   std::wstring levelName;
699   for (;;) {
700     levelName = nm.getNext();
701     if (!parentLevelSet->hasLevel(levelName)) break;
702   }
703   assert(pl->getRefCount() > 0);
704   pl->addRef();
705   m_childScene->getLevelSet()->removeLevel(pl);
706   pl->setName(levelName);
707   bool ret = parentLevelSet->insertLevel(pl);
708   assert(ret);
709   if (m_levelSetFolder != TFilePath())
710     parentLevelSet->moveLevelToFolder(m_levelSetFolder, pl);
711   pl->setScene(m_parentScene);
712   try {
713     pl->load();
714   } catch (...) {
715   }
716   pl->release();
717 }
718 
process(TXshSoundLevel * sl)719 void ChildLevelResourceImporter::process(TXshSoundLevel *sl) {
720   TLevelSet *parentLevelSet = m_parentScene->getLevelSet();
721   TFilePath path =
722       m_importStrategy.process(m_parentScene, m_childScene, sl->getPath());
723   sl->setPath(path);
724   NameModifier nm(sl->getName());
725   std::wstring levelName;
726   for (;;) {
727     levelName = nm.getNext();
728     if (!parentLevelSet->hasLevel(levelName)) break;
729   }
730   assert(sl->getRefCount() > 0);
731   sl->addRef();
732   m_childScene->getLevelSet()->removeLevel(sl);
733   sl->setName(levelName);
734   bool ret = parentLevelSet->insertLevel(sl);
735   assert(ret);
736   if (m_levelSetFolder != TFilePath())
737     parentLevelSet->moveLevelToFolder(m_levelSetFolder, sl);
738   sl->setScene(m_parentScene);
739   try {
740     sl->loadSoundTrack();
741   } catch (...) {
742   }
743   sl->release();
744 }
745 
746 //===========================================================================
747 // loadChildLevel(parentScene, actualPath, row, col, importStrategy)
748 //---------------------------------------------------------------------------
749 
loadChildLevel(ToonzScene * parentScene,TFilePath actualPath,int row,int & col,ResourceImportDialog & importStrategy)750 TXshLevel *loadChildLevel(ToonzScene *parentScene, TFilePath actualPath,
751                           int row, int &col,
752                           ResourceImportDialog &importStrategy) {
753   TProjectP project = TProjectManager::instance()->loadSceneProject(actualPath);
754 
755   // In Tab mode we don't need the project. Otherwise if it is not available
756   // we must exit
757   if (!project && !TProjectManager::instance()->isTabModeEnabled()) return 0;
758 
759   // load the subxsheet
760   ToonzScene scene;
761   scene.loadTnzFile(actualPath);
762   scene.setProject(project.getPointer());
763   std::wstring subSceneName = actualPath.getWideName();
764 
765   // camera settings. get the child camera ...
766   TXsheet *childXsh = scene.getXsheet();
767   TStageObjectId cameraId =
768       childXsh->getStageObjectTree()->getCurrentCameraId();
769   TStageObject *childCameraObject = childXsh->getStageObject(cameraId);
770   TCamera *childCamera            = childCameraObject->getCamera();
771   // ...and the parent camera...
772   TXsheet *parentXsh = parentScene->getXsheet();
773   cameraId           = parentXsh->getStageObjectTree()->getCurrentCameraId();
774   TStageObject *cameraObject = parentXsh->getStageObject(cameraId);
775   TCamera *camera            = cameraObject->getCamera();
776 
777   // if the camera settings are different ask the user
778   const double eps = 0.00001;
779   if (fabs(camera->getSize().lx - childCamera->getSize().lx) > eps ||
780       fabs(camera->getSize().ly - childCamera->getSize().ly) > eps ||
781       camera->getRes() != childCamera->getRes()) {
782     QString question(QObject::tr(
783         "The camera settings of the scene you are loading as sub-xsheet are "
784         "different from those of your current scene. What you want to do?"));
785     QList<QString> list;
786     list.append(QObject::tr("Keep the sub-xsheet original camera settings."));
787     list.append(QObject::tr(
788         "Apply the current scene camera settings to the sub-xsheet."));
789     int ret = DVGui::RadioButtonMsgBox(DVGui::WARNING, question, list);
790     if (ret == 0) return 0;
791     if (ret == 2) {
792       childCamera->setRes(camera->getRes());
793       childCamera->setSize(camera->getSize());
794     }
795   }
796 
797   ChildLevelResourceImporter childLevelResourceImporter(parentScene, &scene,
798                                                         importStrategy);
799   TFilePath dstFolder = importStrategy.getDstFolder();
800   if (dstFolder != TFilePath()) {
801     TLevelSet *levelSet      = parentScene->getLevelSet();
802     TFilePath levelSetFolder = levelSet->createFolder(
803         levelSet->getDefaultFolder() + dstFolder.getParentDir(),
804         dstFolder.getWideName());
805     childLevelResourceImporter.setLevelSetFolder(levelSetFolder);
806   }
807 
808   SceneResources resources(&scene, childXsh);
809   resources.accept(&childLevelResourceImporter);
810 
811   scene.setProject(parentScene->getProject());
812   std::vector<TXshChildLevel *> childLevels;
813   for (int i = 0; i < scene.getLevelSet()->getLevelCount(); i++)
814     if (TXshChildLevel *cl = scene.getLevelSet()->getLevel(i)->getChildLevel())
815       childLevels.push_back(cl);
816   for (int i = 0; i < (int)childLevels.size(); i++) {
817     TXshChildLevel *cl = childLevels[i];
818     parentScene->getLevelSet()->insertLevel(cl);
819     // scene.getLevelSet()->removeLevel(cl);
820   }
821 
822   TXshChildLevel *childLevel =
823       parentScene->createNewLevel(CHILD_XSHLEVEL, subSceneName)
824           ->getChildLevel();
825 
826   int frameCount = scene.getXsheet()->getFrameCount();
827   childXsh->setScene(parentScene);
828   if (frameCount <= 0) frameCount = 1;
829   int dummy, lastOldRow;
830   parentXsh->getCellRange(col, dummy, lastOldRow);
831 
832   childLevel->setXsheet(childXsh);
833   childXsh->getStageObjectTree()->invalidateAll();
834 
835   bool shiftColumn = beforeCellsInsert(parentXsh, row, col, frameCount,
836                                        TXshColumn::eLevelType);
837   if (!shiftColumn) parentXsh->insertCells(row, col, frameCount);
838 
839   for (int i = 0; i < frameCount; i++)
840     parentXsh->setCell(row + i, col, TXshCell(childLevel, TFrameId(1 + i)));
841 
842   for (int i = 0; i < parentScene->getLevelSet()->getLevelCount(); i++)
843     parentScene->getLevelSet()->getLevel(i)->setScene(parentScene);
844 
845   // Inform the cache fx command that a scene was loaded
846   CacheFxCommand::instance()->onSceneLoaded();
847 
848   return childLevel;
849 }
850 
851 //===========================================================================
852 // loadLevel(scene, path, castFolder, row, col)
853 // (path can be coded path)
854 // for loading the all types of level other than scene/palette/sound
855 //---------------------------------------------------------------------------
856 
loadLevel(ToonzScene * scene,const IoCmd::LoadResourceArguments::ResourceData & rd,const TFilePath & castFolder,int row0,int & col0,int row1,int & col1,bool expose,std::vector<TFrameId> & fIds,int xFrom=-1,int xTo=-1,std::wstring levelName=L"",int step=-1,int inc=-1,int frameCount=-1,bool doesFileActuallyExist=true)857 TXshLevel *loadLevel(ToonzScene *scene,
858                      const IoCmd::LoadResourceArguments::ResourceData &rd,
859                      const TFilePath &castFolder, int row0, int &col0, int row1,
860                      int &col1, bool expose, std::vector<TFrameId> &fIds,
861                      int xFrom = -1, int xTo = -1, std::wstring levelName = L"",
862                      int step = -1, int inc = -1, int frameCount = -1,
863                      bool doesFileActuallyExist = true) {
864   TFilePath actualPath = scene->decodeFilePath(rd.m_path);
865 
866   LoadLevelUndo *undo                  = 0;
867   LoadAndReplaceLevelUndo *replaceUndo = 0;
868 
869   TXsheet *xsh = scene->getXsheet();
870 
871   TFileType::Type type = TFileType::getInfo(actualPath);
872   // try to find the level with the same path in the Scene Cast. If found, reuse
873   // it
874   TXshLevel *xl     = getLevelByPath(scene, actualPath);
875   bool isFirstTime  = !xl;
876   std::wstring name = actualPath.getWideName();
877 
878   IoCmd::ConvertingPopup *convertingPopup = new IoCmd::ConvertingPopup(
879       TApp::instance()->getMainWindow(),
880       QString::fromStdWString(name) +
881           QString::fromStdString(actualPath.getDottedType()));
882 
883   convertingPopup->hide();  // Should be unnecessary
884 
885   if (!xl) {
886     try {
887       std::string format = actualPath.getType();
888       if (format == "tzp" || format == "tzu") convertingPopup->show();
889 
890       if (fIds.size() != 0 && doesFileActuallyExist)
891         xl = scene->loadLevel(actualPath, rd.m_options ? &*rd.m_options : 0,
892                               levelName, fIds);
893       else
894         xl = scene->loadLevel(actualPath, rd.m_options ? &*rd.m_options : 0);
895       if (!xl) {
896         error("Failed to create level " + toQString(actualPath) +
897               " : this filetype is not supported.");
898         return 0;
899       }
900 
901       TXshSimpleLevel *sl = xl->getSimpleLevel();
902       if (sl && sl->isReadOnly() &&
903           (sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL ||
904            sl->getType() == OVL_XSHLEVEL)) {
905         TLevelSet *levelSet = new TLevelSet;
906         levelSet->insertLevel(sl);
907         VersionControlManager::instance()->setFrameRange(levelSet, true);
908       }
909 
910       if (convertingPopup->isVisible()) convertingPopup->hide();
911     } catch (TException &e) {
912       if (convertingPopup->isVisible()) convertingPopup->hide();
913 
914       QString msg = QString::fromStdWString(e.getMessage());
915       if (msg == QString("Old 4.1 Palette"))
916         error("It is not possible to load the level " + toQString(actualPath) +
917               " because its version is not supported.");
918       else
919         error(QString::fromStdWString(e.getMessage()));
920 
921       return 0;
922     }
923 
924     if (xl->getSimpleLevel() &&
925         xl->getSimpleLevel()->getProperties()->isForbidden()) {
926       error("It is not possible to load the level " + toQString(actualPath) +
927             " because its version is not supported.");
928       scene->getLevelSet()->removeLevel(xl);
929       return 0;
930     }
931 
932     if (castFolder != TFilePath())
933       scene->getLevelSet()->moveLevelToFolder(castFolder, xl);
934 
935     History::instance()->addItem(actualPath);
936 
937     if (row1 == -1 && col1 == -1) {
938       undo = new LoadLevelUndo();
939       undo->setLevel(xl);
940       undo->setLevelSetFolder(castFolder);
941       undo->setIsFirstTime(isFirstTime);
942     }
943   }
944   // if the level can be obtained (from scene cast or file)
945   if (xl) {
946     // placing in the xsheet
947     if (expose) {
948       // lo importo nell'xsheet
949       if (!undo) {
950         undo = new LoadLevelUndo();
951         undo->setLevel(xl);
952         undo->setIsFirstTime(isFirstTime);
953       }
954 
955       int levelType = xl->getType();
956       TXshColumn::ColumnType newLevelColumnType =
957           TXshColumn::toColumnType(levelType);
958       bool columnInserted =
959           beforeCellsInsert(scene->getXsheet(), row0, col0, xl->getFrameCount(),
960                             newLevelColumnType);
961 
962       scene->getXsheet()->exposeLevel(row0, col0, xl, fIds, xFrom, xTo, step,
963                                       inc, frameCount, doesFileActuallyExist);
964 
965       if (frameCount > 0)
966         undo->setCells(scene->getXsheet(), row0, col0, frameCount);
967       else
968         undo->setCells(scene->getXsheet(), row0, col0, xl->getFrameCount());
969       undo->setColumnInserted(columnInserted);
970     }
971     if (row1 != -1 || col1 != -1)
972       replaceUndo = new LoadAndReplaceLevelUndo(xl->getSimpleLevel(), row0,
973                                                 col0, row1, col1);
974   }
975 
976   if (undo) TUndoManager::manager()->add(undo);
977 
978   if (replaceUndo) TUndoManager::manager()->add(replaceUndo);
979 
980   return xl;
981 }
982 
983 //===========================================================================
984 // loadResource(scene, path, castFolder, row, col, expose)
985 //---------------------------------------------------------------------------
986 
loadResource(ToonzScene * scene,const IoCmd::LoadResourceArguments::ResourceData & rd,const TFilePath & castFolder,int row0,int & col0,int row1,int & col1,bool expose,std::vector<TFrameId> fIds=std::vector<TFrameId> (),int xFrom=-1,int xTo=-1,std::wstring levelName=L"",int step=-1,int inc=-1,int frameCount=-1,bool doesFileActuallyExist=true)987 TXshLevel *loadResource(
988     ToonzScene *scene, const IoCmd::LoadResourceArguments::ResourceData &rd,
989     const TFilePath &castFolder, int row0, int &col0, int row1, int &col1,
990     bool expose, std::vector<TFrameId> fIds = std::vector<TFrameId>(),
991     int xFrom = -1, int xTo = -1, std::wstring levelName = L"", int step = -1,
992     int inc = -1, int frameCount = -1, bool doesFileActuallyExist = true) {
993   IoCmd::LoadResourceArguments::ResourceData actualRd(rd);
994   actualRd.m_path = scene->decodeFilePath(rd.m_path);
995 
996   TFileType::Type type = TFileType::getInfo(actualRd.m_path);
997   return (type == TFileType::PALETTE_LEVEL)
998              ? loadPalette(scene, actualRd.m_path, castFolder, row0, col0)
999              : loadLevel(scene, actualRd, castFolder, row0, col0, row1, col1,
1000                          expose, fIds, xFrom, xTo, levelName, step, inc,
1001                          frameCount, doesFileActuallyExist);
1002 }
1003 
1004 //===========================================================================
1005 
1006 enum ExposeType { eOverWrite, eShiftCells, eNone };
1007 
1008 //===========================================================================
1009 // class ExposeLevelUndo
1010 //---------------------------------------------------------------------------
1011 
1012 class ExposeLevelUndo final : public TUndo {
1013   TXshSimpleLevelP m_sl;
1014   std::vector<TXshCell> m_oldCells;
1015   std::vector<TFrameId> m_fids;
1016   int m_row;
1017   int m_col;
1018   int m_frameCount;
1019   bool m_insertEmptyColumn;
1020   ExposeType m_type;
1021 
1022 public:
ExposeLevelUndo(TXshSimpleLevel * sl,int row,int col,int frameCount,bool insertEmptyColumn,ExposeType type=eNone)1023   ExposeLevelUndo(TXshSimpleLevel *sl, int row, int col, int frameCount,
1024                   bool insertEmptyColumn, ExposeType type = eNone)
1025       : m_sl(sl)
1026       , m_row(row)
1027       , m_col(col)
1028       , m_frameCount(frameCount)
1029       , m_insertEmptyColumn(insertEmptyColumn)
1030       , m_fids()
1031       , m_type(type) {
1032     if (type == eOverWrite) {
1033       TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1034       int r;
1035       for (r = row; r < frameCount + row; r++)
1036         m_oldCells.push_back(xsh->getCell(r, col));
1037     }
1038   }
1039 
setFids(const std::vector<TFrameId> & fids)1040   void setFids(const std::vector<TFrameId> &fids) { m_fids = fids; }
1041 
undo() const1042   void undo() const override {
1043     TApp *app    = TApp::instance();
1044     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1045     if (m_insertEmptyColumn)
1046       xsh->removeColumn(m_col);
1047     else {
1048       xsh->removeCells(m_row, m_col, m_frameCount);
1049       if (m_type == eNone) xsh->insertCells(m_row, m_col, m_frameCount);
1050       if (m_type == eOverWrite) {
1051         xsh->insertCells(m_row, m_col, m_frameCount);
1052         if (!m_oldCells.empty())
1053           xsh->setCells(m_row, m_col, m_frameCount, &m_oldCells[0]);
1054       }
1055       app->getCurrentXsheet()->notifyXsheetChanged();
1056     }
1057   }
1058 
redo() const1059   void redo() const override {
1060     TApp *app    = TApp::instance();
1061     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1062     if (m_insertEmptyColumn) xsh->insertColumn(m_col);
1063     int frameCount = 0;
1064     if (!m_fids.empty()) {
1065       if (m_type == eShiftCells) xsh->insertCells(m_row, m_col, m_frameCount);
1066       frameCount = (int)m_fids.size();
1067       std::vector<TFrameId>::const_iterator it;
1068       int row = m_row;
1069       for (it = m_fids.begin(); it != m_fids.end(); ++it) {
1070         xsh->setCell(row, m_col, TXshCell(m_sl.getPointer(), *it));
1071         ++row;
1072       }
1073     } else {
1074       xsh->removeCells(m_row, m_col, m_frameCount);
1075       frameCount = xsh->exposeLevel(m_row, m_col, m_sl.getPointer());
1076     }
1077     app->getCurrentXsheet()->notifyXsheetChanged();
1078   }
1079 
getSize() const1080   int getSize() const override { return sizeof *this; }
1081 
getHistoryString()1082   QString getHistoryString() override {
1083     return QObject::tr("Expose Level  %1")
1084         .arg(QString::fromStdWString(m_sl->getName()));
1085   }
1086 };
1087 
1088 //===========================================================================
1089 // class ExposeCommentUndo
1090 //---------------------------------------------------------------------------
1091 
1092 class ExposeCommentUndo final : public TUndo {
1093   TXshSoundTextColumnP m_soundtextColumn;
1094   int m_col;
1095   bool m_insertEmptyColumn;
1096   QString m_columnName;
1097 
1098 public:
ExposeCommentUndo(TXshSoundTextColumn * soundtextColumn,int col,bool insertEmptyColumn,QString columnName)1099   ExposeCommentUndo(TXshSoundTextColumn *soundtextColumn, int col,
1100                     bool insertEmptyColumn, QString columnName)
1101       : m_soundtextColumn(soundtextColumn)
1102       , m_col(col)
1103       , m_insertEmptyColumn(insertEmptyColumn)
1104       , m_columnName(columnName) {}
1105 
undo() const1106   void undo() const override {
1107     TApp *app    = TApp::instance();
1108     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1109     xsh->removeColumn(m_col);
1110     if (!m_insertEmptyColumn) xsh->insertColumn(m_col);
1111     app->getCurrentXsheet()->notifyXsheetChanged();
1112   }
1113 
redo() const1114   void redo() const override {
1115     TApp *app    = TApp::instance();
1116     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
1117     if (m_insertEmptyColumn) xsh->insertColumn(m_col);
1118     xsh->insertColumn(m_col, m_soundtextColumn.getPointer());
1119 
1120     // Setto il nome di dafult della colonna al nome del file magpie.
1121     TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(m_col));
1122     std::string str   = m_columnName.toStdString();
1123     obj->setName(str);
1124 
1125     app->getCurrentXsheet()->notifyXsheetChanged();
1126   }
1127 
getSize() const1128   int getSize() const override { return sizeof *this; }
1129 };
1130 
1131 //=============================================================================
1132 
1133 // This class is intended to serve as inhibitor for Previewer and SwatchViewer's
1134 // activity
1135 class RenderingSuspender {
1136 public:
RenderingSuspender()1137   RenderingSuspender() {
1138     Previewer::suspendRendering(true);
1139     PreviewFxManager::suspendRendering(true);
1140     SwatchViewer::suspendRendering(true);
1141   }
1142 
~RenderingSuspender()1143   ~RenderingSuspender() {
1144     Previewer::suspendRendering(false);
1145     PreviewFxManager::suspendRendering(false);
1146     SwatchViewer::suspendRendering(false);
1147   }
1148 };
1149 
1150 //=============================================================================
1151 
dirtyWhite(const TPaletteP & plt)1152 inline TPaletteP dirtyWhite(const TPaletteP &plt) {
1153   TPaletteP out = TPaletteP(plt->clone());
1154   for (int i = 0; i < out->getStyleCount(); i++) {
1155     if (out->getStyle(i)->getMainColor() == TPixel::White) {
1156       out->getStyle(i)->setMainColor(TPixel(254, 254, 254, 255));
1157     }
1158   }
1159   return out;
1160 }
1161 
1162 //---------------------------------------------------------------------------
1163 }  // namespace
1164 //---------------------------------------------------------------------------
1165 
1166 // Per ora e' usato solo per i formato "tzp" e "tzu".
ConvertingPopup(QWidget * parent,QString fileName)1167 IoCmd::ConvertingPopup::ConvertingPopup(QWidget *parent, QString fileName)
1168     : QDialog(parent) {
1169   setModal(true);
1170   setWindowFlags(Qt::Dialog | Qt::WindowTitleHint);
1171   setMinimumSize(70, 50);
1172   QVBoxLayout *mainLayout = new QVBoxLayout;
1173   mainLayout->setMargin(5);
1174   mainLayout->setSpacing(0);
1175 
1176   QLabel *label = new QLabel(QString(
1177       QObject::tr("Converting %1 images to tlv format...").arg(fileName)));
1178   mainLayout->addWidget(label);
1179 
1180   setLayout(mainLayout);
1181 }
1182 
~ConvertingPopup()1183 IoCmd::ConvertingPopup::~ConvertingPopup() {}
1184 
1185 //===========================================================================
1186 // IoCmd::saveSceneIfNeeded(message)
1187 //---------------------------------------------------------------------------
1188 
saveSceneIfNeeded(QString msg)1189 bool IoCmd::saveSceneIfNeeded(QString msg) {
1190   TApp *app = TApp::instance();
1191 
1192   if (app->getCurrentScene()->getDirtyFlag()) {
1193     QString question;
1194     question = QObject::tr(
1195                    "%1: the current scene has been modified.\n"
1196                    "What would you like to do?")
1197                    .arg(msg);
1198     int ret = DVGui::MsgBox(
1199         question, QObject::tr("Save All"), QObject::tr("Save Scene Only"),
1200         QObject::tr("Discard Changes"), QObject::tr("Cancel"), 0);
1201     if (ret == 0 || ret == 4) {
1202       // cancel (or closed message box window)
1203       return false;
1204     } else if (ret == 1) {
1205       // save all
1206       if (!IoCmd::saveAll()) return false;
1207     } else if (ret == 2) {
1208       // save
1209       if (!IoCmd::saveScene()) return false;
1210     } else if (ret == 3) {
1211     }
1212   }
1213 
1214   ToonzScene *scene = app->getCurrentScene()->getScene();
1215   if (scene) {
1216     QStringList dirtyResources;
1217     {
1218       SceneResources resources(scene, 0);
1219       resources.getDirtyResources(dirtyResources);
1220     }
1221 
1222     if (!dirtyResources.empty()) {
1223       // show up to 5 items
1224       int extraCount = dirtyResources.count() - 5;
1225       if (extraCount > 0) {
1226         dirtyResources = dirtyResources.mid(0, 5);
1227         dirtyResources.append(
1228             QObject::tr("and %1 more item(s).").arg(extraCount));
1229       }
1230 
1231       QString question;
1232 
1233       question = msg + ":" +
1234                  QObject::tr(" The following file(s) have been modified.\n\n");
1235 
1236       question += "  " + dirtyResources.join("\n  ");
1237 
1238       question += "\n" + QObject::tr("\nWhat would you like to do? ");
1239 
1240       int ret =
1241           DVGui::MsgBox(question, QObject::tr("Save Changes"),
1242                         msg + QObject::tr(" Anyway"), QObject::tr("Cancel"), 0);
1243       if (ret == 0 || ret == 3) {
1244         // cancel (or closed message box window)
1245         return false;
1246       } else if (ret == 1) {
1247         // save non scene files
1248         IoCmd::saveNonSceneFiles();
1249       } else if (ret == 2) {
1250         // do nothing and continue
1251       }
1252     }
1253 
1254     RenderingSuspender suspender;
1255 
1256     TFilePath scenePath(scene->getScenePath());
1257     scene->clear();  // note: this (possibly) removes the "untitled" folder
1258     app->getCurrentScene()->notifyCastChange();
1259     // Si deve notificare anche il cambiamento che ha subito l'xsheet.
1260     app->getCurrentXsheet()->notifyXsheetSwitched();
1261     FileBrowser::refreshFolder(scenePath.getParentDir());
1262   }
1263   app->getCurrentScene()->setDirtyFlag(false);
1264   return true;
1265 }
1266 
1267 //===========================================================================
1268 // IoCmd::newScene()
1269 //---------------------------------------------------------------------------
1270 
newScene()1271 void IoCmd::newScene() {
1272   RenderingSuspender suspender;
1273   TApp *app        = TApp::instance();
1274   double cameraDpi = 120;  // used to be 64 and 53.33333
1275 
1276   if (!saveSceneIfNeeded(QApplication::tr("New Scene"))) return;
1277 
1278   IconGenerator::instance()->clearRequests();
1279   IconGenerator::instance()->clearSceneIcons();
1280   ImageManager::instance()->clear();
1281   FullColorPalette::instance()->clear();
1282 
1283   CacheFxCommand::instance()->onNewScene();
1284   CacheFxCommand::instance()->onSceneLoaded();
1285 
1286 #ifdef USE_SQLITE_HDPOOL
1287   // INTERMEDIATE CACHE RESULTS MANAGEMENT: Clear all resources not accessed in
1288   // the last 2 sessions
1289   TCacheResourcePool::instance()->clearAccessedAfterSessions(
1290       1);  // 0 would be after this session
1291 #endif
1292 
1293   ToonzScene *scene = new ToonzScene();
1294   TImageStyle::setCurrentScene(scene);
1295 
1296   TCamera *camera = scene->getCurrentCamera();
1297   TDimension res(1920, 1080);
1298   // TDimension res(768, 576);
1299   camera->setRes(res);
1300   camera->setSize(
1301       TDimensionD((double)res.lx / cameraDpi, (double)res.ly / cameraDpi));
1302   scene->getProperties()->setBgColor(TPixel32::White);
1303   TProjectManager::instance()->initializeScene(scene);
1304   if (Preferences::instance()->getPixelsOnly()) {
1305     TCamera *updateCamera = scene->getCurrentCamera();
1306     TDimension updateRes  = updateCamera->getRes();
1307     updateCamera->setSize(TDimensionD((double)updateRes.lx / cameraDpi,
1308                                       (double)updateRes.ly / cameraDpi));
1309   }
1310   // Must set current scene after initializeScene!!
1311   app->getCurrentScene()->setScene(scene);
1312   // initializeScene() load project cleanup palette: set it to cleanup palette
1313   // handle.
1314   TPalette *palette = scene->getProperties()
1315                           ->getCleanupParameters()
1316                           ->m_cleanupPalette.getPointer();
1317   PaletteController *paletteController = app->getPaletteController();
1318   paletteController->getCurrentCleanupPalette()->setPalette(palette, -1);
1319   paletteController->editLevelPalette();
1320 
1321   TFilePath scenePath = scene->getScenePath();
1322   DvDirModel::instance()->refreshFolder(scenePath.getParentDir());
1323 
1324   Previewer::clearAll();
1325   PreviewFxManager::instance()->reset();
1326 
1327   app->getCurrentScene()->notifyNameSceneChange();
1328   IconGenerator::instance()->invalidateSceneIcon();
1329   ToolHandle *toolH = TApp::instance()->getCurrentTool();
1330   if (toolH && toolH->getTool()) toolH->getTool()->reset();
1331 
1332   CommandManager::instance()->execute("T_Hand");
1333 
1334   CommandManager::instance()->enable(MI_SaveSubxsheetAs, false);
1335 
1336   app->getCurrentScene()->notifySceneChanged();
1337   app->getCurrentScene()->setDirtyFlag(false);
1338   app->getCurrentObject()->setIsSpline(false);
1339   app->getCurrentColumn()->setColumnIndex(0);
1340 
1341   CleanupParameters *cp = scene->getProperties()->getCleanupParameters();
1342   CleanupParameters::GlobalParameters.assign(cp);
1343 
1344   // updateCleanupSettingsPopup();
1345 
1346   CleanupPreviewCheck::instance()->setIsEnabled(false);
1347   CameraTestCheck::instance()->setIsEnabled(false);
1348   SetScanCropboxCheck::instance()->setIsEnabled(false);
1349 
1350   if (!TApp::instance()->isApplicationStarting())
1351     QApplication::clipboard()->clear();
1352   TSelection::setCurrent(0);
1353   TUndoManager::manager()->reset();
1354 
1355   bool exist = TSystem::doesExistFileOrLevel(
1356       scene->decodeFilePath(scene->getScenePath()));
1357   QAction *act = CommandManager::instance()->getAction(MI_RevertScene);
1358   if (act) act->setEnabled(exist);
1359 }
1360 
1361 //===========================================================================
1362 // IoCmd::saveScene(scenePath, flags)
1363 //---------------------------------------------------------------------------
1364 
saveScene(const TFilePath & path,int flags)1365 bool IoCmd::saveScene(const TFilePath &path, int flags) {
1366   bool overwrite     = (flags & SILENTLY_OVERWRITE) != 0;
1367   bool saveSubxsheet = (flags & SAVE_SUBXSHEET) != 0;
1368   TApp *app          = TApp::instance();
1369 
1370   assert(!path.isEmpty());
1371   TFilePath scenePath = path;
1372   if (scenePath.getType() == "") scenePath = scenePath.withType("tnz");
1373   if (scenePath.getType() != "tnz") {
1374     error(
1375         QObject::tr("%1 has an invalid file extension.").arg(toQString(path)));
1376     return false;
1377   }
1378   TFileStatus dirStatus(scenePath.getParentDir());
1379   if (!(dirStatus.doesExist() && dirStatus.isWritable())) {
1380     error(QObject::tr("%1 is an invalid path.")
1381               .arg(toQString(scenePath.getParentDir())));
1382     return false;
1383   }
1384 
1385   // notify user if the scene will be saved including any "broken" expression
1386   // reference
1387   if (!ExpressionReferenceManager::instance()->askIfParamIsIgnoredOnSave(
1388           saveSubxsheet))
1389     return false;
1390 
1391   if (!overwrite && TFileStatus(scenePath).doesExist()) {
1392     QString question;
1393     question = QObject::tr(
1394                    "The scene %1 already exists.\nDo you want to overwrite it?")
1395                    .arg(QString::fromStdString(scenePath.getName()));
1396 
1397     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
1398                             QObject::tr("Cancel"), 0);
1399     if (ret == 2 || ret == 0) return false;
1400   }
1401 
1402   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1403 
1404   TXsheet *xsheet = 0;
1405   if (saveSubxsheet) xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
1406 
1407   // If the scene will be saved in the different folder, check out the scene
1408   // cast.
1409   // if the cast contains the level specified with $scenefolder alias,
1410   // open a warning popup notifying that such level will lose link.
1411 
1412   // in case of saving subxsheet, the current scene will not be switched to the
1413   // saved one
1414   // so the level paths are needed to be reverted after saving
1415   QHash<TXshLevel *, TFilePath> orgLevelPaths;
1416   auto revertOrgLevelPaths = [&] {
1417     QHash<TXshLevel *, TFilePath>::const_iterator i =
1418         orgLevelPaths.constBegin();
1419     while (i != orgLevelPaths.constEnd()) {
1420       if (TXshSimpleLevel *sil = i.key()->getSimpleLevel())
1421         sil->setPath(i.value(), true);
1422       else if (TXshPaletteLevel *pal = i.key()->getPaletteLevel())
1423         pal->setPath(i.value());
1424       else if (TXshSoundLevel *sol = i.key()->getSoundLevel())
1425         sol->setPath(i.value());
1426       ++i;
1427     }
1428   };
1429 
1430   if (!overwrite) {
1431     bool ret = takeCareSceneFolderItemsOnSaveSceneAs(scene, scenePath, xsheet,
1432                                                      orgLevelPaths);
1433     if (!ret) {
1434       revertOrgLevelPaths();
1435       return false;
1436     }
1437   }
1438 
1439   TFilePath oldFullPath = scene->decodeFilePath(scene->getScenePath());
1440   TFilePath newFullPath = scene->decodeFilePath(scenePath);
1441 
1442   QApplication::setOverrideCursor(Qt::WaitCursor);
1443   if (app->getCurrentScene()->getDirtyFlag())
1444     scene->getContentHistory(true)->modifiedNow();
1445 
1446   if (oldFullPath != newFullPath) {
1447     IconGenerator::instance()->clearRequests();
1448     IconGenerator::instance()->clearSceneIcons();
1449 
1450 #ifdef USE_SQLITE_HDPOOL
1451     // Open the new cache resources HD pool
1452     TCacheResourcePool::instance()->setPath(
1453         QString::fromStdWString(
1454             ToonzFolder::getCacheRootFolder().getWideString()),
1455         QString::fromStdWString(scene->getProject()->getName().getWideName()),
1456         QString::fromStdWString(scene->getSceneName()));
1457 #endif
1458   }
1459 
1460   CleanupParameters *cp = scene->getProperties()->getCleanupParameters();
1461   CleanupParameters oldCP(*cp);
1462   cp->assign(&CleanupParameters::GlobalParameters);
1463 
1464   try {
1465     scene->save(scenePath, xsheet);
1466   } catch (const TSystemException &se) {
1467     DVGui::warning(QString::fromStdWString(se.getMessage()));
1468   } catch (...) {
1469     DVGui::error(QObject::tr("Couldn't save %1").arg(toQString(scenePath)));
1470   }
1471 
1472   cp->assign(&oldCP);
1473 
1474   // in case of saving subxsheet, revert the level paths after saving
1475   revertOrgLevelPaths();
1476 
1477   if (!overwrite && !saveSubxsheet)
1478     app->getCurrentScene()->notifyNameSceneChange();
1479   FileBrowser::refreshFolder(scenePath.getParentDir());
1480   IconGenerator::instance()->invalidate(scenePath);
1481 
1482   // Le seguenti notifiche non cambiano il dirty flag del livello o della
1483   // paletta
1484   // ma sono necessarie per aggiornare le titlebar dei pannelli.
1485   app->getCurrentLevel()->notifyLevelTitleChange();
1486   app->getCurrentPalette()->notifyPaletteTitleChanged();
1487 
1488   app->getCurrentScene()->setDirtyFlag(false);
1489 
1490   History::instance()->addItem(scenePath);
1491   RecentFiles::instance()->addFilePath(
1492       toQString(scenePath), RecentFiles::Scene,
1493       QString::fromStdString(app->getCurrentScene()
1494                                  ->getScene()
1495                                  ->getProject()
1496                                  ->getName()
1497                                  .getName()));
1498 
1499   QApplication::restoreOverrideCursor();
1500 
1501   bool exist = TSystem::doesExistFileOrLevel(
1502       scene->decodeFilePath(scene->getScenePath()));
1503   QAction *act = CommandManager::instance()->getAction(MI_RevertScene);
1504   if (act) act->setEnabled(exist);
1505 
1506   return true;
1507 }
1508 
1509 //===========================================================================
1510 // IoCmd::saveScene()
1511 //---------------------------------------------------------------------------
1512 
saveScene()1513 bool IoCmd::saveScene() {
1514   TSelection *oldSelection =
1515       TApp::instance()->getCurrentSelection()->getSelection();
1516   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1517   if (scene->isUntitled()) {
1518     static SaveSceneAsPopup *popup = 0;
1519     if (!popup) popup = new SaveSceneAsPopup();
1520     int ret = popup->exec();
1521     if (ret == QDialog::Accepted) {
1522       TApp::instance()->getCurrentScene()->setDirtyFlag(false);
1523       return true;
1524     } else {
1525       TApp::instance()->getCurrentSelection()->setSelection(oldSelection);
1526       return false;
1527     }
1528   } else {
1529     TFilePath fp = scene->getScenePath();
1530     // salva la scena con il nome fp. se fp esiste gia' lo sovrascrive
1531     return saveScene(fp, SILENTLY_OVERWRITE);
1532   }
1533 }
1534 
1535 //===========================================================================
1536 // IoCmd::saveLevel(levelPath)
1537 //---------------------------------------------------------------------------
1538 
saveLevel(const TFilePath & path)1539 bool IoCmd::saveLevel(const TFilePath &path) {
1540   assert(!path.isEmpty());
1541 
1542   TApp *app = TApp::instance();
1543   TXshSimpleLevel *sl =
1544       dynamic_cast<TXshSimpleLevel *>(app->getCurrentLevel()->getLevel());
1545   if (!sl) return false;
1546   std::string ext    = sl->getPath().getType();
1547   std::string dotts  = sl->getPath().getDots();
1548   TFilePath realPath = path;
1549   if (realPath.getType() == "")
1550     realPath = TFilePath(realPath.getWideString() + ::to_wstring(dotts + ext));
1551 
1552   bool ret = saveLevel(realPath, sl, false);
1553   if (!ret) {  // save level failed
1554     return false;
1555   }
1556 
1557   RecentFiles::instance()->addFilePath(toQString(realPath), RecentFiles::Level);
1558 
1559   TApp::instance()
1560       ->getPaletteController()
1561       ->getCurrentLevelPalette()
1562       ->notifyPaletteSwitched();
1563   return true;
1564 }
1565 
1566 //===========================================================================
1567 // IoCmd::saveLevel()
1568 //---------------------------------------------------------------------------
1569 
saveLevel()1570 bool IoCmd::saveLevel() {
1571   TApp *app = TApp::instance();
1572   TXshSimpleLevel *sl =
1573       dynamic_cast<TXshSimpleLevel *>(app->getCurrentLevel()->getLevel());
1574   if (!sl) return false;
1575   if (sl->getPath() == TFilePath()) return false;
1576   TFilePath path =
1577       app->getCurrentScene()->getScene()->decodeFilePath(sl->getPath());
1578   if (path == TFilePath()) return false;
1579   if (path.getWideString()[0] == L'+') return false;
1580   if (!path.isAbsolute()) return false;
1581 
1582   if (!saveLevel(path, sl, true)) return false;
1583 
1584   sl->setDirtyFlag(false);
1585   // for update title bar
1586   app->getCurrentLevel()->notifyLevelChange();
1587   return true;
1588 }
1589 
1590 //===========================================================================
1591 // IoCmd::saveLevel(levelPath, simpleLevel, overwrite)
1592 //---------------------------------------------------------------------------
1593 
saveLevel(const TFilePath & fp,TXshSimpleLevel * sl,bool overwrite)1594 bool IoCmd::saveLevel(const TFilePath &fp, TXshSimpleLevel *sl,
1595                       bool overwrite) {
1596   assert(sl);
1597   bool fileDoesExist = TSystem::doesExistFileOrLevel(fp);
1598   if (!overwrite && fileDoesExist) {
1599     QString question;
1600     question = QObject::tr(
1601                    "The level %1 already exists.\nDo you want to overwrite it?")
1602                    .arg(toQString(fp));
1603     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
1604                             QObject::tr("Cancel"), 0);
1605     if (ret == 2 || ret == 0) return false;
1606   }
1607 
1608   bool overwritePalette = false;
1609   // Confirmation of Overwrite palette
1610   // open dialog IF  1) the level is dirty, and 2) confirmation of overwrite
1611   // palette haven't been asked
1612   // for PLI file, do nothing
1613   if (sl->getPalette() && sl->getPalette()->getAskOverwriteFlag() &&
1614       sl->getPath().getType() != "pli") {
1615     /*-- ファイルが存在しない場合はパレットも必ず保存する --*/
1616     if (!fileDoesExist)
1617       overwritePalette = true;
1618     else {
1619       QString question;
1620       question =
1621           "Palette " +
1622           QString::fromStdWString(sl->getPalette()->getPaletteName()) +
1623           ".tpl has been modified. Do you want to overwrite palette as well ?";
1624       int ret =
1625           DVGui::MsgBox(question, QObject::tr("Overwrite Palette") /*ret = 1*/,
1626                         QObject::tr("Don't Overwrite Palette") /*ret = 2*/, 0);
1627       if (ret == 1) overwritePalette = true;
1628     }
1629   }
1630 
1631   QApplication::setOverrideCursor(Qt::WaitCursor);
1632   try {
1633     sl->save(fp, TFilePath(), overwritePalette);
1634   } catch (TSystemException se) {
1635     QApplication::restoreOverrideCursor();
1636     DVGui::warning(QString::fromStdWString(se.getMessage()));
1637     return false;
1638   } catch (...) {
1639     QApplication::restoreOverrideCursor();
1640     DVGui::error(QObject::tr("Couldn't save %1").arg(toQString(fp)));
1641     return false;
1642   }
1643   IconGenerator::instance()->invalidate(fp);
1644   FileBrowser::refreshFolder(fp.getParentDir());
1645   History::instance()->addItem(fp);
1646 
1647   if (sl->getPalette()) {
1648     if (overwritePalette || sl->getPath().getType() == "pli")
1649       sl->getPalette()->setDirtyFlag(false);
1650     else  // ask only once for save palette
1651       sl->getPalette()->setAskOverwriteFlag(false);
1652   }
1653 
1654   RecentFiles::instance()->addFilePath(toQString(fp), RecentFiles::Level);
1655   QApplication::restoreOverrideCursor();
1656   TApp::instance()->getCurrentLevel()->notifyLevelTitleChange();
1657   TApp::instance()
1658       ->getPaletteController()
1659       ->getCurrentLevelPalette()
1660       ->notifyPaletteTitleChanged();
1661   return true;
1662 }
1663 
1664 //===========================================================================
1665 // IoCmd::saveLevel(simpleLevel)
1666 //---------------------------------------------------------------------------
1667 
saveLevel(TXshSimpleLevel * sl)1668 bool IoCmd::saveLevel(TXshSimpleLevel *sl) {
1669   return saveLevel(sl->getPath(), sl, true);
1670 }
1671 
1672 //===========================================================================
1673 // IoCmd::saveAll()
1674 //---------------------------------------------------------------------------
1675 
saveAll()1676 bool IoCmd::saveAll() {
1677   // try to save as much as possible
1678   // if anything is wrong, return false
1679   bool result = saveScene();
1680 
1681   TApp *app         = TApp::instance();
1682   ToonzScene *scene = app->getCurrentScene()->getScene();
1683   bool untitled     = scene->isUntitled();
1684   SceneResources resources(scene, 0);
1685   resources.save(scene->getScenePath());
1686   resources.updatePaths();
1687 
1688   // for update title bar
1689   app->getCurrentLevel()->notifyLevelTitleChange();
1690   app->getCurrentPalette()->notifyPaletteTitleChanged();
1691   if (untitled) scene->setUntitled();
1692   return result;
1693 }
1694 
1695 //===========================================================================
1696 // IoCmd::saveNonSceneFiles()
1697 //---------------------------------------------------------------------------
1698 
saveNonSceneFiles()1699 void IoCmd::saveNonSceneFiles() {
1700   // try to save non scene files
1701 
1702   TApp *app         = TApp::instance();
1703   ToonzScene *scene = app->getCurrentScene()->getScene();
1704   bool untitled     = scene->isUntitled();
1705   SceneResources resources(scene, 0);
1706   resources.save(scene->getScenePath());
1707   if (untitled) scene->setUntitled();
1708   resources.updatePaths();
1709 
1710   // for update title bar
1711   app->getCurrentLevel()->notifyLevelTitleChange();
1712   app->getCurrentPalette()->notifyPaletteTitleChanged();
1713 }
1714 
1715 //===========================================================================
1716 // IoCmd::saveSound(soundPath, soundColumn, overwrite)
1717 //---------------------------------------------------------------------------
1718 
saveSound(const TFilePath & fp,TXshSoundLevel * sl,bool overwrite)1719 bool IoCmd::saveSound(const TFilePath &fp, TXshSoundLevel *sl, bool overwrite) {
1720   if (!overwrite && TSystem::doesExistFileOrLevel(fp)) {
1721     QString question;
1722     question =
1723         QObject::tr(
1724             "The soundtrack %1 already exists.\nDo you want to overwrite it?")
1725             .arg(toQString(fp));
1726     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
1727                             QObject::tr("Cancel"), 0);
1728     if (ret == 2 || ret == 0) return false;
1729   }
1730   try {
1731     sl->save(fp);
1732   } catch (...) {
1733     DVGui::error(QObject::tr("Couldn't save %1").arg(toQString(fp)));
1734     return false;
1735   }
1736   QApplication::setOverrideCursor(Qt::WaitCursor);
1737   FileBrowser::refreshFolder(fp.getParentDir());
1738   History::instance()->addItem(fp);
1739   QApplication::restoreOverrideCursor();
1740   return true;
1741 }
1742 
1743 //===========================================================================
1744 // IoCmd::saveSound(soundColumn)
1745 //---------------------------------------------------------------------------
1746 
saveSound(TXshSoundLevel * sl)1747 bool IoCmd::saveSound(TXshSoundLevel *sl) {
1748   return saveSound(sl->getPath(), sl, true);
1749 }
1750 
1751 //=========================================================================
1752 // IoCmd::loadScene(scene, scenePath, import)
1753 //---------------------------------------------------------------------------
1754 
loadScene(ToonzScene & scene,const TFilePath & scenePath,bool import)1755 bool IoCmd::loadScene(ToonzScene &scene, const TFilePath &scenePath,
1756                       bool import) {
1757   if (!TSystem::doesExistFileOrLevel(scenePath)) return false;
1758   scene.load(scenePath);
1759   // import if needed
1760   TProjectManager *pm      = TProjectManager::instance();
1761   TProjectP currentProject = pm->getCurrentProject();
1762   if (!scene.getProject()) return false;
1763   if (scene.getProject()->getProjectPath() !=
1764       currentProject->getProjectPath()) {
1765     ResourceImportDialog resourceLoader;
1766     // resourceLoader.setImportEnabled(true);
1767     ResourceImporter importer(&scene, currentProject.getPointer(),
1768                               resourceLoader);
1769     SceneResources resources(&scene, scene.getXsheet());
1770     resources.accept(&importer);
1771     scene.setScenePath(importer.getImportedScenePath());
1772     scene.setProject(currentProject.getPointer());
1773   }
1774   return true;
1775 }
1776 
1777 //===========================================================================
1778 // IoCmd::loadScene(scenePath)
1779 //---------------------------------------------------------------------------
1780 
loadScene(const TFilePath & path,bool updateRecentFile,bool checkSaveOldScene)1781 bool IoCmd::loadScene(const TFilePath &path, bool updateRecentFile,
1782                       bool checkSaveOldScene) {
1783   RenderingSuspender suspender;
1784 
1785   if (checkSaveOldScene)
1786     if (!saveSceneIfNeeded(QApplication::tr("Load Scene"))) return false;
1787   assert(!path.isEmpty());
1788   TFilePath scenePath = path;
1789   bool importScene    = false;
1790   bool isXdts         = scenePath.getType() == "xdts";
1791   if (scenePath.getType() == "") scenePath = scenePath.withType("tnz");
1792   if (scenePath.getType() != "tnz" && !isXdts) {
1793     QString msg;
1794     msg = QObject::tr("File %1 doesn't look like a TOONZ Scene")
1795               .arg(QString::fromStdWString(scenePath.getWideString()));
1796     DVGui::error(msg);
1797     return false;
1798   }
1799   if (!TSystem::doesExistFileOrLevel(scenePath)) return false;
1800 
1801   TFilePath scenePathTemp(scenePath.getWideString() +
1802                           QString(".tmp").toStdWString());
1803   if (TSystem::doesExistFileOrLevel(scenePathTemp)) {
1804     QString question =
1805         QObject::tr(
1806             "A prior save of Scene '%1' was critically interupted. \n\
1807 \nA partial save file was generated and changes may be manually salvaged from '%2'.\n\
1808 \nDo you wish to continue loading the last good save or stop and try to salvage the prior save?")
1809             .arg(QString::fromStdWString(scenePath.getWideString()))
1810             .arg(QString::fromStdWString(scenePathTemp.getWideString()));
1811     QString continueAnswer = QObject::tr("Continue");
1812     QString cancelAnswer   = QObject::tr("Cancel");
1813     int ret = DVGui::MsgBox(question, continueAnswer, cancelAnswer, 0);
1814     if (ret == 2)
1815       return false;
1816     else
1817       TSystem::removeFileOrLevel(scenePathTemp);
1818   }
1819 
1820   TProjectManager *pm    = TProjectManager::instance();
1821   TProjectP sceneProject = pm->loadSceneProject(scenePath);
1822   if (!sceneProject) {
1823     QString msg;
1824     msg = QObject::tr(
1825               "It is not possible to load the scene %1 because it does not "
1826               "belong to any project.")
1827               .arg(QString::fromStdWString(scenePath.getWideString()));
1828     DVGui::warning(msg);
1829   }
1830   if (sceneProject && !sceneProject->isCurrent()) {
1831     QString currentProjectName = QString::fromStdWString(
1832         pm->getCurrentProject()->getName().getWideString());
1833     QString sceneProjectName =
1834         QString::fromStdWString(sceneProject->getName().getWideString());
1835 
1836     /*QString question = "The Scene '"
1837             + QString::fromStdWString(scenePath.getWideString())
1838 + "' belongs to project '" + sceneProjectName + "'.\n"
1839 + "What do you want to do?";*/
1840     QString question =
1841         QObject::tr(
1842             "The Scene '%1' belongs to project '%2'.\nWhat do you want to do?")
1843             .arg(QString::fromStdWString(scenePath.getWideString()))
1844             .arg(sceneProjectName);
1845     QString importAnswer        = QObject::tr("Import Scene");
1846     QString switchProjectAnswer = QObject::tr("Change Project");
1847     QString cancelAnswer        = QObject::tr("Cancel");
1848     int ret = DVGui::MsgBox(question, importAnswer, switchProjectAnswer,
1849                             cancelAnswer, 0);
1850     if (ret == 3 || ret == 0) {
1851       newScene();
1852       return false;
1853     }
1854     if (ret == 2)
1855       pm->setCurrentProjectPath(sceneProject->getProjectPath());
1856     else
1857       importScene = true;
1858   }
1859   QApplication::setOverrideCursor(Qt::WaitCursor);
1860 
1861   TUndoManager::manager()->reset();
1862   IconGenerator::instance()->clearRequests();
1863   IconGenerator::instance()->clearSceneIcons();
1864   ImageManager::instance()->clear();
1865 
1866   CacheFxCommand::instance()->onNewScene();
1867 
1868 #ifdef USE_SQLITE_HDPOOL
1869   // INTERMEDIATE CACHE RESULTS MANAGEMENT: Clear all resources not accessed in
1870   // the last 2 sessions
1871   TCacheResourcePool::instance()->clearAccessedAfterSessions(
1872       1);  // 0 would be after this session
1873 #endif
1874 
1875   ToonzScene *scene = new ToonzScene();
1876   TImageStyle::setCurrentScene(scene);
1877   printf("%s:%s Progressing:\n", __FILE__, __FUNCTION__);
1878   try {
1879     if (isXdts)
1880       XdtsIo::loadXdtsScene(scene, scenePath);
1881     else
1882       /*-- プログレス表示を行いながらLoad --*/
1883       scene->load(scenePath);
1884     // import if needed
1885     TProjectManager *pm      = TProjectManager::instance();
1886     TProjectP currentProject = pm->getCurrentProject();
1887     if (!scene->getProject() || scene->getProject()->getProjectPath() !=
1888                                     currentProject->getProjectPath()) {
1889       ResourceImportDialog resourceLoader;
1890       // resourceLoader.setImportEnabled(true);
1891       ResourceImporter importer(scene, currentProject.getPointer(),
1892                                 resourceLoader);
1893       SceneResources resources(scene, scene->getXsheet());
1894       resources.accept(&importer);
1895       scene->setScenePath(importer.getImportedScenePath());
1896       scene->setProject(currentProject.getPointer());
1897     }
1898     VersionControlManager::instance()->setFrameRange(scene->getLevelSet());
1899   } catch (...) {
1900     printf("%s:%s Exception ...:\n", __FILE__, __FUNCTION__);
1901     QString msg;
1902     msg = QObject::tr(
1903               "There were problems loading the scene %1.\n Some files may be "
1904               "missing.")
1905               .arg(QString::fromStdWString(scenePath.getWideString()));
1906     DVGui::warning(msg);
1907   }
1908   printf("%s:%s end load:\n", __FILE__, __FUNCTION__);
1909   TProject *project = scene->getProject();
1910   if (!project) {
1911     project = new TProject();
1912     project->setFolder("project", scenePath);
1913     scene->setProject(project);
1914   }
1915   TApp *app = TApp::instance();
1916   app->getCurrentScene()->setScene(scene);
1917   app->getCurrentScene()->notifyNameSceneChange();
1918   app->getCurrentFrame()->setFrame(0);
1919   app->getCurrentColumn()->setColumnIndex(0);
1920   TPalette *palette = 0;
1921   if (app->getCurrentLevel() && app->getCurrentLevel()->getSimpleLevel())
1922     palette = app->getCurrentLevel()->getSimpleLevel()->getPalette();
1923   app->getCurrentPalette()->setPalette(palette);
1924   app->getCurrentXsheet()->notifyXsheetSoundChanged();
1925   app->getCurrentObject()->setIsSpline(false);
1926 
1927   Previewer::clearAll();
1928   PreviewFxManager::instance()->reset();
1929   // updateCleanupSettingsPopup();
1930   /*- CleanupParameterの更新 -*/
1931   CleanupParameters *cp = scene->getProperties()->getCleanupParameters();
1932   CleanupParameters::GlobalParameters.assign(cp);
1933 
1934   CacheFxCommand::instance()->onSceneLoaded();
1935 
1936 #ifdef USE_SQLITE_HDPOOL
1937   TCacheResourcePool::instance()->setPath(
1938       QString::fromStdWString(
1939           ToonzFolder::getCacheRootFolder().getWideString()),
1940       QString::fromStdWString(project->getName().getWideName()),
1941       QString::fromStdWString(scene->getSceneName()));
1942 #endif
1943 
1944   UnitParameters::setFieldGuideAspectRatio(
1945       scene->getProperties()->getFieldGuideAspectRatio());
1946   IconGenerator::instance()->invalidateSceneIcon();
1947   DvDirModel::instance()->refreshFolder(scenePath.getParentDir());
1948   // set dirty for xdts files since converted tnz is not yet saved
1949   TApp::instance()->getCurrentScene()->setDirtyFlag(isXdts);
1950   History::instance()->addItem(scenePath);
1951   if (updateRecentFile)
1952     RecentFiles::instance()->addFilePath(
1953         toQString(scenePath), RecentFiles::Scene,
1954         QString::fromStdString(scene->getProject()->getName().getName()));
1955   QApplication::restoreOverrideCursor();
1956 
1957   int forbiddenLevelCount = 0;
1958   for (int i = 0; i < scene->getLevelSet()->getLevelCount(); i++) {
1959     TXshLevel *xl = scene->getLevelSet()->getLevel(i);
1960     if (xl && xl->getSimpleLevel() &&
1961         xl->getSimpleLevel()->getProperties()->isForbidden())
1962       forbiddenLevelCount++;
1963   }
1964   if (forbiddenLevelCount > 0) {
1965     QString msg;
1966     msg = QObject::tr(
1967               "There were problems loading the scene %1.\nSome levels have not "
1968               "been loaded because their version is not supported")
1969               .arg(QString::fromStdWString(scenePath.getWideString()));
1970     DVGui::warning(msg);
1971   }
1972 
1973   bool exist = TSystem::doesExistFileOrLevel(
1974       scene->decodeFilePath(scene->getScenePath()));
1975   QAction *act = CommandManager::instance()->getAction(MI_RevertScene);
1976   if (act) act->setEnabled(exist);
1977 
1978   // check if the output dpi is incompatible with pixels only mode
1979   if (Preferences::instance()->getPixelsOnly()) {
1980     TPointD dpi = scene->getCurrentCamera()->getDpi();
1981     if (!areAlmostEqual(dpi.x, Stage::standardDpi, 0.1) ||
1982         !areAlmostEqual(dpi.y, Stage::standardDpi, 0.1)) {
1983       QString question = QObject::tr(
1984           "This scene is incompatible with pixels only mode of the current "
1985           "OpenToonz version.\nWhat would you like to do?");
1986       QString turnOffPixelAnswer = QObject::tr("Turn off pixels only mode");
1987       QString resizeSceneAnswer =
1988           QObject::tr("Keep pixels only mode on and resize the scene");
1989       int ret =
1990           DVGui::MsgBox(question, turnOffPixelAnswer, resizeSceneAnswer, 0);
1991       if (ret == 0) {
1992       }                     // do nothing
1993       else if (ret == 1) {  // Turn off pixels only mode
1994         Preferences::instance()->setValue(pixelsOnly, false);
1995         app->getCurrentScene()->notifyPixelUnitSelected(false);
1996       } else {  // ret = 2 : Resize the scene
1997         TDimensionD camSize = scene->getCurrentCamera()->getSize();
1998         TDimension camRes(camSize.lx * Stage::standardDpi,
1999                           camSize.ly * Stage::standardDpi);
2000         scene->getCurrentCamera()->setRes(camRes);
2001         app->getCurrentScene()->setDirtyFlag(true);
2002         app->getCurrentXsheet()->notifyXsheetChanged();
2003       }
2004     }
2005   }
2006 
2007   // Check if the scene saved with the previous version AND the premultiply
2008   // option is set to PNG level setting
2009   if (scene->getVersionNumber() <
2010       VersionNumber(71, 1)) {  // V1.4 = 71.0 , V1.5 = 71.1
2011     QStringList modifiedPNGLevelNames;
2012     std::vector<TXshLevel *> levels;
2013     scene->getLevelSet()->listLevels(levels);
2014     for (auto level : levels) {
2015       if (!level || !level->getSimpleLevel()) continue;
2016       TFilePath path = level->getPath();
2017       if (path.isEmpty() || path.getType() != "png") continue;
2018       if (level->getSimpleLevel()->getProperties()->doPremultiply()) {
2019         level->getSimpleLevel()->getProperties()->setDoPremultiply(false);
2020         modifiedPNGLevelNames.append(QString::fromStdWString(level->getName()));
2021       }
2022     }
2023     if (!modifiedPNGLevelNames.isEmpty()) {
2024       DVGui::info(QObject::tr("The Premultiply options in the following levels "
2025                               "are disabled, since PNG files are premultiplied "
2026                               "on loading in the current version: %1")
2027                       .arg(modifiedPNGLevelNames.join(", ")));
2028       app->getCurrentScene()->setDirtyFlag(true);
2029     }
2030   }
2031 
2032   printf("%s:%s loadScene() completed :\n", __FILE__, __FUNCTION__);
2033   return true;
2034 }
2035 
2036 //===========================================================================
2037 // IoCmd::loadScene()
2038 //---------------------------------------------------------------------------
2039 
loadScene()2040 bool IoCmd::loadScene() {
2041   TSelection *oldSelection =
2042       TApp::instance()->getCurrentSelection()->getSelection();
2043   FileSelection *fileSelection =
2044       dynamic_cast<FileSelection *>(TSelection::getCurrent());
2045   if (fileSelection) {
2046     std::vector<TFilePath> files;
2047     fileSelection->getSelectedFiles(files);
2048     if (files.size() == 1 && files[0] != TFilePath() &&
2049         files[0].getType() == "tnz")
2050       return loadScene(files[0]);
2051   }
2052 
2053   static LoadScenePopup *popup = 0;
2054   if (!popup) {
2055     popup = new LoadScenePopup();
2056   }
2057   int ret = popup->exec();
2058   if (ret == QDialog::Accepted) {
2059     return true;
2060   } else {
2061     TApp::instance()->getCurrentSelection()->setSelection(oldSelection);
2062     return false;
2063   }
2064 }
2065 //===========================================================================
2066 // IoCmd::loadSubScene()
2067 //---------------------------------------------------------------------------
2068 
loadSubScene()2069 bool IoCmd::loadSubScene() {
2070   TSelection *oldSelection =
2071       TApp::instance()->getCurrentSelection()->getSelection();
2072   FileSelection *fileSelection =
2073       dynamic_cast<FileSelection *>(TSelection::getCurrent());
2074 
2075   if (fileSelection) {
2076     IoCmd::LoadResourceArguments args;
2077     {
2078       std::vector<TFilePath> filePaths;
2079       fileSelection->getSelectedFiles(filePaths);
2080 
2081       args.resourceDatas.assign(filePaths.begin(), filePaths.end());
2082     }
2083 
2084     if (args.resourceDatas.size() == 1 &&
2085         args.resourceDatas[0].m_path != TFilePath() &&
2086         args.resourceDatas[0].m_path.getType() == "tnz") {
2087       loadResources(args);
2088       return true;
2089     }
2090   } else {
2091     static LoadSubScenePopup *popup = 0;
2092     if (!popup) {
2093       popup = new LoadSubScenePopup();
2094       popup->addFilterType("tnz");
2095     }
2096     int ret = popup->exec();
2097     if (ret == QDialog::Accepted) {
2098       TApp::instance()->getCurrentScene()->setDirtyFlag(false);
2099       return true;
2100     } else {
2101       TApp::instance()->getCurrentSelection()->setSelection(oldSelection);
2102       return false;
2103     }
2104   }
2105   return false;
2106 }
2107 
loadSubScene(const TFilePath & scenePath)2108 bool IoCmd::loadSubScene(const TFilePath &scenePath) {
2109   IoCmd::LoadResourceArguments args(scenePath);
2110 
2111   if (args.resourceDatas.size() == 1 &&
2112       args.resourceDatas[0].m_path != TFilePath() &&
2113       args.resourceDatas[0].m_path.getType() == "tnz") {
2114     loadResources(args);
2115     return true;
2116   }
2117 
2118   return false;
2119 }
2120 
2121 std::vector<int>
2122     loadedPsdLevelIndex;  // memorizza gli indici dei livelli già caricati
2123                           // serve per identificare i subfolder caricati
2124                           // Trovare un metodo alternativo.
2125 
2126 //! Returns the number of actually loaded levels
createSubXSheetFromPSDFolder(IoCmd::LoadResourceArguments & args,TXsheet * xsh,int & col0,int psdLevelIndex,PsdSettingsPopup * popup)2127 static int createSubXSheetFromPSDFolder(IoCmd::LoadResourceArguments &args,
2128                                         TXsheet *xsh, int &col0,
2129                                         int psdLevelIndex,
2130                                         PsdSettingsPopup *popup) {
2131   assert(popup->isFolder(psdLevelIndex));
2132 
2133   int row0  = 0;
2134   int &row1 = args.row1, &col1 = args.col1;
2135 
2136   TApp *app         = TApp::instance();
2137   ToonzScene *scene = app->getCurrentScene()->getScene();
2138 
2139   TXshLevel *cl = scene->createNewLevel(CHILD_XSHLEVEL);
2140   assert(cl);
2141 
2142   TXshChildLevel *childLevel = cl->getChildLevel();
2143   assert(childLevel);
2144 
2145   TXsheet *childXsh = childLevel->getXsheet();
2146 
2147   int count = 0, subCol0 = 0;
2148   for (int i = 0; i < popup->getFramesCount(psdLevelIndex); ++i) {
2149     if (popup->isSubFolder(psdLevelIndex, i)) {
2150       // se è un subfolder allora è un livello
2151       int levelIndex = popup->getSubfolderLevelIndex(psdLevelIndex, i);
2152       count += createSubXSheetFromPSDFolder(args, childXsh, subCol0, levelIndex,
2153                                             popup);
2154     } else {
2155       TFilePath psdpath = popup->getPsdFramePath(psdLevelIndex, i);
2156       TXshLevel *xl     = 0;
2157       try {
2158         xl = ::loadResource(scene, psdpath, args.castFolder, row0, col0, row1,
2159                             col1, false);
2160       } catch (TException &e) {
2161         error(QString::fromStdWString(e.getMessage()));
2162       }
2163       if (xl) {
2164         // lo importo nell'xsheet
2165         childXsh->exposeLevel(0, subCol0, xl);
2166         args.loadedLevels.push_back(xl);
2167 
2168         ++subCol0, ++count;
2169         loadedPsdLevelIndex.push_back(psdLevelIndex);
2170       }
2171     }
2172   }
2173   if (childXsh) {
2174     int rowCount = childXsh->getFrameCount();
2175     int r;
2176     for (r = 0; r < rowCount; r++)
2177       xsh->setCell(r, col0, TXshCell(cl, TFrameId(r + 1)));
2178     col0++;
2179   }
2180   return count;
2181 }
2182 
2183 //  Load a psd file
2184 //! Returns the number of actually loaded levels
loadPSDResource(IoCmd::LoadResourceArguments & args,bool updateRecentFile,PsdSettingsPopup * popup)2185 static int loadPSDResource(IoCmd::LoadResourceArguments &args,
2186                            bool updateRecentFile, PsdSettingsPopup *popup) {
2187   int &row0 = args.row0;
2188   int &col0 = args.col0;
2189   int &row1 = args.row1;
2190   int &col1 = args.col1;
2191 
2192   int count         = 0;
2193   TApp *app         = TApp::instance();
2194   ToonzScene *scene = app->getCurrentScene()->getScene();
2195   TXsheet *xsh      = scene->getXsheet();
2196   if (row0 == -1) row0 = app->getCurrentFrame()->getFrameIndex();
2197   if (col0 == -1) col0 = app->getCurrentColumn()->getColumnIndex();
2198 
2199   TXshLevel *cl     = 0;
2200   TXsheet *childXsh = 0;
2201   // if the option "expose in sub-xsheet" is ON"
2202   if (popup->subxsheet()) {
2203     cl = scene->createNewLevel(CHILD_XSHLEVEL);
2204     assert(cl);
2205     TXshChildLevel *childLevel = cl->getChildLevel();
2206     assert(childLevel);
2207     childXsh = childLevel->getXsheet();
2208   }
2209   int subCol0 = args.col0;
2210   loadedPsdLevelIndex.clear();
2211   // for each layer in psd
2212   for (int i = 0; i < popup->getPsdLevelCount(); i++) {
2213     if (popup->isFolder(i) && popup->getFolderOption() == 1) {
2214       if (find(loadedPsdLevelIndex.begin(), loadedPsdLevelIndex.end(), i) !=
2215           loadedPsdLevelIndex.end())
2216         continue;
2217       if (childXsh)
2218         count +=
2219             createSubXSheetFromPSDFolder(args, childXsh, subCol0, i, popup);
2220       else
2221         count += createSubXSheetFromPSDFolder(args, xsh, subCol0, i, popup);
2222     } else {
2223       TFilePath psdpath = popup->getPsdPath(i);
2224       TXshLevel *xl     = 0;
2225 
2226       try {
2227         xl = ::loadResource(scene, psdpath, args.castFolder, row0, col0, row1,
2228                             col1, !popup->subxsheet());
2229       } catch (TException &e) {
2230         error(QString::fromStdWString(e.getMessage()));
2231       }
2232       if (xl) {
2233         // lo importo nell'xsheet
2234         if (popup->subxsheet() && childXsh) {
2235           childXsh->exposeLevel(0, subCol0, xl);
2236         }
2237         args.loadedLevels.push_back(xl);
2238         subCol0++;
2239         count++;
2240 
2241         // move the current column to the right
2242         col0++;
2243         app->getCurrentColumn()->setColumnIndex(col0);
2244       }
2245     }
2246   }
2247   if (childXsh) {
2248     int rowCount = childXsh->getFrameCount();
2249     int r;
2250     for (r = 0; r < rowCount; r++)
2251       xsh->setCell(row0 + r, col0, TXshCell(cl, TFrameId(r + 1)));
2252   }
2253 
2254   return count;
2255 }
2256 
2257 //===========================================================================
2258 // IoCmd::loadResources(actualPaths[], castFolder, row, col)
2259 //---------------------------------------------------------------------------
2260 
2261 typedef IoCmd::LoadResourceArguments::ScopedBlock LoadScopedBlock;
2262 
2263 struct LoadScopedBlock::Data {
2264   std::unique_ptr<DVGui::ProgressDialog>
2265       m_progressDialog;  //!< Progress dialog displayed on multiple paths.
2266   int m_loadedCount;     //!< Number of loaded levels.
2267   bool m_hasSoundLevel;  //!< Whether a sound level was loaded.
2268 
2269 public:
DataLoadScopedBlock::Data2270   Data() : m_loadedCount(), m_hasSoundLevel() {}
2271 };
2272 
2273 //-----------------------------------------------------------------------------
2274 
ScopedBlock()2275 LoadScopedBlock::ScopedBlock() : m_data(new Data) {}
2276 
2277 //-----------------------------------------------------------------------------
2278 
~ScopedBlock()2279 LoadScopedBlock::~ScopedBlock() {
2280   if (m_data->m_loadedCount > 0) {
2281     TApp *app = TApp::instance();
2282 
2283     app->getCurrentXsheet()->notifyXsheetChanged();
2284     if (m_data->m_hasSoundLevel)
2285       app->getCurrentXsheet()->notifyXsheetSoundChanged();
2286     app->getCurrentScene()->notifyCastChange();
2287     app->getCurrentScene()->setDirtyFlag(true);
2288     app->getCurrentTool()->onImageChanged(
2289         (TImage::Type)app->getCurrentImageType());
2290   }
2291 }
2292 
2293 //-----------------------------------------------------------------------------
2294 
progressDialog() const2295 DVGui::ProgressDialog &LoadScopedBlock::progressDialog() const {
2296   if (!m_data->m_progressDialog.get()) {
2297     m_data->m_progressDialog.reset(
2298         new DVGui::ProgressDialog("", QObject::tr("Cancel"), 0, 0));
2299   }
2300 
2301   return *m_data->m_progressDialog;
2302 }
2303 
2304 //=============================================================================
2305 
loadResources(LoadResourceArguments & args,bool updateRecentFile,LoadScopedBlock * sb)2306 int IoCmd::loadResources(LoadResourceArguments &args, bool updateRecentFile,
2307                          LoadScopedBlock *sb) {
2308   struct locals {
2309     static bool isDir(const LoadResourceArguments::ResourceData &rd) {
2310       return QFileInfo(rd.m_path.getQString()).isDir();
2311     }
2312   };  // locals
2313 
2314   if (args.resourceDatas.empty()) return 0;
2315 
2316   // Redirect to resource folders loading in case they're all dirs
2317   if (std::all_of(args.resourceDatas.begin(), args.resourceDatas.end(),
2318                   locals::isDir))
2319     return loadResourceFolders(args, sb);
2320 
2321   boost::optional<LoadScopedBlock> sb_;
2322   if (!sb) sb = (sb_ = boost::in_place()).get_ptr();
2323 
2324   int &row0 = args.row0, &col0 = args.col0, &row1 = args.row1,
2325       &col1 = args.col1;
2326 
2327   // Setup local variables
2328   TApp *app         = TApp::instance();
2329   ToonzScene *scene = app->getCurrentScene()->getScene();
2330   TXsheet *xsh      = scene->getXsheet();
2331   // use current frame/column if row/col is not set
2332   if (row0 == -1) row0 = app->getCurrentFrame()->getFrameIndex();
2333   if (col0 == -1) col0 = app->getCurrentColumn()->getColumnIndex();
2334 
2335   int rCount = args.resourceDatas.size(), loadedCount = 0;
2336   bool isSoundLevel = false;
2337 
2338   // show wait cursor in case of caching all images because it is time consuming
2339   if (args.cachingBehavior == LoadResourceArguments::ALL_ICONS_AND_IMAGES)
2340     QApplication::setOverrideCursor(Qt::WaitCursor);
2341 
2342   // Initialize progress dialog
2343   DVGui::ProgressDialog *progressDialog =
2344       (rCount > 1) ? &sb->progressDialog() : 0;
2345 
2346   if (progressDialog) {
2347     progressDialog->setModal(true);
2348     progressDialog->setMinimum(0);
2349     progressDialog->setMaximum(rCount);
2350     progressDialog->show();
2351   }
2352 
2353   // Initialize import dialog
2354   ResourceImportDialog importDialog;
2355   if (args.importPolicy != LoadResourceArguments::ASK_USER) {
2356     importDialog.setImportEnabled(args.importPolicy ==
2357                                   LoadResourceArguments::IMPORT);
2358   }
2359 
2360   std::vector<TFilePath> paths;
2361   int all = 0;
2362 
2363   // Loop for all the resources to load
2364   for (int r = 0; r != rCount; ++r) {
2365     if (importDialog.aborted()) break;
2366 
2367     QString origName =
2368         args.resourceDatas[r].m_path.withoutParentDir().getQString();
2369 
2370     LoadResourceArguments::ResourceData rd(args.resourceDatas[r]);
2371     TFilePath &path = rd.m_path;
2372 
2373     if (!rd.m_path.isLevelName())
2374       path = TFilePath(path.getLevelNameW()).withParentDir(path.getParentDir());
2375 
2376     if (std::find(paths.begin(), paths.end(), path) != paths.end()) {
2377       if (!all) {
2378         QString question =
2379             QObject::tr(
2380                 "File '%1' will reload level '%2' as a duplicate column in the "
2381                 "xsheet.\n\nAllow duplicate?")
2382                 .arg(origName)
2383                 .arg(QString::fromStdString(path.getName()));
2384         QString Yes    = QObject::tr("Allow");
2385         QString YesAll = QObject::tr("Allow All Dups");
2386         QString No     = QObject::tr("No");
2387         QString NoAll  = QObject::tr("No to All Dups");
2388         int ret        = DVGui::MsgBox(question, Yes, YesAll, No, NoAll, 0);
2389         switch (ret) {
2390         case 2:
2391           all = 1;  // YesAll
2392         case 1:
2393           break;  // Yes
2394         case 4:
2395           all = 2;  // NoAll
2396         case 3:
2397           continue;
2398         }
2399       } else if (all == 2)
2400         continue;
2401     }
2402     paths.push_back(path);
2403 
2404     if (progressDialog) {
2405       if (progressDialog->wasCanceled())
2406         break;
2407       else {
2408         progressDialog->setLabelText(
2409             DVGui::ProgressDialog::tr("Loading \"%1\"...")
2410                 .arg(path.getQString()));
2411         progressDialog->setValue(r);
2412 
2413         QCoreApplication::processEvents();
2414       }
2415     }
2416 
2417     bool isLastResource = (r == rCount - 1);
2418     importDialog.setIsLastResource(isLastResource);
2419 
2420     bool isScene = (TFileType::getInfo(path) == TFileType::TOONZSCENE);
2421 
2422     if (scene->isExternPath(path) || isScene) {
2423       // extern resource: import or link?
2424       int ret = importDialog.askImportQuestion(path);
2425       if (ret == ResourceImportDialog::A_CANCEL) break;
2426     }
2427 
2428     // for the scene file
2429     TXshLevel *xl = 0;
2430     if (isScene) {
2431       TFilePath oldDstFolder = importDialog.getDstFolder();
2432       TFilePath dstFolder = (Preferences::instance()->isSubsceneFolderEnabled())
2433                                 ? TFilePath(path.getName())
2434                                 : TFilePath();
2435 
2436       importDialog.setDstFolder(dstFolder);
2437       importDialog.setIsLastResource(false);
2438 
2439       // load the scene as subXsheet
2440       try {
2441         xl = loadChildLevel(scene, path, row0, col0, importDialog);
2442         if (dstFolder != TFilePath())
2443           app->getCurrentScene()->notifyCastFolderAdded(
2444               scene->getLevelSet()->getDefaultFolder() + dstFolder);
2445         // increment the number of resources actually loaded
2446         ++loadedCount;
2447         // move the column to the right
2448         ++col0;
2449         // register the loaded level to args
2450         args.loadedLevels.push_back(xl);
2451         app->getCurrentXsheet()->notifyXsheetSoundChanged();
2452       } catch (...) {
2453       }
2454 
2455       importDialog.setIsLastResource(isLastResource);
2456       importDialog.setDstFolder(oldDstFolder);
2457 
2458       app->getCurrentColumn()->setColumnIndex(col0);
2459 
2460       continue;
2461     }
2462     // for other level files
2463     else {
2464       try {
2465         path = importDialog.process(scene, 0, path);
2466         // path = scene->decodeFilePath(codedPath);
2467       } catch (std::string msg) {
2468         error(QString::fromStdString(msg));
2469         continue;
2470       }
2471 
2472       if (importDialog.aborted()) break;
2473     }
2474     if (path.getType() == "psd") {
2475       static PsdSettingsPopup *popup = 0;
2476       if (!popup) {
2477         popup = new PsdSettingsPopup();
2478       }
2479       popup->setPath(path);
2480 
2481       int ret = popup->exec();
2482       if (ret == 0) continue;
2483 
2484       loadedCount += loadPSDResource(args, updateRecentFile, popup);
2485 
2486       if (updateRecentFile)
2487         RecentFiles::instance()->addFilePath(
2488             toQString(scene->decodeFilePath(path)), RecentFiles::Level);
2489     } else {
2490       // reuse TFrameIds retrieved by FileBrowser
2491       std::vector<TFrameId> fIds;
2492       if ((int)args.frameIdsSet.size() > r)  // if there is fIds to be reused
2493       {
2494         fIds = args.frameIdsSet[r];
2495       }
2496 
2497       try {
2498         xl = ::loadResource(
2499             scene, rd, args.castFolder, row0, col0, row1, col1, args.expose,
2500 #if (__cplusplus > 199711L)
2501             std::move(fIds),
2502 #else
2503             fIds,
2504 #endif
2505             args.xFrom, args.xTo, args.levelName, args.step, args.inc,
2506             args.frameCount, args.doesFileActuallyExist);
2507         if (updateRecentFile) {
2508           RecentFiles::instance()->addFilePath(
2509               toQString(scene->decodeFilePath(path)), RecentFiles::Level);
2510         }
2511       } catch (TException &e) {
2512         error(QString::fromStdWString(e.getMessage()));
2513       }
2514       // if load success
2515       if (xl) {
2516         isSoundLevel = isSoundLevel || xl->getType() == SND_XSHLEVEL;
2517         // register the loaded level to args
2518         args.loadedLevels.push_back(xl);
2519         // increment the number of loaded resources
2520         ++loadedCount;
2521 
2522         // load the image data of all frames to cache at the beginning
2523         if (args.cachingBehavior != LoadResourceArguments::ON_DEMAND) {
2524           TXshSimpleLevel *simpleLevel = xl->getSimpleLevel();
2525           if (simpleLevel && simpleLevel->getType() == TZP_XSHLEVEL) {
2526             bool cacheImagesAsWell =
2527                 (args.cachingBehavior ==
2528                  LoadResourceArguments::ALL_ICONS_AND_IMAGES);
2529             simpleLevel->loadAllIconsAndPutInCache(cacheImagesAsWell);
2530           }
2531         }
2532       }
2533     }
2534   }
2535 
2536   if (loadedCount) app->getCurrentFrame()->setFrameIndex(row0);
2537 
2538   sb->data().m_loadedCount += loadedCount;
2539   sb->data().m_hasSoundLevel = sb->data().m_hasSoundLevel || isSoundLevel;
2540 
2541   // revert the cursor
2542   if (args.cachingBehavior == LoadResourceArguments::ALL_ICONS_AND_IMAGES)
2543     QApplication::restoreOverrideCursor();
2544 
2545   return loadedCount;
2546 }
2547 
2548 //===========================================================================
2549 // IoCmd::exposeLevel(simpleLevel, row, col)
2550 //---------------------------------------------------------------------------
2551 
exposeLevel(TXshSimpleLevel * sl,int row,int col,bool insert,bool overWrite)2552 bool IoCmd::exposeLevel(TXshSimpleLevel *sl, int row, int col, bool insert,
2553                         bool overWrite) {
2554   TApp *app    = TApp::instance();
2555   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2556   assert(xsh);
2557   std::vector<TFrameId> fids;
2558   sl->getFids(fids);
2559   return exposeLevel(sl, row, col, fids, insert, overWrite);
2560 }
2561 
2562 //===========================================================================
2563 // IoCmd::exposeLevel(simpleLevel, row, col)
2564 //---------------------------------------------------------------------------
2565 
exposeLevel(TXshSimpleLevel * sl,int row,int col,const std::vector<TFrameId> & fids,bool insert,bool overWrite)2566 bool IoCmd::exposeLevel(TXshSimpleLevel *sl, int row, int col,
2567                         const std::vector<TFrameId> &fids, bool insert,
2568                         bool overWrite) {
2569   assert(sl);
2570 
2571   TApp *app    = TApp::instance();
2572   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2573   assert(xsh);
2574   int frameCount         = fids.size();
2575   bool insertEmptyColumn = false;
2576   if (!insert && !overWrite)
2577     insertEmptyColumn = beforeCellsInsert(
2578         xsh, row, col, fids.size(), TXshColumn::toColumnType(sl->getType()));
2579   ExposeType type = eNone;
2580   if (insert) type = eShiftCells;
2581   if (overWrite) type = eOverWrite;
2582   ExposeLevelUndo *undo =
2583       new ExposeLevelUndo(sl, row, col, frameCount, insertEmptyColumn, type);
2584   xsh->exposeLevel(row, col, sl, fids, overWrite);
2585   undo->setFids(fids);
2586   TUndoManager::manager()->add(undo);
2587   app->getCurrentXsheet()->notifyXsheetChanged();
2588   return true;
2589 }
2590 
2591 //===========================================================================
2592 // exposeComment
2593 //---------------------------------------------------------------------------
2594 
exposeComment(int row,int & col,QList<QString> commentList,QString fileName)2595 bool IoCmd::exposeComment(int row, int &col, QList<QString> commentList,
2596                           QString fileName) {
2597   TApp *app                         = TApp::instance();
2598   TXsheet *xsh                      = app->getCurrentXsheet()->getXsheet();
2599   TXshSoundTextColumn *textSoundCol = new TXshSoundTextColumn();
2600   textSoundCol->setXsheet(xsh);
2601   textSoundCol->createSoundTextLevel(row, commentList);
2602   bool columnInserted = beforeCellsInsert(xsh, row, col, commentList.size(),
2603                                           TXshColumn::eSoundTextType);
2604   xsh->insertColumn(col, textSoundCol);
2605 
2606   // Setto il nome di dafult della colonna al nome del file magpie.
2607   TStageObject *obj = xsh->getStageObject(TStageObjectId::ColumnId(col));
2608   std::string str   = fileName.toStdString();
2609   obj->setName(str);
2610 
2611   TUndoManager::manager()->add(
2612       new ExposeCommentUndo(textSoundCol, col, columnInserted, fileName));
2613 
2614   return true;
2615 }
2616 
2617 //===========================================================================
2618 // importLipSync
2619 //---------------------------------------------------------------------------
2620 
importLipSync(TFilePath levelPath,QList<TFrameId> frameList,QList<QString> commentList,QString fileName)2621 bool IoCmd::importLipSync(TFilePath levelPath, QList<TFrameId> frameList,
2622                           QList<QString> commentList, QString fileName) {
2623   TApp *app = TApp::instance();
2624   int col   = app->getCurrentColumn()->getColumnIndex();
2625   int row   = app->getCurrentFrame()->getFrameIndex();
2626 
2627   IoCmd::LoadResourceArguments args;
2628   IoCmd::LoadResourceArguments::ScopedBlock sb;
2629 
2630   // Create text column
2631   IoCmd::exposeComment(row, col, commentList, fileName);
2632 
2633   // Load Level
2634   args.resourceDatas.push_back(levelPath);
2635   args.expose = false;
2636 
2637   if (!IoCmd::loadResources(args, false, &sb)) {
2638     DVGui::error(QObject::tr("It is not possible to load the level %1")
2639                      .arg(toQString(levelPath)));
2640     return false;
2641   }
2642 
2643   // Expose Level in xsheet
2644   assert(args.loadedLevels.size() == 1);
2645   TXshLevel *loadedLevel = args.loadedLevels.at(0);
2646 
2647   std::vector<TFrameId> fids;
2648   for (int i = 0; i < frameList.size(); i++) fids.push_back(frameList.at(i));
2649 
2650   if (!IoCmd::exposeLevel(loadedLevel->getSimpleLevel(), row, col, fids)) {
2651     DVGui::error(QObject::tr("It is not possible to load the level %1")
2652                      .arg(toQString(levelPath)));
2653     return false;
2654   }
2655 
2656   return true;
2657 }
2658 
2659 //===========================================================================
2660 // If the scene will be saved in the different folder, check out the scene
2661 // cast.
2662 // if the cast contains the level specified with $scenefolder alias,
2663 // open a warning popup notifying that such level will lose link.
2664 // return false if cancelled.
takeCareSceneFolderItemsOnSaveSceneAs(ToonzScene * scene,const TFilePath & newPath,TXsheet * subxsh,QHash<TXshLevel *,TFilePath> & orgLevelPaths)2665 bool IoCmd::takeCareSceneFolderItemsOnSaveSceneAs(
2666     ToonzScene *scene, const TFilePath &newPath, TXsheet *subxsh,
2667     QHash<TXshLevel *, TFilePath> &orgLevelPaths) {
2668   auto setPathToLevel = [&](TXshLevel *level, TFilePath fp) {
2669     // in case of saving subxsheet, the current scene will not be switched to
2670     // the saved one
2671     // so the level paths are needed to be reverted after saving
2672     if (subxsh) orgLevelPaths.insert(level, level->getPath());
2673     if (TXshSimpleLevel *sil = level->getSimpleLevel())
2674       sil->setPath(fp, true);
2675     else if (TXshPaletteLevel *pal = level->getPaletteLevel())
2676       pal->setPath(fp);
2677     else if (TXshSoundLevel *sol = level->getSoundLevel())
2678       sol->setPath(fp);
2679   };
2680 
2681   TFilePath oldSceneFolder =
2682       scene->decodeFilePath(scene->getScenePath()).getParentDir();
2683   TFilePath newSceneFolder = scene->decodeFilePath(newPath).getParentDir();
2684 
2685   // in case of saving in the same folder
2686   if (oldSceneFolder == newSceneFolder) return true;
2687 
2688   TLevelSet *levelSet = scene->getLevelSet();
2689   std::vector<TXshLevel *> levels;
2690 
2691   // in case of saving subxsheet, checking only used levels.
2692   if (subxsh) {
2693     std::set<TXshLevel *> saveSet;
2694     subxsh->getUsedLevels(saveSet);
2695     levels = std::vector<TXshLevel *>(saveSet.begin(), saveSet.end());
2696   }
2697   // in case of saving the scene (i.e. top xsheet)
2698   else
2699     levelSet->listLevels(levels);
2700 
2701   QList<TXshLevel *> sceneFolderLevels;
2702   QString str;
2703   int count = 0;
2704   for (TXshLevel *level : levels) {
2705     if (!level->getPath().isEmpty() &&
2706         TFilePath("$scenefolder").isAncestorOf(level->getPath())) {
2707       TFilePath levelFullPath = scene->decodeFilePath(level->getPath());
2708       // check if the path can be re-coded with the new scene folder path
2709       if (newSceneFolder.isAncestorOf(levelFullPath)) {
2710         // just replace the path without warning
2711         TFilePath fp =
2712             TFilePath("$scenefolder") + (levelFullPath - newSceneFolder);
2713         setPathToLevel(level, fp);
2714       }
2715       // if re-coding is not possible, then it needs to ask user's preference
2716       else {
2717         sceneFolderLevels.append(level);
2718         if (count < 10) {
2719           str.append("    " + QString::fromStdWString(level->getName()) + " (" +
2720                      level->getPath().getQString() + ")\n");
2721         }
2722         count++;
2723       }
2724     }
2725   }
2726   // list maximum 10 levels
2727   if (count > 10)
2728     str.append(QObject::tr("    + %1 more level(s) \n").arg(count - 10));
2729 
2730   // in case there is no items with $scenefolder
2731   if (sceneFolderLevels.isEmpty()) return true;
2732 
2733   str = QObject::tr(
2734             "The following level(s) use path with $scenefolder alias.\n\n") +
2735         str +
2736         QObject::tr(
2737             "\nThey will not be opened properly when you load the "
2738             "scene next time.\nWhat do you want to do?");
2739 
2740   int ret = DVGui::MsgBox(
2741       str, QObject::tr("Copy the levels to correspondent paths"),
2742       QObject::tr("Decode all $scenefolder aliases"),
2743       QObject::tr("Save the scene only"), QObject::tr("Cancel"), 0);
2744   if (ret == 4 || ret == 0) return false;
2745 
2746   if (ret == 1) {  // copy the levels case
2747     enum OVERWRITEPOLICY { ASK, YES_FOR_ALL, NO_FOR_ALL } policy = ASK;
2748     for (int i = 0; i < sceneFolderLevels.size(); i++) {
2749       TXshLevel *level = sceneFolderLevels.at(i);
2750       TFilePath fp     = level->getPath() - TFilePath("$scenefolder");
2751       fp               = fp.withParentDir(newSceneFolder);
2752       // check the level existence
2753       if (TSystem::doesExistFileOrLevel(fp)) {
2754         bool overwrite = (policy == YES_FOR_ALL);
2755         if (policy == ASK) {
2756           QString question =
2757               QObject::tr(
2758                   "File %1 already exists.\nDo you want to overwrite it?")
2759                   .arg(fp.getQString());
2760           int ret_overwrite = DVGui::MsgBox(
2761               question, QObject::tr("Overwrite"),
2762               QObject::tr("Overwrite for All"), QObject::tr("Don't Overwrite"),
2763               QObject::tr("Don't Overwrite for All"), 0);
2764           if (ret_overwrite == 0) return false;
2765           if (ret_overwrite == 1)
2766             overwrite = true;
2767           else if (ret_overwrite == 2) {
2768             overwrite = true;
2769             policy    = YES_FOR_ALL;
2770           } else if (ret_overwrite == 4)
2771             policy = NO_FOR_ALL;
2772         }
2773         if (!overwrite) continue;
2774       }
2775 
2776       TFilePath srcFp = scene->decodeFilePath(level->getPath());
2777       if (!TSystem::copyFileOrLevel(fp, srcFp))
2778         warning(QObject::tr("Failed to overwrite %1").arg(fp.getQString()));
2779       // copy the palette as well
2780       if (level->getType() == TZP_XSHLEVEL) {
2781         if (!TSystem::copyFileOrLevel(fp.withType("tpl"),
2782                                       srcFp.withType("tpl")))
2783           warning(QObject::tr("Failed to overwrite %1")
2784                       .arg(fp.withType("tpl").getQString()));
2785       }
2786     }
2787   } else if (ret == 2) {  // decode $scenefolder aliases case
2788     Preferences::PathAliasPriority oldPriority =
2789         Preferences::instance()->getPathAliasPriority();
2790     Preferences::instance()->setValue(pathAliasPriority,
2791                                       Preferences::ProjectFolderOnly);
2792     for (int i = 0; i < sceneFolderLevels.size(); i++) {
2793       TXshLevel *level = sceneFolderLevels.at(i);
2794 
2795       // decode and code again
2796       TFilePath fp =
2797           scene->codeFilePath(scene->decodeFilePath(level->getPath()));
2798       setPathToLevel(level, fp);
2799     }
2800     Preferences::instance()->setValue(pathAliasPriority, oldPriority);
2801   }
2802 
2803   // Save the scene only case (ret == 3), do nothing
2804 
2805   return true;
2806 }
2807 
2808 //===========================================================================
2809 // Commands
2810 //---------------------------------------------------------------------------
2811 
2812 class SaveSceneCommandHandler final : public MenuItemHandler {
2813 public:
SaveSceneCommandHandler()2814   SaveSceneCommandHandler() : MenuItemHandler(MI_SaveScene) {}
execute()2815   void execute() override { IoCmd::saveScene(); }
2816 } saveSceneCommandHandler;
2817 
2818 //---------------------------------------------------------------------------
2819 
2820 class SaveLevelCommandHandler final : public MenuItemHandler {
2821 public:
SaveLevelCommandHandler()2822   SaveLevelCommandHandler() : MenuItemHandler(MI_SaveLevel) {}
execute()2823   void execute() override {
2824     TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
2825     if (!sl) {
2826       DVGui::warning(QObject::tr("No Current Level"));
2827       return;
2828     }
2829     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2830     if (!scene) {
2831       DVGui::warning(QObject::tr("No Current Scene"));
2832       return;  // non dovrebbe succedere mai
2833     }
2834     TFilePath levelPath = sl->getPath();
2835     levelPath           = scene->decodeFilePath(levelPath);
2836     QString str         = QString::fromStdWString(levelPath.getWideString());
2837     if (!(sl->getPath().isAbsolute() || !scene->isUntitled() ||
2838           (!sl->getPath().isAbsolute() && !str.contains("untitled")))) {
2839       error(QObject::tr("Save the scene first"));
2840       return;
2841     }
2842 
2843     // reset the undo before save level
2844     if (Preferences::instance()->getBoolValue(resetUndoOnSavingLevel))
2845       TUndoManager::manager()->reset();
2846 
2847     if (!IoCmd::saveLevel()) error(QObject::tr("Save level Failed"));
2848   }
2849 } saveLevelCommandHandler;
2850 
2851 //-----------------------------------------------------------------------------
2852 
2853 class SaveProjectTemplate final : public MenuItemHandler {
2854 public:
SaveProjectTemplate()2855   SaveProjectTemplate() : MenuItemHandler(MI_SaveDefaultSettings) {}
execute()2856   void execute() override {
2857     QString question;
2858     question =
2859         QObject::tr("Are you sure you want to save the Default Settings?");
2860     int ret =
2861         DVGui::MsgBox(question, QObject::tr("Save"), QObject::tr("Cancel"), 0);
2862     if (ret == 2 || ret == 0) return;
2863     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2864     try {
2865       TProjectManager::instance()->saveTemplate(scene);
2866     } catch (TSystemException se) {
2867       DVGui::warning(QString::fromStdWString(se.getMessage()));
2868       return;
2869     }
2870   }
2871 } saveProjectTemplate;
2872 
2873 //-----------------------------------------------------------------------------
2874 
2875 class OpenRecentSceneFileCommandHandler final : public MenuItemHandler {
2876 public:
OpenRecentSceneFileCommandHandler()2877   OpenRecentSceneFileCommandHandler() : MenuItemHandler(MI_OpenRecentScene) {}
execute()2878   void execute() override {
2879     QAction *act = CommandManager::instance()->getAction(MI_OpenRecentScene);
2880     DVMenuAction *menu = dynamic_cast<DVMenuAction *>(act->menu());
2881     int index          = menu->getTriggeredActionIndex();
2882     QString path =
2883         RecentFiles::instance()->getFilePath(index, RecentFiles::Scene);
2884     IoCmd::loadScene(TFilePath(path.toStdWString()), false);
2885     RecentFiles::instance()->moveFilePath(index, 0, RecentFiles::Scene);
2886   }
2887 } openRecentSceneFileCommandHandler;
2888 
2889 //-----------------------------------------------------------------------------
2890 
2891 class OpenRecentLevelFileCommandHandler final : public MenuItemHandler {
2892 public:
OpenRecentLevelFileCommandHandler()2893   OpenRecentLevelFileCommandHandler() : MenuItemHandler(MI_OpenRecentLevel) {}
execute()2894   void execute() override {
2895     QAction *act = CommandManager::instance()->getAction(MI_OpenRecentLevel);
2896     DVMenuAction *menu = dynamic_cast<DVMenuAction *>(act->menu());
2897     int index          = menu->getTriggeredActionIndex();
2898     QString path =
2899         RecentFiles::instance()->getFilePath(index, RecentFiles::Level);
2900     IoCmd::LoadResourceArguments args(TFilePath(path.toStdWString()));
2901     IoCmd::loadResources(args, false);
2902 
2903     RecentFiles::instance()->moveFilePath(index, 0, RecentFiles::Level);
2904     if (args.loadedLevels.empty()) {
2905       QString msg;
2906       msg = QObject::tr("It is not possible to load the %1 level.").arg(path);
2907       DVGui::error(msg);
2908     }
2909   }
2910 } openRecentLevelFileCommandHandler;
2911 
2912 //-----------------------------------------------------------------------------
2913 
2914 class ClearRecentSceneFileListCommandHandler final : public MenuItemHandler {
2915 public:
ClearRecentSceneFileListCommandHandler()2916   ClearRecentSceneFileListCommandHandler()
2917       : MenuItemHandler(MI_ClearRecentScene) {}
execute()2918   void execute() override {
2919     RecentFiles::instance()->clearRecentFilesList(RecentFiles::Scene);
2920   }
2921 } clearRecentSceneFileListCommandHandler;
2922 
2923 //-----------------------------------------------------------------------------
2924 
2925 class ClearRecentLevelFileListCommandHandler final : public MenuItemHandler {
2926 public:
ClearRecentLevelFileListCommandHandler()2927   ClearRecentLevelFileListCommandHandler()
2928       : MenuItemHandler(MI_ClearRecentLevel) {}
execute()2929   void execute() override {
2930     RecentFiles::instance()->clearRecentFilesList(RecentFiles::Level);
2931   }
2932 } clearRecentLevelFileListCommandHandler;
2933 
2934 //-----------------------------------------------------------------------------
2935 
2936 class RevertScene final : public MenuItemHandler {
2937 public:
RevertScene()2938   RevertScene() : MenuItemHandler(MI_RevertScene) {}
execute()2939   void execute() override {
2940     TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
2941     ToonzScene *scene         = sceneHandle->getScene();
2942     assert(scene);
2943     TFilePath path       = scene->getScenePath();
2944     TFilePath decodePath = scene->decodeFilePath(scene->getScenePath());
2945     if (!TSystem::doesExistFileOrLevel(decodePath)) {
2946       DVGui::warning(QObject::tr("The scene %1 doesn't exist.")
2947                          .arg(toQString(decodePath)));
2948       return;
2949     }
2950     if (sceneHandle->getDirtyFlag()) {
2951       int ret = DVGui::MsgBox(
2952           QString(
2953               QObject::tr("Revert: the current scene has been modified.\nAre "
2954                           "you sure you want to revert to previous version?")),
2955           QString(QObject::tr("Revert")), QString(QObject::tr("Cancel")));
2956       if (ret == 2 || ret == 0) return;
2957     }
2958     IoCmd::loadScene(path, false, false);
2959   }
2960 } RevertScene;
2961 
2962 //=============================================================================
2963 // Overwrite palette
2964 //-----------------------------------------------------------------------------
2965 class OverwritePaletteCommandHandler final : public MenuItemHandler {
2966 public:
OverwritePaletteCommandHandler()2967   OverwritePaletteCommandHandler() : MenuItemHandler(MI_OverwritePalette) {}
2968 
execute()2969   void execute() override {
2970     TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
2971     if (!level) {
2972       DVGui::warning("No current level.");
2973       return;
2974     }
2975     TXshSimpleLevel *sl  = level->getSimpleLevel();
2976     TXshPaletteLevel *pl = level->getPaletteLevel();
2977     if (!sl && !pl) {
2978       DVGui::warning("Current level has no palette.");
2979       return;
2980     }
2981     /*-- SimpleLevel/PaletteLevelの場合毎にパレット/パスの取得の仕方を変える
2982      * --*/
2983     TPalette *palette;
2984     TFilePath palettePath;
2985     /*- SimpleLevelの場合 -*/
2986     if (sl) {
2987       palette = sl->getPalette();
2988       if (!palette) {
2989         DVGui::warning("No current palette");
2990         return;
2991       }
2992       if (sl->getPath().getType() == "pli")
2993         palettePath = sl->getPath();
2994       else if (sl->getType() & FULLCOLOR_TYPE)
2995         palettePath = FullColorPalette::instance()->getPath();
2996       else
2997         palettePath = sl->getPath().withType("tpl");
2998     }
2999     /*- PaletteLevelの場合 -*/
3000     else if (pl) {
3001       palette = pl->getPalette();
3002       if (!palette) {
3003         DVGui::warning("No current palette");
3004         return;
3005       }
3006       palettePath = pl->getPath();
3007     } else {
3008       DVGui::warning("This level is not SimpleLevel or PaletteLevel");
3009       return;
3010     }
3011 
3012     QString question;
3013     int ret;
3014     if (sl && sl->getPath().getType() == "pli") {
3015       question = "Saving " + toQString(palettePath) +
3016                  "\nThis command will ovewrite the level data as well.  Are "
3017                  "you sure ?";
3018       ret =
3019           DVGui::MsgBox(question, QObject::tr("OK"), QObject::tr("Cancel"), 0);
3020     } else {
3021       question = "Do you want to overwrite current palette to " +
3022                  toQString(palettePath) + " ?";
3023       ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
3024                           QObject::tr("Don't Overwrite"), 0);
3025     }
3026     if (ret == 2 || ret == 0) return;
3027 
3028     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
3029     if (!palettePath.isAbsolute() && scene) {
3030       palettePath = scene->decodeFilePath(palettePath);
3031     }
3032 
3033     if (sl && sl->getPath().getType() == "pli")
3034       sl->save(palettePath, TFilePath(), true);
3035     else if (sl && sl->getType() & FULLCOLOR_TYPE)
3036       FullColorPalette::instance()->savePalette(scene);
3037     else
3038       StudioPalette::instance()->save(palettePath, palette);
3039     /*- Dirtyフラグの変更 -*/
3040     if (sl)
3041       sl->getPalette()->setDirtyFlag(false);
3042     else if (pl)
3043       pl->getPalette()->setDirtyFlag(false);
3044 
3045     if (Preferences::instance()->getBoolValue(resetUndoOnSavingLevel))
3046       TUndoManager::manager()->reset();
3047 
3048     TApp::instance()
3049         ->getPaletteController()
3050         ->getCurrentLevelPalette()
3051         ->notifyPaletteDirtyFlagChanged();
3052   }
3053 } overwritePaletteCommandHandler;
3054 
3055 //=============================================================================
3056 // Save scene and levels
3057 //-----------------------------------------------------------------------------
3058 class SaveAllCommandHandler final : public MenuItemHandler {
3059 public:
SaveAllCommandHandler()3060   SaveAllCommandHandler() : MenuItemHandler(MI_SaveAll) {}
execute()3061   void execute() override { IoCmd::saveAll(); }
3062 } saveAllCommandHandler;
3063 
3064 //=============================================================================
3065 // Save all levels
3066 //-----------------------------------------------------------------------------
3067 class SaveAllLevelsCommandHandler : public MenuItemHandler {
3068 public:
SaveAllLevelsCommandHandler()3069   SaveAllLevelsCommandHandler() : MenuItemHandler(MI_SaveAllLevels) {}
execute()3070   void execute() { IoCmd::saveNonSceneFiles(); }
3071 } saveAllLevelsCommandHandler;
3072