1 
2 
3 // TnzCore includes
4 #include "tstream.h"
5 #include "tsystem.h"
6 
7 // ToonzLib includes
8 #include "toonz/toonzscene.h"
9 #include "toonz/sceneproperties.h"
10 #include "toonz/txshleveltypes.h"
11 #include "toonz/txshsimplelevel.h"
12 #include "toonz/levelproperties.h"
13 #include "toonz/cleanupparameters.h"
14 #include "toonz/tcleanupper.h"
15 
16 #include "toonz/tscenehandle.h"
17 #include "toonz/txsheethandle.h"
18 #include "toonz/txshlevelhandle.h"
19 #include "toonz/tcolumnhandle.h"
20 #include "toonz/tframehandle.h"
21 #include "toonz/tpalettehandle.h"
22 #include "toonz/palettecontroller.h"
23 #include "toonz/tproject.h"
24 
25 // ToonzQt includes
26 #include "toonzqt/gutil.h"
27 #include "toonzqt/dvdialog.h"
28 #include "toonzqt/tselectionhandle.h"
29 
30 // Toonz includes
31 #include "tapp.h"
32 #include "filebrowserpopup.h"
33 
34 // Qt includes
35 #include <QMetaType>
36 #include <QApplication>
37 
38 #include "cleanupsettingsmodel.h"
39 
40 //**********************************************************************
41 //    Local classes
42 //**********************************************************************
43 
44 namespace {
45 
46 //==================================================================
47 //    IOSettingsPopup
48 //==================================================================
49 
50 class IOSettingsPopup : public FileBrowserPopup {
51 public:
IOSettingsPopup(const QString & title)52   IOSettingsPopup(const QString &title) : FileBrowserPopup(title) {
53     setModal(true);
54     addFilterType("cln");
55   }
56 
setPath(const TFilePath & levelPath)57   void setPath(const TFilePath &levelPath) {
58     TFilePath folderPath(levelPath);
59     if (!folderPath.isEmpty())
60       folderPath = folderPath.getParentDir();
61     else {
62       TProject *currentProject =
63           TProjectManager::instance()->getCurrentProject().getPointer();
64       if (currentProject)
65         folderPath =
66             currentProject->decode(currentProject->getFolder(TProject::Inputs));
67     }
68 
69     setFolder(folderPath);
70     setFilename(levelPath.withoutParentDir());
71   }
72 };
73 
74 //==================================================================
75 //    SaveSettingsPopup
76 //==================================================================
77 
78 class SaveSettingsPopup final : public IOSettingsPopup {
79 public:
SaveSettingsPopup()80   SaveSettingsPopup() : IOSettingsPopup(QObject::tr("Save Cleanup Settings")) {
81     // addFilterType("tif");
82     // addFilterType("tga");
83     // addFilterType("png");
84 
85     setOkText(QObject::tr("Save"));
86   }
87 
execute()88   bool execute() override {
89     if (m_selectedPaths.empty()) return false;
90 
91     TFilePath savePath(*m_selectedPaths.begin());
92     if (savePath.isEmpty()) return false;
93 
94     savePath = savePath.withNoFrame().withType("cln");  // Just to be sure
95     if (TFileStatus(savePath).doesExist()) {
96       int ret = DVGui::MsgBox(
97           QObject::tr("The cleanup settings file for the %1 level already "
98                       "exists.\n Do you want to overwrite it?")
99               .arg(toQString(savePath.withoutParentDir())),
100           QObject::tr("Overwrite"), QObject::tr("Don't Overwrite"), 0);
101 
102       if (ret == 2) return false;
103     }
104 
105     CleanupSettingsModel::instance()->saveSettings(savePath);
106     return true;
107   }
108 };
109 
110 //==================================================================
111 //    LoadSettingsPopup
112 //==================================================================
113 
114 class LoadSettingsPopup final : public IOSettingsPopup {
115 public:
LoadSettingsPopup()116   LoadSettingsPopup() : IOSettingsPopup(QObject::tr("Load Cleanup Settings")) {
117     setOkText(QObject::tr("Load"));
118   }
119 
execute()120   bool execute() override {
121     if (m_selectedPaths.empty()) return false;
122 
123     const TFilePath &loadPath = *m_selectedPaths.begin();
124     if (loadPath.isEmpty()) return false;
125 
126     if (!TSystem::doesExistFileOrLevel(loadPath)) {
127       DVGui::error(QObject::tr("%1 does not exist.").arg(toQString(loadPath)));
128       return false;
129     }
130 
131     CleanupSettingsModel::instance()->loadSettings(loadPath);
132     return true;
133   }
134 };
135 
136 }  // namespace
137 
138 //******************************************************************************
139 //    CleanupSettingsModel implementation
140 //******************************************************************************
141 
CleanupSettingsModel()142 CleanupSettingsModel::CleanupSettingsModel()
143     : m_listenersCount(0)
144     , m_previewersCount(0)
145     , m_cameraTestsCount(0)
146     , m_allowedActions(FULLPROCESS)
147     , m_action(NONE)
148     , m_c(-1)
149     , m_sl(0) {
150   CleanupParameters *currentParams = getCurrentParameters();
151 
152   // Don't clone the palette. Palette changes are tracked through
153   // palette handle signals.
154   m_backupParams.assign(currentParams, false);
155 
156   TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
157 
158   bool ret = true;
159   ret      = ret &&
160         connect(sceneHandle, SIGNAL(sceneSwitched()), SLOT(onSceneSwitched()));
161   assert(ret);
162 
163   onSceneSwitched();
164 }
165 
166 //-----------------------------------------------------------------------
167 
~CleanupSettingsModel()168 CleanupSettingsModel::~CleanupSettingsModel() {}
169 
170 //-----------------------------------------------------------------------
171 
instance()172 CleanupSettingsModel *CleanupSettingsModel::instance() {
173   static CleanupSettingsModel theInstance;
174   return &theInstance;
175 }
176 
177 //-----------------------------------------------------------------------
178 
getCurrentParameters()179 CleanupParameters *CleanupSettingsModel::getCurrentParameters() {
180   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
181   return scene ? scene->getProperties()->getCleanupParameters() : 0;
182 }
183 
184 //-----------------------------------------------------------------------
185 
getCleanupFrame(TXshSimpleLevel * & sl,TFrameId & fid)186 void CleanupSettingsModel::getCleanupFrame(TXshSimpleLevel *&sl,
187                                            TFrameId &fid) {
188   TApp *app = TApp::instance();
189 
190   int r = app->getCurrentFrame()->getFrame();
191   int c = app->getCurrentColumn()->getColumnIndex();
192 
193   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
194   assert(xsh);
195 
196   const TXshCell &cell = xsh->getCell(r, c);
197 
198   fid = cell.getFrameId();
199   sl  = cell.isEmpty() ? 0 : cell.getSimpleLevel();
200 }
201 
202 //-----------------------------------------------------------------------
203 
attach(int objectFlag,bool doPreview)204 void CleanupSettingsModel::attach(int objectFlag, bool doPreview) {
205   if ((objectFlag & LISTENER) && (m_listenersCount++ <= 0)) connectSignals();
206 
207   bool doRebuild = false;
208 
209   if ((objectFlag & PREVIEWER) && (m_previewersCount++ <= 0)) doRebuild = true;
210 
211   if ((objectFlag & CAMERATEST) && (m_cameraTestsCount++ <= 0))
212     doRebuild = true;
213 
214   if (doRebuild && doPreview) rebuildPreview();
215 }
216 
217 //-----------------------------------------------------------------------
218 
detach(int objectFlag)219 void CleanupSettingsModel::detach(int objectFlag) {
220   if ((objectFlag & LISTENER) && (--m_listenersCount <= 0)) disconnectSignals();
221 
222   if ((objectFlag & PREVIEWER) && (--m_previewersCount <= 0)) {
223     m_previewTransformed = TRasterImageP();
224     m_transform          = TAffine();
225   }
226 
227   if ((objectFlag & CAMERATEST) && (--m_cameraTestsCount <= 0))
228     m_cameraTestTransformed = TRasterImageP();
229 
230   assert(m_listenersCount >= 0);
231   assert(m_previewersCount >= 0);
232   assert(m_cameraTestsCount >= 0);
233 }
234 
235 //-----------------------------------------------------------------------
236 
connectSignals()237 void CleanupSettingsModel::connectSignals() {
238   TApp *app = TApp::instance();
239 
240   TFrameHandle *frameHandle   = app->getCurrentFrame();
241   TColumnHandle *columnHandle = app->getCurrentColumn();
242   TPaletteHandle *paletteHandle =
243       app->getPaletteController()->getCurrentCleanupPalette();
244 
245   // The following are queued. This is best to prevent double-updates and
246   // selection issues with cln cancels.
247   bool ret = true;
248 
249   ret = ret && connect(frameHandle, SIGNAL(frameSwitched()),
250                        SLOT(onCellChanged()), Qt::QueuedConnection);
251   ret = ret && connect(columnHandle, SIGNAL(columnIndexSwitched()),
252                        SLOT(onCellChanged()), Qt::QueuedConnection);
253   ret = ret && connect(paletteHandle, SIGNAL(colorStyleChanged(bool)),
254                        SLOT(onPaletteChanged()), Qt::QueuedConnection);
255   ret = ret && connect(paletteHandle, SIGNAL(paletteChanged()),
256                        SLOT(onPaletteChanged()), Qt::QueuedConnection);
257 
258   assert(ret);
259 
260   QMetaObject::invokeMethod(this, "onCellChanged", Qt::QueuedConnection);
261 }
262 
263 //-----------------------------------------------------------------------
264 
disconnectSignals()265 void CleanupSettingsModel::disconnectSignals() {
266   TApp *app = TApp::instance();
267 
268   TFrameHandle *frameHandle   = app->getCurrentFrame();
269   TColumnHandle *columnHandle = app->getCurrentColumn();
270   TPaletteHandle *paletteHandle =
271       app->getPaletteController()->getCurrentCleanupPalette();
272 
273   bool ret = true;
274 
275   ret = ret && frameHandle->disconnect(this);
276   ret = ret && columnHandle->disconnect(this);
277   ret = ret && paletteHandle->disconnect(this);
278 
279   assert(ret);
280 }
281 
282 //-----------------------------------------------------------------------
283 
setCommitMask(CommitMask allowedProcessing)284 void CleanupSettingsModel::setCommitMask(CommitMask allowedProcessing) {
285   m_allowedActions = allowedProcessing;
286   commitChanges();
287 }
288 
289 //-----------------------------------------------------------------------
290 
commitChanges()291 void CleanupSettingsModel::commitChanges() {
292   CleanupParameters *currentParams = getCurrentParameters();
293 
294   int action = NONE;
295 
296   // Analyze param changes
297   {
298     // Check for simple changes
299     if (currentParams->m_path != m_backupParams.m_path) action = INTERFACE;
300 
301     // Check post-processing-related changes
302     if (currentParams->m_transparencyCheckEnabled !=
303             m_backupParams.m_transparencyCheckEnabled ||
304         currentParams->m_noAntialias != m_backupParams.m_noAntialias ||
305         currentParams->m_postAntialias != m_backupParams.m_postAntialias ||
306         currentParams->m_despeckling != m_backupParams.m_despeckling ||
307         currentParams->m_aaValue != m_backupParams.m_aaValue)
308 
309       action = POSTPROCESS;
310 
311     // Check for full preview rebuilds
312     if (currentParams->m_lineProcessingMode !=
313             m_backupParams.m_lineProcessingMode ||
314         currentParams->m_autocenterType != m_backupParams.m_autocenterType ||
315         currentParams->m_autoAdjustMode != m_backupParams.m_autoAdjustMode ||
316         currentParams->m_camera.getRes() != m_backupParams.m_camera.getRes() ||
317         currentParams->m_camera.getSize() !=
318             m_backupParams.m_camera.getSize() ||
319         currentParams->m_rotate != m_backupParams.m_rotate ||
320         currentParams->m_flipx != m_backupParams.m_flipx ||
321         currentParams->m_flipy != m_backupParams.m_flipy ||
322         currentParams->m_offx != m_backupParams.m_offx ||
323         currentParams->m_offy != m_backupParams.m_offy ||
324         currentParams->m_sharpness != m_backupParams.m_sharpness ||
325         currentParams->m_closestField != m_backupParams.m_closestField)
326 
327       action = FULLPROCESS;
328 
329     if (currentParams->m_autocenterType != CleanupTypes::AUTOCENTER_NONE) {
330       // Check Autocenter changes
331       if (!(currentParams->getFdgInfo() == m_backupParams.getFdgInfo()) ||
332           currentParams->m_pegSide != m_backupParams.m_pegSide)
333 
334         action = FULLPROCESS;
335     }
336   }
337 
338   commitChanges(action);
339 }
340 
341 //-----------------------------------------------------------------------
342 
commitChanges(int action)343 void CleanupSettingsModel::commitChanges(int action) {
344   CleanupParameters *currentParams = getCurrentParameters();
345 
346   if (action > NONE) {
347     m_backupParams.assign(currentParams, false);
348 
349     currentParams->setDirtyFlag(true);
350 
351     // Deal with scene stuff
352     if (m_clnPath.isEmpty()) {
353       TApp::instance()->getCurrentScene()->setDirtyFlag(
354           true);  // This should be moved outside... sadly not supported at the
355                   // moment...
356       CleanupParameters::GlobalParameters.assign(
357           currentParams);  // The global settings are being changed
358     }
359   }
360 
361   // Perform actions
362   int maxAction =
363       std::max(action, m_action);  // Add previuosly required actions
364   action   = std::min(maxAction,
365                     m_allowedActions);  // But only up to the allowed action
366   m_action = (action == maxAction)
367                  ? NONE
368                  : maxAction;  // Then, update the previously required action
369 
370   if (action >= FULLPROCESS) rebuildPreview();
371 
372   if (action >= INTERFACE) emit modelChanged(action == POSTPROCESS);
373 }
374 
375 //-----------------------------------------------------------------------
376 
rebuildPreview()377 void CleanupSettingsModel::rebuildPreview() {
378   // If no previewer is active, do nothing
379   if (m_previewersCount <= 0 && m_cameraTestsCount <= 0) return;
380 
381   // Reset data
382   m_original = m_previewTransformed = m_cameraTestTransformed = TRasterImageP();
383   m_transform                                                 = TAffine();
384 
385   // Retrieve frame to cleanup
386   TXshSimpleLevel *sl;
387   TFrameId fid;
388   getCleanupFrame(sl, fid);
389 
390   if (sl) {
391     // Inform that we're beginning a long computation
392     QApplication::setOverrideCursor(Qt::WaitCursor);
393 
394     // Perform the preview
395     processFrame(sl, fid);
396 
397     QApplication::restoreOverrideCursor();
398   }
399 
400   emit previewDataChanged();
401 }
402 
403 //-----------------------------------------------------------------------
404 
processFrame(TXshSimpleLevel * sl,TFrameId fid)405 void CleanupSettingsModel::processFrame(TXshSimpleLevel *sl, TFrameId fid) {
406   assert(sl);
407 
408   TRasterImageP imageToCleanup = sl->getFrameToCleanup(fid);
409   if (!imageToCleanup) return;
410 
411   // Store the original image
412   m_original = imageToCleanup;
413 
414   // Retrieve cleanup params
415   CleanupParameters *params = getCurrentParameters();
416   TCleanupper *cl           = TCleanupper::instance();
417 
418   bool doProcessing =
419       (params->m_lineProcessingMode != lpNone) && (m_previewersCount > 0);
420   bool doCameraTest = (m_cameraTestsCount > 0);
421 
422   // Retrieve new image dpi
423   TPointD dpi;
424   imageToCleanup->getDpi(dpi.x, dpi.y);
425   if (dpi.x == 0 && dpi.y == 0) dpi = sl->getProperties()->getDpi();
426   cl->setSourceDpi(dpi);
427 
428   // Perform primary cleanup processing
429   if (doProcessing) {
430     // Warning: the process() call below will put imageToCleanup = 0.
431     CleanupPreprocessedImage *cpi =
432         cl->process(imageToCleanup, true, m_previewTransformed, false, true,
433                     true, &m_transform);
434     delete cpi;
435   }
436 
437   // Perform camera test processing
438   if (doCameraTest) {
439     m_cameraTestTransformed = m_original;
440     if (params->m_autocenterType != CleanupTypes::AUTOCENTER_NONE ||
441         params->m_rotate != 0 || params->m_flipx || params->m_flipy) {
442       bool autocentered;
443       m_cameraTestTransformed =
444           cl->autocenterOnly(m_original.getPointer(), true, autocentered);
445 
446       if (params->m_autocenterType != CleanupTypes::AUTOCENTER_NONE &&
447           !autocentered)
448         DVGui::warning(
449             QObject::tr("The autocentering failed on the current drawing."));
450     }
451   }
452 }
453 
454 //-----------------------------------------------------------------------
455 
onSceneSwitched()456 void CleanupSettingsModel::onSceneSwitched() {
457   // When the scene switches, current parameters are reverted the scene's
458   // originals.
459   // So, since we store originals in CleanupParameters::GlobalParameters, we
460   // copy them there.
461   CleanupParameters *params = getCurrentParameters();
462   CleanupParameters::GlobalParameters.assign(params);
463 
464   // The cleanupper always uses current cleanup parameters. It has to be
465   // specified somewhere,
466   // so let's do it once here.
467   TCleanupper::instance()->setParameters(params);
468 
469   // Finally, send notifications, deal with backups, etc.
470   restoreGlobalSettings();
471 }
472 
473 //-----------------------------------------------------------------------
474 
onCellChanged()475 void CleanupSettingsModel::onCellChanged() {
476   // Process events. This slot wants to be invoked when no other slot/event
477   // is pending. This is best since current selection may change inside.
478   QCoreApplication::processEvents();
479 
480   TApp *app = TApp::instance();
481 
482   // Update xsheet position
483   int oldC = m_c;
484   m_c      = app->getCurrentColumn()->getColumnIndex();
485 
486   // Retrieve new cleanup image reference (not updated yet)
487   TXshSimpleLevel *sl;
488   TFrameId fid;
489 
490   /*---
491    * カラムを上から走査して、最初にLevelの入っているセルのLevelパスを取ってくる
492    * ---*/
493   TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
494   if (xsh->getFrameCount() == 0) return;
495 
496   for (int f = 0; f < xsh->getFrameCount(); f++) {
497     TXshCell cell = xsh->getCell(f, m_c);
498     sl            = cell.getSimpleLevel();
499     if (sl) {
500       fid = cell.getFrameId();
501       break;
502     }
503   }
504 
505   // Deal with cln switches
506   bool rebuiltPreview = false;
507 
508   if (sl != m_sl) {
509     // Build new cln path
510     const TFilePath &clnPath = getClnPath(sl);
511 
512     // If there is a switch in cln paths
513     if (m_clnPath != clnPath) {
514       // In case, save the old cln
515       if (!m_clnPath.isEmpty() && TFileStatus(m_clnPath).doesExist()) {
516         if (!saveSettingsIfNeeded()) {
517           // Unselect current selection, and return to the old cell
518           app->getCurrentSelection()->setSelection(0);
519           app->getCurrentColumn()->setColumnIndex(oldC);
520           m_c = oldC;
521           return;
522         }
523       }
524 
525       // Load the new cleanup settings, if any
526       if (loadSettings(
527               clnPath))  // modelChanged() emitted here, plust preview rebuild
528       {
529         m_clnPath      = clnPath;
530         rebuiltPreview = true;
531       } else if (!m_clnPath.isEmpty()) {
532         // Otherwise, in case there was an old cln, reload global settings
533         restoreGlobalSettings();  // modelChanged() emitted here
534         rebuiltPreview = true;
535       }
536     }
537   }
538 
539   m_sl = sl, m_fid = fid;
540 
541   if ((m_previewersCount > 0 || m_cameraTestsCount > 0) && !rebuiltPreview)
542     rebuildPreview();
543 
544   emit imageSwitched();
545 }
546 
547 //-----------------------------------------------------------------------
548 
onPaletteChanged()549 void CleanupSettingsModel::onPaletteChanged() { commitChanges(POSTPROCESS); }
550 
551 //-----------------------------------------------------------------------
552 
saveSettingsIfNeeded()553 bool CleanupSettingsModel::saveSettingsIfNeeded() {
554   CleanupParameters *params = getCurrentParameters();
555 
556   if (params->getDirtyFlag() && !m_clnPath.isEmpty() &&
557       TFileStatus(m_clnPath).doesExist()) {
558     QString question(
559         QObject::tr("The cleanup settings for the current level have been "
560                     "modified...\n\nDo you want to save your changes?"));
561 
562     int ret = DVGui::MsgBox(question, QObject::tr("Save"),
563                             QObject::tr("Discard"), QObject::tr("Cancel"), 0);
564     if (ret == 1) {
565       // WARNING: This is legacy behavior, but is it really needed? I think
566       // there should be no further
567       // request of user interaction - why invoking the popup to choose the save
568       // path?
569       promptSave();
570     } else if (ret == 3)
571       return false;
572   }
573 
574   return true;
575 }
576 
577 //-----------------------------------------------------------------------------
578 
restoreGlobalSettings()579 void CleanupSettingsModel::restoreGlobalSettings() {
580   CleanupParameters *currentParams = getCurrentParameters();
581   currentParams->assign(&CleanupParameters::GlobalParameters);
582 
583   // Make sure that the current cleanup palette is set to currentParams' palette
584   TApp::instance()
585       ->getPaletteController()
586       ->getCurrentCleanupPalette()
587       ->setPalette(currentParams->m_cleanupPalette.getPointer());
588 
589   m_clnPath = TFilePath();
590   m_backupParams.assign(currentParams, false);
591 
592   if (m_previewersCount > 0 || m_cameraTestsCount > 0) rebuildPreview();
593 
594   emit modelChanged(false);
595   emit clnLoaded();
596 }
597 
598 //-----------------------------------------------------------------------------
599 
saveSettings(CleanupParameters * params,const TFilePath & clnPath)600 void CleanupSettingsModel::saveSettings(CleanupParameters *params,
601                                         const TFilePath &clnPath) {
602   if (clnPath.isEmpty() || !params) return;
603 
604   TOStream os(clnPath);
605 
606   os.openChild("version");
607   os << 1 << 19;
608   os.closeChild();
609 
610   params->saveData(os);
611   params->setDirtyFlag(false);
612 }
613 
614 //-----------------------------------------------------------------------------
615 
saveSettings(const TFilePath & clnPath)616 void CleanupSettingsModel::saveSettings(const TFilePath &clnPath) {
617   saveSettings(getCurrentParameters(), clnPath);
618 
619   // Saving current settings should put the model in the same situation we
620   // have after a model is loaded (ie see loadSettings(clnPath))
621   m_clnPath = clnPath;
622 
623   emit clnLoaded();
624 }
625 
626 //-----------------------------------------------------------------------------
627 
loadSettings(CleanupParameters * params,const TFilePath & clnPath)628 bool CleanupSettingsModel::loadSettings(CleanupParameters *params,
629                                         const TFilePath &clnPath) {
630   assert(params);
631   if (clnPath.isEmpty() || !TSystem::doesExistFileOrLevel(clnPath))
632     return false;
633 
634   TIStream *is = new TIStream(clnPath);
635 
636   CleanupParameters defaultParameters;
637   params->assign(&defaultParameters);
638 
639   std::string tagName;
640   is->matchTag(tagName);
641   if (tagName == "version") {
642     int minor, major;
643     *is >> major >> minor;
644 
645     is->matchEndTag();
646     is->setVersion(VersionNumber(major, minor));
647   } else {
648     delete is;
649     is = new TIStream(clnPath);
650   }
651 
652   params->loadData(*is, false);
653   delete is;
654 
655   return true;
656 }
657 
658 //-----------------------------------------------------------------------------
659 
loadSettings(const TFilePath & clnPath)660 bool CleanupSettingsModel::loadSettings(const TFilePath &clnPath) {
661   CleanupParameters *cp = getCurrentParameters();
662   if (!loadSettings(cp, clnPath)) return false;
663 
664   // The same as restoreGlobalSettings()
665   TApp::instance()
666       ->getPaletteController()
667       ->getCurrentCleanupPalette()
668       ->setPalette(cp->m_cleanupPalette.getPointer());
669 
670   /*---
671    * LoadSettingsPopupからこの関数が呼ばれたとき、ロードしたパラメータをGlobal設定に格納する---*/
672   if (m_clnPath.isEmpty()) {
673     TApp::instance()->getCurrentScene()->setDirtyFlag(
674         true);  // This should be moved outside... sadly not supported at the
675                 // moment...
676     CleanupParameters::GlobalParameters.assign(
677         cp);  // The global settings are being changed
678   }
679 
680   m_backupParams.assign(cp, false);
681 
682   if (m_previewersCount > 0 || m_cameraTestsCount > 0) rebuildPreview();
683 
684   emit modelChanged(false);
685   emit clnLoaded();
686 
687   return true;
688 }
689 
690 //-----------------------------------------------------------------------------
691 
promptSave()692 void CleanupSettingsModel::promptSave() {
693   static SaveSettingsPopup *savePopup = 0;
694   if (!savePopup) savePopup = new SaveSettingsPopup;
695 
696   savePopup->setPath(getClnPath(m_sl));
697   savePopup->exec();
698 }
699 
700 //-----------------------------------------------------------------------------
701 
promptLoad()702 void CleanupSettingsModel::promptLoad() {
703   static LoadSettingsPopup *loadPopup = 0;
704   if (!loadPopup) loadPopup = new LoadSettingsPopup;
705 
706   loadPopup->setPath(getClnPath(m_sl));
707   loadPopup->exec();
708 }
709 
710 //-----------------------------------------------------------------------------
711 
getClnPath(TXshSimpleLevel * sl)712 TFilePath CleanupSettingsModel::getClnPath(TXshSimpleLevel *sl) {
713   if (!sl) return TFilePath();
714 
715   TFilePath clnPath(sl->getScannedPath());
716   if (clnPath == TFilePath()) clnPath = sl->getPath();
717 
718   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
719   return scene->decodeFilePath(clnPath).withNoFrame().withType("cln");
720 }
721 
722 //-----------------------------------------------------------------------------
723 
getInputPath(TXshSimpleLevel * sl)724 TFilePath CleanupSettingsModel::getInputPath(TXshSimpleLevel *sl) {
725   const TFilePath &scannedPath = sl->getScannedPath();
726   return scannedPath.isEmpty() ? sl->getPath() : scannedPath;
727 }
728 
729 //-----------------------------------------------------------------------------
730 
getOutputPath(TXshSimpleLevel * sl,const CleanupParameters * params)731 TFilePath CleanupSettingsModel::getOutputPath(TXshSimpleLevel *sl,
732                                               const CleanupParameters *params) {
733   const TFilePath &inPath = getInputPath(sl);  // Used just to get level name
734 
735   if (inPath.isEmpty() || !params) return TFilePath();
736 
737   bool lineProcessing = (params->m_lineProcessingMode != lpNone);
738   ToonzScene *scene   = TApp::instance()->getCurrentScene()->getScene();
739 
740   // Check if the cleaned up level already exists
741   const TFilePath &outDir = params->getPath(scene);
742 
743   return lineProcessing ? (outDir + inPath.getWideName()).withType("tlv")
744                         : (outDir + inPath.getLevelNameW()).withType("tif");
745 }
746