1 
2 
3 #include "toonz/toonzscene.h"
4 
5 // TnzLib includes
6 #include "toonz/txsheet.h"
7 #include "toonz/txshcell.h"
8 #include "toonz/txshsimplelevel.h"
9 #include "toonz/txshchildlevel.h"
10 #include "toonz/txshleveltypes.h"
11 #include "toonz/txshlevelcolumn.h"
12 #include "toonz/txshsoundcolumn.h"
13 #include "toonz/txshsoundlevel.h"
14 #include "toonz/sceneproperties.h"
15 #include "toonz/stage.h"
16 #include "toonz/stagevisitor.h"
17 #include "toonz/levelset.h"
18 #include "toonz/tstageobjecttree.h"
19 #include "toonz/observer.h"
20 #include "toonz/namebuilder.h"
21 #include "toonz/tproject.h"
22 #include "toonz/toonzimageutils.h"
23 #include "toonz/childstack.h"
24 #include "toonz/levelproperties.h"
25 #include "toonz/tcamera.h"
26 #include "toonz/sceneresources.h"
27 #include "toonz/preferences.h"
28 #include "toonz/fullcolorpalette.h"
29 #include "toonz/txshpalettecolumn.h"
30 #include "toonz/txshpalettelevel.h"
31 #include "toonz/toonzfolders.h"
32 
33 // TnzCore includes
34 #include "timagecache.h"
35 #include "tstream.h"
36 #include "tstreamexception.h"
37 #include "tofflinegl.h"
38 #include "tenv.h"
39 #include "tsystem.h"
40 #include "tfiletype.h"
41 #include "tlevel_io.h"
42 #include "ttoonzimage.h"
43 #include "tlogger.h"
44 #include "tvectorimage.h"
45 #include "tcontenthistory.h"
46 #include "toutputproperties.h"
47 #include "trop.h"
48 
49 TOfflineGL *currentOfflineGL = 0;
50 
51 #include <QProgressDialog>
52 
53 #ifdef MACOSX
54 #include <QSurfaceFormat>
55 #include <QOffscreenSurface>
56 #include <QOpenGLContext>
57 #include <QOpenGLFramebufferObject>
58 #endif
59 //=============================================================================
60 // Utility functions
61 //=============================================================================
62 namespace {
63 // tentatively update the scene file version from 71.0 to 71.1 in order to
64 // manage PNG level settings
65 const VersionNumber l_currentVersion(71, 1);
66 // TODO: Revise VersionNumber with OT version (converting 71.0 -> 14.0 , 71.1
67 // -> 15.0)
68 
69 //-----------------------------------------------------------------------------
70 
getFolderName(int levelType)71 std::string getFolderName(int levelType) {
72   switch (levelType) {
73   case TZI_XSHLEVEL:
74     return TProject::Inputs;
75   case PLI_XSHLEVEL:
76     return TProject::Drawings;
77   case TZP_XSHLEVEL:
78     return TProject::Drawings;
79   case OVL_XSHLEVEL:
80     return TProject::Extras;
81   default:
82     return TProject::Extras;
83   }
84 }
85 
86 //-----------------------------------------------------------------------------
87 
88 /*
89 TFilePath adjustTypeAndFrame(int levelType, TFilePath fp)
90 {
91   switch(levelType)
92   {
93     case TZI_XSHLEVEL:
94       if(fp.getType()=="") fp=fp.withType("tif");
95       return fp.withFrame(TFrameId::EMPTY_FRAME);
96     case PLI_XSHLEVEL:
97       return fp.withFrame(TFrameId::NO_FRAME)
98                .withType("pli");
99     case TZP_XSHLEVEL:
100       return fp.withFrame(TFrameId::NO_FRAME)
101                .withType("tlv");
102     case OVL_XSHLEVEL:
103     default:
104       if(fp.getType()=="") fp=fp.withType("png");
105       return fp;
106   }
107 }
108 */
109 
110 //-----------------------------------------------------------------------------
111 
getUntitledScenesDir()112 TFilePath getUntitledScenesDir() {
113   return ToonzFolder::getCacheRootFolder() + "temp";
114 }
115 
116 //-----------------------------------------------------------------------------
117 
118 // se path = head + tail ritorna true e assegna head
checkTail(TFilePath path,TFilePath tail,TFilePath & head)119 bool checkTail(TFilePath path, TFilePath tail, TFilePath &head) {
120   for (;;) {
121     if (tail == TFilePath()) {
122       head = path;
123       return true;
124     }
125     if (path == TFilePath()) return false;
126     if (path.withoutParentDir() != tail.withoutParentDir()) return false;
127     path = path.getParentDir();
128     tail = tail.getParentDir();
129   }
130 }
131 
132 //-----------------------------------------------------------------------------
133 
makeSceneIcon(ToonzScene * scene)134 void makeSceneIcon(ToonzScene *scene) {
135   TDimension cameraSize = scene->getCurrentCamera()->getRes();
136 
137   int maxSize               = 128;
138   TDimension iconCameraSize = cameraSize;
139   if (cameraSize.lx > cameraSize.ly) {
140     iconCameraSize.ly = maxSize * cameraSize.ly / cameraSize.lx;
141     iconCameraSize.lx = maxSize;
142   } else {
143     iconCameraSize.lx = maxSize * cameraSize.lx / cameraSize.ly;
144     iconCameraSize.ly = maxSize;
145   }
146 
147   TRaster32P ras(iconCameraSize);
148   TPixel32 bgColor = scene->getProperties()->getBgColor();
149   ras->fill(bgColor);
150   scene->renderFrame(ras, 0);
151 
152   TFilePath iconPath = scene->getIconPath();
153   if (TSystem::touchParentDir(iconPath)) TImageWriter::save(iconPath, ras);
154 }
155 
156 //-----------------------------------------------------------------------------
157 
deleteUntitledScene(const TFilePath & fp)158 void deleteUntitledScene(const TFilePath &fp) {
159   if (TFileStatus(fp).isDirectory()) {
160     TFilePath tempDir = getUntitledScenesDir();
161     if (TFileStatus(tempDir).isDirectory() &&
162         tempDir.isAncestorOf(fp))  // per sicurezza
163     {
164       try {
165         TSystem::rmDirTree(fp);
166       } catch (...) {
167         assert(0);
168       }
169     } else
170       assert(0);
171   }
172 }
173 
174 //-----------------------------------------------------------------------------
175 
saveBackup(TFilePath path)176 static void saveBackup(TFilePath path) {
177   int totalBackups = Preferences::instance()->getBackupKeepCount();
178   totalBackups -= 1;
179   TFilePath backup = path.withType(path.getType() + ".bak");
180   TFilePath prevBackup =
181       path.withType(path.getType() + ".bak" + std::to_string(totalBackups));
182   while (--totalBackups >= 0) {
183     std::string bakExt =
184         ".bak" + (totalBackups > 0 ? std::to_string(totalBackups) : "");
185     backup = path.withType(path.getType() + bakExt);
186     if (TSystem::doesExistFileOrLevel(backup)) {
187       try {
188         TSystem::copyFileOrLevel_throw(prevBackup, backup);
189       } catch (...) {
190       }
191     }
192     prevBackup = backup;
193   }
194 
195   try {
196     if (TSystem::doesExistFileOrLevel(backup))
197       TSystem::removeFileOrLevel_throw(backup);
198     TSystem::copyFileOrLevel_throw(backup, path);
199   } catch (...) {
200   }
201 }
202 
203 //-----------------------------------------------------------------------------
204 
205 // serve per correggere un problema verificatosi da Bianco (con la beta3)
206 // vengono create coppie di livelli con lo stesso nome e in genere path diverso
207 
fixBiancoProblem(ToonzScene * scene,TXsheet * xsh)208 void fixBiancoProblem(ToonzScene *scene, TXsheet *xsh) {
209   TLevelSet *levelSet = scene->getLevelSet();
210   std::set<TXsheet *> visited, tovisit;
211   tovisit.insert(xsh);
212   while (!tovisit.empty()) {
213     xsh = *tovisit.begin();
214     xsh->setScene(scene);  // sound problem
215     visited.insert(xsh);
216     tovisit.erase(xsh);
217     int c0 = 0, c1 = xsh->getColumnCount() - 1;
218     for (int c = c0; c <= c1; c++) {
219       if (xsh->isColumnEmpty(c)) continue;
220       TXshColumn *column = xsh->getColumn(c);
221       if (!column || !column->getLevelColumn()) continue;
222       TXshLevelColumn *lcolumn = column->getLevelColumn();
223       int r0 = 0, r1 = -1;
224       lcolumn->getRange(r0, r1);
225       for (int r = r0; r <= r1; r++) {
226         TXshCell cell = lcolumn->getCell(r);
227         if (cell.isEmpty()) continue;
228         TXshLevel *xl = cell.m_level.getPointer();
229         scene->getLevelSet()->insertLevel(xl);  // per sicurezza
230         xl->setScene(scene);
231         if (TXshChildLevel *childLevel = xl->getChildLevel()) {
232           TXsheet *childXsh = childLevel->getXsheet();
233           if (visited.count(childXsh) > 0) continue;
234           tovisit.insert(childXsh);
235         } else {
236           TXshLevel *xl2 = levelSet->getLevel(xl->getName());
237           if (xl2) {
238             if (xl2 != xl) {
239               cell.m_level = xl2;
240               lcolumn->setCell(r, cell);
241             }
242           } else {
243             xl->setScene(scene);
244             levelSet->insertLevel(xl);
245           }
246         }
247       }
248     }
249   }
250 }
251 
252 //-----------------------------------------------------------------------------
253 }  // namespace
254 //-----------------------------------------------------------------------------
255 
256 //=============================================================================
257 
deleteAllUntitledScenes()258 static void deleteAllUntitledScenes() {
259   TFilePath tempDir = getUntitledScenesDir();
260   try {
261     if (TFileStatus(tempDir).isDirectory()) {
262       TFilePathSet fps;
263       TSystem::readDirectory(fps, tempDir);
264       TFilePathSet::iterator fpsIt;
265       for (fpsIt = fps.begin(); fpsIt != fps.end(); ++fpsIt) {
266         TFilePath fp = *fpsIt;
267         if (TFileStatus(fp).isDirectory() &&
268             fp.getName().find("untitled") != -1)
269           TSystem::rmDirTree(fp);
270       }
271     }
272   } catch (...) {
273   }
274 }
275 
276 //=============================================================================
277 // ToonzScene
278 
ToonzScene()279 ToonzScene::ToonzScene() : m_contentHistory(0), m_isUntitled(true) {
280   m_childStack = new ChildStack(this);
281   m_properties = new TSceneProperties();
282   m_levelSet   = new TLevelSet();
283   m_project    = new TProject();
284   m_project->addRef();
285 }
286 
287 //-----------------------------------------------------------------------------
288 
~ToonzScene()289 ToonzScene::~ToonzScene() {
290   delete m_properties;
291   delete m_levelSet;
292   delete m_childStack;
293   delete m_contentHistory;
294 
295   assert(m_project);
296   if (m_project) m_project->release();
297 }
298 
299 //-----------------------------------------------------------------------------
300 
setSceneName(std::wstring name)301 void ToonzScene::setSceneName(std::wstring name) {
302   m_scenePath = m_scenePath.withName(name);
303 }
304 
305 //-----------------------------------------------------------------------------
306 
clear()307 void ToonzScene::clear() {
308   if (isUntitled()) deleteUntitledScene(getScenePath().getParentDir());
309 
310   m_childStack->clear();
311 
312   m_scenePath                  = TFilePath();
313   TSceneProperties *properties = m_properties;
314   m_properties                 = new TSceneProperties();
315   delete properties;
316   m_levelSet->clear();
317 }
318 
319 //-----------------------------------------------------------------------------
320 
setProject(TProject * project)321 void ToonzScene::setProject(TProject *project) {
322   assert(project);
323 
324   if (project != m_project) {
325     if (project) project->addRef();
326     if (m_project) m_project->release();
327     m_project = project;
328   }
329 }
330 
331 //-----------------------------------------------------------------------------
332 
getProject() const333 TProject *ToonzScene::getProject() const { return m_project; }
334 
335 //-----------------------------------------------------------------------------
336 
setScenePath(const TFilePath & fp)337 void ToonzScene::setScenePath(const TFilePath &fp) {
338   m_scenePath  = fp;
339   m_isUntitled = false;
340 }
341 
342 //-----------------------------------------------------------------------------
343 
isUntitled() const344 bool ToonzScene::isUntitled() const {
345   return m_scenePath == TFilePath() || m_isUntitled;
346 }
347 
348 //-----------------------------------------------------------------------------
349 
load(const TFilePath & path,bool withProgressDialog)350 void ToonzScene::load(const TFilePath &path, bool withProgressDialog) {
351   loadNoResources(path);
352   loadResources(withProgressDialog);
353 }
354 
355 //-----------------------------------------------------------------------------
356 
357 // Funzioncina veloce per trovare il numero di frame di una scena senza caricare
358 // nulla. (implementato per Toonz 6.0 beta 1)
loadFrameCount(const TFilePath & fp)359 int ToonzScene::loadFrameCount(const TFilePath &fp) {
360   TIStream is(fp);
361   if (!is) throw TException(fp.getWideString() + L": Can't open file");
362   try {
363     // Leggo il primo tag (<tnz/tab>) ed estraggo il framecount (se c'e')
364     std::string tagName = "";
365     if (!is.matchTag(tagName)) throw TException("Bad file format");
366 
367     if (tagName == "tab" || tagName == "tnz") {
368       int frameCount;
369       if (is.getTagParam("framecount", frameCount))
370         return frameCount;
371       else
372         return 0;
373     } else
374       throw TException("Bad file format");
375   } catch (TException &e) {
376     throw TIStreamException(is, e);
377   } catch (...) {
378     throw TIStreamException(is);
379   }
380 
381   return 0;
382 }
383 
384 //-----------------------------------------------------------------------------
385 
loadNoResources(const TFilePath & fp)386 void ToonzScene::loadNoResources(const TFilePath &fp) {
387   clear();
388 
389   TProjectManager *pm    = TProjectManager::instance();
390   TProjectP sceneProject = pm->loadSceneProject(fp);
391   if (!sceneProject) return;
392 
393   setProject(sceneProject.getPointer());
394 
395   loadTnzFile(fp);
396   getXsheet()->updateFrameCount();
397 }
398 
399 //-----------------------------------------------------------------------------
400 /*--
401  * プログレスダイアログをGUIからの実行時でのみ表示させる。tcomposerから実行の場合は表示させない
402  * --*/
loadResources(bool withProgressDialog)403 void ToonzScene::loadResources(bool withProgressDialog) {
404   /*--- m_levelSet->getLevelCount()が10個以上のとき表示させる ---*/
405   QProgressDialog *progressDialog = 0;
406   if (withProgressDialog && m_levelSet->getLevelCount() >= 10) {
407     progressDialog = new QProgressDialog("Loading Scene Resources", "", 0,
408                                          m_levelSet->getLevelCount());
409     progressDialog->setModal(true);
410     progressDialog->setAutoReset(
411         true); /*--maximumに到達したら自動でresetを呼ぶ--*/
412     progressDialog->setAutoClose(true); /*--resetが呼ばれたら自動で閉じる--*/
413     progressDialog->setAttribute(Qt::WA_DeleteOnClose,
414                                  true); /*--閉じたら自動でDeleteされる--*/
415     progressDialog->setCancelButton(0);
416     progressDialog->setValue(0);
417     progressDialog->show();
418   }
419 
420   int i;
421   for (i = 0; i < m_levelSet->getLevelCount(); i++) {
422     if (progressDialog) progressDialog->setValue(i + 1);
423 
424     TXshLevel *level = m_levelSet->getLevel(i);
425     try {
426       level->load();
427     } catch (...) {
428     }
429   }
430   getXsheet()->updateFrameCount();
431 }
432 
433 //-----------------------------------------------------------------------------
434 
loadTnzFile(const TFilePath & fp)435 void ToonzScene::loadTnzFile(const TFilePath &fp) {
436   bool reading22 = false;
437   TIStream is(fp);
438   if (!is) throw TException(fp.getWideString() + L": Can't open file");
439   try {
440     std::string tagName = "";
441     if (!is.matchTag(tagName)) throw TException("Bad file format");
442 
443     if (tagName == "tab" || tagName == "tnz") {
444       std::string rootTagName = tagName;
445       std::string v           = is.getTagAttribute("version");
446       VersionNumber versionNumber(0, 0);
447       int k = v.find(".");
448       if (k != (int)std::string::npos && 0 < k && k < (int)v.length()) {
449         versionNumber.first  = std::stoi(v.substr(0, k));
450         versionNumber.second = std::stoi(v.substr(k + 1));
451       }
452       if (versionNumber == VersionNumber(0, 0))
453         throw TException("Bad version number :" + v);
454       setVersionNumber(versionNumber);
455       is.setVersion(versionNumber);
456       while (is.matchTag(tagName)) {
457         if (tagName == "generator") {
458           std::string program = is.getString();
459           // TODO: This obsolete condition should be removed before releasing OT
460           // v2.2 !
461           reading22 = program.find("2.2") != std::string::npos;
462         } else if (tagName == "properties")
463           m_properties->loadData(is, false);
464         else if (tagName == "palette")  // per compatibilita' beta1
465         {
466           TPalette *palette = new TPalette;
467           is >> *palette;
468           delete palette;
469         } else if (tagName == "levelSet")
470           m_levelSet->loadData(is);
471         else if (tagName == "levels") {
472           // obsoleto
473           if (!reading22) assert(0);
474           while (!is.eos()) {
475             TPersist *p = 0;
476             is >> p;
477             TXshLevel *xshLevel = dynamic_cast<TXshLevel *>(p);
478             if (xshLevel) {
479               xshLevel->setScene(this);
480               TXshSimpleLevel *sl = xshLevel->getSimpleLevel();
481               if (reading22 && sl && sl->getPath() == TFilePath()) {
482                 sl->setType(PLI_XSHLEVEL);
483                 sl->setPath(TFilePath("+drawings/") +
484                             (sl->getName() + L".pli"));
485               }
486               m_levelSet->insertLevel(xshLevel);
487             }
488           }
489         } else if (tagName == "xsheet")
490           is >> *getXsheet();
491         else if (tagName == "history") {
492           std::string historyData, s;
493           while (!is.eos()) {
494             is >> s;
495             historyData += s;
496           }
497           TContentHistory *history = getContentHistory(true);
498           history->deserialize(QString::fromStdString(historyData));
499         } else
500           throw TException(tagName + " : unexpected tag");
501 
502         if (!is.matchEndTag()) throw TException(tagName + " : missing end tag");
503       }
504       if (!is.matchEndTag())
505         throw TException(rootTagName + " : missing end tag");
506     } else
507       throw TException("Bad file format");
508 
509     setScenePath(fp);
510 
511     for (int i = 0; i < m_levelSet->getLevelCount(); i++)
512       m_levelSet->getLevel(i)->setScene(this);
513 
514   } catch (TException &e) {
515     throw TIStreamException(is, e);
516   } catch (...) {
517     throw TIStreamException(is);
518   }
519 
520   m_properties->cloneCamerasTo(getXsheet()->getStageObjectTree());
521   fixBiancoProblem(this, getXsheet());
522 }
523 
524 //-----------------------------------------------------------------------------
525 
526 // saveUntitled() viene chiamata subito dopo newScene
527 // serve principalmente come lock per evitare che vengano create due scene
528 // con lo stesso nome.
529 
setUntitled()530 void ToonzScene::setUntitled() {
531   m_isUntitled               = true;
532   const std::string baseName = "untitled";
533   TFilePath tempDir          = getUntitledScenesDir();
534   if (TFileStatus(tempDir).doesExist() == false) {
535     try {
536       TSystem::mkDir(tempDir);
537     } catch (...) {
538     }
539   }
540 
541   std::string name = baseName;
542   if (TFileStatus(tempDir + name).doesExist()) {
543     int count = 2;
544     do {
545       name = baseName + std::to_string(count++);
546     } while (TFileStatus(tempDir + name).doesExist());
547   }
548   TFilePath fp = tempDir + name + (name + ".tnz");
549   try {
550     TSystem::touchParentDir(fp);
551   } catch (...) {
552     assert(0);
553   }
554   m_scenePath = fp;
555 }
556 
557 //-----------------------------------------------------------------------------
558 
559 // When saving as sub-xsheet, the sub becomes top. So, its cameras must be
560 // associated to the scene properties.
561 class CameraRedirection {
562   ToonzScene *m_scene;
563   TXsheet *m_xsh;
564 
565 public:
CameraRedirection(ToonzScene * scene,TXsheet * xsh)566   CameraRedirection(ToonzScene *scene, TXsheet *xsh)
567       : m_scene(scene), m_xsh(xsh) {
568     if (!xsh) xsh = m_scene->getTopXsheet();
569 
570     m_scene->getProperties()->cloneCamerasFrom(xsh->getStageObjectTree());
571   }
572 
~CameraRedirection()573   ~CameraRedirection() {
574     if (m_xsh)
575       m_scene->getProperties()->cloneCamerasFrom(
576           m_scene->getTopXsheet()->getStageObjectTree());
577   }
578 };
579 
580 //-----------------------------------------------------------------------------
581 
save(const TFilePath & fp,TXsheet * subxsh)582 void ToonzScene::save(const TFilePath &fp, TXsheet *subxsh) {
583   TFilePath oldScenePath = getScenePath();
584   TFilePath newScenePath = fp;
585 
586   CameraRedirection redir(this, subxsh);
587 
588   bool wasUntitled = isUntitled();
589 
590   setScenePath(fp);
591 
592   TFileStatus fs(newScenePath);
593   if (fs.doesExist() && !fs.isWritable())
594     throw TSystemException(newScenePath,
595                            "The scene cannot be saved: it is a read only "
596                            "scene.\n All resources have been saved.");
597 
598   TFilePath scenePath = decodeFilePath(fp);
599   TFilePath scenePathTemp(scenePath.getWideString() +
600                           QString(".tmp").toStdWString());
601 
602   if (Preferences::instance()->isBackupEnabled() &&
603       oldScenePath == newScenePath && TFileStatus(scenePath).doesExist())
604     saveBackup(scenePath);
605 
606   if (TFileStatus(scenePathTemp).doesExist())
607     TSystem::removeFileOrLevel(scenePathTemp);
608 
609   //  TSystem::touchFile(scenePath);
610   TSystem::touchFile(scenePathTemp);
611   makeSceneIcon(this);
612 
613   // TOStream os(scenePath, compressionEnabled);
614   //  TOStream os(scenePath, false);
615   {
616     TOStream os(scenePathTemp, false);
617     if (!os.checkStatus())
618       throw TException("Could not open temporary save file");
619 
620     TXsheet *xsh = subxsh;
621     if (xsh == 0) xsh = m_childStack->getTopXsheet();
622 
623     std::map<std::string, std::string> attr;
624     attr["version"] =
625         (QString::number(l_currentVersion.first) +
626          "."  // From now on, version numbers in saved files will have
627          + QString::number(
628                l_currentVersion.second))  // the signature "MAJOR.MINOR", where:
629             .toStdString();               //
630     attr["framecount"] =
631         QString::number(  //    MAJOR = Toonz version number * 10 (eg 7.0 => 70)
632             xsh->getFrameCount())
633             .toStdString();  //    MINOR = Reset to 0 after each major
634                              //    increment,
635                              //    and
636     //            advancing on its own when fixing bugs.
637     os.openChild("tnz", attr);
638 
639     os.child("generator") << TEnv::getApplicationFullName();
640     os.openChild("properties");
641     m_properties->saveData(os);
642     os.closeChild();
643 
644     if (subxsh) {
645       std::set<TXshLevel *> saveSet;
646       subxsh->getUsedLevels(saveSet);
647       m_levelSet->setSaveSet(saveSet);
648     }
649     os.openChild("levelSet");
650     m_levelSet->saveData(os);
651     os.closeChild();
652     std::set<TXshLevel *> emptySaveSet;
653     m_levelSet->setSaveSet(emptySaveSet);
654 
655     os.openChild("xsheet");
656     os << *xsh;
657     os.closeChild();
658 
659     if (getContentHistory()) {
660       os.openChild("history");
661       QString data = getContentHistory()->serialize();
662       int i        = 0, j;
663       // non scrivo tutta la std::string di seguito per evitare problemi se
664       // diventa
665       // troppo lunga. Cerco di spezzarla in modo che sia "bella da leggere" nel
666       // tnz
667       while ((j = data.indexOf("||", i)) >= i) {
668         os << data.mid(i, j - i + 1).toStdWString();
669         os.cr();
670         i = j + 1;
671       }
672       os << data.mid(i).toStdWString();
673       os.closeChild();
674     }
675 
676     os.closeChild();
677     bool status = os.checkStatus();
678     if (!status) throw TException("Could not complete the temporary save");
679   }
680 
681   if (TFileStatus(scenePathTemp).doesExist())
682     TSystem::renameFile(scenePath, scenePathTemp, true);
683 
684   if (subxsh) {
685     setScenePath(oldScenePath);
686     if (wasUntitled) setUntitled();
687   } else {
688     if (wasUntitled) deleteUntitledScene(oldScenePath.getParentDir());
689   }
690   // update the last saved version
691   setVersionNumber(l_currentVersion);
692 }
693 
694 //-----------------------------------------------------------------------------
695 
getFrameCount() const696 int ToonzScene::getFrameCount() const {
697   TXsheet *xsh = getXsheet();
698   return xsh ? xsh->getFrameCount() : 0;
699 }
700 
701 //-----------------------------------------------------------------------------
702 
renderFrame(const TRaster32P & ras,int row,const TXsheet * xsh,bool checkFlags) const703 void ToonzScene::renderFrame(const TRaster32P &ras, int row, const TXsheet *xsh,
704                              bool checkFlags) const {
705   if (xsh == 0) xsh = getXsheet();
706 
707   TCamera *camera        = xsh->getStageObjectTree()->getCurrentCamera();
708   TDimension cameraRes   = camera->getRes();
709   TDimensionD cameraSize = camera->getSize();
710 
711   // voglio che la camera sia completamente contenuta dentro raster
712   double sx = (double)ras->getLx() / (double)cameraSize.lx;
713   double sy = (double)ras->getLy() / (double)cameraSize.ly;
714   double sc = (sx < sy) ? sx : sy;
715 
716   const TAffine &cameraAff =
717       xsh->getPlacement(xsh->getStageObjectTree()->getCurrentCameraId(), row);
718   const TAffine &viewAff = TScale(sc / Stage::inch) * cameraAff.inv();
719 
720   TRect clipRect(ras->getBounds());
721   TOfflineGL ogl(ras->getSize());
722   currentOfflineGL = &ogl;
723 
724   ogl.makeCurrent();
725   {
726     glTranslated(0.5 * ras->getLx(), 0.5 * ras->getLy(), 0.0);
727 
728     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
729     glClear(GL_COLOR_BUFFER_BIT);
730 
731     ImagePainter::VisualSettings vs;
732     vs.m_plasticVisualSettings.m_drawMeshesWireframe = false;
733     vs.m_forSceneIcon                                = true;
734 
735     Stage::RasterPainter painter(ras->getSize(), viewAff, clipRect, vs,
736                                  checkFlags);
737     Stage::visit(painter, const_cast<ToonzScene *>(this),
738                  const_cast<TXsheet *>(xsh), row);
739 
740     painter.flushRasterImages();
741     glFlush();
742 
743     TRop::over(ras, ogl.getRaster());
744   }
745   ogl.doneCurrent();
746 
747   currentOfflineGL = 0;
748 }
749 
750 //-----------------------------------------------------------------------------
751 
752 //! Performs a camera-stand render of the specified xsheet in the specified
753 //! placedRect,
754 //! with known world/placed reference change - and returns the result in a
755 //! 32-bit raster.
756 
renderFrame(const TRaster32P & ras,int row,const TXsheet * xsh,const TRectD & placedRect,const TAffine & worldToPlacedAff) const757 void ToonzScene::renderFrame(const TRaster32P &ras, int row, const TXsheet *xsh,
758                              const TRectD &placedRect,
759                              const TAffine &worldToPlacedAff) const {
760   // Build reference change affines
761   const TAffine &placedToOglRefAff =
762       TScale(ras->getLx() / placedRect.getLx(),
763              ras->getLy() / placedRect.getLy()) *
764       TTranslation(-0.5 * (placedRect.x0 + placedRect.x1),
765                    -0.5 * (placedRect.y0 + placedRect.y1));
766 
767   const TAffine &cameraAff =
768       xsh->getPlacement(xsh->getStageObjectTree()->getCurrentCameraId(), row);
769 
770   const TAffine &worldToOglRefAff =
771       placedToOglRefAff * worldToPlacedAff * cameraAff.inv();
772 
773   TRect clipRect(ras->getBounds());
774 
775 // fix for plastic tool applied to subxsheet
776 #ifdef MACOSX
777   QSurfaceFormat format;
778   format.setProfile(QSurfaceFormat::CompatibilityProfile);
779 
780   std::unique_ptr<QOffscreenSurface> surface(new QOffscreenSurface());
781   surface->setFormat(format);
782   surface->create();
783 
784   glPushAttrib(GL_ALL_ATTRIB_BITS);
785   glMatrixMode(GL_MODELVIEW), glPushMatrix();
786   glMatrixMode(GL_PROJECTION), glPushMatrix();
787 #else
788   TOfflineGL ogl(ras->getSize());
789   currentOfflineGL = &ogl;
790   ogl.makeCurrent();
791 #endif
792   {
793 #ifdef MACOSX
794     std::unique_ptr<QOpenGLFramebufferObject> fb(
795         new QOpenGLFramebufferObject(ras->getLx(), ras->getLy()));
796 
797     fb->bind();
798     assert(glGetError() == GL_NO_ERROR);
799 
800     glViewport(0, 0, ras->getLx(), ras->getLy());
801 
802     glMatrixMode(GL_PROJECTION);
803     glLoadIdentity();
804     gluOrtho2D(0, ras->getLx(), 0, ras->getLy());
805 
806     glMatrixMode(GL_MODELVIEW);
807     glLoadIdentity();
808 #endif
809     glTranslated(0.5 * ras->getLx(), 0.5 * ras->getLy(), 0.0);
810 
811     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
812     glClear(GL_COLOR_BUFFER_BIT);
813 
814     ImagePainter::VisualSettings vs;
815     vs.m_plasticVisualSettings.m_drawMeshesWireframe = false;
816 
817     Stage::RasterPainter painter(ras->getSize(), worldToOglRefAff, clipRect, vs,
818                                  false);
819     Stage::visit(painter, const_cast<ToonzScene *>(this),
820                  const_cast<TXsheet *>(xsh), row);
821 
822     painter.flushRasterImages();
823     glFlush();
824 #ifdef MACOSX
825     QImage img =
826         fb->toImage().scaled(QSize(ras->getLx(), ras->getLy()),
827                              Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
828 
829     int wrap      = ras->getLx() * sizeof(TPixel32);
830     uchar *srcPix = img.bits();
831     uchar *dstPix = ras->getRawData() + wrap * (ras->getLy() - 1);
832     for (int y = 0; y < ras->getLy(); y++) {
833       memcpy(dstPix, srcPix, wrap);
834       dstPix -= wrap;
835       srcPix += wrap;
836     }
837     fb->release();
838     assert(glGetError() == GL_NO_ERROR);
839 #else
840     TRop::over(ras, ogl.getRaster());
841 #endif
842   }
843 #ifdef MACOSX
844   glMatrixMode(GL_MODELVIEW), glPopMatrix();
845   glMatrixMode(GL_PROJECTION), glPopMatrix();
846 
847   glPopAttrib();
848 #else
849   ogl.doneCurrent();
850   currentOfflineGL = 0;
851 #endif
852 }
853 
854 //-----------------------------------------------------------------------------
855 
createNewLevel(int type,std::wstring levelName,const TDimension & dim,double dpi,TFilePath fp)856 TXshLevel *ToonzScene::createNewLevel(int type, std::wstring levelName,
857                                       const TDimension &dim, double dpi,
858                                       TFilePath fp) {
859   TLevelSet *levelSet = getLevelSet();
860 
861   if (type == TZI_XSHLEVEL)  // TZI type corresponds to the 'Scan Level'
862     type = OVL_XSHLEVEL;     // default option. See Toonz Preferences class.
863 
864   if (type == CHILD_XSHLEVEL && levelName == L"") levelName = L"sub";
865 
866   // Select a different unique level name in case it already exists (either in
867   // scene or on disk)
868   {
869     const std::unique_ptr<NameBuilder> nameBuilder(
870         NameBuilder::getBuilder(levelName));
871 
872     for (;;) {
873       levelName = nameBuilder->getNext();
874       /*-- levelが既にロード済みなら、次の名前を取得 --*/
875       if (m_levelSet->getLevel(levelName) != 0) continue;
876 
877       /*-- LevelSetの中に同じファイルパスのLevelがあるかをチェック --*/
878       if (type != CHILD_XSHLEVEL && type != PLT_XSHLEVEL) {
879         if (fp.isEmpty()) fp = getDefaultLevelPath(type, levelName);
880         TFilePath actualFp = decodeFilePath(fp);
881 
882         if (TSystem::doesExistFileOrLevel(
883                 actualFp))  // if(TFileStatus(actualFp).doesExist()) continue;
884         {
885           fp = TFilePath();
886           continue;
887         }
888 
889         int l, lCount = levelSet->getLevelCount();
890         for (l = 0; l != lCount; ++l) {
891           TXshLevel *xl = levelSet->getLevel(l);
892           if (!xl) continue;
893 
894           TXshSimpleLevel *sl = xl->getSimpleLevel();
895           if (!sl) continue;
896 
897           TFilePath lfp = decodeFilePath(sl->getPath());
898           if (actualFp == lfp) break;
899         }
900 
901         if (l < lCount) {
902           /*-- fpが既存のLevelと重複したため、再設定する --*/
903           fp = TFilePath();
904           continue;
905         }
906       }
907       break;
908     }
909   }
910 
911   TXshLevel *xl = 0;
912   if (type == CHILD_XSHLEVEL) {
913     TXshChildLevel *cl = new TXshChildLevel(levelName);
914     cl->setScene(this);
915     cl->getXsheet()->setScene(this);
916     xl = cl;
917 
918     // Include the project's default cameras
919     const TSceneProperties &props =
920         TProjectManager::instance()->getCurrentProject()->getSceneProperties();
921 
922     props.cloneCamerasTo(cl->getXsheet()->getStageObjectTree());
923   } else if (type == PLT_XSHLEVEL) {
924     TXshPaletteLevel *pl = new TXshPaletteLevel(levelName);
925     pl->setScene(this);
926     xl = pl;
927   } else {
928     TXshSimpleLevel *sl = new TXshSimpleLevel(levelName);
929 
930     sl->setScene(this);
931     sl->setType(type);
932     sl->setPath(fp);
933     sl->setDirtyFlag(true);
934 
935     sl->initializePalette();
936     sl->initializeResolutionAndDpi();
937 
938     xl = sl;
939   }
940 
941   m_levelSet->insertLevel(xl);
942   TNotifier::instance()->notify(TCastChange());
943 
944   return xl;
945 }
946 
947 //-----------------------------------------------------------------------------
948 
getXsheet() const949 TXsheet *ToonzScene::getXsheet() const { return m_childStack->getXsheet(); }
950 
951 //-----------------------------------------------------------------------------
952 
getTopXsheet() const953 TXsheet *ToonzScene::getTopXsheet() const {
954   return m_childStack->getTopXsheet();
955 }
956 
957 //-----------------------------------------------------------------------------
958 
959 struct LevelType {
960   int m_ltype;
961   bool m_oldLevelFlag;
962   bool m_vectorNotPli;
963   std::string m_ext;
964 };
965 
966 //-----------------------------------------------------------------------------
967 
getLevelType(const TFilePath & fp)968 static LevelType getLevelType(const TFilePath &fp) {
969   LevelType ret;
970   ret.m_ltype        = UNKNOWN_XSHLEVEL;
971   ret.m_oldLevelFlag = false;
972   ret.m_vectorNotPli = false;
973   ret.m_ext          = fp.getType();
974   std::string format = ret.m_ext;
975 
976   TFileType::Type type = TFileType::getInfo(fp);
977 
978   switch (type) {
979   case TFileType::RASTER_IMAGE:
980   case TFileType::RASTER_LEVEL:
981   case TFileType::CMAPPED_LEVEL:
982     if (format == "tzp" || format == "tzu") {
983       ret.m_ltype        = TZP_XSHLEVEL;
984       ret.m_oldLevelFlag = true;
985       ret.m_ext          = "tlv";
986     } else if (format == "tzl" || format == "tlv")
987       ret.m_ltype = TZP_XSHLEVEL;
988     else if (format == "tzi")
989       ret.m_ltype = TZI_XSHLEVEL;
990     else
991       ret.m_ltype = OVL_XSHLEVEL;
992     break;
993 
994   case TFileType::VECTOR_LEVEL:
995     if (format == "svg") {
996       ret.m_vectorNotPli = true;
997       ret.m_ext          = "pli";
998     }
999     /*  if (format == "svg")
1000             ret.m_ext = "pli";*/
1001     ret.m_ltype = PLI_XSHLEVEL;
1002     break;
1003 
1004   case TFileType::AUDIO_LEVEL:
1005     ret.m_ltype = SND_XSHLEVEL;
1006     break;
1007 
1008   case TFileType::MESH_IMAGE:
1009   case TFileType::MESH_LEVEL:
1010     ret.m_ltype = MESH_XSHLEVEL;
1011     break;
1012   default:
1013     break;
1014   }
1015 
1016   return ret;
1017 }
1018 
1019 //-----------------------------------------------------------------------------
1020 
getImportedLevelPath(const TFilePath path) const1021 TFilePath ToonzScene::getImportedLevelPath(const TFilePath path) const {
1022   if (TFileType::getInfo(path) == TFileType::AUDIO_LEVEL)
1023     return path.withParentDir(TFilePath("+extras"));
1024   else if (TFileType::getInfo(path) == TFileType::PALETTE_LEVEL)
1025     return path.withParentDir(TFilePath("+palettes"));
1026 
1027   const LevelType &ltype = getLevelType(path);
1028   if (ltype.m_ltype == UNKNOWN_XSHLEVEL) return path;
1029 
1030   const std::wstring &levelName = path.getWideName();
1031   const std::string &dots       = path.getDots();
1032 
1033   TFilePath importedLevelPath =
1034       getDefaultLevelPath(ltype.m_ltype, levelName).getParentDir() +
1035       path.getLevelNameW();
1036 
1037   if (dots == "..")
1038     importedLevelPath = importedLevelPath.withFrame(TFrameId::EMPTY_FRAME);
1039 
1040   if (importedLevelPath.getType() == "tlv")  // Type shouldn't have changed...
1041     importedLevelPath =
1042         importedLevelPath.withNoFrame();  // Plus, should be unnecessary...
1043 
1044   return importedLevelPath;
1045 }
1046 
1047 //-----------------------------------------------------------------------------
1048 
1049 /* tzp,tzu->tlv */
convertLevelIfNeeded(TFilePath & levelPath)1050 bool ToonzScene::convertLevelIfNeeded(TFilePath &levelPath) {
1051   LevelType ltype = getLevelType(levelPath);
1052   TFilePath fp    = levelPath;
1053   if (ltype.m_vectorNotPli) {
1054     // livello flash o svg
1055     levelPath = levelPath.withType("pli");
1056     TLevelWriterP lw(levelPath);
1057     TLevelReaderP lr(fp);
1058     if (!lr) return false;
1059     TLevelP inLevel = lr->loadInfo();
1060     if (!inLevel || inLevel->getFrameCount() == 0) return false;
1061     TLevelP outLevel;
1062     for (TLevel::Iterator it = inLevel->begin(); it != inLevel->end(); ++it) {
1063       TVectorImageP img = lr->getFrameReader(it->first)->load();
1064       if (!img) continue;
1065       lw->getFrameWriter(it->first)->save(img);
1066     }
1067   } else if (ltype.m_oldLevelFlag) {
1068     TLevelP outLevel;
1069     // livello Toonz 4.6
1070     levelPath = TFilePath(levelPath.getParentDir().getWideString() + L"\\" +
1071                           levelPath.getWideName() + L".tlv");
1072     if (TSystem::doesExistFileOrLevel(levelPath))
1073       TSystem::removeFileOrLevel(levelPath);
1074     TFilePath pltPath = TFilePath(levelPath.getParentDir().getWideString() +
1075                                   L"\\" + levelPath.getWideName() + L".tpl");
1076     if (TSystem::doesExistFileOrLevel(pltPath))
1077       TSystem::removeFileOrLevel(pltPath);
1078 
1079     TLevelWriterP lw(levelPath);
1080     lw->setIconSize(Preferences::instance()->getIconSize());
1081     TPaletteP palette =
1082         ToonzImageUtils::loadTzPalette(fp.withType("plt").withNoFrame());
1083     TLevelReaderP lr(fp);
1084     if (!lr) return false;
1085     TLevelP inLevel = lr->loadInfo();
1086     if (!inLevel || inLevel->getFrameCount() == 0) return false;
1087     outLevel->setPalette(palette.getPointer());
1088     try {
1089       for (TLevel::Iterator it = inLevel->begin(); it != inLevel->end(); ++it) {
1090         TToonzImageP img = lr->getFrameReader(it->first)->load();
1091         if (!img) continue;
1092         img->setPalette(palette.getPointer());
1093         lw->getFrameWriter(it->first)->save(img);
1094       }
1095     } catch (TException &e) {
1096       // devo buttare il tlv che stavo salvando!
1097       QString msg = QString::fromStdWString(e.getMessage());
1098       if (msg == QString("Old 4.1 Palette")) {
1099         lw = TLevelWriterP();
1100         if (TSystem::doesExistFileOrLevel(levelPath))
1101           TSystem::removeFileOrLevel(levelPath);
1102         throw e;
1103       }
1104     }
1105     lw = TLevelWriterP();  // bisogna liberare prima lw di outLevel,
1106     // altrimenti la paletta che lw vuole scrivere e' gia' stata
1107     // loutLeveliberata.
1108   }
1109   return true;
1110 }
1111 
1112 //-----------------------------------------------------------------------------
1113 
loadLevel(const TFilePath & actualPath,const LevelOptions * levelOptions,std::wstring levelName,const std::vector<TFrameId> & fIds)1114 TXshLevel *ToonzScene::loadLevel(const TFilePath &actualPath,
1115                                  const LevelOptions *levelOptions,
1116                                  std::wstring levelName,
1117                                  const std::vector<TFrameId> &fIds) {
1118   LevelType ltype = getLevelType(actualPath);
1119   if (ltype.m_ltype == UNKNOWN_XSHLEVEL) return 0;
1120 
1121   TFilePath levelPath = actualPath;
1122 
1123   // Ensure the level name is unique
1124 
1125   if (QString::fromStdWString(levelName).isEmpty()) {
1126     // if the option is set in the preferences,
1127     // remove the scene numbers("c####_") from the file name
1128     levelName = getLevelNameWithoutSceneNumber(levelPath.getWideName());
1129   }
1130 
1131   NameModifier nm(levelName);
1132   levelName = nm.getNext();
1133   while (m_levelSet->hasLevel(levelName)) levelName = nm.getNext();
1134 
1135   // Discriminate sound levels
1136   if (ltype.m_ltype == SND_XSHLEVEL) {
1137     TXshSoundLevel *sl = new TXshSoundLevel(levelName);
1138     sl->setType(ltype.m_ltype);
1139     sl->setScene(this);
1140     sl->setPath(codeFilePath(levelPath));
1141 
1142     try {
1143       sl->load();
1144     } catch (const std::string &msg)  // Intercepting std::string exceptions
1145     {
1146       throw TException(msg);
1147     }  // from load, and rethrowing... curious!
1148 
1149     m_levelSet->insertLevel(sl);
1150     return sl;
1151   } else {
1152     TXshSimpleLevel *xl = new TXshSimpleLevel(levelName);
1153     xl->setType(ltype.m_ltype);
1154     xl->setScene(this);
1155 
1156     if (!convertLevelIfNeeded(levelPath))
1157       return 0;  // Conversion failed, bail out
1158 
1159     xl->setPath(codeFilePath(levelPath), true);
1160 
1161     try {
1162       if (fIds.size() != 0 && !ltype.m_oldLevelFlag)
1163         xl->load(fIds);
1164       else
1165         xl->load();
1166     } catch (const std::string &msg) {
1167       throw TException(msg);
1168     }
1169 
1170     if (ltype.m_oldLevelFlag)
1171       xl->setDirtyFlag(true);  // Not set on old level formats?
1172 
1173     LevelProperties *lp = xl->getProperties();
1174     assert(lp);
1175 
1176     if (levelOptions)
1177       lp->options() = *levelOptions;
1178     else {
1179       const Preferences &prefs = *Preferences::instance();
1180       int formatIdx            = prefs.matchLevelFormat(
1181           levelPath);  // Should I use actualPath here? It's mostly
1182                                   // irrelevant anyway, it's for old tzp/tzu...
1183       if (formatIdx >= 0)
1184         lp->options() = prefs.levelFormat(formatIdx).m_options;
1185       else {
1186         // Default subsampling values are assigned from scene properties
1187         if (xl->getType() == OVL_XSHLEVEL)
1188           lp->setSubsampling(getProperties()->getFullcolorSubsampling());
1189         else if (xl->getType() == TZP_XSHLEVEL)
1190           lp->setSubsampling(getProperties()->getTlvSubsampling());
1191       }
1192     }
1193 
1194     if (lp->getDpiPolicy() == LevelProperties::DP_ImageDpi) {
1195       // We must check whether the image actually has a dpi.
1196       const TPointD &imageDpi = xl->getImageDpi();
1197 
1198       if (imageDpi == TPointD() ||
1199           Preferences::instance()->getUnits() == "pixel" ||
1200           Preferences::instance()->isIgnoreImageDpiEnabled()) {
1201         // Change to "Custom Dpi" policy and use camera dpi
1202         TStageObjectId cameraId =
1203             getXsheet()->getStageObjectTree()->getCurrentCameraId();
1204 
1205         lp->setDpiPolicy(LevelProperties::DP_CustomDpi);
1206         lp->setDpi(getCurrentCamera()->getDpi());
1207       } else {
1208         // Has dpi alright - assign it to custom dpi, too
1209         lp->setDpi(imageDpi);
1210       }
1211     }
1212 
1213     m_levelSet->insertLevel(xl);
1214 
1215     return xl;
1216   }
1217 
1218   return 0;
1219 }
1220 
1221 //-----------------------------------------------------------------------------
1222 
decodeFilePath(const TFilePath & path) const1223 TFilePath ToonzScene::decodeFilePath(const TFilePath &path) const {
1224   TProject *project   = getProject();
1225   bool projectIsEmpty = project->getFolderCount() ? false : true;
1226   TFilePath fp        = path;
1227 
1228   std::wstring head;
1229   TFilePath tail;
1230   path.split(head, tail);
1231 
1232   std::string h;
1233   std::wstring s;
1234   if (head != L"") {
1235     // in case of the project folder aliases
1236     if (head[0] == L'+') {
1237       if (TProjectManager::instance()->isTabModeEnabled()) {
1238         return m_scenePath.getParentDir() +
1239                (m_scenePath.getWideName() + L"_files") + tail;
1240       }
1241 
1242       if (TProjectManager::instance()->isTabKidsModeEnabled()) {
1243         return m_scenePath.getParentDir() + m_scenePath.getWideName() + tail;
1244       }
1245 
1246       if (projectIsEmpty) {
1247         TFilePath dir = m_scenePath.getParentDir();
1248         if (dir.getName() == "scenes")
1249           return dir.withName(head.substr(1)) + tail;
1250         else
1251           return dir + tail;
1252       }
1253       if (project) {
1254         h           = ::to_string(head.substr(1));
1255         TFilePath f = project->getFolder(h);
1256         if (f != TFilePath()) s = f.getWideString();
1257       }
1258     }
1259     // in case of the scene folder alias
1260     else if (head == L"$scenefolder") {
1261       TFilePath dir = m_scenePath.getParentDir();
1262       return dir + tail;
1263     }
1264   }
1265   if (s != L"") {
1266     std::map<std::wstring, std::wstring> table;
1267 
1268     // if the scene is untitled and path expansion depends on the scene
1269     // (because the expansion contains $ scenename, $ scenepath, or it uses the
1270     // savepath)
1271     if (m_isUntitled &&
1272         (s.find(L"$scene") != std::wstring::npos ||
1273          project->getUseScenePath(h) ||
1274          fp.getParentDir().getName() == getScenePath().getName())) {
1275       TFilePath parentDir = getScenePath().getParentDir();
1276       fp                  = parentDir + head.substr(1) + tail;
1277     } else {
1278       TFilePath scenePath = getScenePath();
1279       TFilePath scenePathRoot;
1280       if (project)
1281         scenePathRoot = project->getFolder(TProject::Scenes);
1282       else
1283         scenePathRoot = scenePath.getParentDir();
1284 
1285       if (scenePathRoot != TFilePath() && !scenePathRoot.isAbsolute() &&
1286           project)
1287         scenePathRoot = project->getProjectFolder() + scenePathRoot;
1288 
1289       if (TSystem::isUNC(scenePath) && !TSystem::isUNC(scenePathRoot))
1290         scenePathRoot = TSystem::toUNC(scenePathRoot);
1291 
1292       if (scenePathRoot.isAncestorOf(scenePath))
1293         scenePath = scenePath - scenePathRoot;
1294 
1295       table[L"$scenepath"] = scenePath.withType("").getWideString();
1296       table[L"$scenename"] = scenePath.withType("").getWideString();
1297 
1298       std::map<std::wstring, std::wstring>::reverse_iterator it;
1299       for (it = table.rbegin(); it != table.rend(); ++it) {
1300         std::wstring keyword = it->first;
1301         int i                = 0;
1302         for (;;) {
1303           int j = s.find(keyword, i);
1304           if (j == (int)std::wstring::npos) break;
1305           s.replace(j, keyword.length(), it->second);
1306           i = j;
1307         }
1308       }
1309       fp = TFilePath(s) + tail;
1310     }
1311   }
1312 
1313   if (fp != TFilePath() && !fp.isAbsolute() && project)
1314     fp = project->getProjectFolder() + fp;
1315   return fp;
1316 }
1317 
1318 //-----------------------------------------------------------------------------
1319 
codeFilePath(const TFilePath & path) const1320 TFilePath ToonzScene::codeFilePath(const TFilePath &path) const {
1321   TFilePath fp(path);
1322   TProject *project = getProject();
1323 
1324   Preferences::PathAliasPriority priority =
1325       Preferences::instance()->getPathAliasPriority();
1326   if (priority == Preferences::SceneFolderAlias &&
1327       codeFilePathWithSceneFolder(fp)) {
1328     return fp;
1329   }
1330 
1331   if (project)
1332     for (int i = 0; i < project->getFolderCount(); i++) {
1333       TFilePath folderName("+" + project->getFolderName(i));
1334       TFilePath folderPath = decodeFilePath(folderName);
1335       if (folderPath.isAncestorOf(fp)) {
1336         fp = folderName + (fp - folderPath);
1337         return fp;
1338       }
1339     }
1340 
1341   if (priority == Preferences::ProjectFolderAliases)
1342     codeFilePathWithSceneFolder(fp);
1343 
1344   return fp;
1345 }
1346 
1347 //-----------------------------------------------------------------------------
1348 // if the path is codable with $scenefolder alias, replace it and return true
1349 
codeFilePathWithSceneFolder(TFilePath & path) const1350 bool ToonzScene::codeFilePathWithSceneFolder(TFilePath &path) const {
1351   // if the scene is untitled, then do nothing and return false
1352   if (isUntitled()) return false;
1353   TFilePath sceneFolderPath = m_scenePath.getParentDir();
1354   if (sceneFolderPath.isAncestorOf(path)) {
1355     path = TFilePath("$scenefolder") + (path - sceneFolderPath);
1356     return true;
1357   }
1358   return false;
1359 }
1360 
1361 //-----------------------------------------------------------------------------
1362 
getDefaultLevelPath(int levelType,std::wstring levelName) const1363 TFilePath ToonzScene::getDefaultLevelPath(int levelType,
1364                                           std::wstring levelName) const {
1365   TProject *project = getProject();
1366   assert(project);
1367   TFilePath levelPath;
1368   QString scanLevelType;
1369   switch (levelType) {
1370   case TZI_XSHLEVEL:
1371     scanLevelType = Preferences::instance()->getScanLevelType();
1372     levelPath     = TFilePath(levelName + L".." + scanLevelType.toStdWString());
1373     break;
1374   case PLI_XSHLEVEL:
1375     levelPath = TFilePath(levelName).withType("pli");
1376     break;
1377   case TZP_XSHLEVEL:
1378     levelPath = TFilePath(levelName).withType("tlv");
1379     break;
1380   case OVL_XSHLEVEL:
1381     levelPath = TFilePath(levelName + L"..tif");
1382     break;
1383   default:
1384     levelPath = TFilePath(levelName + L"..png");
1385   }
1386 
1387   if (!isUntitled() && Preferences::instance()->getPathAliasPriority() ==
1388                            Preferences::SceneFolderAlias)
1389     return TFilePath("$scenefolder") + levelPath;
1390 
1391   std::string folderName = getFolderName(levelType);
1392   if (project->getUseScenePath(folderName))
1393     return TFilePath("+" + folderName) + getSavePath() + levelPath;
1394   else
1395     return TFilePath("+" + folderName) + levelPath;
1396 }
1397 
1398 //-----------------------------------------------------------------------------
1399 
1400 const std::wstring savePathString(L"$savepath");
1401 
1402 //-----------------------------------------------------------------------------
1403 
codeSavePath(TFilePath path) const1404 TFilePath ToonzScene::codeSavePath(TFilePath path) const {
1405   if (path == TFilePath()) return path;
1406   TFilePath savePath = getSavePath();
1407   if (savePath == TFilePath()) return path;  // non dovrebbe succedere mai
1408   TFilePath filename;
1409   TFilePath originalPath = path;
1410   if (savePath.withoutParentDir() != path.withoutParentDir()) {
1411     TFilePath parentDir = path.getParentDir();
1412     if (parentDir != TFilePath() && !parentDir.isRoot()) {
1413       filename = path.withoutParentDir();
1414       path     = parentDir;
1415     } else
1416       return originalPath;
1417   }
1418 
1419   TFilePath head;
1420   if (!checkTail(path, savePath, head)) return originalPath;
1421   if (head.getParentDir() != TFilePath() || head == TFilePath() ||
1422       head.getWideString()[0] != L'+')
1423     return originalPath;
1424   std::string folderName = ::to_string(head.getWideString().substr(1));
1425   if (!getProject()->getUseScenePath(folderName)) return originalPath;
1426   return head + savePathString + filename;
1427 }
1428 
1429 //-----------------------------------------------------------------------------
1430 
decodeSavePath(TFilePath path) const1431 TFilePath ToonzScene::decodeSavePath(TFilePath path) const {
1432   std::wstring s = path.getWideString();
1433   int i          = s.find(savePathString);
1434   if (i != (int)std::wstring::npos) {
1435     TFilePath savePath = getSavePath();
1436     s.replace(i, savePathString.length(), savePath.getWideString());
1437     return TFilePath(s);
1438   }
1439   // in case of the scene folder alias (start with "$scenefolder")
1440   else if (s.find(L"$scenefolder") == 0) {
1441     s.replace(0, 12, m_scenePath.getParentDir().getWideString());
1442     return TFilePath(s);
1443   } else
1444     return path;
1445 }
1446 
1447 //-----------------------------------------------------------------------------
1448 
isExternPath(const TFilePath & fp) const1449 bool ToonzScene::isExternPath(const TFilePath &fp) const {
1450   TProject *project = m_project;
1451   assert(project);
1452   for (int i = 0; i < project->getFolderCount(); i++) {
1453     if (project->getFolderName(i) == "scenes") continue;
1454 
1455     TFilePath folderPath =
1456         decodeFilePath(TFilePath("+" + project->getFolderName(i)));
1457     if (folderPath.isAncestorOf(fp)) return false;
1458   }
1459   return true;
1460 }
1461 
1462 //-----------------------------------------------------------------------------
1463 
getCurrentCamera()1464 TCamera *ToonzScene::getCurrentCamera() {
1465   return getXsheet()->getStageObjectTree()->getCurrentCamera();
1466 }
1467 
1468 //-----------------------------------------------------------------------------
1469 
getCurrentPreviewCamera()1470 TCamera *ToonzScene::getCurrentPreviewCamera() {
1471   return getXsheet()->getStageObjectTree()->getCurrentPreviewCamera();
1472 }
1473 
1474 //-----------------------------------------------------------------------------
1475 
getContentHistory(bool createIfNeeded)1476 TContentHistory *ToonzScene::getContentHistory(bool createIfNeeded) {
1477   if (!m_contentHistory && createIfNeeded)
1478     m_contentHistory = new TContentHistory(false);
1479   return m_contentHistory;
1480 }
1481 
1482 //-----------------------------------------------------------------------------
1483 
getSoundColumns(std::vector<TXshSoundColumn * > & columns)1484 void ToonzScene::getSoundColumns(std::vector<TXshSoundColumn *> &columns) {
1485   ToonzScene *scene = this;
1486   std::set<TXsheet *> visited, toVisit;
1487   TXsheet *xsh = scene->getChildStack()->getTopXsheet();
1488   visited.insert(xsh);
1489   toVisit.insert(xsh);
1490   while (!toVisit.empty()) {
1491     xsh = *toVisit.begin();
1492     toVisit.erase(xsh);
1493     for (int i = 0; i < xsh->getColumnCount(); i++) {
1494       TXshColumn *column = xsh->getColumn(i);
1495       if (!column) continue;
1496       if (TXshSoundColumn *sc = column->getSoundColumn())
1497         columns.push_back(sc);
1498       else if (TXshCellColumn *cc = column->getCellColumn()) {
1499         int r0 = 0, r1 = -1;
1500         cc->getRange(r0, r1);
1501         if (!cc->isEmpty() && r0 <= r1) {
1502           for (int r = r0; r <= r1; r++) {
1503             TXshCell cell = cc->getCell(r);
1504             if (cell.m_level && cell.m_level->getChildLevel()) {
1505               TXsheet *subxsh = cell.m_level->getChildLevel()->getXsheet();
1506               if (visited.find(subxsh) == visited.end()) {
1507                 visited.insert(subxsh);
1508                 toVisit.insert(subxsh);
1509               }
1510             }
1511           }
1512         }
1513       }
1514     }
1515   }
1516 }
1517 
1518 //-----------------------------------------------------------------------------
1519 
updateSoundColumnFrameRate()1520 void ToonzScene::updateSoundColumnFrameRate() {
1521   std::vector<TXshSoundColumn *> soundColumns;
1522   getSoundColumns(soundColumns);
1523 
1524   TSceneProperties *properties = getProperties();
1525   if (!properties) return;
1526 
1527   TOutputProperties *outputProperties = properties->getOutputProperties();
1528   if (!outputProperties) return;
1529 
1530   double frameRate = outputProperties->getFrameRate();
1531 
1532   int i;
1533   for (i = 0; i < (int)soundColumns.size(); i++)
1534     soundColumns[i]->updateFrameRate(frameRate);
1535 }
1536 
1537 //-----------------------------------------------------------------------------
1538 
getIconPath(const TFilePath & scenePath)1539 TFilePath ToonzScene::getIconPath(const TFilePath &scenePath) {
1540   return scenePath.getParentDir() + "sceneIcons" +
1541          (scenePath.getWideName() + L" .png");
1542 }
1543 
1544 //-----------------------------------------------------------------------------
1545 
1546 // se la scena sta in +scenes/pippo.tnz => pippo
1547 // se la scena sta in +scenes/pluto/pippo.tnz => pluto/pippo
1548 // se la scena e' untitledxxx.tnz => untitledxxx
1549 
getSavePath() const1550 TFilePath ToonzScene::getSavePath() const {
1551   std::string sceneName = getScenePath().getName();
1552   if (isUntitled()) return TFilePath(sceneName);
1553   TFilePath sceneRoot = decodeFilePath(TFilePath("+" + TProject::Scenes));
1554   TFilePath scenePath = getScenePath().withType("");
1555   TFilePath savePath(sceneName);
1556   if (sceneRoot.isAncestorOf(scenePath)) savePath = scenePath - sceneRoot;
1557   return savePath;
1558 }
1559 
1560 //---------------------------------------------------------------------------
1561 
shiftCameraX(double val)1562 double ToonzScene::shiftCameraX(double val) {
1563   TStageObjectTree *tree = getXsheet()->getStageObjectTree();
1564 
1565   TStageObject *stageObject = tree->getStageObject(tree->getCurrentCameraId());
1566   TPointD ret               = stageObject->getOffset();
1567   stageObject->setOffset(TPointD(ret.x + val, ret.y));
1568   return ret.x;
1569 }
1570 
1571 //----------------------------------------------------------------------------
1572 // if the option is set in the preferences,
1573 // remove the scene numbers("c####_") from the file name
1574 
getLevelNameWithoutSceneNumber(std::wstring orgName)1575 std::wstring ToonzScene::getLevelNameWithoutSceneNumber(std::wstring orgName) {
1576   if (!Preferences::instance()->isRemoveSceneNumberFromLoadedLevelNameEnabled())
1577     return orgName;
1578 
1579   QString orgNameQstr = QString::fromStdWString(orgName);
1580 
1581   // do nothing if the file name has less than 7 letters
1582   if (orgNameQstr.size() <= 6) return orgName;
1583 
1584   QString sceneName = QString::fromStdWString(getSceneName()).left(5);
1585 
1586   // check if the first 5 letters of the level name is the same as the scene
1587   // name.
1588   // note that we must consider following both cases; "c0001_hogehoge.tif" and
1589   // "c0001A_####.tif"
1590   if (!orgNameQstr.startsWith(sceneName)) return orgName;
1591 
1592   if (!orgNameQstr.contains("_")) return orgName;
1593 
1594   return orgNameQstr.right(orgNameQstr.size() - orgNameQstr.indexOf("_") - 1)
1595       .toStdWString();
1596 }
1597