1 
2 
3 #include "filebrowserpopup.h"
4 
5 // Tnz6 includes
6 #include "menubarcommandids.h"
7 #include "iocommand.h"
8 #include "exportlevelcommand.h"
9 #include "filebrowser.h"
10 #include "tapp.h"
11 #include "filebrowsermodel.h"
12 #include "formatsettingspopups.h"
13 #include "magpiefileimportpopup.h"
14 #include "columnselection.h"
15 #include "convertpopup.h"
16 #include "matchline.h"
17 #include "colormodelbehaviorpopup.h"
18 
19 // TnzQt includes
20 #include "toonzqt/gutil.h"
21 #include "toonzqt/icongenerator.h"
22 #include "toonzqt/colorfield.h"
23 #include "toonzqt/tselectionhandle.h"
24 
25 // TnzLib includes
26 #include "toonz/tscenehandle.h"
27 #include "toonz/tpalettehandle.h"
28 #include "toonz/txshlevelhandle.h"
29 #include "toonz/txsheethandle.h"
30 #include "toonz/palettecontroller.h"
31 #include "toonz/studiopalette.h"
32 #include "toonz/toonzscene.h"
33 #include "toonz/tproject.h"
34 #include "toonz/txshcell.h"
35 #include "toonz/txshsimplelevel.h"
36 #include "toonz/tcamera.h"
37 #include "toonz/sceneproperties.h"
38 #include "toonz/tstageobjecttree.h"
39 #include "toonz/txshleveltypes.h"
40 // specify in the preference whether to replace the level after saveLevelAs
41 // command
42 #include "toonz/preferences.h"
43 #include "toonz/tcolumnhandle.h"
44 #include "toonz/tframehandle.h"
45 #include "toonz/levelset.h"
46 #include "toonz/palettecmd.h"
47 #include "toonz/stage.h"
48 
49 // TnzCore includes
50 #include "tsystem.h"
51 #include "tiio.h"
52 #include "tlevel_io.h"
53 #include "tundo.h"
54 
55 // Qt includes
56 #include <QVBoxLayout>
57 #include <QHBoxLayout>
58 #include <QGridLayout>
59 #include <QFrame>
60 #include <QPushButton>
61 #include <QLabel>
62 #include <QComboBox>
63 #include <QGroupBox>
64 #include <QCoreApplication>
65 #include <QMainWindow>
66 #include <QApplication>
67 
68 //***********************************************************************************
69 //    FileBrowserPopup  implementation
70 //***********************************************************************************
71 
FileBrowserPopup(const QString & title,Options options,QString applyButtonTxt,QWidget * customWidget)72 FileBrowserPopup::FileBrowserPopup(const QString &title, Options options,
73                                    QString applyButtonTxt,
74                                    QWidget *customWidget)
75     : QDialog(TApp::instance()->getMainWindow())
76     , m_isDirectoryOnly(false)
77     , m_multiSelectionEnabled(options & MULTISELECTION)
78     , m_forSaving(options & FOR_SAVING)
79     , m_dialogSize(800, 600)
80     , m_customWidget(customWidget) {
81   setWindowTitle(title);
82   setModal(false);
83 
84   m_browser        = new FileBrowser(this, 0, false, m_multiSelectionEnabled);
85   m_nameFieldLabel = new QLabel(tr("File name:"));
86   m_nameField      = new DVGui::LineEdit(this);
87   m_okButton       = new QPushButton(tr("OK"), this);
88   m_cancelButton   = new QPushButton(tr("Cancel"), this);
89   QPushButton *applyButton = 0;
90   if (options & WITH_APPLY_BUTTON)
91     applyButton = new QPushButton(
92         (applyButtonTxt.isEmpty()) ? tr("Apply") : applyButtonTxt, this);
93 
94   std::list<std::vector<TFrameId>> tmp_list;
95   m_currentFIdsSet = tmp_list;
96 
97   //-------------
98 
99   m_okButton->setMaximumWidth(100);
100   m_okButton->setAutoDefault(false);
101   m_cancelButton->setMaximumWidth(100);
102   m_cancelButton->setAutoDefault(false);
103   if (applyButton) {
104     applyButton->setMaximumWidth(100);
105     applyButton->setAutoDefault(false);
106   }
107 
108   // layout
109   if (!(options & CUSTOM_LAYOUT)) {
110     QVBoxLayout *mainLayout = new QVBoxLayout();
111     mainLayout->setMargin(0);
112     mainLayout->setSpacing(3);
113     {
114       mainLayout->addWidget(m_browser, 1);
115 
116       QHBoxLayout *bottomLay = new QHBoxLayout();
117       bottomLay->setMargin(5);
118       bottomLay->setSpacing(3);
119       {
120         bottomLay->addWidget(m_nameFieldLabel, 0);
121         bottomLay->addWidget(m_nameField, 1);
122       }
123       mainLayout->addLayout(bottomLay);
124 
125       if (m_customWidget) mainLayout->addWidget(m_customWidget);
126 
127       QHBoxLayout *buttonsLay = new QHBoxLayout();
128       buttonsLay->setMargin(5);
129       buttonsLay->setSpacing(15);
130       {
131         buttonsLay->addStretch();
132         buttonsLay->addWidget(m_okButton);
133         if (applyButton) buttonsLay->addWidget(applyButton);
134         buttonsLay->addWidget(m_cancelButton);
135       }
136       mainLayout->addLayout(buttonsLay);
137     }
138     setLayout(mainLayout);
139   }
140 
141   // Establish connections
142   bool ret = true;
143   ret =
144       ret && connect(m_okButton, SIGNAL(clicked()), this, SLOT(onOkPressed()));
145   ret = ret && connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(close()));
146   ret =
147       ret &&
148       connect(
149           m_browser,
150           SIGNAL(filePathsSelected(const std::set<TFilePath> &,
151                                    const std::list<std::vector<TFrameId>> &)),
152           this,
153           SLOT(onFilePathsSelected(const std::set<TFilePath> &,
154                                    const std::list<std::vector<TFrameId>> &)));
155   ret = ret && connect(m_browser, SIGNAL(filePathClicked(const TFilePath &)),
156                        this, SIGNAL(filePathClicked(const TFilePath &)));
157   if (applyButton) {
158     ret = ret &&
159           connect(applyButton, SIGNAL(clicked()), this, SLOT(onApplyPressed()));
160   }
161   assert(ret);
162 
163   resize(m_dialogSize);
164   /*- Qt5 でQDialogがParentWidgetの中心位置に来ない不具合を回避する -*/
165   QPoint dialogCenter       = mapToGlobal(rect().center());
166   QPoint parentWindowCenter = TApp::instance()->getMainWindow()->mapToGlobal(
167       TApp::instance()->getMainWindow()->rect().center());
168   move(parentWindowCenter - dialogCenter);
169 }
170 
171 //-----------------------------------------------------------------------------
172 
setFilterTypes(const QStringList & typesList)173 void FileBrowserPopup::setFilterTypes(const QStringList &typesList) {
174   m_browser->setFilterTypes(typesList);
175 }
176 
177 //-----------------------------------------------------------------------------
178 
removeFilterType(const QString & type)179 void FileBrowserPopup::removeFilterType(const QString &type) {
180   m_browser->removeFilterType(type);
181 }
182 
183 //-----------------------------------------------------------------------------
184 
addFilterType(const QString & type)185 void FileBrowserPopup::addFilterType(const QString &type) {
186   m_browser->addFilterType(type);
187 }
188 
189 //-----------------------------------------------------------------------------
190 
setFileMode(bool isDirectoryOnly)191 void FileBrowserPopup::setFileMode(bool isDirectoryOnly) {
192   m_isDirectoryOnly = isDirectoryOnly;
193   if (isDirectoryOnly) {
194     m_nameFieldLabel->setText(tr("Folder name:"));
195     connect(m_browser, SIGNAL(treeFolderChanged(const TFilePath &)), this,
196             SLOT(onFilePathClicked(const TFilePath &)));
197   } else {
198     m_nameFieldLabel->setText(tr("File name:"));
199     disconnect(m_browser, SIGNAL(treeFolderChanged(const TFilePath &)), this,
200                SLOT(onFilePathClicked(const TFilePath &)));
201   }
202 }
203 
204 //-----------------------------------------------------------------------------
205 
setFolder(const TFilePath & folderPath)206 void FileBrowserPopup::setFolder(const TFilePath &folderPath) {
207   m_browser->setFolder(folderPath, true);
208 }
209 
210 //-----------------------------------------------------------------------------
211 
setFilename(const TFilePath & filename)212 void FileBrowserPopup::setFilename(const TFilePath &filename) {
213   m_nameField->setText(QString::fromStdWString(filename.getWideString()));
214 }
215 
216 //-----------------------------------------------------------------------------
217 
onOkPressed()218 void FileBrowserPopup::onOkPressed() {
219   const TFilePath &folder = m_browser->getFolder();
220 
221   // Rebuild paths selection in case the text field has an explicit entry
222   if (!m_nameField->text().isEmpty()) {
223     const QString &str = m_nameField->text();
224     if (!isValidFileName(QFileInfo(str).baseName()) && !m_isDirectoryOnly) {
225       DVGui::error(
226           QObject::tr("A filename cannot be empty or contain any of the "
227                       "following characters:\n \\ / : * ? \" < > |"));
228       return;
229     }
230     if (isReservedFileName_message(QFileInfo(str).baseName())) return;
231 
232     m_selectedPaths.clear();
233     if (!m_isDirectoryOnly)
234       m_selectedPaths.insert(folder +
235                              TFilePath(m_nameField->text().toStdWString()));
236     else
237       m_selectedPaths.insert(TFilePath(m_nameField->text().toStdWString()));
238   }
239 
240   // Analyze and correct the paths selection
241   std::set<TFilePath> pathSet;
242 
243   std::set<TFilePath>::const_iterator pt, pEnd(m_selectedPaths.end());
244   for (pt = m_selectedPaths.begin(); pt != pEnd; ++pt) {
245     if (folder == TFilePath()) {
246       // history                                      // That means, TFilePath()
247       // represents
248       if (*pt == TFilePath() ||
249           !pt->isAbsolute())  // the History folder? Really? That's lame...
250       {
251         DVGui::error(tr("Invalid file"));
252         return;
253       }
254     } else {
255       if (!m_isDirectoryOnly)
256         pathSet.insert(*pt);
257       else
258         pathSet.insert(folder);
259     }
260 
261     if (!m_multiSelectionEnabled) break;
262   }
263 
264   m_browser->selectNone();
265 
266   m_selectedPaths.swap(pathSet);
267   if (execute()) {
268     QCoreApplication::processEvents();  // <Solves a bug on XP... Bugzilla
269                                         // #6041>
270     accept();  // Tempted to remove the above - it should
271   }            // NOT be here, maybe in the executes()...
272 }
273 
274 //-----------------------------------------------------------------------------
275 /*! process without closing the browser
276  */
onApplyPressed()277 void FileBrowserPopup::onApplyPressed() {
278   TFilePath folder = m_browser->getFolder();
279   std::set<TFilePath> pathSet;
280   if (!m_nameField->text().isEmpty())  // if the user has written in the text
281                                        // field that wins on the item
282                                        // sselection!
283   {
284     m_selectedPaths.clear();
285     if (!m_isDirectoryOnly)
286       m_selectedPaths.insert(folder +
287                              TFilePath(m_nameField->text().toStdString()));
288     else
289       m_selectedPaths.insert(TFilePath(m_nameField->text().toStdWString()));
290   }
291 
292   std::set<TFilePath>::const_iterator it = m_selectedPaths.begin();
293   while (it != m_selectedPaths.end()) {
294     if (folder == TFilePath()) {
295       // history
296       if (*it == TFilePath() || !it->isAbsolute()) {
297         DVGui::error(tr("Invalid file"));
298         return;
299       }
300     } else {
301       if (!m_isDirectoryOnly)
302         pathSet.insert(*it);
303       else
304         pathSet.insert(folder);
305     }
306     if (!m_multiSelectionEnabled) break;
307     ++it;
308   }
309 
310   m_browser->selectNone();
311 
312   m_selectedPaths.swap(pathSet);
313   if (executeApply()) {
314     QCoreApplication::processEvents();
315   }
316 }
317 //-----------------------------------------------------------------------------
318 
onFilePathClicked(const TFilePath & fp)319 void FileBrowserPopup::onFilePathClicked(const TFilePath &fp) {
320   std::set<TFilePath> app;
321   app.insert(fp);
322   std::list<std::vector<TFrameId>> tmp;
323   onFilePathsSelected(app, tmp);
324 }
325 
326 //-----------------------------------------------------------------------------
327 
onFilePathsSelected(const std::set<TFilePath> & paths,const std::list<std::vector<TFrameId>> & fIds)328 void FileBrowserPopup::onFilePathsSelected(
329     const std::set<TFilePath> &paths,
330     const std::list<std::vector<TFrameId>> &fIds) {
331   if (paths.size() == 0 && m_forSaving) return;
332 
333   m_selectedPaths  = paths;
334   m_currentFIdsSet = fIds;
335 
336   if (paths.size() == 1) {
337     const TFilePath &fp = *paths.begin();
338     QString text;
339     if (!m_isDirectoryOnly)
340       text = QString::fromStdWString(fp.getLevelNameW());
341     else
342       text = QString::fromStdWString(m_browser->getFolder().getWideString());
343 
344     m_nameField->setText(text);
345   } else
346     m_nameField->setText("");
347 }
348 
349 //-----------------------------------------------------------------------------
350 
onFilePathDoubleClicked(const TFilePath &)351 void FileBrowserPopup::onFilePathDoubleClicked(const TFilePath &) {
352   // do nothing by default
353 }
354 
355 //-----------------------------------------------------------------------------
356 
setOkText(const QString & text)357 void FileBrowserPopup::setOkText(const QString &text) {
358   m_okButton->setText(text);
359   // if the button label is "Save" then the browser is assumed as for saving
360   if (text == QObject::tr("Save")) m_forSaving = true;
361 }
362 
363 //-----------------------------------------------------------------------------
364 
hideEvent(QHideEvent * e)365 void FileBrowserPopup::hideEvent(QHideEvent *e) {
366   TSelectionHandle::getCurrent()->popSelection();
367   m_dialogSize = size();
368   move(pos());
369   resize(size());
370 }
371 
372 //-----------------------------------------------------------------------------
373 
showEvent(QShowEvent *)374 void FileBrowserPopup::showEvent(QShowEvent *) {
375   TSelectionHandle::getCurrent()->pushSelection();
376   m_selectedPaths.clear();
377   m_currentFIdsSet.clear();
378 
379   TFilePath projectPath = TProjectManager::instance()->getCurrentProjectPath();
380   if (m_currentProjectPath != projectPath) {
381     m_currentProjectPath = projectPath;
382     initFolder();
383 
384     // set initial folder of all browsers to $scenefolder when the scene folder
385     // mode is set in user preferences
386     if (Preferences::instance()->getPathAliasPriority() ==
387         Preferences::SceneFolderAlias) {
388       ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
389       if (scene && !scene->isUntitled())
390         setFolder(scene->getScenePath().getParentDir());
391     }
392 
393     m_nameField->update();
394     m_nameField->setFocus();
395   }
396   resize(m_dialogSize);
397 }
398 
399 //-----------------------------------------------------------------------------
400 // utility function. Make the widget to be a child of modal file browser in
401 // order to allow control.
402 
setModalBrowserToParent(QWidget * widget)403 void FileBrowserPopup::setModalBrowserToParent(QWidget *widget) {
404   if (!widget) return;
405   for (QWidget *pwidget : QApplication::topLevelWidgets()) {
406     if ((pwidget->isWindow()) && (pwidget->isModal()) &&
407         (pwidget->isVisible())) {
408       FileBrowserPopup *popup = qobject_cast<FileBrowserPopup *>(pwidget);
409       if (popup) {
410         // According to the description of QDialog;
411         // "setParent() function  will clear the window flags specifying the
412         // window-system properties for the widget (in particular it will reset
413         // the Qt::Dialog flag)."
414         // So keep the window flags and set back after calling setParent().
415         Qt::WindowFlags flags = widget->windowFlags();
416         widget->setParent(pwidget);
417         widget->setWindowFlags(flags);
418         return;
419       }
420     }
421   }
422 }
423 
424 //***********************************************************************************
425 //    GenericLoadFilePopup  implementation
426 //***********************************************************************************
427 
GenericLoadFilePopup(const QString & title)428 GenericLoadFilePopup::GenericLoadFilePopup(const QString &title)
429     : FileBrowserPopup(title) {}
430 
431 //-----------------------------------------------------------------------------
432 
execute()433 bool GenericLoadFilePopup::execute() {
434   if (m_selectedPaths.empty()) return false;
435 
436   const TFilePath &path = *m_selectedPaths.begin();
437   assert(!path.isEmpty());  // Should always be, see
438                             // FileBrowserPopup::onOk..()
439   return TFileStatus(path).doesExist();
440 }
441 
442 //-----------------------------------------------------------------------------
443 
getPath()444 TFilePath GenericLoadFilePopup::getPath() {
445   return (exec() == QDialog::Rejected) ? TFilePath() : *m_selectedPaths.begin();
446 }
447 
448 //***********************************************************************************
449 //    GenericSaveFilePopup  implementation
450 //***********************************************************************************
451 
GenericSaveFilePopup(const QString & title)452 GenericSaveFilePopup::GenericSaveFilePopup(const QString &title)
453     : FileBrowserPopup(title, Options(FOR_SAVING)) {
454   connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton,
455           SLOT(animateClick()));
456 }
457 
458 //-----------------------------------------------------------------------------
459 
execute()460 bool GenericSaveFilePopup::execute() {
461   if (m_selectedPaths.empty()) return false;
462 
463   TFilePath path(*m_selectedPaths.begin());
464   assert(!path.isEmpty());
465 
466   // In case the path is not coherent with the specified type filter,
467   // it means that the user specified it by typing and forgot the right type
468   // (yep, even if a DIFFERENT type was specified - next time do it right :P)
469   const QStringList &extList = m_browser->getFilterTypes();
470 
471   if (!m_isDirectoryOnly &&
472       !extList.contains(QString::fromStdString(path.getType()))) {
473     path =
474         TFilePath(path.getWideString() + L"." + extList.first().toStdWString());
475   }
476 
477   // Ask for user permission to overwrite if necessary
478   if (!m_isDirectoryOnly && TFileStatus(path).doesExist()) {
479     const QString &question =
480         QObject::tr("File %1 already exists.\nDo you want to overwrite it?")
481             .arg(toQString(path));
482 
483     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
484                             QObject::tr("Cancel"));
485     if (ret == 0 || ret == 2) return false;
486   }
487 
488   m_selectedPaths.clear();
489   m_selectedPaths.insert(path);
490 
491   return true;
492 }
493 
494 //-----------------------------------------------------------------------------
495 
getPath()496 TFilePath GenericSaveFilePopup::getPath() {
497   return (exec() == QDialog::Rejected) ? TFilePath() : *m_selectedPaths.begin();
498 }
499 
500 //=============================================================================
501 // LoadScenePopup
502 
LoadScenePopup()503 LoadScenePopup::LoadScenePopup() : FileBrowserPopup(tr("Load Scene")) {
504   setOkText(tr("Load"));
505   addFilterType("tnz");
506   addFilterType("xdts");
507 
508   // set the initial current path according to the current module
509   setInitialFolderByCurrentRoom();
510 
511   connect(m_browser, SIGNAL(filePathDoubleClicked(const TFilePath &)), this,
512           SLOT(onFilePathDoubleClicked(const TFilePath &)));
513 }
514 
execute()515 bool LoadScenePopup::execute() {
516   if (m_selectedPaths.empty()) return false;
517 
518   const TFilePath &fp = *m_selectedPaths.begin();
519 
520   if (fp.getType() != "tnz" && fp.getType() != "xdts") {
521     DVGui::error(toQString(fp) + tr(" is not a scene file."));
522     return false;
523   }
524 
525   if (!TFileStatus(fp).doesExist()) {
526     DVGui::error(toQString(fp) + tr(" does not exist."));
527     return false;
528   }
529 
530   return IoCmd::loadScene(fp);
531 }
532 
initFolder()533 void LoadScenePopup::initFolder() { setInitialFolderByCurrentRoom(); }
534 
setInitialFolderByCurrentRoom()535 void LoadScenePopup::setInitialFolderByCurrentRoom() {
536   QString roomName  = TApp::instance()->getCurrentRoomName();
537   TProjectP project = TProjectManager::instance()->getCurrentProject();
538   TFilePath scenePath;
539   if (roomName == "Cleanup" || roomName == "InknPaint")
540     scenePath = project->getFolder(TProject::Drawings, true);
541   else if (roomName == "PltEdit")
542     scenePath = project->getFolder(TProject::Palettes, true);
543   else
544     scenePath = project->getFolder(TProject::Scenes, true);
545   setFolder(scenePath);
546 }
547 
showEvent(QShowEvent * e)548 void LoadScenePopup::showEvent(QShowEvent *e) {
549   m_nameField->clear();
550   FileBrowserPopup::showEvent(e);
551 }
552 
onFilePathDoubleClicked(const TFilePath & path)553 void LoadScenePopup::onFilePathDoubleClicked(const TFilePath &path) {
554   Q_UNUSED(path);
555   onOkPressed();
556 }
557 
558 //=============================================================================
559 // LoadSubScenePopup
560 
LoadSubScenePopup()561 LoadSubScenePopup::LoadSubScenePopup()
562     : FileBrowserPopup(tr("Load Sub-Xsheet")) {
563   setOkText(tr("Load"));
564   addFilterType("tnz");
565   TFilePath scenePath =
566       TProjectManager::instance()->getCurrentProject()->getScenesPath();
567   setFolder(scenePath);
568 }
569 
execute()570 bool LoadSubScenePopup::execute() {
571   if (m_selectedPaths.empty()) return false;
572 
573   const TFilePath &fp = *m_selectedPaths.begin();
574 
575   if (fp.getType() != "tnz") {
576     DVGui::error(toQString(fp) + tr(" is not a scene file."));
577     return false;
578   }
579 
580   if (!TFileStatus(fp).doesExist()) {
581     DVGui::error(toQString(fp) + tr(" does not exist."));
582     return false;
583   }
584 
585   return IoCmd::loadSubScene(fp);
586 }
587 
initFolder()588 void LoadSubScenePopup::initFolder() {
589   setFolder(TProjectManager::instance()->getCurrentProject()->getScenesPath());
590 }
591 
showEvent(QShowEvent * e)592 void LoadSubScenePopup::showEvent(QShowEvent *e) {
593   m_nameField->clear();
594   FileBrowserPopup::showEvent(e);
595 }
596 
597 //=============================================================================
598 // SaveSceneAsPopup
599 
SaveSceneAsPopup()600 SaveSceneAsPopup::SaveSceneAsPopup()
601     : FileBrowserPopup(tr("Save Scene"), Options(FOR_SAVING)) {
602   setOkText(tr("Save"));
603   addFilterType("tnz");
604   connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton,
605           SLOT(animateClick()));
606 }
607 
execute()608 bool SaveSceneAsPopup::execute() {
609   if (m_selectedPaths.empty()) return false;
610 
611   const TFilePath &fp = *m_selectedPaths.begin();
612 
613   if (isSpaceString(QString::fromStdString(
614           fp.getName())))  // Lol. Cmon! Really necessary?
615     return false;
616 
617   return IoCmd::saveScene(fp, 0);
618 }
619 
initFolder()620 void SaveSceneAsPopup::initFolder() {
621   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
622   if (!scene->isUntitled())
623     setFolder(scene->getScenePath().getParentDir());
624   else
625     setFolder(
626         TProjectManager::instance()->getCurrentProject()->getScenesPath());
627 }
628 
629 //=============================================================================
630 // SaveSubSceneAsPopup
631 
SaveSubSceneAsPopup()632 SaveSubSceneAsPopup::SaveSubSceneAsPopup()
633     : FileBrowserPopup(tr("Sub-xsheet"), Options(FOR_SAVING)) {
634   setOkText(tr("Save"));
635   connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton,
636           SLOT(animateClick()));
637 }
638 
execute()639 bool SaveSubSceneAsPopup::execute() {
640   if (m_selectedPaths.empty()) return false;
641 
642   return IoCmd::saveScene(*m_selectedPaths.begin(), IoCmd::SAVE_SUBXSHEET);
643 }
644 
initFolder()645 void SaveSubSceneAsPopup::initFolder() {
646   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
647   if (!scene->isUntitled())
648     setFolder(scene->getScenePath().getParentDir());
649   else
650     setFolder(
651         TProjectManager::instance()->getCurrentProject()->getScenesPath());
652 }
653 
654 //=============================================================================
655 // LoadLevelPopup
656 namespace {
createShowButton(QWidget * parent)657 QPushButton *createShowButton(QWidget *parent) {
658   QPushButton *button = new QPushButton(parent);
659   button->setObjectName("menuToggleButton");
660   button->setFixedSize(15, 15);
661   button->setIcon(createQIcon("menu_toggle"));
662   button->setCheckable(true);
663   button->setChecked(false);
664   button->setAutoDefault(false);
665   return button;
666 }
667 }  // namespace
668 
LoadLevelPopup()669 LoadLevelPopup::LoadLevelPopup()
670     : FileBrowserPopup(tr("Load Level"),
671                        Options(MULTISELECTION | WITH_APPLY_BUTTON), "",
672                        new QWidget(0)) {
673   setModal(false);
674   setOkText(tr("Load"));
675 
676   QWidget *optionWidget = (QWidget *)m_customWidget;
677 
678   // choose tlv caching behavior
679   QLabel *cacheBehaviorLabel = new QLabel(tr("TLV Caching Behavior"), this);
680   m_loadTlvBehaviorComboBox  = new QComboBox(this);
681 
682   //----Load Subsequence Level
683   QPushButton *showSubsequenceButton = createShowButton(this);
684   QLabel *subsequenceLabel = new QLabel(tr("Load Subsequence Level"), this);
685   m_subsequenceFrame       = new QFrame(this);
686   m_fromFrame              = new DVGui::IntLineEdit(this, 1, 1);
687   m_toFrame                = new DVGui::IntLineEdit(this, 1, 1);
688 
689   //----Arrangement in Xsheet
690   m_arrLvlPropWidget                 = new QWidget(this);
691   QPushButton *showArrangementButton = createShowButton(this);
692   QLabel *arrangementLabel =
693       new QLabel(tr("Level Settings & Arrangement in Xsheet"), this);
694   m_arrangementFrame = new QFrame(this);
695   m_xFrom            = new DVGui::IntLineEdit(this, 1, 1);
696   m_xTo              = new DVGui::IntLineEdit(this, 1, 1);
697   m_stepCombo        = new QComboBox(this);
698   m_incCombo         = new QComboBox(this);
699   m_posFrom          = new DVGui::IntLineEdit(this, 1, 1);
700   m_posTo            = new DVGui::IntLineEdit(this, 1, 1);
701 
702   //----Level Properties
703   m_levelPropertiesFrame = new QFrame(this);
704   m_levelName            = new DVGui::LineEdit(this);
705   m_dpiWidget            = new QWidget(this);
706   m_dpiPolicy            = new QComboBox(this);
707   m_dpi                  = new DVGui::DoubleLineEdit(this);
708   m_subsampling          = new DVGui::IntLineEdit(this, 1, 1);
709   m_antialias            = new DVGui::IntLineEdit(this, 10, 0, 100);
710   m_premultiply          = new DVGui::CheckBox(tr("Premultiply"), this);
711   m_whiteTransp = new DVGui::CheckBox(tr("White As Transparent"), this);
712 
713   m_notExistLabel = new QLabel(tr("(FILE DOES NOT EXIST)"));
714 
715   //----
716   m_loadTlvBehaviorComboBox->addItem(tr("On Demand"),
717                                      IoCmd::LoadResourceArguments::ON_DEMAND);
718   m_loadTlvBehaviorComboBox->addItem(tr("All Icons"),
719                                      IoCmd::LoadResourceArguments::ALL_ICONS);
720   m_loadTlvBehaviorComboBox->addItem(
721       tr("All Icons & Images"),
722       IoCmd::LoadResourceArguments::ALL_ICONS_AND_IMAGES);
723   // use the default value set in the preference
724   m_loadTlvBehaviorComboBox->setCurrentIndex(
725       m_loadTlvBehaviorComboBox->findData(
726           Preferences::instance()->getInitialLoadTlvCachingBehavior()));
727   cacheBehaviorLabel->setObjectName("TitleTxtLabel");
728 
729   //----Load Subsequence Level
730   m_subsequenceFrame->setObjectName("LoadLevelFrame");
731   subsequenceLabel->setObjectName("TitleTxtLabel");
732   m_subsequenceFrame->hide();
733   m_fromFrame->setMaximumWidth(50);
734   m_toFrame->setMaximumWidth(50);
735 
736   //----Arrangement in Xsheet
737   m_arrangementFrame->setObjectName("LoadLevelFrame");
738   m_levelPropertiesFrame->setObjectName("LoadLevelFrame");
739   arrangementLabel->setObjectName("TitleTxtLabel");
740   m_arrLvlPropWidget->hide();
741 
742   QStringList sList;
743   sList << QString("Auto") << QString("1") << QString("2") << QString("3")
744         << QString("4") << QString("5") << QString("6") << QString("7")
745         << QString("8");
746   m_stepCombo->addItems(sList);
747   m_incCombo->addItems(sList);
748 
749   //----Level Properties
750   m_dpiPolicy->addItem(QObject::tr("Image DPI"), LevelOptions::DP_ImageDpi);
751   m_dpiPolicy->addItem(QObject::tr("Custom DPI"), LevelOptions::DP_CustomDpi);
752   m_dpi->setRange(1, (std::numeric_limits<double>::max)());
753   m_dpi->setFixedWidth(54);
754 
755   // initialize with the default value
756   LevelOptions options;
757   setLevelProperties(options);
758 
759   //"FILE DOES NOT EXIST" lavel
760   m_notExistLabel->setObjectName("FileDoesNotExistLabel");
761   m_notExistLabel->hide();
762 
763   //----layout
764   auto createVBoxLayout = [](int margin, int spacing) {
765     QVBoxLayout *layout = new QVBoxLayout();
766     layout->setMargin(margin);
767     layout->setSpacing(spacing);
768     return layout;
769   };
770   auto createHBoxLayout = [](int margin, int spacing) {
771     QHBoxLayout *layout = new QHBoxLayout();
772     layout->setMargin(margin);
773     layout->setSpacing(spacing);
774     return layout;
775   };
776 
777   QVBoxLayout *mainLayout = createVBoxLayout(5, 3);
778   {
779     QHBoxLayout *cacheLay = createHBoxLayout(0, 5);
780     {
781       cacheLay->addStretch(1);
782       cacheLay->addWidget(cacheBehaviorLabel, 0);
783       cacheLay->addWidget(m_loadTlvBehaviorComboBox, 0);
784     }
785     mainLayout->addLayout(cacheLay, 0);
786 
787     //----Load Subsequence Level
788 
789     QHBoxLayout *subsequenceHeadLay = createHBoxLayout(0, 5);
790     {
791       QFontMetrics metrics(font());
792       subsequenceHeadLay->addSpacing(metrics.width("File name:") + 3);
793       subsequenceHeadLay->addWidget(m_notExistLabel, 0);
794       subsequenceHeadLay->addStretch(1);
795 
796       subsequenceHeadLay->addWidget(subsequenceLabel, 0);
797       subsequenceHeadLay->addWidget(showSubsequenceButton, 0);
798     }
799     mainLayout->addLayout(subsequenceHeadLay, 0);
800 
801     QHBoxLayout *subsequenceLay = createHBoxLayout(5, 5);
802     {
803       subsequenceLay->addWidget(new QLabel(tr("From:"), this), 0);
804       subsequenceLay->addWidget(m_fromFrame, 0);
805       subsequenceLay->addWidget(new QLabel(tr(" To:"), this), 0);
806       subsequenceLay->addWidget(m_toFrame, 0);
807     }
808     m_subsequenceFrame->setLayout(subsequenceLay);
809     mainLayout->addWidget(m_subsequenceFrame, 0,
810                           Qt::AlignRight | Qt::AlignVCenter);
811 
812     //----Arrangement in Xsheet
813 
814     QHBoxLayout *arrangementHeadLay = createHBoxLayout(0, 3);
815     {
816       arrangementHeadLay->addWidget(arrangementLabel, 1,
817                                     Qt::AlignRight | Qt::AlignVCenter);
818       arrangementHeadLay->addWidget(showArrangementButton, 0);
819     }
820     mainLayout->addLayout(arrangementHeadLay);
821 
822     QHBoxLayout *bottomLay = createHBoxLayout(0, 10);
823     {
824       QGridLayout *levelLay = new QGridLayout();
825       levelLay->setMargin(5);
826       levelLay->setSpacing(5);
827       {
828         levelLay->addWidget(new QLabel(tr("Level Name:"), this), 0, 0,
829                             Qt::AlignRight | Qt::AlignVCenter);
830         levelLay->addWidget(m_levelName, 0, 1);
831         QHBoxLayout *dpiLay = createHBoxLayout(0, 5);
832         {
833           dpiLay->addSpacing(10);
834           dpiLay->addWidget(new QLabel(tr("DPI:"), this), 0,
835                             Qt::AlignRight | Qt::AlignVCenter);
836           dpiLay->addWidget(m_dpiPolicy, 0);
837           dpiLay->addWidget(m_dpi, 1);
838         }
839         m_dpiWidget->setLayout(dpiLay);
840         levelLay->addWidget(m_dpiWidget, 0, 2, 1, 3);
841 
842         levelLay->addWidget(m_premultiply, 1, 0, 1, 2);
843         levelLay->addWidget(m_whiteTransp, 2, 0, 1, 2);
844 
845         // levelLay->addWidget(m_doAntialias, 1, 3, 1, 2);
846         levelLay->addWidget(new QLabel(tr("Antialias Softness:"), this), 1, 2,
847                             1, 2, Qt::AlignRight | Qt::AlignVCenter);
848         levelLay->addWidget(m_antialias, 1, 4);
849         levelLay->addWidget(new QLabel(tr("Subsampling:"), this), 2, 2, 1, 2,
850                             Qt::AlignRight | Qt::AlignVCenter);
851         levelLay->addWidget(m_subsampling, 2, 4);
852       }
853       levelLay->setColumnStretch(1, 1);
854       levelLay->setColumnStretch(4, 1);
855       m_levelPropertiesFrame->setLayout(levelLay);
856       bottomLay->addWidget(m_levelPropertiesFrame, 0);
857 
858       QGridLayout *arrLay = new QGridLayout();
859       arrLay->setMargin(5);
860       arrLay->setSpacing(5);
861       {
862         arrLay->addWidget(new QLabel(tr("From:"), this), 0, 0,
863                           Qt::AlignRight | Qt::AlignVCenter);
864         arrLay->addWidget(m_xFrom, 0, 1);
865         arrLay->addWidget(new QLabel(tr(" To:"), this), 0, 2,
866                           Qt::AlignRight | Qt::AlignVCenter);
867         arrLay->addWidget(m_xTo, 0, 3);
868         arrLay->addWidget(new QLabel(tr(" Step:"), this), 1, 0,
869                           Qt::AlignRight | Qt::AlignVCenter);
870         arrLay->addWidget(m_stepCombo, 1, 1);
871         arrLay->addWidget(new QLabel(tr(" Inc:"), this), 1, 2,
872                           Qt::AlignRight | Qt::AlignVCenter);
873         arrLay->addWidget(m_incCombo, 1, 3);
874         arrLay->addWidget(new QLabel(tr(" Frames:"), this), 2, 0,
875                           Qt::AlignRight | Qt::AlignVCenter);
876         arrLay->addWidget(m_posFrom, 2, 1);
877         arrLay->addWidget(new QLabel(tr("::"), this), 2, 2, Qt::AlignCenter);
878         arrLay->addWidget(m_posTo, 2, 3);
879       }
880       arrLay->setColumnStretch(1, 1);
881       arrLay->setColumnStretch(3, 1);
882       m_arrangementFrame->setLayout(arrLay);
883       bottomLay->addWidget(m_arrangementFrame, 0);
884     }
885     m_arrLvlPropWidget->setLayout(bottomLay);
886 
887     mainLayout->addWidget(m_arrLvlPropWidget, 0,
888                           Qt::AlignRight | Qt::AlignVCenter);
889   }
890   optionWidget->setLayout(mainLayout);
891 
892   //----signal-slot connections
893   //----Load Subsequence Level
894   connect(showSubsequenceButton, SIGNAL(toggled(bool)), m_subsequenceFrame,
895           SLOT(setVisible(bool)));
896   connect(m_fromFrame, SIGNAL(editingFinished()),
897           SLOT(onSubsequentFrameChanged()));
898   connect(m_toFrame, SIGNAL(editingFinished()),
899           SLOT(onSubsequentFrameChanged()));
900 
901   //----Arrangement in Xsheet
902   connect(showArrangementButton, SIGNAL(toggled(bool)), m_arrLvlPropWidget,
903           SLOT(setVisible(bool)));
904   connect(m_xFrom, SIGNAL(editingFinished()), SLOT(updatePosTo()));
905   connect(m_xTo, SIGNAL(editingFinished()), SLOT(updatePosTo()));
906   connect(m_posFrom, SIGNAL(editingFinished()), SLOT(updatePosTo()));
907   connect(m_stepCombo, SIGNAL(currentIndexChanged(int)), SLOT(updatePosTo()));
908   connect(m_incCombo, SIGNAL(currentIndexChanged(int)), SLOT(updatePosTo()));
909 
910   connect(m_nameField, SIGNAL(editingFinished()), this,
911           SLOT(onNameSetEditted()));
912   connect(m_browser, SIGNAL(treeFolderChanged(const TFilePath &)), this,
913           SLOT(onNameSetEditted()));
914   connect(m_browser, SIGNAL(filePathDoubleClicked(const TFilePath &)), this,
915           SLOT(onFilePathDoubleClicked(const TFilePath &)));
916   //----Level Properties
917   connect(m_dpiPolicy, SIGNAL(activated(int)), this,
918           SLOT(onDpiPolicyActivated()));
919   connect(m_premultiply, SIGNAL(clicked(bool)), this,
920           SLOT(onDoPremultiplyClicked()));
921   connect(m_whiteTransp, SIGNAL(clicked(bool)), this,
922           SLOT(onWhiteTranspClicked()));
923 }
924 
925 //-----------------------------------------------------------------------
926 
onNameSetEditted()927 void LoadLevelPopup::onNameSetEditted() {
928   getCurrentPathSet().clear();
929   getCurrentFIdsSet().clear();
930 
931   // if nothing input
932   if (m_nameField->text() == "") {
933     m_notExistLabel->hide();
934     updateBottomGUI();
935   }
936   // if the path exists
937   else {
938     TFilePath path =
939         m_browser->getFolder() + TFilePath(m_nameField->text().toStdString());
940     getCurrentPathSet().insert(path);
941     if (TSystem::doesExistFileOrLevel(path)) {
942       m_notExistLabel->hide();
943       updateBottomGUI();
944     } else {
945       m_notExistLabel->show();
946 
947       m_fromFrame->setText("1");
948       m_toFrame->setText("1");
949       m_subsequenceFrame->setEnabled(true);
950 
951       m_xFrom->setText("1");
952       m_xTo->setText("1");
953 
954       m_levelName->setText(QString::fromStdString(path.getName()));
955 
956       m_arrangementFrame->setEnabled(true);
957       m_levelName->setEnabled(true);
958       m_levelPropertiesFrame->setEnabled(true);
959       updatePosTo();
960     }
961   }
962 
963   update();
964 }
965 
966 //-----------------------------------------------------------------------
967 
updatePosTo()968 void LoadLevelPopup::updatePosTo() {
969   // calcurate how mane frames to be occupied in the xsheet
970   TFilePath fp = getCurrentPath();
971 
972   if (QString::fromStdString(fp.getType()) == "tpl") {
973     m_posTo->setText(m_posFrom->text());
974     return;
975   }
976 
977   int xFrom = m_xFrom->text().toInt();
978   int xTo   = m_xTo->text().toInt();
979 
980   int frameLength;
981 
982   bool isScene = (QString::fromStdString(fp.getType()) == "tnz");
983 
984   //--- if loading the "missing" level
985   if (m_notExistLabel->isVisible()) {
986     int inc = m_incCombo->currentIndex();
987     if (inc == 0)  // Inc = Auto
988     {
989       frameLength = (xTo - xFrom + 1) * ((m_stepCombo->currentIndex() == 0)
990                                              ? 1
991                                              : m_stepCombo->currentIndex());
992 
993     } else  // Inc =! Auto
994     {
995       int loopAmount;
996       loopAmount  = tceil((double)(xTo - xFrom + 1) / (double)inc);
997       frameLength = loopAmount * ((m_stepCombo->currentIndex() == 0)
998                                       ? inc
999                                       : m_stepCombo->currentIndex());
1000     }
1001   }
1002 
1003   //-- if loading the existing level
1004   else if (m_incCombo->currentIndex() == 0)  // Inc = Auto
1005   {
1006     if (isScene) {
1007       frameLength = xTo - xFrom + 1;
1008     } else {
1009       std::vector<TFrameId> fIds = getCurrentFIds();
1010       //--- If loading the level with sequencial files, reuse the list of
1011       // TFrameId
1012       if (fIds.size() != 0) {
1013         if (m_stepCombo->currentIndex() == 0)  // Step = Auto
1014         {
1015           std::vector<TFrameId>::iterator it;
1016           int firstFrame = 0;
1017           int lastFrame  = 0;
1018           for (it = fIds.begin(); it != fIds.end(); it++) {
1019             if (xFrom <= it->getNumber()) {
1020               firstFrame = it->getNumber();
1021               break;
1022             }
1023           }
1024           for (it = fIds.begin(); it != fIds.end(); it++) {
1025             if (it->getNumber() <= xTo) {
1026               lastFrame = it->getNumber();
1027             }
1028           }
1029           frameLength = lastFrame - firstFrame + 1;
1030         } else  // Step != Auto
1031         {
1032           std::vector<TFrameId>::iterator it;
1033           int loopAmount = 0;
1034           for (it = fIds.begin(); it != fIds.end(); it++) {
1035             if (xFrom <= it->getNumber() && it->getNumber() <= xTo)
1036               loopAmount++;
1037           }
1038           frameLength = loopAmount * m_stepCombo->currentIndex();
1039         }
1040 
1041       }
1042       // loading another type of level such as tlv
1043       else {
1044         if (fp.isEmpty()) return;
1045         try {
1046           TLevelReaderP lr(fp);
1047           TLevelP level;
1048           if (lr) level = lr->loadInfo();
1049           if (!level.getPointer()) return;
1050 
1051           if (m_stepCombo->currentIndex() == 0)  // Step = Auto
1052           {
1053             TLevel::Iterator it;
1054             int firstFrame = 0;
1055             int lastFrame  = 0;
1056             for (it = level->begin(); it != level->end(); it++) {
1057               if (xFrom <= it->first.getNumber()) {
1058                 firstFrame = it->first.getNumber();
1059                 break;
1060               }
1061             }
1062             for (it = level->begin(); it != level->end(); it++) {
1063               if (it->first.getNumber() <= xTo) {
1064                 lastFrame = it->first.getNumber();
1065               }
1066             }
1067             frameLength = lastFrame - firstFrame + 1;
1068           } else  // Step != Auto
1069           {
1070             TLevel::Iterator it;
1071             int loopAmount = 0;
1072             for (it = level->begin(); it != level->end(); it++) {
1073               if (xFrom <= it->first.getNumber() &&
1074                   it->first.getNumber() <= xTo)
1075                 loopAmount++;
1076             }
1077             frameLength = loopAmount * m_stepCombo->currentIndex();
1078           }
1079         } catch (...) {
1080           return;
1081         }
1082       }
1083     }
1084   }
1085   // Inc != Auto
1086   else {
1087     int inc = m_incCombo->currentIndex();
1088     int loopAmount;
1089     loopAmount  = tceil((double)(xTo - xFrom + 1) / (double)inc);
1090     frameLength = loopAmount * ((m_stepCombo->currentIndex() == 0)
1091                                     ? inc
1092                                     : m_stepCombo->currentIndex());
1093   }
1094 
1095   m_posTo->setText(
1096       QString::number(m_posFrom->text().toInt() + frameLength - 1));
1097 }
1098 //-----------------------------------------------------------------------
1099 /*! if the from / to values in the subsequent box, update m_xFrom and m_xTo
1100  */
onSubsequentFrameChanged()1101 void LoadLevelPopup::onSubsequentFrameChanged() {
1102   m_xFrom->setText(m_fromFrame->text());
1103   m_xTo->setText(m_toFrame->text());
1104   updatePosTo();
1105 }
1106 
1107 //-----------------------------------------------------------------------
1108 
showEvent(QShowEvent * e)1109 void LoadLevelPopup::showEvent(QShowEvent *e) {
1110   m_nameField->clear();
1111 
1112   FileBrowserPopup::showEvent(e);
1113 
1114   bool ret         = true;
1115   TFrameHandle *fh = TApp::instance()->getCurrentFrame();
1116   ret              = ret &&
1117         connect(fh, SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
1118   ret = ret &&
1119         connect(fh, SIGNAL(frameTypeChanged()), this, SLOT(onFrameSwitched()));
1120 
1121   TSelectionHandle *sh = TApp::instance()->getCurrentSelection();
1122   ret = ret && connect(sh, SIGNAL(selectionChanged(TSelection *)), this,
1123                        SLOT(onSelectionChanged(TSelection *)));
1124   ret = ret && connect(TApp::instance()->getCurrentScene(),
1125                        SIGNAL(preferenceChanged(const QString &)), this,
1126                        SLOT(onPreferenceChanged(const QString &)));
1127   assert(ret);
1128 
1129   onFrameSwitched();
1130   onSelectionChanged(sh->getSelection());
1131   onPreferenceChanged("");
1132   onNameSetEditted();  // clear currentPathSet
1133 }
1134 
1135 //-----------------------------------------------------------------------
1136 
hideEvent(QHideEvent * e)1137 void LoadLevelPopup::hideEvent(QHideEvent *e) {
1138   FileBrowserPopup::hideEvent(e);
1139 
1140   TFrameHandle *fh = TApp::instance()->getCurrentFrame();
1141   disconnect(fh, SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
1142   disconnect(fh, SIGNAL(frameTypeChanged()), this, SLOT(onFrameSwitched()));
1143 
1144   TSelectionHandle *sh = TApp::instance()->getCurrentSelection();
1145   disconnect(sh, SIGNAL(selectionChanged(TSelection *)), this,
1146              SLOT(onSelectionChanged(TSelection *)));
1147   disconnect(TApp::instance()->getCurrentScene(),
1148              SIGNAL(preferenceChanged(const QString &)), this,
1149              SLOT(onPreferenceChanged(const QString &)));
1150 }
1151 
1152 //-----------------------------------------------------------------------
1153 
onFrameSwitched()1154 void LoadLevelPopup::onFrameSwitched() {
1155   TFrameHandle *fh = TApp::instance()->getCurrentFrame();
1156   if (fh->isEditingLevel())
1157     m_posFrom->setText("1");
1158   else
1159     m_posFrom->setText(QString::number(fh->getFrame() + 1));
1160   updatePosTo();
1161 }
1162 
1163 //-----------------------------------------------------------------------
1164 
execute()1165 bool LoadLevelPopup::execute() {
1166   if (m_selectedPaths.size() == 1) {
1167     const TFilePath &fp = *m_selectedPaths.begin();
1168     //---- SubSequent load
1169     // if loading the "missing" level
1170     if (m_notExistLabel->isVisible()) {
1171       int firstFrameNumber = m_fromFrame->text().toInt();
1172       int lastFrameNumber  = m_toFrame->text().toInt();
1173       setLoadingLevelRange(firstFrameNumber, lastFrameNumber);
1174     } else if (m_subsequenceFrame->isEnabled() &&
1175                m_subsequenceFrame->isVisible()) {
1176       std::vector<TFrameId> fIds = getCurrentFIds();
1177       TFrameId firstFrame;
1178       TFrameId lastFrame;
1179       // if the level is sequencial and there is a reusable list of TFrameId
1180       if (fIds.size() != 0) {
1181         firstFrame = fIds[0];
1182         lastFrame  = fIds[fIds.size() - 1];
1183       }
1184       // another case such as loading tlv
1185       else {
1186         try {
1187           TLevelReaderP lr(fp);
1188           TLevelP level;
1189           if (lr) level = lr->loadInfo();
1190           if (!level.getPointer()) return false;
1191 
1192           firstFrame = level->begin()->first;
1193           lastFrame  = (--level->end())->first;
1194           lr         = TLevelReaderP();
1195         } catch (...) {
1196           return false;
1197         }
1198       }
1199       int firstFrameNumber = m_fromFrame->text().toInt();
1200       int lastFrameNumber  = m_toFrame->text().toInt();
1201       if (firstFrame.getNumber() != firstFrameNumber ||
1202           lastFrame.getNumber() != lastFrameNumber)
1203         setLoadingLevelRange(firstFrameNumber, lastFrameNumber);
1204     }
1205 
1206     IoCmd::LoadResourceArguments args(fp);
1207 
1208     args.row0 = m_posFrom->text().toInt() - 1;
1209 
1210     args.frameCount = m_posTo->text().toInt() - m_posFrom->text().toInt() + 1;
1211 
1212     if ((int)getCurrentFIdsSet().size() != 0)
1213       args.frameIdsSet.push_back(*getCurrentFIdsSet().begin());
1214 
1215     else if (m_notExistLabel->isVisible()) {
1216       int firstFrameNumber = m_fromFrame->text().toInt();
1217       int lastFrameNumber  = m_toFrame->text().toInt();
1218       // putting the Fids in order to avoid LoadInfo later
1219       std::vector<TFrameId> tmp_fids;
1220       for (int i = firstFrameNumber; i <= lastFrameNumber; i++) {
1221         tmp_fids.push_back(TFrameId(i));
1222       }
1223       args.frameIdsSet.push_back(tmp_fids);
1224     }
1225 
1226     int xFrom = m_xFrom->text().toInt();
1227     if (xFrom) args.xFrom = xFrom;
1228     int xTo = m_xTo->text().toInt();
1229     if (xTo) args.xTo = xTo;
1230 
1231     args.levelName             = m_levelName->text().toStdWString();
1232     args.step                  = m_stepCombo->currentIndex();
1233     args.inc                   = m_incCombo->currentIndex();
1234     args.doesFileActuallyExist = !m_notExistLabel->isVisible();
1235     args.cachingBehavior       = IoCmd::LoadResourceArguments::CacheTlvBehavior(
1236         m_loadTlvBehaviorComboBox->currentData().toInt());
1237 
1238     if (m_arrLvlPropWidget->isVisible() &&
1239         m_levelPropertiesFrame->isEnabled()) {
1240       for (IoCmd::LoadResourceArguments::ResourceData &rd :
1241            args.resourceDatas) {
1242         rd.m_options = LevelOptions();
1243         getLevelProperties(*rd.m_options);
1244       }
1245     }
1246 
1247     return 0 < IoCmd::loadResources(args, true, 0);
1248 
1249   } else {
1250     std::set<TFilePath>::const_iterator it;
1251     IoCmd::LoadResourceArguments args;
1252 
1253     args.row0 = m_posFrom->text().toInt() - 1;
1254 
1255     for (it = m_selectedPaths.begin(); it != m_selectedPaths.end(); ++it)
1256       args.resourceDatas.push_back(*it);
1257 
1258     std::list<std::vector<TFrameId>> fIdsSet = getCurrentFIdsSet();
1259     if (fIdsSet.size() > 0) {
1260       std::list<std::vector<TFrameId>>::const_iterator fIdIt;
1261       for (fIdIt = fIdsSet.begin(); fIdIt != fIdsSet.end(); ++fIdIt)
1262         args.frameIdsSet.insert(args.frameIdsSet.begin(), *fIdIt);
1263     }
1264 
1265     args.cachingBehavior = IoCmd::LoadResourceArguments::CacheTlvBehavior(
1266         m_loadTlvBehaviorComboBox->currentData().toInt());
1267 
1268     if (m_arrLvlPropWidget->isVisible() &&
1269         m_levelPropertiesFrame->isEnabled()) {
1270       for (IoCmd::LoadResourceArguments::ResourceData &rd :
1271            args.resourceDatas) {
1272         rd.m_options = LevelOptions();
1273         getLevelProperties(*rd.m_options);
1274       }
1275     }
1276 
1277     return 0 < IoCmd::loadResources(args, true, 0);
1278   }
1279 }
1280 
1281 //-----------------------------------------------------------------------
1282 
initFolder()1283 void LoadLevelPopup::initFolder() {
1284   TFilePath fp;
1285 
1286   TProject *project =
1287       TProjectManager::instance()->getCurrentProject().getPointer();
1288   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1289 
1290   if (scene) fp = scene->decodeFilePath(project->getProjectFolder());
1291 
1292   setFolder(fp);
1293   onFilePathsSelected(getCurrentPathSet(), getCurrentFIdsSet());
1294 }
1295 
1296 //----------------------------------------------------------------------------
1297 
onFilePathDoubleClicked(const TFilePath & path)1298 void LoadLevelPopup::onFilePathDoubleClicked(const TFilePath &path) {
1299   Q_UNUSED(path);
1300   onOkPressed();
1301 }
1302 
1303 //----------------------------------------------------------------------------
1304 
onFilePathsSelected(const std::set<TFilePath> & paths,const std::list<std::vector<TFrameId>> & fIds)1305 void LoadLevelPopup::onFilePathsSelected(
1306     const std::set<TFilePath> &paths,
1307     const std::list<std::vector<TFrameId>> &fIds) {
1308   m_notExistLabel->hide();
1309   FileBrowserPopup::onFilePathsSelected(paths, fIds);
1310   updateBottomGUI();
1311 }
1312 
1313 //----------------------------------------------------------------------------
1314 
updateBottomGUI()1315 void LoadLevelPopup::updateBottomGUI() {
1316   auto disableAll = [&]() {
1317     m_fromFrame->setText("");
1318     m_toFrame->setText("");
1319     m_subsequenceFrame->setEnabled(false);
1320 
1321     m_xFrom->setText("");
1322     m_xTo->setText("");
1323     m_levelName->setText("");
1324     m_posTo->setText("");
1325     m_arrangementFrame->setEnabled(false);
1326     m_levelPropertiesFrame->setEnabled(false);
1327   };
1328 
1329   std::set<TFilePath> paths                = getCurrentPathSet();
1330   std::list<std::vector<TFrameId>> fIdsSet = getCurrentFIdsSet();
1331 
1332   if (paths.empty() || paths.size() > 1) {
1333     disableAll();
1334     if (paths.size() > 1) {
1335       m_levelName->setEnabled(false);
1336       m_levelPropertiesFrame->setEnabled(true);
1337     }
1338     return;
1339   }
1340 
1341   TFilePath fp               = getCurrentPath();
1342   std::vector<TFrameId> fIds = getCurrentFIds();
1343 
1344   QString ext = QString::fromStdString(fp.getType());
1345 
1346   // initialize
1347   if (fp.isEmpty() || ext == "") {
1348     disableAll();
1349     return;
1350   } else if (ext == "tpl") {
1351     QString str;
1352     m_fromFrame->setText(str.number(1));
1353     m_toFrame->setText(str.number(1));
1354     m_subsequenceFrame->setEnabled(false);
1355 
1356     m_xFrom->setText("1");
1357     m_xTo->setText("1");
1358     m_levelName->setText(QString::fromStdString(fp.getName()));
1359     m_posTo->setText(m_posFrom->text());
1360     m_arrangementFrame->setEnabled(false);
1361     m_levelPropertiesFrame->setEnabled(false);
1362   } else if (ext == "tnz") {
1363     ToonzScene scene;
1364     scene.setScenePath(fp);
1365     int sceneLength = scene.getFrameCount();
1366     QString str;
1367     m_fromFrame->setText(str.number(std::min(1, sceneLength)));
1368     m_toFrame->setText(str.number(sceneLength));
1369     m_subsequenceFrame->setEnabled(false);
1370 
1371     m_xFrom->setText(m_fromFrame->text());
1372     m_xTo->setText(m_toFrame->text());
1373     m_levelName->setText(QString::fromStdString(fp.getName()));
1374     m_stepCombo->setCurrentIndex(0);
1375     m_incCombo->setCurrentIndex(0);
1376     m_arrangementFrame->setEnabled(false);
1377     m_levelPropertiesFrame->setEnabled(false);
1378   } else {
1379     TFrameId firstFrame;
1380     TFrameId lastFrame;
1381     // for level with sequential frames
1382     if (fIds.size() != 0) {
1383       firstFrame = fIds[0];
1384       lastFrame  = fIds[fIds.size() - 1];
1385     } else {
1386       try {
1387         TLevelReaderP lr(fp);
1388         TLevelP level;
1389         if (lr) level = lr->loadInfo();
1390         if (!level.getPointer() || level->getTable()->size() == 0) return;
1391 
1392         firstFrame = level->begin()->first;
1393         lastFrame  = (--level->end())->first;
1394       } catch (...) {
1395         disableAll();
1396         return;
1397       }
1398     }
1399 
1400     m_fromFrame->setText(QString().number(firstFrame.getNumber()));
1401     m_toFrame->setText(QString().number(lastFrame.getNumber()));
1402     m_subsequenceFrame->setEnabled(true);
1403 
1404     m_xFrom->setText(m_fromFrame->text());
1405     m_xTo->setText(m_toFrame->text());
1406 
1407     // if some option in the preferences is selected, load the level with
1408     // removing
1409     // six letters of the scene name from the level name
1410     m_levelName->setText(getLevelNameWithoutSceneNumber(fp.getName()));
1411 
1412     // If the option "Show "ABC" Appendix to the Frame Number in Xsheet Cell" is
1413     // ON, frame numbers normally increment at interval of 10.
1414     // Placing such level with "Auto" step option will cause unwanted
1415     // spacing between frames in Xsheet. Setting the step to "1" can prevent
1416     // such problem.
1417     if (Preferences::instance()->isShowFrameNumberWithLettersEnabled() &&
1418         m_stepCombo->currentIndex() == 0)
1419       m_stepCombo->setCurrentIndex(1);
1420 
1421     m_arrangementFrame->setEnabled(true);
1422 
1423     m_levelName->setEnabled(true);
1424     m_levelPropertiesFrame->setEnabled(true);
1425   }
1426   updatePosTo();
1427 }
1428 //----------------------------------------------------------------------------
1429 
1430 //----------------------------------------------------------------------------
1431 /*! if some option in the preferences is selected, load the level with removing
1432         six letters of the scene name from the level name
1433 */
getLevelNameWithoutSceneNumber(std::string orgName)1434 QString LoadLevelPopup::getLevelNameWithoutSceneNumber(std::string orgName) {
1435   QString levelOrgName = QString::fromStdString(orgName);
1436 
1437   // check out the preference
1438   if (!Preferences::instance()->isRemoveSceneNumberFromLoadedLevelNameEnabled())
1439     return levelOrgName;
1440 
1441   // do nothing if the level name has less than 7 letters
1442   if (levelOrgName.size() <= 6) return levelOrgName;
1443 
1444   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1445   if (!scene) return levelOrgName;
1446 
1447   QString sceneName = QString::fromStdWString(scene->getSceneName()).left(5);
1448 
1449   // if the first 5 letters are same a the scene name, then remove the letters
1450   // to the under score
1451   // this code is intended to cover both the case "c0001_hogehoge.tif" and
1452   // "c0001A_tif"
1453   if (!levelOrgName.startsWith(sceneName)) return levelOrgName;
1454 
1455   if (!levelOrgName.contains("_")) return levelOrgName;
1456 
1457   return levelOrgName.right(levelOrgName.size() - levelOrgName.indexOf("_") -
1458                             1);
1459 }
1460 
1461 //----------------------------------------------------------------------------
1462 /*! if the x-sheet cells are selected, load levels at the upper-left corner of
1463  * the selection
1464  */
onSelectionChanged(TSelection * selection)1465 void LoadLevelPopup::onSelectionChanged(TSelection *selection) {
1466   TCellSelection *cellSelection = dynamic_cast<TCellSelection *>(selection);
1467 
1468   if (!cellSelection) return;
1469 
1470   int r0, r1, c0, c1;
1471   cellSelection->getSelectedCells(r0, c0, r1, c1);
1472 
1473   m_posFrom->setText(QString::number(r0 + 1));
1474 
1475   updatePosTo();
1476 }
1477 
1478 //----------------------------------------------------------------------------
1479 
onPreferenceChanged(const QString & propertyName)1480 void LoadLevelPopup::onPreferenceChanged(const QString &propertyName) {
1481   if (!propertyName.isEmpty() && propertyName != "pixelsOnly") return;
1482   bool pixelsMode = Preferences::instance()->getBoolValue(pixelsOnly);
1483   m_dpiWidget->setHidden(pixelsMode);
1484   if (pixelsMode) {
1485     m_dpiPolicy->setCurrentIndex(
1486         m_dpiPolicy->findData(LevelOptions::DP_ImageDpi));
1487     m_dpi->setValue(Stage::standardDpi);
1488     m_dpi->setEnabled(false);
1489   }
1490 }
1491 
1492 //----------------------------------------------------------------------------
1493 
setLevelProperties(LevelOptions & options)1494 void LoadLevelPopup::setLevelProperties(LevelOptions &options) {
1495   m_dpiPolicy->setCurrentIndex(m_dpiPolicy->findData(options.m_dpiPolicy));
1496   m_dpi->setValue(options.m_dpi);
1497   m_subsampling->setValue(options.m_subsampling);
1498   m_antialias->setValue(options.m_antialias);
1499   m_whiteTransp->setChecked(options.m_whiteTransp);
1500   m_premultiply->setChecked(options.m_premultiply);
1501   onDpiPolicyActivated();
1502 }
1503 
1504 //----------------------------------------------------------------------------
1505 
getLevelProperties(LevelOptions & options)1506 void LoadLevelPopup::getLevelProperties(LevelOptions &options) {
1507   options.m_dpiPolicy =
1508       LevelOptions::DpiPolicy(m_dpiPolicy->currentData().toInt());
1509   options.m_dpi         = m_dpi->getValue();
1510   options.m_subsampling = m_subsampling->getValue();
1511   options.m_antialias   = m_antialias->getValue();
1512   options.m_whiteTransp = m_whiteTransp->isChecked();
1513   options.m_premultiply = m_premultiply->isChecked();
1514 }
1515 
1516 //----------------------------------------------------------------------------
1517 
onDpiPolicyActivated()1518 void LoadLevelPopup::onDpiPolicyActivated() {
1519   m_dpi->setEnabled(m_dpiPolicy->currentData().toInt() ==
1520                     LevelOptions::DP_CustomDpi);
1521 }
1522 
1523 //----------------------------------------------------------------------------
1524 // exclusive with the whiteTransp option
onDoPremultiplyClicked()1525 void LoadLevelPopup::onDoPremultiplyClicked() {
1526   if (m_whiteTransp->isChecked()) m_whiteTransp->setChecked(false);
1527 }
1528 
1529 //----------------------------------------------------------------------------
1530 // exclusive with the doPremultiply option
onWhiteTranspClicked()1531 void LoadLevelPopup::onWhiteTranspClicked() {
1532   if (m_premultiply->isChecked()) m_premultiply->setChecked(false);
1533 }
1534 
1535 //=============================================================================
1536 // SaveLevelAsPopup
1537 
SaveLevelAsPopup()1538 SaveLevelAsPopup::SaveLevelAsPopup()
1539     : FileBrowserPopup(tr("Save Level"), Options(FOR_SAVING)) {
1540   setOkText(tr("Save"));
1541   connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton,
1542           SLOT(animateClick()));
1543 }
1544 
execute()1545 bool SaveLevelAsPopup::execute() {
1546   if (m_selectedPaths.empty()) return false;
1547 
1548   TFilePath &fp = (TFilePath &)*m_selectedPaths.begin();
1549 
1550   if (isSpaceString(QString::fromStdString(fp.getName()))) return false;
1551 
1552   // pointer to be replaced
1553   TXshLevel *levelToBeReplaced =
1554       TApp::instance()->getCurrentLevel()->getLevel();
1555   TXsheet *xsh       = TApp::instance()->getCurrentXsheet()->getXsheet();
1556   int curColumnIndex = TApp::instance()->getCurrentColumn()->getColumnIndex();
1557 
1558   bool ret = IoCmd::saveLevel(fp);
1559 
1560   // ask whether to expose the saved level in xsheet
1561   bool doExpose = true;
1562   if (levelToBeReplaced->getType() & FULLCOLOR_TYPE)
1563     doExpose = false;
1564   else if (ret &&
1565            !Preferences::instance()->isReplaceAfterSaveLevelAsEnabled()) {
1566     QString question(QObject::tr("Do you want to expose the renamed level ?"));
1567     int val = DVGui::MsgBox(question,
1568                             QObject::tr("Expose"),            // val = 1
1569                             QObject::tr("Don't expose"), 0);  // val = 2
1570     if (val == 0) return false;                               // close button
1571     if (val == 2) doExpose = false;
1572   }
1573 
1574   // exposing the level
1575   if (ret && doExpose) {
1576     // if the extensions are missing, add them here
1577     TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(
1578         TApp::instance()->getCurrentLevel()->getLevel());
1579     if (!sl) return false;
1580     std::string ext = sl->getPath().getType();
1581     if (fp.getType() == "") fp = fp.withType(ext);
1582 
1583     IoCmd::LoadResourceArguments args(fp);
1584     args.expose = false;
1585     int count   = IoCmd::loadResources(args);
1586     if (count == 0) return false;
1587     TXshLevelP xl = args.loadedLevels[0];
1588 
1589     // in case of replacing with a saved file
1590     if (Preferences::instance()->isReplaceAfterSaveLevelAsEnabled()) {
1591       for (int c = 0; c < xsh->getColumnCount(); c++) {
1592         if (xsh->isColumnEmpty(c)) continue;
1593         int r0, r1;
1594         xsh->getCellRange(c, r0, r1);
1595         for (int r = r0; r <= r1; r++) {
1596           TXshCell cell = xsh->getCell(r, c);
1597           if (cell.m_level.getPointer() != levelToBeReplaced) continue;
1598           cell.m_level = xl;
1599           xsh->setCell(r, c, cell);
1600         }
1601       }
1602       TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1603 
1604       //--- update the scene cast
1605       TLevelSet *levelSet =
1606           TApp::instance()->getCurrentScene()->getScene()->getLevelSet();
1607       for (int i = 0; i < levelSet->getLevelCount(); i++) {
1608         TXshLevel *tmpLevel = levelSet->getLevel(i);
1609         if (tmpLevel != levelToBeReplaced) continue;
1610         if (!TApp::instance()
1611                  ->getCurrentScene()
1612                  ->getScene()
1613                  ->getTopXsheet()
1614                  ->isLevelUsed(tmpLevel))
1615           levelSet->removeLevel(tmpLevel);
1616       }
1617 
1618     }
1619     // In case of loading the saved file into a vacant column
1620     else {
1621       // find the leftmost empty column
1622       int emptyColumnIndex = xsh->getFirstFreeColumnIndex();
1623 
1624       // if the scene frame is selected and the old level is found in the
1625       // current column,
1626       // then place the new level at the same frame with the old one
1627       if (TApp::instance()->getCurrentFrame()->isEditingScene()) {
1628         // check out the current column
1629         int r0, r1;
1630         xsh->getCellRange(curColumnIndex, r0, r1);
1631 
1632         for (int r = r0; r <= r1; r++) {
1633           TXshCell cell = xsh->getCell(r, curColumnIndex);
1634           if (!cell.isEmpty() &&
1635               cell.m_level.getPointer() == levelToBeReplaced) {
1636             // set the new level at the same frame with the old one
1637             cell.m_level = xl;
1638             xsh->setCell(r, emptyColumnIndex, cell);
1639           }
1640         }
1641       }
1642       // if the level editing mode, then just place the new level
1643       else {
1644         std::vector<TFrameId> dummy_fIds;
1645         xsh->exposeLevel(0, emptyColumnIndex, xl.getPointer(), dummy_fIds);
1646       }
1647     }
1648 
1649     TApp::instance()->getCurrentLevel()->setLevel(xl.getPointer());
1650 
1651     TApp::instance()->getCurrentScene()->notifyCastChange();
1652     TApp::instance()->getCurrentLevel()->notifyLevelChange();
1653 
1654     DvDirModel::instance()->refreshFolder(fp.getParentDir());
1655 
1656     // reset undo memory!!
1657     if (Preferences::instance()->getBoolValue(resetUndoOnSavingLevel))
1658       TUndoManager::manager()->reset();
1659   }
1660 
1661   if (ret)
1662     std::cout << "SaveLevelAs complete." << std::endl;
1663   else
1664     std::cout << "SaveLevelAs failed for some reason." << std::endl;
1665 
1666   return ret;
1667 }
1668 
initFolder()1669 void SaveLevelAsPopup::initFolder() {
1670   TProject *project =
1671       TProjectManager::instance()->getCurrentProject().getPointer();
1672   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1673   TFilePath fp;
1674   if (scene) fp = scene->decodeFilePath(project->getFolder(TProject::Drawings));
1675   setFolder(fp);
1676 }
1677 
1678 //---------------------------------------------------------------------------
1679 /*
1680   For Save Level As command, it is needed to check if the current level is
1681   selected (just like Save Level command) BEFORE opening the popup. So I decided
1682   to use an original MenuItemHandler rather than OpenPopupCommandHandler.
1683   06/07/2016 Shun
1684 */
1685 
1686 class SaveLevelAsCommandHandler final : public MenuItemHandler {
1687   SaveLevelAsPopup *m_popup;
1688 
1689 public:
SaveLevelAsCommandHandler()1690   SaveLevelAsCommandHandler() : MenuItemHandler(MI_SaveLevelAs), m_popup(0) {}
execute()1691   void execute() override {
1692     TXshSimpleLevel *sl = TApp::instance()->getCurrentLevel()->getSimpleLevel();
1693     if (!sl) {
1694       DVGui::warning(QObject::tr("No Current Level"));
1695       return;
1696     }
1697     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1698     if (!scene) {
1699       DVGui::warning(QObject::tr("No Current Scene"));
1700       return;
1701     }
1702     if (!m_popup) m_popup = new SaveLevelAsPopup();
1703     m_popup->show();
1704     m_popup->raise();
1705     m_popup->activateWindow();
1706   }
1707 } saveLevelAsCommandHandler;
1708 
1709 //=============================================================================
1710 // Used both for ReplaceLevelPopup and ReplaceParentDirectoryPopup
1711 // which are needed to maintain the current selection on open.
1712 
1713 template <class T>
1714 class OpenReplaceFilePopupHandler final : public MenuItemHandler {
1715   T *m_popup;
1716 
1717 public:
OpenReplaceFilePopupHandler(CommandId cmdId)1718   OpenReplaceFilePopupHandler(CommandId cmdId)
1719       : MenuItemHandler(cmdId), m_popup(0) {}
1720 
1721   // The current selection is cleared on the construction of FileBrowser
1722   // ( by calling makeCurrent() in DvDirTreeView::currentChanged() ).
1723   // Thus checking the selection must be done BEFORE making the browser
1724   // or it fails to get the selection on the first call of this command.
initialize(TCellSelection::Range & range,std::set<int> & columnRange,bool & replaceCells)1725   bool initialize(TCellSelection::Range &range, std::set<int> &columnRange,
1726                   bool &replaceCells) {
1727     TSelection *sel = TApp::instance()->getCurrentSelection()->getSelection();
1728     if (!sel) return false;
1729     TCellSelection *cellSel     = dynamic_cast<TCellSelection *>(sel);
1730     TColumnSelection *columnSel = dynamic_cast<TColumnSelection *>(sel);
1731     if ((!cellSel && !columnSel) || sel->isEmpty()) {
1732       DVGui::error(
1733           QObject::tr("Nothing to replace: no cells or columns selected."));
1734       return false;
1735     }
1736     if (cellSel) {
1737       range        = cellSel->getSelectedCells();
1738       replaceCells = true;
1739     } else if (columnSel) {
1740       columnRange  = columnSel->getIndices();
1741       replaceCells = false;
1742     }
1743     return true;
1744   }
1745 
execute()1746   void execute() override {
1747     TCellSelection::Range range;
1748     std::set<int> columnRange;
1749     bool replaceCells;
1750     if (!initialize(range, columnRange, replaceCells)) return;
1751     if (!m_popup) m_popup = new T();
1752     m_popup->setRange(range, columnRange, replaceCells);
1753     m_popup->show();
1754     m_popup->raise();
1755     m_popup->activateWindow();
1756   }
1757 };
1758 
1759 //=============================================================================
1760 // ReplaceLevelPopup
1761 
ReplaceLevelPopup()1762 ReplaceLevelPopup::ReplaceLevelPopup()
1763     : FileBrowserPopup(tr("Replace Level"), Options(WITH_APPLY_BUTTON)) {
1764   setOkText(tr("Replace"));
1765   connect(TApp::instance()->getCurrentSelection(),
1766           SIGNAL(selectionChanged(TSelection *)), this,
1767           SLOT(onSelectionChanged(TSelection *)));
1768 }
1769 
setRange(TCellSelection::Range & range,std::set<int> & columnRange,bool & replaceCells)1770 void ReplaceLevelPopup::setRange(TCellSelection::Range &range,
1771                                  std::set<int> &columnRange,
1772                                  bool &replaceCells) {
1773   m_range        = range;
1774   m_columnRange  = columnRange;
1775   m_replaceCells = replaceCells;
1776 }
1777 
execute()1778 bool ReplaceLevelPopup::execute() {
1779   if (m_selectedPaths.empty()) return false;
1780 
1781   const TFilePath &fp = *m_selectedPaths.begin();
1782   if (!TSystem::doesExistFileOrLevel(fp)) {
1783     DVGui::error(tr("File not found\n") + toQString(fp));
1784     return false;
1785   }
1786 
1787   IoCmd::LoadResourceArguments args(fp);
1788   args.expose = false;
1789   args.col0   = m_range.m_c0;
1790   args.col1   = m_range.m_c1;
1791   args.row0   = m_range.m_r0;
1792   args.row1   = m_range.m_r1;
1793   int count   = IoCmd::loadResources(args);
1794   if (count == 0) return false;
1795   TXshLevelP xl = args.loadedLevels[0];
1796 
1797   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
1798 
1799   // cell selection
1800   if (m_replaceCells) {
1801     int r, c;
1802     for (c = m_range.m_c0; c <= m_range.m_c1; c++)
1803       for (r = m_range.m_r0; r <= m_range.m_r1; r++) {
1804         TXshCell cell = xsh->getCell(r, c);
1805         if (!cell.m_level.getPointer()) continue;
1806         cell.m_level = xl;
1807         xsh->setCell(r, c, cell);
1808       }
1809   }
1810   // column selection
1811   else {
1812     int frameLength           = xsh->getFrameCount();
1813     std::set<int>::iterator i = m_columnRange.begin();
1814     while (i != m_columnRange.end()) {
1815       for (int r = 0; r < frameLength; r++) {
1816         TXshCell cell = xsh->getCell(r, *i);
1817         if (!cell.m_level.getPointer()) continue;
1818         cell.m_level = xl;
1819         xsh->setCell(r, *i, cell);
1820       }
1821       i++;
1822     }
1823   }
1824 
1825   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1826   TApp::instance()->getCurrentScene()->notifyCastChange();
1827 
1828   DvDirModel::instance()->refreshFolder(fp.getParentDir());
1829 
1830   return true;
1831 }
1832 //-----------------------------------------------------------------------------
1833 
initFolder()1834 void ReplaceLevelPopup::initFolder() {
1835   setFolder(
1836       TProjectManager::instance()->getCurrentProject()->getProjectFolder());
1837 }
1838 
1839 //-----------------------------------------------------------------------------
1840 
onSelectionChanged(TSelection * sel)1841 void ReplaceLevelPopup::onSelectionChanged(TSelection *sel) {
1842   if (!sel) return;
1843   TCellSelection *cellSel     = dynamic_cast<TCellSelection *>(sel);
1844   TColumnSelection *columnSel = dynamic_cast<TColumnSelection *>(sel);
1845   if ((!cellSel && !columnSel) || sel->isEmpty()) return;
1846   if (cellSel) {
1847     m_range        = cellSel->getSelectedCells();
1848     m_replaceCells = true;
1849   } else if (columnSel) {
1850     m_columnRange  = columnSel->getIndices();
1851     m_replaceCells = false;
1852   }
1853 }
1854 
1855 //=============================================================================
1856 // SavePaletteAsPopup
1857 
SavePaletteAsPopup()1858 SavePaletteAsPopup::SavePaletteAsPopup()
1859     : FileBrowserPopup(tr("Save Palette"), Options(FOR_SAVING)) {
1860   setOkText(tr("Save"));
1861   addFilterType("tpl");
1862   connect(m_nameField, SIGNAL(returnPressedNow()), m_okButton,
1863           SLOT(animateClick()));
1864 }
1865 
execute()1866 bool SavePaletteAsPopup::execute() {
1867   if (m_selectedPaths.empty()) return false;
1868 
1869   TFilePath fp(*m_selectedPaths.begin());
1870 
1871   TPaletteHandle *paletteHandle =
1872       TApp::instance()->getPaletteController()->getCurrentLevelPalette();
1873   TPalette *palette = paletteHandle->getPalette();
1874 
1875   if (!palette) {
1876     DVGui::warning("No current palette exists");
1877     return true;
1878   }
1879 
1880   const std::string &type = fp.getType();
1881 
1882   if (!type.empty() && type != "tpl")
1883     return false;
1884   else if (type.empty())
1885     fp = fp.getParentDir() + TFilePath(fp.getName() + ".tpl");
1886 
1887   if (TFileStatus(fp).doesExist()) {
1888     const QString &question =
1889         QObject::tr(
1890             "The palette %1 already exists.\nDo you want to overwrite it?")
1891             .arg(toQString(fp));
1892     int ret = DVGui::MsgBox(question, QObject::tr("Overwrite"),
1893                             QObject::tr("Cancel"), 0);
1894     if (ret == 2 || ret == 0) return false;
1895   }
1896 
1897   const TFilePath &refImagePath = palette->getRefImgPath();
1898   if (!refImagePath.isEmpty()) palette->setRefImgPath(TFilePath());
1899 
1900   // In questo caso non voglio salvare la reference image.
1901   StudioPalette::instance()->save(fp, palette);
1902   if (!refImagePath.isEmpty()) palette->setRefImgPath(refImagePath);
1903 
1904   palette->setDirtyFlag(false);
1905   paletteHandle->notifyPaletteChanged();
1906 
1907   return true;
1908 }
1909 
initFolder()1910 void SavePaletteAsPopup::initFolder() {
1911   setFolder(
1912       TProjectManager::instance()->getCurrentProjectPath().getParentDir());
1913 }
1914 
1915 //=============================================================================
1916 // LoadColorModelPopup
1917 
LoadColorModelPopup()1918 LoadColorModelPopup::LoadColorModelPopup()
1919     : FileBrowserPopup(tr("Load Color Model"), Options(), "", new QFrame(0)) {
1920   QFrame *optionFrame = (QFrame *)m_customWidget;
1921   m_paletteFrame      = new DVGui::LineEdit("", this);
1922 
1923   // layout
1924   QHBoxLayout *mainLayout = new QHBoxLayout();
1925   mainLayout->setMargin(5);
1926   mainLayout->setSpacing(5);
1927   {
1928     mainLayout->addStretch(1);
1929     mainLayout->addWidget(new QLabel(tr("Frames :"), this), 0);
1930     mainLayout->addWidget(m_paletteFrame, 0);
1931   }
1932   optionFrame->setLayout(mainLayout);
1933 
1934   setOkText(tr("Load"));
1935   addFilterType("bmp");
1936   addFilterType("jpg");
1937   addFilterType("nol");
1938   addFilterType("pic");
1939   addFilterType("pict");
1940   addFilterType("pct");
1941   addFilterType("png");
1942   addFilterType("rgb");
1943   addFilterType("sgi");
1944   addFilterType("tga");
1945   addFilterType("tif");
1946   addFilterType("tiff");
1947   addFilterType("tlv");
1948   addFilterType("pli");
1949   addFilterType("psd");
1950 }
1951 
1952 //--------------------------------------------------------------
1953 
onFilePathsSelected(const std::set<TFilePath> & paths)1954 void LoadColorModelPopup::onFilePathsSelected(
1955     const std::set<TFilePath> &paths) {
1956   std::list<std::vector<TFrameId>> tmp;
1957   FileBrowserPopup::onFilePathsSelected(paths, tmp);
1958 
1959   m_paletteFrame->setText("");
1960   if (paths.size() == 1) {
1961     // Initialize the line with the level's starting frame
1962     const TFilePath &fp = *paths.begin();
1963     try {
1964       TLevelReaderP lr(fp);
1965       TLevelP level;
1966       if (lr) level = lr->loadInfo();
1967 
1968       if (level.getPointer() && level->begin() != level->end()) {
1969         int firstFrame = level->begin()->first.getNumber();
1970         if (firstFrame > 0)
1971           m_paletteFrame->setText(QString::number(firstFrame));
1972       }
1973     } catch (...) {
1974     }
1975   }
1976 }
1977 
1978 //--------------------------------------------------------------
1979 
execute()1980 bool LoadColorModelPopup::execute() {
1981   if (m_selectedPaths.empty()) return false;
1982 
1983   const TFilePath &fp = *m_selectedPaths.begin();
1984 
1985   TPaletteHandle *paletteHandle =
1986       TApp::instance()->getPaletteController()->getCurrentLevelPalette();
1987 
1988   TPalette *palette = paletteHandle->getPalette();
1989   if (!palette || palette->isCleanupPalette()) {
1990     DVGui::error(QObject::tr("Cannot load Color Model in current palette."));
1991     return false;
1992   }
1993 
1994   PaletteCmd::ColorModelLoadingConfiguration config;
1995 
1996   // if the palette is locked, replace the color model's palette with the
1997   // destination
1998   if (palette->isLocked()) {
1999     // do nothing as config will use behavior = ReplaceColorModelPlt by default
2000     // config.behavior = PaletteCmd::ReplaceColorModelPlt;
2001   } else {
2002     ColorModelBehaviorPopup popup(m_selectedPaths, 0);
2003     int ret = popup.exec();
2004     if (ret == QDialog::Rejected) return false;
2005     popup.getLoadingConfiguration(config);
2006   }
2007 
2008   std::vector<int> framesInput = string2Indexes(m_paletteFrame->text());
2009 
2010   if (!framesInput.empty()) {
2011     for (int i = 0; i < framesInput.size(); i++)
2012       std::cout << framesInput[i] << std::endl;
2013   }
2014 
2015   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2016 
2017   int isLoaded = PaletteCmd::loadReferenceImage(paletteHandle, config, fp,
2018                                                 scene, framesInput);
2019   // return value - isLoaded
2020   // 2: failed to get palette
2021   // 1: failed to get image
2022   // 0: OK
2023   if (isLoaded == 2) {
2024     std::cout << "GetCurrentPalette Failed!" << std::endl;
2025     return false;
2026   } else if (isLoaded == 1) {
2027     std::cout << "GetReferenceImage Failed!" << std::endl;
2028     return false;
2029   } else if (0 != isLoaded) {
2030     std::cout << "loadReferenceImage Failed for some reason." << std::endl;
2031     return false;
2032   }
2033 
2034   // no changes in the icon with replace (Keep the destination palette) option
2035   if (config.behavior != PaletteCmd::ReplaceColorModelPlt) {
2036     TXshLevel *level = TApp::instance()->getCurrentLevel()->getLevel();
2037     if (!level) return true;
2038     std::vector<TFrameId> fids;
2039     level->getFids(fids);
2040     invalidateIcons(level, fids);
2041   }
2042 
2043   return true;
2044 }
2045 
2046 //--------------------------------------------------------------
2047 
showEvent(QShowEvent * e)2048 void LoadColorModelPopup::showEvent(QShowEvent *e) {
2049   m_nameField->clear();
2050   FileBrowserPopup::showEvent(e);
2051 }
2052 
2053 //=============================================================================
2054 /*! replace the parent folder path of the levels in the selected cells
2055  */
2056 
ReplaceParentDirectoryPopup()2057 ReplaceParentDirectoryPopup::ReplaceParentDirectoryPopup()
2058     : FileBrowserPopup(tr("Replace Parent Directory")) {
2059   setOkText(tr("Replace"));
2060   setFileMode(true);  // isDirectoryOnly
2061 }
2062 
setRange(TCellSelection::Range & range,std::set<int> & columnRange,bool & replaceCells)2063 void ReplaceParentDirectoryPopup::setRange(TCellSelection::Range &range,
2064                                            std::set<int> &columnRange,
2065                                            bool &replaceCells) {
2066   m_range        = range;
2067   m_columnRange  = columnRange;
2068   m_replaceCells = replaceCells;
2069 }
2070 
execute()2071 bool ReplaceParentDirectoryPopup::execute() {
2072   if (m_selectedPaths.empty()) return false;
2073 
2074   const TFilePath &fp = *m_selectedPaths.begin();
2075 
2076   // make the level list in the selected cells
2077   TXsheet *xsh = TApp::instance()->getCurrentXsheet()->getXsheet();
2078   std::vector<TXshLevel *> levelsToBeReplaced;
2079 
2080   if (m_replaceCells) {
2081     int r, c;
2082     for (c = m_range.m_c0; c <= m_range.m_c1; c++) {
2083       for (r = m_range.m_r0; r <= m_range.m_r1; r++) {
2084         TXshCell cell = xsh->getCell(r, c);
2085         if (cell.isEmpty() ||
2086             !cell.m_level->getSimpleLevel())  // TLV and PLI only
2087           continue;
2088         levelsToBeReplaced.push_back(cell.m_level.getPointer());
2089       }
2090     }
2091   } else {
2092     // calcurate scene length
2093     int frameLength           = xsh->getFrameCount();
2094     std::set<int>::iterator i = m_columnRange.begin();
2095     while (i != m_columnRange.end()) {
2096       for (int r = 0; r < frameLength; r++) {
2097         TXshCell cell = xsh->getCell(r, *i);
2098         if (!cell.m_level.getPointer()) continue;
2099         levelsToBeReplaced.push_back(cell.m_level.getPointer());
2100       }
2101       i++;
2102     }
2103   }
2104 
2105   // avoid level duplication
2106   std::sort(levelsToBeReplaced.begin(), levelsToBeReplaced.end());
2107   levelsToBeReplaced.erase(
2108       std::unique(levelsToBeReplaced.begin(), levelsToBeReplaced.end()),
2109       levelsToBeReplaced.end());
2110 
2111   if (levelsToBeReplaced.empty()) return false;
2112 
2113   // check if the file exists. If exists, load it and store them in the map
2114   // rewrite the path in the level settings
2115   bool somethingChanged                 = false;
2116   std::vector<TXshLevel *>::iterator it = levelsToBeReplaced.begin();
2117   for (; it != levelsToBeReplaced.end(); it++) {
2118     TFilePath orgPath = (*it)->getPath();
2119     if (orgPath.isEmpty()) continue;
2120 
2121     TFilePath newPath = orgPath.withParentDir(fp);
2122 
2123     if (orgPath == newPath) continue;
2124 
2125     // If the file exists
2126     if (TSystem::doesExistFileOrLevel(newPath)) {
2127       TXshSimpleLevel *sl = (*it)->getSimpleLevel();
2128       if (!sl) continue;
2129 
2130       // replace the file with aliases, if possible
2131       newPath = TApp::instance()->getCurrentScene()->getScene()->codeFilePath(
2132           newPath);
2133 
2134       sl->setPath(newPath);
2135       sl->invalidateFrames();
2136       std::vector<TFrameId> frames;
2137       sl->getFids(frames);
2138       std::vector<TFrameId>::iterator f_it = frames.begin();
2139       for (; f_it != frames.end(); f_it++)
2140         IconGenerator::instance()->invalidate(sl, *f_it);
2141 
2142       somethingChanged = true;
2143     }
2144   }
2145 
2146   if (!somethingChanged) return false;
2147 
2148   TApp::instance()->getCurrentLevel()->notifyLevelChange();
2149   TApp::instance()->getCurrentScene()->notifySceneChanged();
2150   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
2151 
2152   return true;
2153 }
2154 
initFolder()2155 void ReplaceParentDirectoryPopup::initFolder() {
2156   setFolder(
2157       TProjectManager::instance()->getCurrentProjectPath().getParentDir());
2158 }
2159 
2160 //=============================================================================
2161 // ImportMagpieFilePopup
2162 
ImportMagpieFilePopup()2163 ImportMagpieFilePopup::ImportMagpieFilePopup()
2164     : FileBrowserPopup(tr("Import Toonz Lip Sync File")) {
2165   setOkText(tr("Load"));
2166   addFilterType("tls");
2167 }
2168 
execute()2169 bool ImportMagpieFilePopup::execute() {
2170   if (m_selectedPaths.empty()) return false;
2171 
2172   const TFilePath &fp = *m_selectedPaths.begin();
2173 
2174   if (!TSystem::doesExistFileOrLevel(fp)) {
2175     DVGui::error(tr("%1 does not exist.").arg(toQString(fp)));
2176     return false;
2177   }
2178 
2179   static MagpieFileImportPopup *magpieFileImportPopup =
2180       new MagpieFileImportPopup();
2181   magpieFileImportPopup->setFilePath(fp);
2182   magpieFileImportPopup->show();
2183 
2184   return true;
2185 }
2186 
initFolder()2187 void ImportMagpieFilePopup::initFolder() {
2188   TProject *project =
2189       TProjectManager::instance()->getCurrentProject().getPointer();
2190   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2191   TFilePath fp;
2192   if (scene) fp = scene->decodeFilePath(project->getFolder(TProject::Drawings));
2193   setFolder(fp);
2194 }
2195 
showEvent(QShowEvent * e)2196 void ImportMagpieFilePopup::showEvent(QShowEvent *e) {
2197   m_nameField->clear();
2198   FileBrowserPopup::showEvent(e);
2199 }
2200 
2201 //=============================================================================
2202 // BrowserPopup
2203 
BrowserPopup()2204 BrowserPopup::BrowserPopup() : FileBrowserPopup("") {
2205   setOkText(tr("Choose"));
2206   m_browser->enableGlobalSelection(false);
2207 }
2208 
execute()2209 bool BrowserPopup::execute() {
2210   if (m_selectedPaths.empty()) return false;
2211 
2212   const TFilePath &fp = *m_selectedPaths.begin();
2213 
2214   if (!TSystem::doesExistFileOrLevel(fp)) {
2215     const QString &msg = tr("Path %1 doesn't exists.").arg(toQString(fp));
2216     DVGui::info(msg);
2217 
2218     return false;
2219   }
2220 
2221   m_path = fp;
2222   return true;
2223 }
2224 
initFolder(TFilePath path)2225 void BrowserPopup::initFolder(TFilePath path) {
2226   // if the path is empty
2227   if (path.isEmpty()) {
2228     TProject *project =
2229         TProjectManager::instance()->getCurrentProject().getPointer();
2230     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2231     if (scene)
2232       setFolder(scene->decodeFilePath(project->getFolder(TProject::Drawings)));
2233     return;
2234   }
2235   if (!TFileStatus(path).doesExist()) {
2236     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2237     if (scene) path = scene->decodeFilePath(path);
2238   }
2239 
2240   if (!path.getType().empty()) path = path.getParentDir();
2241 
2242   setFolder(path);
2243   setFilename(TFilePath());
2244 }
2245 
2246 //=============================================================================
2247 // BrowserPopupController
2248 /* N.B. Eliminare nel momento in cui la classe FileBrowserPopup, con tutte le
2249    classi annesse
2250                                 (FileBrowser, DvDirTreeView, ...), sara'
2251    spostata nella libreria toonzQt. */
BrowserPopupController()2252 BrowserPopupController::BrowserPopupController() : m_browserPopup() {
2253   m_isExecute = false;
2254   DVGui::FileField::setBrowserPopupController(this);
2255 }
2256 
openPopup(QStringList filters,bool isDirectoryOnly,QString lastSelectedPath,const QWidget * parentWidget)2257 void BrowserPopupController::openPopup(QStringList filters,
2258                                        bool isDirectoryOnly,
2259                                        QString lastSelectedPath,
2260                                        const QWidget *parentWidget) {
2261   if (!m_browserPopup) m_browserPopup = new BrowserPopup();
2262   m_browserPopup->setWindowTitle(QString(""));
2263 
2264   m_browserPopup->setFilterTypes(filters);
2265 
2266   m_browserPopup->setWindowTitle((isDirectoryOnly)
2267                                      ? QString(QObject::tr("Choose Folder"))
2268                                      : QString(QObject::tr("File Browser")));
2269   m_browserPopup->initFolder(TFilePath(lastSelectedPath.toStdWString()));
2270   m_browserPopup->setFileMode(isDirectoryOnly);
2271 
2272   Qt::WindowFlags flags = m_browserPopup->windowFlags();
2273   bool parentSet        = false;
2274   if (parentWidget) {
2275     for (QWidget *pwidget : QApplication::topLevelWidgets()) {
2276       if (pwidget->isWindow() && pwidget->isVisible() &&
2277           pwidget->isAncestorOf(parentWidget)) {
2278         m_browserPopup->setParent(pwidget);
2279         m_browserPopup->setWindowFlags(flags);
2280         parentSet = true;
2281         break;
2282       }
2283     }
2284   }
2285 
2286   if (isDirectoryOnly)
2287     m_browserPopup->setFilename(TFilePath(lastSelectedPath.toStdWString()));
2288 
2289   if (m_browserPopup->exec() == QDialog::Accepted)
2290     m_isExecute = true;
2291   else
2292     m_isExecute = false;
2293 
2294   // set back the parent to the main window in order to prevent to be
2295   // deleted along with the parent widget
2296   if (parentSet) {
2297     m_browserPopup->setParent(TApp::instance()->getMainWindow());
2298     m_browserPopup->setWindowFlags(flags);
2299   }
2300 }
2301 
2302 // codePath is set to true by default
getPath(bool codePath)2303 QString BrowserPopupController::getPath(bool codePath) {
2304   m_isExecute = false;
2305   if (!m_browserPopup) return QString();
2306   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
2307   TFilePath fp      = m_browserPopup->getPath();
2308   if (scene && codePath) fp = scene->codeFilePath(fp);
2309   std::cout << ::to_string(fp) << std::endl;
2310   return toQString(fp);
2311 }
2312 
2313 //=============================================================================
2314 BrowserPopupController browserPopupController;
2315 //-----------------------------------------------------------------------------
2316 
2317 OpenPopupCommandHandler<SaveSceneAsPopup> saveSceneAsPopupCommand(
2318     MI_SaveSceneAs);
2319 OpenPopupCommandHandler<SaveSubSceneAsPopup> saveSubSceneAsPopupCommand(
2320     MI_SaveSubxsheetAs);
2321 OpenPopupCommandHandler<LoadLevelPopup> loadLevelPopupCommand(MI_LoadLevel);
2322 OpenPopupCommandHandler<ConvertPopupWithInput> convertWithInputPopupCommand(
2323     MI_ConvertFileWithInput);
2324 OpenPopupCommandHandler<SavePaletteAsPopup> savePalettePopupCommand(
2325     MI_SavePaletteAs);
2326 OpenPopupCommandHandler<LoadColorModelPopup> loadColorModelPopupCommand(
2327     MI_LoadColorModel);
2328 OpenPopupCommandHandler<ImportMagpieFilePopup> importMagpieFilePopupCommand(
2329     MI_ImportMagpieFile);
2330 
2331 // Note: MI_ReplaceLevel and MI_ReplaceParentDirectory uses the original
2332 // handler in order to obtain the selection information before making the
2333 // browser.
2334 OpenReplaceFilePopupHandler<ReplaceLevelPopup> replaceLevelPopupCommand(
2335     MI_ReplaceLevel);
2336 OpenReplaceFilePopupHandler<ReplaceParentDirectoryPopup>
2337     replaceParentFolderPopupCommand(MI_ReplaceParentDirectory);
2338