1
2
3 #include "toonz/tproject.h"
4
5 // TnzLib includes
6 #include "toonz/sceneproperties.h"
7 #include "toonz/toonzscene.h"
8 #include "toonz/txsheet.h"
9 #include "toonz/observer.h"
10 #include "toonz/toonzfolders.h"
11 #include "toonz/cleanupparameters.h"
12
13 // TnzBase includes
14 #include "tenv.h"
15
16 // TnzCore includes
17 #include "tsystem.h"
18 #include "tstream.h"
19 #include "tfilepath_io.h"
20 #include "tconvert.h"
21
22 // Qt includes
23 #include <QFileInfo>
24 #include <QDir>
25
26 // STD includes
27 #include <fstream>
28 #include <stdlib.h>
29
30 using namespace std;
31
32 //===================================================================
33
34 /* Version-related strings added to project files, in reversed chronological
35 * order */
36 const std::wstring prjSuffix[4] = {L"_otprj", L"_prj63ml", L"_prj6", L"_prj"};
37 const std::wstring xmlExt = L".xml";
38 const int prjSuffixCount = 4;
39
40 //===================================================================
41 /*! Default inputs folder: is used to save all scanned immage.*/
42 const std::string
43 TProject::Inputs = "inputs",
44 /*! Default drawings folder: is used to save all tlv and pli levels.*/
45 TProject::Drawings = "drawings",
46 /*! Default scenes folder: is used to save all scenes.*/
47 TProject::Scenes = "scenes",
48 /*! Default scripts folder: is used to save all the script.*/
49 TProject::Scripts = "scripts",
50 /*! Default extras folder: is used to save all imported images and levels
51 not pli or tlv.*/
52 TProject::Extras = "extras",
53 /*! Default outputs folder: is used to save all rendered scenes.*/
54 TProject::Outputs = "outputs",
55 /*! Default palettes folder: is used for color design (色指定)*/
56 TProject::Palettes = "palettes";
57 //! Default project name
58 const TFilePath TProject::SandboxProjectName("sandbox");
59
60 TProjectP currentProject;
61
62 //===================================================================
63
64 namespace {
65
66 //===================================================================
67 //
68 // helper functions
69 //
70 //===================================================================
71
makeRelative(TFilePath ref,TFilePath fp)72 TFilePath makeRelative(TFilePath ref, TFilePath fp) {
73 if (!fp.isAbsolute()) return fp;
74 TFilePath dots;
75 for (;;) {
76 if (ref.isAncestorOf(fp)) {
77 TFilePath relativePath = dots + (fp - ref);
78 return relativePath;
79 }
80 if (ref.isRoot()) return fp;
81 ref = ref.getParentDir();
82 dots = dots + "..";
83 }
84 }
85
86 //-------------------------------------------------------------------
87
makeAbsolute(TFilePath ref,TFilePath fp)88 TFilePath makeAbsolute(TFilePath ref, TFilePath fp) {
89 if (fp.isAbsolute()) return fp;
90 const TFilePath twoDots("..");
91 while (twoDots.isAncestorOf(fp)) {
92 TFilePath refParent = ref.getParentDir();
93 if (refParent == TFilePath()) break; // non dovrebbe succedere
94 ref = refParent;
95 fp = fp - twoDots;
96 }
97 fp = ref + fp;
98
99 return fp;
100 }
101
102 //===================================================================
103
104 TEnv::StringVar currentProjectPath("CurrentProject", "");
105
106 //===================================================================
107
108 //! Returns the project suffix (ie anything beyond the last '_' included)
109 //! of the passed file path.
getProjectSuffix(const TFilePath & path)110 std::wstring getProjectSuffix(const TFilePath &path) {
111 const std::wstring &name = path.getWideName();
112 int idx = name.find_last_of(L'_');
113 if (idx == string::npos) return L"";
114
115 return name.substr(idx);
116 }
117
118 //-------------------------------------------------------------------
119
120 /*! Looks in the directory for a project file. If nothing found, returns a
121 * blank TFilePath
122 */
getProjectFile(const TFilePath & fp)123 TFilePath getProjectFile(const TFilePath &fp) {
124 const std::wstring &fpName = fp.getWideName();
125 const std::wstring &folderName = fp.getParentDir().getWideName();
126 QDir dir(fp.getQString());
127 for (int i = 0; i < prjSuffixCount; ++i) {
128 TFilePath path = fp + (fpName + prjSuffix[i] + xmlExt);
129 if (TFileStatus(path).doesExist()) return path;
130
131 QStringList filters;
132 filters << "*" + QString::fromStdWString(prjSuffix[i] + xmlExt);
133 QStringList prjfiles =
134 dir.entryList(filters, QDir::Files, (QDir::Time | QDir::Reversed));
135 if (prjfiles.size()) return fp + TFilePath(prjfiles[0]);
136 }
137
138 return TFilePath();
139 }
140
141 //-------------------------------------------------------------------
142
143 //! In case the supplied path has an old version suffix,
144 //! this function updates it to the most recent; otherwise,
145 //! it is left untouched.
getLatestVersionProjectPath(const TFilePath & path)146 TFilePath getLatestVersionProjectPath(const TFilePath &path) {
147 const std::wstring &suffix = getProjectSuffix(path);
148 for (int i = 1; i < prjSuffixCount; ++i)
149 if (suffix == prjSuffix[i]) {
150 const std::wstring &name = path.getWideName();
151 int pos = name.size() - suffix.size();
152 return path.withName(path.getWideName().substr(0, pos) + prjSuffix[0]);
153 }
154
155 return path;
156 }
157
158 //===================================================================
159
160 /*! Return project path with right suffix.
161 VERSION 6.0: _prj6.xml;
162 PRECEDENT VERSION: _prj.xml.
163 If in \b folder exists ONLY old version project path return old
164 version path;
165 otherwise return 6.0 version project path.
166 */
searchProjectPath(TFilePath folder)167 TFilePath searchProjectPath(TFilePath folder) {
168 assert(folder.isAbsolute());
169 wstring projectName = folder.getWideName();
170
171 // Search for the first available project file, starting from the most recent.
172 TFilePath projectPath = getProjectFile(folder);
173 if (projectPath != TFilePath()) return projectPath;
174
175 // If none exist in the folder, build the name with the most recent suffix
176 return folder + TFilePath(projectName + prjSuffix[0] + xmlExt);
177 }
178
179 //===================================================================
180
isFolderUnderVersionControl(const TFilePath & folderPath)181 bool isFolderUnderVersionControl(const TFilePath &folderPath) {
182 QDir dir(QString::fromStdWString(folderPath.getWideString()));
183 if (dir.entryList(QDir::AllDirs | QDir::Hidden).contains(".svn")) return true;
184 // For SVN 1.7 and greater, check parent directories to see if it's under
185 // version control
186 while (dir.cdUp()) {
187 if (dir.entryList(QDir::AllDirs | QDir::Hidden).contains(".svn"))
188 return true;
189 }
190
191 return false;
192 }
193
194 //===================================================================
195
hideOlderProjectFiles(const TFilePath & folderPath)196 void hideOlderProjectFiles(const TFilePath &folderPath) {
197 const std::wstring &name = folderPath.getWideName();
198
199 TFilePath path;
200 for (int i = 1; i < prjSuffixCount; ++i) {
201 path = folderPath + (name + prjSuffix[i] + xmlExt);
202 if (TFileStatus(path).doesExist())
203 TSystem::renameFile(path.withType("xml_"), path);
204 }
205 }
206
207 } // namespace
208
209 //===================================================================
210
211 //===================================================================
212 //
213 // TProject
214 //
215
216 //-------------------------------------------------------------------
217
218 /*! \class TProject tproject.h
219 \brief Define and handle a toonz project.
220
221 A toonz project is identified by a project name that matches a folder with
222 the same name in the project root.\n
223 The project folder can contains saveral other folders.
224 By default, five folders are created: Inputs, Drawings, Scenes, Extras
225 and Outputs.
226 Each of this folders can be renamed using the setFolder(string name,
227 TFilePath path) method.
228 Usually, the \b name parameter is chosen from inputs, drawings, scenes,
229 extras and outputs;
230 the \b path parameter contains the folder that can have a different name
231 from them.
232 All association between names and folder are kept in a mapping.\n
233 A folder of a project can be constant or scene dependent. A constant
234 folder is used by
235 every scene created in the project to save or load data. A scene
236 dependent folder is used only by the scene
237 from which the folder depends. A scene dependent folder contains the
238 string "$scene" in its path.
239
240 \code
241 e.g.
242 Scene path: "...\\prodA\\episode1\\scenes\\sceneA.tzn"
243 Drawings scene dependent folder:
244 "...\\prodA\\episode1\\$scene\\drawings"
245 Drawings folder path: "...\\prodA\\episode1\\SceneA\\drawings"
246 \endcode
247 \n\n
248 By default, from the toonz installation, exist always a toonz project
249 called "sandbox".
250 \see TProjectManager, TSceneProperties.
251 */
252
253 /*! \fn TFilePath TProject::getName() const
254 Returns the name of the project.
255 \code
256 e.g. "prodA\\episode1"
257 \endcode
258 */
259
260 /*! \fn TFilePath TProject::getProjectPath() const
261 Returns the path of the project. It is an absolute path.
262 \code
263 e.g. "prodA\\episode1" -> "...\\prodA\\episode1\\episode1_prj.xml"
264 \endcode
265 */
266
267 /*! \fn TFilePath TProject::getProjectFolder() const
268 Returns the project folder path. It is an absolute path.
269 \code
270 e.g. "prodA\\episode1" -> "...\\prodA\\episode1\\"
271 \endcode
272 */
273
274 /*! \fn const TSceneProperties &TProject::getSceneProperties() const
275 Returns the scene properties of the project.
276 \see TSceneProperties
277 */
278
279 /*! \fn void TProject::save()
280 Saves the project.
281 Is equivalent to save(getProjectPath()).
282 The project is saved as a xml file.\n
283 Uses TProjectManager and TOStream.
284 \note Exceptions can be thrown.
285 \see TProjectManager and TOStream.
286 */
287
TProject()288 TProject::TProject() : m_name(), m_path(), m_sprop(new TSceneProperties()) {}
289
290 //-------------------------------------------------------------------
291
~TProject()292 TProject::~TProject() { delete m_sprop; }
293
294 //-------------------------------------------------------------------
295 /*! Associates the \b name to the specified \b path.
296 \code
297 e.g. setFolder(TProject::Drawings, TFilePath("C:\\temp\\drawings"))
298 \endcode
299 Usually, the \b name parameter is chosen from inputs, drawings, scenes,
300 extras and outputs;
301 the \b path contains the folder that can have a different name from
302 them.
303 Every association between names and paths is contained in a mapping.
304 \note Not absolute path are thought relative to the project folder.
305 */
setFolder(string name,TFilePath path)306 void TProject::setFolder(string name, TFilePath path) {
307 std::map<std::string, TFilePath>::iterator it;
308 it = m_folders.find(name);
309 if (it == m_folders.end()) {
310 m_folderNames.push_back(name);
311 m_folders[name] = path;
312 } else {
313 it->second = path;
314 }
315 }
316
317 //-------------------------------------------------------------------
318 /*! Create a folder named with \b name.
319 Call the setFolder(name,TFilePath(name)) method.\n
320 e.g. setFolder(TProject::Drawings) is equivalent to
321 setFolder(TProject::Drawings, TFilePath("drawings"))\n
322 The resulting is "..\\projectFolder\\drawings"
323 */
setFolder(string name)324 void TProject::setFolder(string name) { setFolder(name, TFilePath(name)); }
325
326 //-------------------------------------------------------------------
327 /*! Returns the path of the folder named with \b name.\n
328 Returns TFilePath() if there isn't a folder named with \b name.
329 \note The returned path could be a relative path if \b absolute is
330 false.
331 */
getFolder(string name,bool absolute) const332 TFilePath TProject::getFolder(string name, bool absolute) const {
333 std::map<std::string, TFilePath>::const_iterator it;
334 it = m_folders.find(name);
335 if (it != m_folders.end())
336 return (absolute) ? makeAbsolute(getProjectFolder(), it->second)
337 : it->second;
338 else
339 return TFilePath();
340 }
341
342 //-------------------------------------------------------------------
343 /*! Returns the path of the Scene folder.\n
344 The Scene folder contains all the saved scene. The returned path is an
345 absolute path.
346 */
getScenesPath() const347 TFilePath TProject::getScenesPath() const {
348 TFilePath scenes = getFolder(Scenes);
349 return makeAbsolute(getProjectFolder(), scenes);
350 }
351
352 //-------------------------------------------------------------------
353 /*! Returns the path of the folder indexed with \b index.\n
354 Returns TFilePath() if there isn't a folder indexed with \b index.
355 \note The returned path could be a relative path.
356 */
getFolder(int index) const357 TFilePath TProject::getFolder(int index) const {
358 if (0 <= index && index < (int)m_folderNames.size())
359 return getFolder(m_folderNames[index]);
360 else
361 return TFilePath();
362 }
363
364 //-------------------------------------------------------------------
365 /*! Returns true if the folder indexed with \b index isn't scene dependent.\n
366 A scene dependent folder is a folder containing "$scene" in its path.
367 */
isConstantFolder(int index) const368 bool TProject::isConstantFolder(int index) const {
369 TFilePath fp = getFolder(index);
370 return fp.getWideString().find(L"$scene") == wstring::npos;
371 }
372
373 //-------------------------------------------------------------------
374 //! Returns the number of the folders contained in the project folder.
getFolderCount() const375 int TProject::getFolderCount() const { return m_folders.size(); }
376
377 //-------------------------------------------------------------------
378 //! Returns the name of the folder indexed with \b index.
getFolderName(int index) const379 string TProject::getFolderName(int index) const {
380 if (0 <= index && index < (int)m_folderNames.size())
381 return m_folderNames[index];
382 else
383 return "";
384 }
385
386 //-------------------------------------------------------------------
387
388 /*! Returns the index of the folder named with \b folderName.\n
389 If a folder named with \b folderName doesn't exist, return -1.
390 */
getFolderIndex(string name) const391 int TProject::getFolderIndex(string name) const {
392 std::vector<std::string>::const_iterator it;
393 it = std::find(m_folderNames.begin(), m_folderNames.end(), name);
394 if (it == m_folderNames.end()) return -1;
395 return std::distance(it, m_folderNames.begin());
396 }
397
398 //-------------------------------------------------------------------
399 /*! Returns true if this project is the current project.
400 Uses \b TProjectManager.
401 \see TProjectManager
402 */
isCurrent() const403 bool TProject::isCurrent() const {
404 TFilePath currentProjectPath =
405 TProjectManager::instance()->getCurrentProjectPath();
406 if (getProjectPath() == currentProjectPath)
407 return true;
408 else
409 return getLatestVersionProjectPath(currentProjectPath) ==
410 getLatestVersionProjectPath(getProjectPath());
411 }
412
413 //-------------------------------------------------------------------
414 /*! Set the scene properties \b sprop to the project.
415 \see TSceneProperties*/
setSceneProperties(const TSceneProperties & sprop)416 void TProject::setSceneProperties(const TSceneProperties &sprop) {
417 m_sprop->assign(&sprop);
418 }
419
420 //-------------------------------------------------------------------
421 /*! Returns the absolute path of \b fp.
422 If \b fp contains "$project", replaces it with the name of the project.
423 \note the returned path can contain "$scene"
424 */
decode(TFilePath fp) const425 TFilePath TProject::decode(TFilePath fp) const {
426 for (;;) {
427 wstring fpstr = fp.getWideString();
428 int j = fpstr.find(L"$project");
429 if (j == (int)wstring::npos) break;
430 fpstr.replace(j, 8, getName().getWideString());
431 fp = TFilePath(fpstr);
432 }
433 return makeAbsolute(getProjectFolder(), fp);
434 }
435
436 //-------------------------------------------------------------------
437
setUseScenePath(string folderName,bool on)438 void TProject::setUseScenePath(string folderName, bool on) {
439 m_useScenePathFlags[folderName] = on;
440 }
441
442 //-------------------------------------------------------------------
443
getUseScenePath(string folderName) const444 bool TProject::getUseScenePath(string folderName) const {
445 std::map<std::string, bool>::const_iterator it;
446 it = m_useScenePathFlags.find(folderName);
447 return it != m_useScenePathFlags.end() ? it->second : false;
448 }
449
450 //-------------------------------------------------------------------
451 /*! Returns the index of the folder specified in the path \b folderDir.
452 Returns -1 if \b folderDir isn't a folder of the project.
453 */
getFolderIndexFromPath(const TFilePath & folderDir)454 int TProject::getFolderIndexFromPath(const TFilePath &folderDir) {
455 TFilePath scenePath = decode(getFolder(Scenes));
456 bool sceneDependentScenePath = false;
457 if (scenePath.getName().find("$scene") != string::npos) {
458 scenePath = scenePath.getParentDir();
459 sceneDependentScenePath = true;
460 }
461 int folderIndex;
462 for (folderIndex = 0; folderIndex < getFolderCount(); folderIndex++)
463 if (isConstantFolder(folderIndex)) {
464 TFilePath fp = decode(getFolder(folderIndex));
465 if (fp == folderDir) return folderIndex;
466 } else {
467 TFilePath fp = decode(getFolder(folderIndex));
468 wstring a = fp.getWideString();
469 wstring b = folderDir.getWideString();
470 int alen = a.length();
471 int blen = b.length();
472 int i = a.find(L"$scene");
473 assert(i != (int)wstring::npos);
474 if (i == (int)wstring::npos) continue;
475 int j = i + 1;
476 while (j < alen && isalnum(a[j])) j++;
477 // a.substr(i,j-i) == "$scenexxxx"
478 int k = j + blen - alen;
479 if (!(0 <= i && i < k && k <= blen)) continue;
480 assert(i < blen);
481 if (i > 0 && a.substr(0, i) != b.substr(0, i)) continue;
482 if (k < blen && (j >= alen || a.substr(j) != b.substr(k))) continue;
483 wstring v = b.substr(i, k - i);
484 TFilePath scene(v + L".tnz");
485 if (sceneDependentScenePath)
486 scene = scenePath + scene.getWideName() + scene;
487 else
488 scene = scenePath + scene;
489 if (TFileStatus(scene).doesExist()) return folderIndex;
490 }
491 return -1;
492 }
493
494 //-------------------------------------------------------------------
495 /*! Returns the folder's name of the specified TFilePath \b folderDir.\n
496 Returns the empty string if \b folderDir isn't a folder of the
497 project.*/
getFolderNameFromPath(const TFilePath & folderDir)498 wstring TProject::getFolderNameFromPath(const TFilePath &folderDir) {
499 int index = getFolderIndexFromPath(folderDir);
500 if (index < 0) return L"";
501 if (getFolder(index).isAbsolute())
502 return ::to_wstring("+" + getFolderName(index));
503 else
504 return folderDir.getWideName();
505 }
506
507 //-------------------------------------------------------------------
508 /*! Saves the project in the specified path.
509 The TfilePath fp must be an absolute path. The project is saved as a xml
510 file.\n
511 Uses TProjectManager and TOStream.
512 \note Exceptions can be thrown.
513 \see TProjectManager and TOStream.
514 */
save(const TFilePath & projectPath)515 bool TProject::save(const TFilePath &projectPath) {
516 assert(isAProjectPath(projectPath));
517
518 TProjectManager *pm = TProjectManager::instance();
519 m_name = pm->projectPathToProjectName(projectPath);
520 m_path = getLatestVersionProjectPath(projectPath);
521 TFilePath projectFolder = projectPath.getParentDir();
522
523 if (!TFileStatus(projectFolder).doesExist()) {
524 try {
525 TSystem::mkDir(projectFolder);
526 } catch (...) {
527 return false;
528 }
529 }
530
531 TFilePath sceneFolder = decode(getFolder(TProject::Scenes));
532 TFilePath scenesDescPath = sceneFolder + "scenes.xml";
533
534 TFileStatus fs(projectPath);
535 if (fs.doesExist() && !fs.isWritable()) {
536 throw TSystemException(
537 projectPath,
538 "Cannot save the project settings. The file is read-only.");
539 return false;
540 }
541 TFileStatus fs2(scenesDescPath);
542 if (fs2.doesExist() && !fs2.isWritable()) {
543 throw TSystemException(
544 projectPath,
545 "Cannot save the project settings. The scenes file is read-only.");
546 return false;
547 }
548
549 TOStream os(m_path);
550 os.openChild("project");
551 os.openChild("version");
552 os << 70 << 1; // Standard version signature:
553 os.closeChild(); // <Major Toonz version number * 10>.<Major version
554 // advancement>
555 os.openChild("folders");
556 int i = 0;
557 for (i = 0; i < getFolderCount(); i++) {
558 TFilePath folderRelativePath = getFolder(i);
559 if (folderRelativePath == TFilePath()) continue;
560 std::map<std::string, string> attr;
561 string folderName = getFolderName(i);
562 attr["name"] = folderName;
563 attr["path"] = ::to_string(folderRelativePath); // escape()
564 if (getUseScenePath(folderName)) attr["useScenePath"] = "yes";
565 os.openCloseChild("folder", attr);
566 }
567 os.closeChild();
568
569 os.openChild("sceneProperties");
570 getSceneProperties().saveData(os);
571 os.closeChild();
572 os.closeChild();
573
574 // crea (se necessario) le directory relative ai vari folder
575 for (i = 0; i < getFolderCount(); i++)
576 if (isConstantFolder(i)) {
577 TFilePath fp = getFolder(i);
578 if (fp == TFilePath()) continue;
579 fp = decode(fp);
580 // if(!fp.isAbsolute()) fp = projectFolder + fp;
581 if (!TFileStatus(fp).doesExist()) {
582 try {
583 TSystem::mkDir(fp);
584 } catch (...) {
585 }
586 }
587 }
588
589 /*-- +scenes だけでなく、全てのProject Folderにscenes.xmlを生成する --*/
590 std::vector<std::string> foldernames;
591 pm->getFolderNames(foldernames);
592 for (int f = 0; f < foldernames.size(); f++) {
593 TFilePath folderpath = decode(getFolder(foldernames.at(f)));
594 if (folderpath.isEmpty() || !isConstantFolder(f)) continue;
595
596 TFilePath xmlPath = folderpath + "scenes.xml";
597 TFileStatus xmlfs(xmlPath);
598 if (xmlfs.doesExist() && !xmlfs.isWritable()) continue;
599
600 TFilePath relativeProjectFolder =
601 makeRelative(folderpath, m_path.getParentDir());
602
603 TOStream os2(xmlPath);
604 std::map<std::string, string> attr;
605 attr["type"] = "projectFolder";
606 os2.openChild("parentProject", attr);
607 os2 << relativeProjectFolder;
608 os2.closeChild();
609 }
610
611 // The project has been successfully saved. In case there are other
612 // project files from older Toonz project versions, those files are
613 // renamed so that older Toonz versions can no longer 'see' it.
614 if (!isFolderUnderVersionControl(projectFolder))
615 hideOlderProjectFiles(projectFolder);
616
617 return true;
618 }
619
620 //-------------------------------------------------------------------
621
save()622 bool TProject::save() { return save(m_path); }
623
624 //-------------------------------------------------------------------
625 /*! Loads the project specified in \b projectPath.\n
626 \b projectPath must be an absolute path.
627 */
load(const TFilePath & projectPath)628 void TProject::load(const TFilePath &projectPath) {
629 assert(isAProjectPath(projectPath));
630
631 TFilePath latestProjectPath = getLatestVersionProjectPath(projectPath);
632 TFilePath inputProjectPath = searchProjectPath(projectPath.getParentDir());
633
634 TProjectManager *pm = TProjectManager::instance();
635 m_name = pm->projectPathToProjectName(latestProjectPath);
636 m_path = latestProjectPath;
637
638 m_folderNames.clear();
639 m_folders.clear();
640 m_useScenePathFlags.clear();
641 delete m_sprop;
642 m_sprop = new TSceneProperties();
643
644 // Read the project
645 TIStream is(inputProjectPath);
646 if (!is) return;
647
648 string tagName;
649 if (!is.matchTag(tagName) || tagName != "project") return;
650
651 while (is.matchTag(tagName)) {
652 if (tagName == "folders") {
653 while (is.matchTag(tagName)) {
654 if (tagName == "folder") {
655 string name = is.getTagAttribute("name");
656 TFilePath path(is.getTagAttribute("path"));
657 setFolder(name, path);
658 string useScenePath = is.getTagAttribute("useScenePath");
659 setUseScenePath(name, useScenePath == "yes");
660 } else
661 throw TException("expected <folder>");
662 }
663 is.matchEndTag();
664 } else if (tagName == "version") {
665 int major, minor;
666 is >> major >> minor;
667 is.setVersion(VersionNumber(major, minor));
668 is.matchEndTag();
669 } else if (tagName == "sceneProperties") {
670 TSceneProperties sprop;
671 try {
672 sprop.loadData(is, true);
673 } catch (...) {
674 }
675 setSceneProperties(sprop);
676 is.matchEndTag();
677 }
678 }
679 }
680
681 //-------------------------------------------------------------------
682 /*! Returns true if the specified path is a project path.\n
683 A project path must be absolute, must be an xml file and must have the
684 name of the
685 parent root with either one of the version-dependent suffixes "_prj*".\n
686 \code
687 e.g. "C:\\Toonz 5.2 stuff\\projects\\prodA\\episode1\\episode1_prj.xml"
688 is a project path.
689 \endcode
690 */
isAProjectPath(const TFilePath & fp)691 bool TProject::isAProjectPath(const TFilePath &fp) {
692 if (fp.isAbsolute() && fp.getType() == "xml") {
693 const std::wstring &fpName = fp.getWideName();
694 for (int i = 0; i < prjSuffixCount; ++i)
695 if (fpName.find(prjSuffix[i]) != std::wstring::npos) return true;
696 }
697
698 return false;
699 }
700
701 //-------------------------------------------------------------------
702
703 namespace {
704
705 /*
706 class SimpleProject final : public TProject {
707 public:
708 SimpleProject() : TProject(TFilePath("___simpleProject")) {
709 }
710
711 };
712 */
713 } // namespace
714
715 //===================================================================
716 //
717 // TProjectManager
718 //
719 //-------------------------------------------------------------------
720
721 /*! \class TProjectManager tproject.h
722 \brief Manages all toonz projects. The class provides all needed method
723 to retrieve projects paths, names
724 and folders.
725
726 It is possible to handle more than one project root.
727 The class maintains a container this purpose. All the projects roots
728 must be set by hand in the windows registery. By default, only one project
729 root is created when toonz is installed.\n The project root container can be
730 updated using addProjectsRoot(const TFilePath &root),
731 addDefaultProjectsRoot() methods.
732
733 The class maintains also information about the current project. The
734 class provides all needed method to retrieve the current project path, name
735 and folder. \see TProject
736
737 */
738
739 /*! \fn bool TProjectManager::isTabModeEnabled() const
740 Returns the tab mode.
741 \note the tab mode is used for Tab Application
742 */
743
744 /*! \fn void TProjectManager::enableTabMode(bool tabMode)
745 Set the tab mode to the passed \b tabMode.
746 \note the tab mode is used for Tab Application
747 */
748
TProjectManager()749 TProjectManager::TProjectManager() : m_tabMode(false), m_tabKidsMode(false) {}
750
751 //-------------------------------------------------------------------
752
~TProjectManager()753 TProjectManager::~TProjectManager() {}
754
755 //-------------------------------------------------------------------
756 /*! Returns the instance to the TProjectManager.\n
757 If an instance doesn't exist, creates one.*/
instance()758 TProjectManager *TProjectManager::instance() {
759 static TProjectManager _instance;
760 return &_instance;
761 }
762
763 //-------------------------------------------------------------------
764 /*! Adds the specified folder \b fp in the projecs roots container.\n
765 If \b fp is already contained in the container, the method does nothing.
766 \note \b fp must be a folder and not a file path.*/
addProjectsRoot(const TFilePath & root)767 void TProjectManager::addProjectsRoot(const TFilePath &root) {
768 // assert(TFileStatus(root).isDirectory());
769 if (std::find(m_projectsRoots.begin(), m_projectsRoots.end(), root) ==
770 m_projectsRoots.end())
771 m_projectsRoots.push_back(root);
772 }
773
774 //-------------------------------------------------------------------
775
776 /*! Adds the specified folder \b fp in the version control projecs roots
777 container.\n
778 If \b fp is already contained in the container, the method does nothing.
779 \note \b fp must be a folder and not a file path.*/
addSVNProjectsRoot(const TFilePath & root)780 void TProjectManager::addSVNProjectsRoot(const TFilePath &root) {
781 assert(TFileStatus(root).isDirectory());
782 if (std::find(m_svnProjectsRoots.begin(), m_svnProjectsRoots.end(), root) ==
783 m_svnProjectsRoots.end())
784 m_svnProjectsRoots.push_back(root);
785 }
786
787 //-------------------------------------------------------------------
788
addDefaultProjectsRoot()789 void TProjectManager::addDefaultProjectsRoot() {
790 addProjectsRoot(TEnv::getStuffDir() + "projects");
791 }
792
793 //-------------------------------------------------------------------
794
getCurrentProjectRoot()795 TFilePath TProjectManager::getCurrentProjectRoot() {
796 TFilePath currentProjectPath = getCurrentProjectPath();
797 int i;
798 for (i = 0; i < (int)m_projectsRoots.size(); i++)
799 if (m_projectsRoots[i].isAncestorOf(currentProjectPath))
800 return m_projectsRoots[i];
801 for (i = 0; i < (int)m_svnProjectsRoots.size(); i++)
802 if (m_svnProjectsRoots[i].isAncestorOf(currentProjectPath))
803 return m_svnProjectsRoots[i];
804 if (m_projectsRoots.empty())
805 addDefaultProjectsRoot(); // shouldn't be necessary
806 return m_projectsRoots[0];
807 }
808
809 //-------------------------------------------------------------------
810 /*! Returns the name of the specified \b projectPath.
811 \note projectPath must be an absolute path.
812 */
projectPathToProjectName(const TFilePath & projectPath)813 TFilePath TProjectManager::projectPathToProjectName(
814 const TFilePath &projectPath) {
815 assert(projectPath.isAbsolute());
816 TFilePath projectFolder = projectPath.getParentDir();
817 if (m_projectsRoots.empty()) addDefaultProjectsRoot();
818
819 std::wstring fpName = projectPath.getWideName();
820 for (int i = 0; i < prjSuffixCount; ++i) {
821 // std::wstring::size_type const i = fpName.find(prjSuffix[i]);
822 if (fpName.find(prjSuffix[i]) != std::wstring::npos)
823 return TFilePath(fpName.substr(0, fpName.find(prjSuffix[i])));
824 }
825
826 int i;
827 for (i = 0; i < (int)m_projectsRoots.size(); i++) {
828 if (m_projectsRoots[i].isAncestorOf(projectFolder))
829 return projectFolder - m_projectsRoots[i];
830 }
831 for (i = 0; i < (int)m_svnProjectsRoots.size(); i++) {
832 if (m_svnProjectsRoots[i].isAncestorOf(projectFolder))
833 return projectFolder - m_svnProjectsRoots[i];
834 }
835 // non dovrei mai arrivare qui: il progetto non sta sotto un project root
836 return projectFolder.withoutParentDir();
837 }
838
839 //-------------------------------------------------------------------
840 /*! Returns an absolute path of the specified \b projectName.\n
841 \note The returned project path is always computed used the first
842 project root in the container.*/
projectNameToProjectPath(const TFilePath & projectName)843 TFilePath TProjectManager::projectNameToProjectPath(
844 const TFilePath &projectName) {
845 assert(!TProject::isAProjectPath(projectName));
846 assert(!projectName.isAbsolute());
847 if (m_projectsRoots.empty()) addDefaultProjectsRoot();
848 if (projectName == TProject::SandboxProjectName)
849 return searchProjectPath(TEnv::getStuffDir() + projectName);
850 return searchProjectPath(m_projectsRoots[0] + projectName);
851 }
852
853 //-------------------------------------------------------------------
854 /*! Returns the absolute path of the project file respect to the specified \b
855 projectFolder.\n
856 \note \b projectName must be an absolute path.*/
projectFolderToProjectPath(const TFilePath & projectFolder)857 TFilePath TProjectManager::projectFolderToProjectPath(
858 const TFilePath &projectFolder) {
859 assert(projectFolder.isAbsolute());
860 return searchProjectPath(projectFolder);
861 }
862
863 //-------------------------------------------------------------------
864 /*! Returns the absolute path of the specified \b projectName only if the
865 project already exist.\n
866 Returns TFilePath() if a project with the specified \b projectName
867 doesn't exist.\n
868 \note \b projectName must be a relative path.*/
getProjectPathByName(const TFilePath & projectName)869 TFilePath TProjectManager::getProjectPathByName(const TFilePath &projectName) {
870 assert(!TProject::isAProjectPath(projectName));
871 assert(!projectName.isAbsolute());
872 // TFilePath relativeProjectPath = projectName + (projectName.getName() +
873 // projectPathSuffix);
874 if (m_projectsRoots.empty()) addDefaultProjectsRoot();
875 if (projectName == TProject::SandboxProjectName)
876 return searchProjectPath(TEnv::getStuffDir() + projectName);
877 int i, n = (int)m_projectsRoots.size();
878 for (i = 0; i < n; i++) {
879 TFilePath projectPath = searchProjectPath(m_projectsRoots[i] + projectName);
880 assert(TProject::isAProjectPath(projectPath));
881 if (TFileStatus(projectPath).doesExist()) return projectPath;
882 }
883 for (i = 0; i < (int)m_svnProjectsRoots.size(); i++) {
884 TFilePath projectPath =
885 searchProjectPath(m_svnProjectsRoots[i] + projectName);
886 assert(TProject::isAProjectPath(projectPath));
887 if (TFileStatus(projectPath).doesExist()) return projectPath;
888 }
889 return TFilePath();
890 }
891
892 //-------------------------------------------------------------------
893
getProjectPathByProjectFolder(const TFilePath & projectFolder)894 TFilePath TProjectManager::getProjectPathByProjectFolder(
895 const TFilePath &projectFolder) {
896 assert(projectFolder.isAbsolute());
897 TFilePath projectPath = searchProjectPath(projectFolder);
898 return projectPathToProjectName(projectPath);
899 }
900
901 //-------------------------------------------------------------------
902 /*! Gets all project folder names and put them in the passed vector \b names.
903 \note All previous data contained in \b names are lost.*/
904
getFolderNames(std::vector<std::string> & names)905 void TProjectManager::getFolderNames(std::vector<std::string> &names) {
906 names.clear();
907 TFilePath fp = ToonzFolder::getProfileFolder() + "project_folders.txt";
908 try {
909 Tifstream is(fp);
910 if (is)
911 for (;;) {
912 char buffer[1024];
913 is.getline(buffer, sizeof(buffer));
914 if (is.eof()) break;
915 char *s = buffer;
916 while (*s == ' ' || *s == '\t') s++; // skips blanks
917 char *t = s;
918 while (*t && *t != '\r' && *t != '\n') t++; // reads up to end of line
919 while (t > s && (t[-1] == ' ' || t[-1] == '\t'))
920 t--; // remove trailing blanks
921 t[0] = '\0';
922 if (s[0]) names.push_back(string(s));
923 }
924 } catch (...) {
925 }
926 const std::string stdNames[] = {TProject::Inputs, TProject::Drawings,
927 TProject::Scenes, TProject::Extras,
928 TProject::Outputs, TProject::Scripts};
929 for (auto const &name : stdNames) {
930 // se il nome non e' gia' stato inserito lo aggiungo
931 if (std::find(names.begin(), names.end(), name) == names.end())
932 names.push_back(name);
933 }
934 }
935
936 //-------------------------------------------------------------------
937 /*! Set the the path \b fp as current project path.\n
938 \b fp must be an absolute path.*/
setCurrentProjectPath(const TFilePath & fp)939 void TProjectManager::setCurrentProjectPath(const TFilePath &fp) {
940 assert(TProject::isAProjectPath(fp));
941 currentProjectPath = ::to_string(fp.getWideString());
942 currentProject = TProjectP();
943 notifyListeners();
944 }
945
946 //-------------------------------------------------------------------
947 /*! Returns the current project path.\n
948 The project path, usually, is set in key registry. If a current
949 project path isn't set,
950 TProject::SandboxProjectName is set as current project.
951 */
getCurrentProjectPath()952 TFilePath TProjectManager::getCurrentProjectPath() {
953 TFilePath fp(currentProjectPath);
954 if (fp == TFilePath())
955 fp = projectNameToProjectPath(TProject::SandboxProjectName);
956 if (!TProject::isAProjectPath(fp)) {
957 // in Toonz 5.1 e precedenti era un project name
958 if (!fp.isAbsolute()) fp = getProjectPathByName(fp);
959 }
960 fp = searchProjectPath(fp.getParentDir());
961 if (!TFileStatus(fp).doesExist())
962 fp = projectNameToProjectPath(TProject::SandboxProjectName);
963 fp = getLatestVersionProjectPath(fp);
964 string s = ::to_string(fp);
965 if (s != (string)currentProjectPath) currentProjectPath = s;
966 return fp;
967 }
968
969 //-------------------------------------------------------------------
970 /*! Returns the current TProject.\n
971 If a current TProject() doesn't exist, load the project in the the
972 current project path.
973 */
getCurrentProject()974 TProjectP TProjectManager::getCurrentProject() {
975 if (currentProject.getPointer() == 0) {
976 TFilePath fp = getCurrentProjectPath();
977 assert(TProject::isAProjectPath(fp));
978 currentProject = new TProject();
979 currentProject->load(fp);
980 }
981 return currentProject;
982 }
983
984 //-------------------------------------------------------------------
985 /*! Returns the TProjectP in which the specified \b scenePath is saved.\n
986 Returns 0 if \b scenePath isn't a valid scene, or isn't saved in a valid
987 folder of a project root.
988 \note \b scenePath must be an absolute path.\n
989 Creates a new TProject. The caller gets ownership.*/
loadSceneProject(const TFilePath & scenePath)990 TProjectP TProjectManager::loadSceneProject(const TFilePath &scenePath) {
991 // cerca il file scenes.xml nella stessa directory della scena
992 // oppure in una
993 // directory superiore
994
995 TFilePath folder = scenePath.getParentDir();
996 TFilePath sceneDesc;
997 bool found = true;
998 for (;;) {
999 sceneDesc = folder + "scenes.xml";
1000 if (TFileStatus(sceneDesc).doesExist()) break;
1001 if (folder.isRoot()) {
1002 found = false;
1003 break;
1004 }
1005 folder = folder.getParentDir();
1006 }
1007
1008 // legge il path (o il nome) del progetto
1009 TFilePath projectPath;
1010 if (found) {
1011 try {
1012 TIStream is(sceneDesc);
1013 string tagName;
1014 is.matchTag(tagName);
1015 string type = is.getTagAttribute("type");
1016 TFilePath projectFolderPath;
1017 is >> projectFolderPath;
1018 if (type == "") {
1019 projectFolderPath = TFilePath("..");
1020 }
1021 is.matchEndTag();
1022 projectPath = makeAbsolute(folder, projectFolderPath);
1023
1024 TFilePath path = getProjectFile(projectPath);
1025
1026 projectPath = path;
1027
1028 } catch (...) {
1029 }
1030 if (projectPath == TFilePath()) return 0;
1031 } else
1032 projectPath = getSandboxProjectPath();
1033
1034 if (!TProject::isAProjectPath(projectPath)) {
1035 // in Toonz 5.1 e precedenti era un project name
1036 if (!projectPath.isAbsolute())
1037 projectPath = getProjectPathByName(projectPath);
1038 else
1039 return 0;
1040 }
1041 if (!TFileStatus(projectPath).doesExist()) return 0;
1042
1043 TProject *project = new TProject();
1044 project->load(projectPath);
1045 return project;
1046 }
1047
1048 //-------------------------------------------------------------------
1049
notifyListeners()1050 void TProjectManager::notifyListeners() {
1051 for (std::set<Listener *>::iterator i = m_listeners.begin();
1052 i != m_listeners.end(); ++i)
1053 (*i)->onProjectSwitched();
1054 }
1055
1056 //-------------------------------------------------------------------
1057
notifyProjectChanged()1058 void TProjectManager::notifyProjectChanged() {
1059 for (std::set<Listener *>::iterator i = m_listeners.begin();
1060 i != m_listeners.end(); ++i)
1061 (*i)->onProjectChanged();
1062 }
1063
1064 //-------------------------------------------------------------------
1065 /*! Adds \b listener to the listeners container.*/
addListener(Listener * listener)1066 void TProjectManager::addListener(Listener *listener) {
1067 m_listeners.insert(listener);
1068 }
1069
1070 //-------------------------------------------------------------------
1071 /*! Removes \b listener from the listeners container.*/
removeListener(Listener * listener)1072 void TProjectManager::removeListener(Listener *listener) {
1073 m_listeners.erase(listener);
1074 }
1075
1076 //-------------------------------------------------------------------
1077 /*! Initializes the specified \b scene using the TSceneProperties of the current
1078 project.\n
1079 \see TSceneProperties
1080 */
initializeScene(ToonzScene * scene)1081 void TProjectManager::initializeScene(ToonzScene *scene) {
1082 TProject *project = scene->getProject();
1083 TSceneProperties *sprop = scene->getProperties();
1084
1085 TFilePath currentProjectPath = getCurrentProjectPath();
1086 project->load(currentProjectPath);
1087
1088 sprop->assign(&project->getSceneProperties());
1089 CleanupParameters::GlobalParameters.assign(
1090 project->getSceneProperties().getCleanupParameters());
1091
1092 // scene->setProject(this);
1093 scene->setUntitled();
1094 sprop->cloneCamerasTo(scene->getTopXsheet()->getStageObjectTree());
1095 sprop->onInitialize();
1096 // scene->save(scene->getScenePath());
1097 }
1098
1099 //-------------------------------------------------------------------
1100 /*! Saves the TSceneProperties of the specified scene in the current project.*/
saveTemplate(ToonzScene * scene)1101 void TProjectManager::saveTemplate(ToonzScene *scene) {
1102 TSceneProperties props;
1103 props.assign(scene->getProperties());
1104 props.cloneCamerasFrom(scene->getXsheet()->getStageObjectTree());
1105
1106 // camera capture's "save in" path is saved in env, not in the project
1107 props.setCameraCaptureSaveInPath(TFilePath());
1108
1109 TProjectP currentProject = getCurrentProject();
1110 currentProject->setSceneProperties(props);
1111 currentProject->save();
1112 }
1113
1114 //-------------------------------------------------------------------
1115 /*! Creates the standard project folder "sandbox" if it doesn't exist.*/
createSandboxIfNeeded()1116 void TProjectManager::createSandboxIfNeeded() {
1117 TFilePath path = getSandboxProjectPath();
1118 if (!TFileStatus(path).doesExist()) {
1119 TProjectP project = createStandardProject();
1120 try {
1121 project->save(path);
1122 } catch (...) {
1123 }
1124 }
1125 }
1126
1127 //-------------------------------------------------------------------
1128 /*! Create a standard project.\n
1129 A standard project is a project containing the standard named and
1130 constant folder.
1131 \see TProject. */
createStandardProject()1132 TProjectP TProjectManager::createStandardProject() {
1133 TProject *project = new TProject();
1134 // set default folders (+drawings, ecc.)
1135 std::vector<std::string> names;
1136 getFolderNames(names);
1137 std::vector<std::string>::iterator it;
1138 for (it = names.begin(); it != names.end(); ++it) project->setFolder(*it);
1139 return project;
1140 }
1141
1142 //! Return the absolute path of the standard folder "sandbox".
getSandboxProjectFolder()1143 TFilePath TProjectManager::getSandboxProjectFolder() {
1144 return getSandboxProjectPath().getParentDir();
1145 }
1146 //! Return the absolute path of the standard project "sandbox_prj6.xml" file.
getSandboxProjectPath()1147 TFilePath TProjectManager::getSandboxProjectPath() {
1148 return getProjectPathByName(TProject::SandboxProjectName);
1149 }
1150
isProject(const TFilePath & projectFolder)1151 bool TProjectManager::isProject(const TFilePath &projectFolder) {
1152 TFilePath projectPath = projectFolderToProjectPath(projectFolder);
1153 return TFileStatus(projectPath).doesExist();
1154 }
1155