1 
2 
3 #include "filebrowsermodel.h"
4 #include "tutil.h"
5 #include "tsystem.h"
6 #include "tconvert.h"
7 #include "tenv.h"
8 #include "toonz/tproject.h"
9 #include "toonz/toonzscene.h"
10 #include "toonzqt/gutil.h"
11 
12 #include "filebrowser.h"
13 #include "history.h"
14 #include "iocommand.h"
15 
16 #include "tapp.h"
17 #include "toonz/tscenehandle.h"
18 
19 #include <QFileInfo>
20 #include <QDir>
21 #include <QDirIterator>
22 
23 #ifdef _WIN32
24 #include <shlobj.h>
25 #include <winnetwk.h>
26 #include <wctype.h>
27 #endif
28 #ifdef MACOSX
29 #include <Cocoa/Cocoa.h>
30 #endif
31 
32 namespace {
getMyDocumentsPath()33 TFilePath getMyDocumentsPath() {
34 #ifdef _WIN32
35   WCHAR szPath[MAX_PATH];
36   if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_PERSONAL, 0)) {
37     return TFilePath(szPath);
38   }
39   return TFilePath();
40 #elif defined MACOSX
41   NSArray *foundref = NSSearchPathForDirectoriesInDomains(
42       NSDocumentDirectory, NSUserDomainMask, YES);
43   if (!foundref) return TFilePath();
44   int c = [foundref count];
45   assert(c == 1);
46   NSString *documentsDirectory = [foundref objectAtIndex:0];
47   return TFilePath((const char *)[documentsDirectory
48       cStringUsingEncoding:NSASCIIStringEncoding]);
49 #else
50   QDir dir = QDir::home();
51   if (!dir.cd("Documents")) return TFilePath();
52   return TFilePath(dir.absolutePath().toStdString());
53 #endif
54 }
55 
56 // Desktop Path
getDesktopPath()57 TFilePath getDesktopPath() {
58 #ifdef _WIN32
59   WCHAR szPath[MAX_PATH];
60   if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOP, 0)) {
61     return TFilePath(szPath);
62   }
63   return TFilePath();
64 #elif defined MACOSX
65   NSArray *foundref = NSSearchPathForDirectoriesInDomains(
66       NSDesktopDirectory, NSUserDomainMask, YES);
67   if (!foundref) return TFilePath();
68   int c = [foundref count];
69   assert(c == 1);
70   NSString *desktopDirectory = [foundref objectAtIndex:0];
71   return TFilePath((const char *)[desktopDirectory
72       cStringUsingEncoding:NSASCIIStringEncoding]);
73 #else
74   QDir dir = QDir::home();
75   if (!dir.cd("Desktop")) return TFilePath();
76   return TFilePath(dir.absolutePath().toStdString());
77 #endif
78 }
79 }  // namespace
80 
81 //=============================================================================
82 //
83 // DvDirModelNode
84 //
85 //-----------------------------------------------------------------------------
86 
DvDirModelNode(DvDirModelNode * parent,std::wstring name)87 DvDirModelNode::DvDirModelNode(DvDirModelNode *parent, std::wstring name)
88     : m_parent(parent)
89     , m_name(name)
90     , m_oldName()
91     , m_row(-1)
92     , m_childrenValid(false)
93     , m_renameEnabled(false)
94     , m_nodeType("") {}
95 
96 //-----------------------------------------------------------------------------
97 
~DvDirModelNode()98 DvDirModelNode::~DvDirModelNode() { clearPointerContainer(m_children); }
99 
100 //-----------------------------------------------------------------------------
101 
removeChildren(int row,int count)102 void DvDirModelNode::removeChildren(int row, int count) {
103   assert(0 <= row && row + count <= (int)m_children.size());
104   int i;
105   for (i = 0; i < count; i++) delete m_children[row + i];
106   m_children.erase(m_children.begin() + row, m_children.begin() + row + count);
107   for (i = 0; i < (int)m_children.size(); i++) m_children[i]->setRow(i);
108 }
109 
110 //-----------------------------------------------------------------------------
111 
addChild(DvDirModelNode * child)112 void DvDirModelNode::addChild(DvDirModelNode *child) {
113   child->setRow(m_children.size());
114   m_children.push_back(child);
115 }
116 
117 //-----------------------------------------------------------------------------
118 
getChildCount()119 int DvDirModelNode::getChildCount() {
120   if (!m_childrenValid) refreshChildren();
121   return (int)m_children.size();
122 }
123 
124 //-----------------------------------------------------------------------------
125 
getChild(int index)126 DvDirModelNode *DvDirModelNode::getChild(int index) {
127   if (!m_childrenValid) refreshChildren();
128   assert(0 <= index && index < getChildCount());
129   return m_children[index];
130 }
131 
132 //-----------------------------------------------------------------------------
133 
hasChildren()134 bool DvDirModelNode::hasChildren() {
135   if (m_childrenValid)
136     return getChildCount() > 0;
137   else
138     return true;
139 }
140 
141 //-----------------------------------------------------------------------------
142 
getPixmap(bool isOpen) const143 QPixmap DvDirModelNode::getPixmap(bool isOpen) const { return QPixmap(); }
144 
145 //-----------------------------------------------------------------------------
146 
setTemporaryName(const std::wstring & newName)147 void DvDirModelNode::setTemporaryName(const std::wstring &newName) {
148   m_oldName = getName();
149   m_name    = newName;
150 }
151 
152 //-----------------------------------------------------------------------------
153 
restoreName()154 void DvDirModelNode::restoreName() {
155   if (m_oldName != L"") {
156     m_name    = m_oldName;
157     m_oldName = L"";
158   }
159 }
160 
161 //=============================================================================
162 //
163 // DvDirModelFileFolderNode [File folder]
164 //
165 //-----------------------------------------------------------------------------
166 
DvDirModelFileFolderNode(DvDirModelNode * parent,std::wstring name,const TFilePath & path)167 DvDirModelFileFolderNode::DvDirModelFileFolderNode(DvDirModelNode *parent,
168                                                    std::wstring name,
169                                                    const TFilePath &path)
170     : DvDirModelNode(parent, name)
171     , m_path(path)
172     , m_isProjectFolder(false)
173     , m_existsChecked(false)
174     , m_exists(true)
175     , m_hasChildren(false)
176     , m_peeks(true) {
177   m_nodeType = "FileFolder";
178 }
179 
180 //-----------------------------------------------------------------------------
181 
DvDirModelFileFolderNode(DvDirModelNode * parent,const TFilePath & path)182 DvDirModelFileFolderNode::DvDirModelFileFolderNode(DvDirModelNode *parent,
183                                                    const TFilePath &path)
184     : DvDirModelNode(parent, path.withoutParentDir().getWideString())
185     , m_path(path)
186     , m_isProjectFolder(false)
187     , m_existsChecked(false)
188     , m_exists(true)
189     , m_hasChildren(false)
190     , m_peeks(true) {
191   m_nodeType = "FileFolder";
192 }
193 
194 //-----------------------------------------------------------------------------
195 
exists()196 bool DvDirModelFileFolderNode::exists() {
197   return m_existsChecked ? m_exists : m_peeks
198              ? m_existsChecked = true,
199                m_exists        = TFileStatus(m_path).doesExist() : true;
200 }
201 
202 //-----------------------------------------------------------------------------
203 
visualizeContent(FileBrowser * browser)204 void DvDirModelFileFolderNode::visualizeContent(FileBrowser *browser) {
205   browser->setFolder(m_path);
206 }
207 
208 //-----------------------------------------------------------------------------
209 
makeChild(std::wstring name)210 DvDirModelNode *DvDirModelFileFolderNode::makeChild(std::wstring name) {
211   return createNode(this, m_path + name);
212 }
213 
214 //-----------------------------------------------------------------------------
215 
createNode(DvDirModelNode * parent,const TFilePath & path)216 DvDirModelFileFolderNode *DvDirModelFileFolderNode::createNode(
217     DvDirModelNode *parent, const TFilePath &path) {
218   DvDirModelFileFolderNode *node;
219   // check the project nodes under the Poject Root Node
220   if (QString::fromStdWString(parent->getName()).startsWith("Project root") &&
221       TProjectManager::instance()->isProject(path))
222     node = new DvDirModelProjectNode(parent, path);
223   else {
224     node = new DvDirModelFileFolderNode(parent, path);
225     if (path.getName().find("_files") == std::string::npos)
226       node->enableRename(true);
227   }
228   return node;
229 }
230 
231 //-----------------------------------------------------------------------------
232 
setName(std::wstring newName)233 bool DvDirModelFileFolderNode::setName(std::wstring newName) {
234   if (isSpaceString(QString::fromStdWString(newName))) return true;
235 
236   TFilePath srcPath = getPath();
237   TFilePath dstPath = getPath().getParentDir() + newName;
238   if (srcPath == dstPath) return false;
239   try {
240     TSystem::renameFile(dstPath, srcPath);
241   } catch (...) {
242     return false;
243   }
244   m_path = dstPath;
245   m_name = newName;
246   return true;
247 }
248 
249 //-----------------------------------------------------------------------------
250 
hasChildren()251 bool DvDirModelFileFolderNode::hasChildren() {
252   if (m_childrenValid) return m_hasChildren;
253 
254   if (m_peeks) {
255     // Using QDirIterator and only checking existence of the first item
256     QDir dir(m_path.getQString());
257     dir.setFilter(QDir::AllDirs | QDir::NoDotAndDotDot);
258     QDirIterator it(dir);
259     return (it.hasNext());
260   } else
261     return true;  // Not peeking nodes allow users to actively scan for
262                   // sub-folders
263 }
264 
265 //------------------------------------------------------------------------------
266 
refreshChildren()267 void DvDirModelFileFolderNode::refreshChildren() {
268   m_childrenValid = true;
269 
270   std::vector<std::wstring> names;
271   getChildrenNames(names);
272 
273   std::vector<DvDirModelNode *> oldChildren;
274   oldChildren.swap(m_children);
275 
276   std::vector<DvDirModelNode *>::iterator j;
277   int i;
278   for (i = 0; i < (int)names.size(); i++) {
279     std::wstring name = names[i];
280     for (j = oldChildren.begin();
281          j != oldChildren.end() && (*j)->getName() != name; ++j) {
282     }
283     DvDirModelNode *child = 0;
284     if (j != oldChildren.end()) {
285       child = *j;
286       oldChildren.erase(j);
287     } else {
288       child = makeChild(name);
289       if (DvDirModelFileFolderNode *folderNode =
290               dynamic_cast<DvDirModelFileFolderNode *>(child))
291         folderNode->setPeeking(m_peeks);
292     }
293 
294     if (!child) continue;
295 
296     addChild(child);
297   }
298   for (j = oldChildren.begin(); j != oldChildren.end(); ++j) {
299     DvDirModelNode *child = *j;
300     if (!!child && child->hasChildren())
301       child->removeChildren(0, child->getChildCount());
302 
303     delete *j;
304   }
305 
306   m_hasChildren = (m_children.size() > 0);
307 }
308 
309 //-----------------------------------------------------------------------------
310 
getChildrenNames(std::vector<std::wstring> & names) const311 void DvDirModelFileFolderNode::getChildrenNames(
312     std::vector<std::wstring> &names) const {
313   if (m_path.isEmpty()) return;
314   TFileStatus folderPathStatus(m_path);
315   if (folderPathStatus.isLink()) return;
316 
317   if (!folderPathStatus.isDirectory()) return;
318 
319   QStringList dirItems;
320   TSystem::readDirectory_DirItems(dirItems, m_path);
321   for (const QString &name : dirItems) names.push_back(name.toStdWString());
322 
323   QDir dir(toQString(m_path));
324 }
325 
326 //-----------------------------------------------------------------------------
327 
rowByName(const std::wstring & name) const328 int DvDirModelFileFolderNode::rowByName(const std::wstring &name) const {
329   std::vector<std::wstring> names;
330   getChildrenNames(names);
331   std::vector<std::wstring>::iterator it =
332       std::find(names.begin(), names.end(), name);
333   if (it == names.end())
334     return -1;
335   else
336     return std::distance(names.begin(), it);
337 }
338 
339 //-----------------------------------------------------------------------------
340 
getNodeByPath(const TFilePath & path)341 DvDirModelNode *DvDirModelFileFolderNode::getNodeByPath(const TFilePath &path) {
342   if (path == m_path) return this;
343   if (!m_path.isAncestorOf(path)) return 0;
344   int i, n = getChildCount();
345   for (i = 0; i < n; i++) {
346     DvDirModelNode *node = getChild(i)->getNodeByPath(path);
347     if (node) return node;
348   }
349   return 0;
350 }
351 
352 //-----------------------------------------------------------------------------
353 
getPixmap(bool isOpen) const354 QPixmap DvDirModelFileFolderNode::getPixmap(bool isOpen) const {
355   static QPixmap openFolderPixmap(
356       svgToPixmap(getIconThemePath("actions/18/folder_on.svg")));
357   static QPixmap closeFolderPixmap(
358       svgToPixmap(getIconThemePath("actions/18/folder.svg")));
359   return isOpen ? openFolderPixmap : closeFolderPixmap;
360 }
361 
362 //=============================================================================
363 //
364 // DvDirModelSpecialFileFolderNode [Special File Folder]
365 //
366 //-----------------------------------------------------------------------------
367 
DvDirModelSpecialFileFolderNode(DvDirModelNode * parent,std::wstring name,const TFilePath & path)368 DvDirModelSpecialFileFolderNode::DvDirModelSpecialFileFolderNode(
369     DvDirModelNode *parent, std::wstring name, const TFilePath &path)
370     : DvDirModelFileFolderNode(parent, name, path) {}
371 
372 //-----------------------------------------------------------------------------
373 
getPixmap(bool isOpen) const374 QPixmap DvDirModelSpecialFileFolderNode::getPixmap(bool isOpen) const {
375   return m_pixmap;
376 }
377 
378 //-----------------------------------------------------------------------------
379 
setPixmap(const QPixmap & pixmap)380 void DvDirModelSpecialFileFolderNode::setPixmap(const QPixmap &pixmap) {
381   m_pixmap = pixmap;
382 }
383 
384 //=============================================================================
385 //
386 // DvDirVersionControlNode [Special File Folder]
387 //
388 //-----------------------------------------------------------------------------
389 
DvDirVersionControlNode(DvDirModelNode * parent,std::wstring name,const TFilePath & path)390 DvDirVersionControlNode::DvDirVersionControlNode(DvDirModelNode *parent,
391                                                  std::wstring name,
392                                                  const TFilePath &path)
393     : DvDirModelFileFolderNode(parent, name, path)
394     , m_isSynched(false)
395     , m_isUnversioned(false)
396     , m_oldName() {
397   this->setExists(TFileStatus(path).doesExist());
398   setIsUnderVersionControl(
399       VersionControl::instance()->isFolderUnderVersionControl(toQString(path)));
400 }
401 
402 //-----------------------------------------------------------------------------
403 
getMissingFiles() const404 QList<TFilePath> DvDirVersionControlNode::getMissingFiles() const {
405   QList<TFilePath> list;
406   QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
407   while (i != m_statusMap.constEnd()) {
408     SVNStatus s = i.value();
409     if (s.m_item == "missing" ||
410         (s.m_item == "none" && s.m_repoStatus == "added")) {
411       TFilePath path(getPath() + TFilePath(s.m_path.toStdWString()));
412       std::string dots = path.getDots();
413       if (dots != "") {
414         if (dots == "..") path = path.getParentDir() + path.getLevelNameW();
415         if (!list.contains(path)) list.append(path);
416       }
417     }
418     i++;
419   }
420   return list;
421 }
422 
423 //-----------------------------------------------------------------------------
424 
getMissingFiles(const QRegExp & filter) const425 QStringList DvDirVersionControlNode::getMissingFiles(
426     const QRegExp &filter) const {
427   QStringList list;
428   QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
429   for (; i != m_statusMap.constEnd(); i++) {
430     SVNStatus s = i.value();
431     if (s.m_item == "missing" ||
432         (s.m_item == "none" && s.m_repoStatus == "added")) {
433       TFilePath path(s.m_path.toStdWString());
434       if (!filter.exactMatch(
435               QString::fromStdWString(path.withoutParentDir().getWideString())))
436         continue;
437       std::string dots = path.getDots();
438       if (dots != "") list.append(toQString(path));
439     }
440   }
441   return list;
442 }
443 
444 //-----------------------------------------------------------------------------
445 
getMissingFolders() const446 QList<TFilePath> DvDirVersionControlNode::getMissingFolders() const {
447   QList<TFilePath> list;
448   QMap<QString, SVNStatus>::const_iterator i = m_statusMap.constBegin();
449   while (i != m_statusMap.constEnd()) {
450     SVNStatus s = i.value();
451     if (s.m_item == "missing" ||
452         (s.m_item == "none" && s.m_repoStatus == "added")) {
453       TFilePath path(getPath() + TFilePath(s.m_path.toStdWString()));
454       if (path.getDots() == "" || path.getType() == "tnz") {
455         QString currentFolderName =
456             QString::fromStdWString(getPath().getWideName());
457         if (currentFolderName != s.m_path) {
458           if (!s.m_path.contains("\\"))
459             list.append(path);
460           else {
461             QString newPath = s.m_path.replace(currentFolderName + "\\", "");
462             if (!newPath.contains("\\"))
463               list.append(getPath() + TFilePath(newPath.toStdWString()));
464           }
465         }
466       }
467     }
468     i++;
469   }
470   return list;
471 }
472 
473 //-----------------------------------------------------------------------------
474 
getChildrenNames(std::vector<std::wstring> & names) const475 void DvDirVersionControlNode::getChildrenNames(
476     std::vector<std::wstring> &names) const {
477   DvDirModelFileFolderNode::getChildrenNames(names);
478 
479   QList<TFilePath> missingFolders = getMissingFolders();
480   int count                       = missingFolders.size();
481 
482   for (int i = 0; i < count; i++) {
483     std::wstring name = missingFolders.at(i).getWideName();
484     if (find(names.begin(), names.end(), name) == names.end())
485       names.push_back(name);
486   }
487 }
488 
489 //-----------------------------------------------------------------------------
490 
setIsUnderVersionControl(bool value)491 void DvDirVersionControlNode::setIsUnderVersionControl(bool value) {
492   // Set the flags and enable/disable renaming accordling
493   m_isUnderVersionControl = value;
494   enableRename(!value);
495 }
496 
497 //-----------------------------------------------------------------------------
498 
makeChild(std::wstring name)499 DvDirModelNode *DvDirVersionControlNode::makeChild(std::wstring name) {
500   if (TProjectManager::instance()->isProject(getPath() + name))
501     return new DvDirVersionControlProjectNode(this, name, getPath() + name);
502   return new DvDirVersionControlNode(this, name, getPath() + name);
503 }
504 
505 //-----------------------------------------------------------------------------
506 
getPixmap(bool isOpen) const507 QPixmap DvDirVersionControlNode::getPixmap(bool isOpen) const {
508   static QPixmap openFolderPixmap(
509       svgToPixmap(getIconThemePath("actions/18/folder_on.svg")));
510   static QPixmap closeFolderPixmap(
511       svgToPixmap(getIconThemePath("actions/18/folder.svg")));
512   static QPixmap openMissingPixmap(
513       svgToPixmap(":Resources/vcfolder_mis_open.svg"));
514   static QPixmap closeMissingPixmap(
515       svgToPixmap(":Resources/vcfolder_mis_close.svg"));
516   static QPixmap openSceneFolderPixmap(
517       svgToPixmap(":Resources/browser_scene_open.svg"));
518   static QPixmap closeSceneFolderPixmap(
519       svgToPixmap(":Resources/browser_scene_close.svg"));
520 
521   if (TFileStatus(getPath()).doesExist()) {
522     if (getPath().getType() == "tnz")
523       return isOpen ? openSceneFolderPixmap : closeSceneFolderPixmap;
524     else
525       return isOpen ? openFolderPixmap : closeFolderPixmap;
526   } else
527     return isOpen ? openMissingPixmap : closeMissingPixmap;
528 }
529 
530 //-----------------------------------------------------------------------------
531 
insertVersionControlStatus(const QString & fileName,SVNStatus status)532 void DvDirVersionControlNode::insertVersionControlStatus(
533     const QString &fileName, SVNStatus status) {
534   m_statusMap.insert(fileName, status);
535 }
536 
537 //-----------------------------------------------------------------------------
538 
getVersionControlStatus(const QString & fileName)539 SVNStatus DvDirVersionControlNode::getVersionControlStatus(
540     const QString &fileName) {
541   if (m_statusMap.contains(fileName)) return m_statusMap.value(fileName);
542 
543   SVNStatus s         = SVNStatus();
544   s.m_isLocked        = false;
545   s.m_isPartialEdited = false;
546   s.m_isPartialLocked = false;
547   return s;
548 }
549 
550 //-----------------------------------------------------------------------------
551 
refreshVersionControl(const QList<SVNStatus> & status)552 QStringList DvDirVersionControlNode::refreshVersionControl(
553     const QList<SVNStatus> &status) {
554   TFilePath nodePath = getPath();
555   if (nodePath.isEmpty()) return QStringList();
556 
557   int listSize   = status.size();
558   bool isSynched = true;
559 
560   QStringList checkPartialLockList;
561 
562   for (int i = 0; i < listSize; i++) {
563     SVNStatus s = status.at(i);
564 
565     // It is not needed to check and cache SVNStatus for files that are
566     // "outside" the node "scope"
567     if (s.m_path == "." || s.m_path == ".." || s.m_path.split("\\").size() > 2)
568       continue;
569 
570     QString fileName = s.m_path;
571     if (nodePath.getType() == "") insertVersionControlStatus(fileName, s);
572 
573     // Update also the status of the "scene" child folders
574     TFilePath path(fileName.toStdWString());
575     if (path.getType() == "tnz") {
576       DvDirVersionControlNode *childVcNode =
577           dynamic_cast<DvDirVersionControlNode *>(
578               getNodeByPath(nodePath + path));
579       if (childVcNode) {
580         childVcNode->setIsUnderVersionControl(true);
581         if (s.m_repoStatus == "modified" || s.m_item == "modified" ||
582             s.m_item == "missing" || s.m_item == "none")
583           childVcNode->setIsSynched(false);
584         else if (s.m_item == "unversioned")
585           childVcNode->setIsUnversioned(true);
586         else
587           childVcNode->setIsSynched(true);
588       }
589     }
590     // If a folder is unversioned, I set its status and even the unversioned
591     // status for its children
592     else if (path.getType() == "" && s.m_item == "unversioned") {
593       DvDirVersionControlNode *childVcNode =
594           dynamic_cast<DvDirVersionControlNode *>(
595               getNodeByPath(nodePath + path));
596       if (childVcNode) {
597         childVcNode->setIsUnversioned(true);
598         int childCount = childVcNode->getChildCount();
599         for (int i = 0; i < childCount; i++) {
600           DvDirVersionControlNode *subChildNode =
601               dynamic_cast<DvDirVersionControlNode *>(childVcNode->getChild(i));
602           if (subChildNode) subChildNode->setIsUnversioned(true);
603         }
604       }
605     }
606 
607     // Check also the 'partial-lock' property
608     std::string type = TFilePath(s.m_path.toStdWString()).getType();
609     if (type == "tlv" || type == "pli") {
610       if (s.m_item != "unversioned" && s.m_item != "missing" &&
611           s.m_repoStatus != "added")
612         checkPartialLockList.append(s.m_path);
613     }
614 
615     if (s.m_repoStatus == "modified" || s.m_item == "modified" ||
616         s.m_item == "unversioned" || s.m_item == "missing" ||
617         s.m_item == "none")
618       isSynched = false;
619   }
620 
621   setIsSynched(isSynched);
622   return checkPartialLockList;
623 }
624 
625 //-----------------------------------------------------------------------------
626 
627 DvDirVersionControlRootNode *
getVersionControlRootNode()628 DvDirVersionControlNode::getVersionControlRootNode() {
629   DvDirModelNode *parent = getParent();
630   if (parent) return parent->getVersionControlRootNode();
631   return 0;
632 }
633 
634 //=============================================================================
635 //
636 // DvDirVersionControlRootNode [Special File Folder]
637 //
638 //-----------------------------------------------------------------------------
639 
DvDirVersionControlRootNode(DvDirModelNode * parent,std::wstring name,const TFilePath & path)640 DvDirVersionControlRootNode::DvDirVersionControlRootNode(DvDirModelNode *parent,
641                                                          std::wstring name,
642                                                          const TFilePath &path)
643     : DvDirVersionControlNode(parent, name, path) {
644   setPixmap(svgToPixmap(":Resources/vcroot.svg"));
645   setIsUnderVersionControl(true);
646 }
647 
648 //-----------------------------------------------------------------------------
649 
refreshChildren()650 void DvDirVersionControlRootNode::refreshChildren() {
651   DvDirModelFileFolderNode::refreshChildren();
652 }
653 
654 //=============================================================================
655 // DvDirVersionControlProjectNode
656 //-----------------------------------------------------------------------------
657 
DvDirVersionControlProjectNode(DvDirModelNode * parent,std::wstring name,const TFilePath & path)658 DvDirVersionControlProjectNode::DvDirVersionControlProjectNode(
659     DvDirModelNode *parent, std::wstring name, const TFilePath &path)
660     : DvDirVersionControlNode(parent, name, path) {
661   m_nodeType = "Project";
662   setIsUnderVersionControl(true);
663 }
664 
665 //-----------------------------------------------------------------------------
666 
getProjectPath() const667 TFilePath DvDirVersionControlProjectNode::getProjectPath() const {
668   return TProjectManager::instance()->projectFolderToProjectPath(getPath());
669 }
670 
671 //-----------------------------------------------------------------------------
672 
isCurrent() const673 bool DvDirVersionControlProjectNode::isCurrent() const {
674   TProjectManager *pm = TProjectManager::instance();
675   return pm->getCurrentProjectPath().getParentDir() == getPath();
676 }
677 
678 //-----------------------------------------------------------------------------
679 
makeCurrent()680 void DvDirVersionControlProjectNode::makeCurrent() {
681   TProjectManager *pm   = TProjectManager::instance();
682   TFilePath projectPath = getProjectPath();
683   if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project"))) return;
684   pm->setCurrentProjectPath(projectPath);
685   IoCmd::newScene();
686 }
687 
688 //-----------------------------------------------------------------------------
689 
getPixmap(bool isOpen) const690 QPixmap DvDirVersionControlProjectNode::getPixmap(bool isOpen) const {
691   static QPixmap openPixmap(
692       svgToPixmap(":Resources/browser_vcproject_open.svg"));
693   static QPixmap closePixmap(
694       svgToPixmap(":Resources/browser_vcproject_close.svg"));
695   static QPixmap openMissingPixmap(
696       svgToPixmap(":Resources/vcfolder_mis_open.svg"));
697   static QPixmap closeMissingPixmap(
698       svgToPixmap(":Resources/vcfolder_mis_close.svg"));
699 
700   if (TFileStatus(getPath()).doesExist())
701     return isOpen ? openPixmap : closePixmap;
702   else
703     return isOpen ? openMissingPixmap : closeMissingPixmap;
704 }
705 
706 //-----------------------------------------------------------------------------
707 
refreshChildren()708 void DvDirVersionControlProjectNode::refreshChildren() {
709   DvDirModelFileFolderNode::refreshChildren();
710   int i;
711   TProjectManager *pm = TProjectManager::instance();
712   TProject *project   = new TProject();
713   project->load(getProjectPath());
714   for (i = 0; i < getChildCount(); i++) {
715     DvDirModelFileFolderNode *node =
716         dynamic_cast<DvDirModelFileFolderNode *>(getChild(i));
717     if (node) {
718       int k = project->getFolderIndexFromPath(node->getPath());
719       node->setIsProjectFolder(k >= 0);
720     }
721   }
722   delete project;
723 }
724 
725 //-----------------------------------------------------------------------------
726 
getChildrenNames(std::vector<std::wstring> & names) const727 void DvDirVersionControlProjectNode::getChildrenNames(
728     std::vector<std::wstring> &names) const {
729   DvDirVersionControlNode::getChildrenNames(names);
730   TProjectManager *pm = TProjectManager::instance();
731   TProject *project   = new TProject();
732   project->load(getProjectPath());
733   int i;
734   for (i = 0; i < project->getFolderCount(); i++) {
735     std::string folderName = project->getFolderName(i);
736     TFilePath folderPath   = project->getFolder(i);
737     // if(folderPath.isAbsolute() || folderPath.getParentDir() != TFilePath())
738     if (folderPath.isAbsolute() && project->isConstantFolder(i)) {
739       names.push_back(L"+" + ::to_wstring(folderName));
740     }
741   }
742   delete project;
743 }
744 
745 //=============================================================================
746 //
747 // DvDirModelProjectNode [Project Folder]
748 //
749 //-----------------------------------------------------------------------------
750 
DvDirModelProjectNode(DvDirModelNode * parent,const TFilePath & path)751 DvDirModelProjectNode::DvDirModelProjectNode(DvDirModelNode *parent,
752                                              const TFilePath &path)
753     : DvDirModelFileFolderNode(parent, path) {
754   m_nodeType = "Project";
755 }
756 
757 //-----------------------------------------------------------------------------
758 
getProjectPath() const759 TFilePath DvDirModelProjectNode::getProjectPath() const {
760   return TProjectManager::instance()->projectFolderToProjectPath(getPath());
761 }
762 
763 //-----------------------------------------------------------------------------
764 
isCurrent() const765 bool DvDirModelProjectNode::isCurrent() const {
766   TProjectManager *pm = TProjectManager::instance();
767   return pm->getCurrentProjectPath().getParentDir() == getPath();
768 }
769 
770 //-----------------------------------------------------------------------------
771 
makeCurrent()772 void DvDirModelProjectNode::makeCurrent() {
773   TProjectManager *pm   = TProjectManager::instance();
774   TFilePath projectPath = getProjectPath();
775   if (!IoCmd::saveSceneIfNeeded(QObject::tr("Change project"))) return;
776 
777   pm->setCurrentProjectPath(projectPath);
778   IoCmd::newScene();
779 }
780 
781 //-----------------------------------------------------------------------------
782 
getPixmap(bool isOpen) const783 QPixmap DvDirModelProjectNode::getPixmap(bool isOpen) const {
784   static QPixmap openProjectPixmap(
785       svgToPixmap(getIconThemePath("actions/18/folder_project_on.svg")));
786   static QPixmap closeProjectPixmap(
787       svgToPixmap(getIconThemePath("actions/18/folder_project.svg")));
788   return isOpen ? openProjectPixmap : closeProjectPixmap;
789 }
790 
791 //-----------------------------------------------------------------------------
792 
refreshChildren()793 void DvDirModelProjectNode::refreshChildren() {
794   DvDirModelFileFolderNode::refreshChildren();
795   int i;
796   TProjectManager *pm = TProjectManager::instance();
797   TProject *project   = new TProject();
798   project->load(getProjectPath());
799   for (i = 0; i < getChildCount(); i++) {
800     DvDirModelFileFolderNode *node =
801         dynamic_cast<DvDirModelFileFolderNode *>(getChild(i));
802     if (node) {
803       int k = project->getFolderIndexFromPath(node->getPath());
804       node->setIsProjectFolder(k >= 0);
805     }
806   }
807   delete project;
808 }
809 
810 //-----------------------------------------------------------------------------
811 
getChildrenNames(std::vector<std::wstring> & names) const812 void DvDirModelProjectNode::getChildrenNames(
813     std::vector<std::wstring> &names) const {
814   DvDirModelFileFolderNode::getChildrenNames(names);
815   TProjectManager *pm = TProjectManager::instance();
816   TProject *project   = new TProject();
817   project->load(getProjectPath());
818   int i;
819   for (i = 0; i < project->getFolderCount(); i++) {
820     std::string folderName = project->getFolderName(i);
821     TFilePath folderPath   = project->getFolder(i);
822     // if(folderPath.isAbsolute() || folderPath.getParentDir() != TFilePath())
823     if (folderPath.isAbsolute() && project->isConstantFolder(i)) {
824       names.push_back(L"+" + ::to_wstring(folderName));
825     }
826   }
827   delete project;
828 }
829 
830 //-----------------------------------------------------------------------------
831 
makeChild(std::wstring name)832 DvDirModelNode *DvDirModelProjectNode::makeChild(std::wstring name) {
833   if (name != L"" && name[0] == L'+') {
834     TProjectManager *pm = TProjectManager::instance();
835     TProject *project   = new TProject();
836     project->load(getProjectPath());
837     std::string folderName = ::to_string(name.substr(1));
838     TFilePath folderPath   = project->getFolder(folderName);
839     DvDirModelNode *node = new DvDirModelFileFolderNode(this, name, folderPath);
840     delete project;
841     return node;
842   } else
843     return DvDirModelFileFolderNode::makeChild(name);
844 }
845 
846 //=============================================================================
847 //
848 // DvDirModelDayNode [Day folder]
849 //
850 //-----------------------------------------------------------------------------
851 
DvDirModelDayNode(DvDirModelNode * parent,std::wstring dayDateString)852 DvDirModelDayNode::DvDirModelDayNode(DvDirModelNode *parent,
853                                      std::wstring dayDateString)
854     : DvDirModelNode(parent, dayDateString)
855     , m_dayDateString(::to_string(dayDateString)) {
856   m_nodeType = "Day";
857 }
858 
859 //-----------------------------------------------------------------------------
860 
visualizeContent(FileBrowser * browser)861 void DvDirModelDayNode::visualizeContent(FileBrowser *browser) {
862   browser->setHistoryDay(m_dayDateString);
863 }
864 
865 //-----------------------------------------------------------------------------
866 
getPixmap(bool isOpen) const867 QPixmap DvDirModelDayNode::getPixmap(bool isOpen) const {
868   static QPixmap openFolderPixmap(
869       svgToPixmap(getIconThemePath("actions/18/folder_on.svg")));
870   static QPixmap closeFolderPixmap(
871       svgToPixmap(getIconThemePath("actions/18/folder.svg")));
872   return isOpen ? openFolderPixmap : closeFolderPixmap;
873 }
874 
875 //=============================================================================
876 //
877 // DvDirModelHistoryNode [History]
878 //
879 //-----------------------------------------------------------------------------
880 
DvDirModelHistoryNode(DvDirModelNode * parent)881 DvDirModelHistoryNode::DvDirModelHistoryNode(DvDirModelNode *parent)
882     : DvDirModelNode(parent, L"History") {
883   m_nodeType = "History";
884 }
885 
886 //-----------------------------------------------------------------------------
887 
refreshChildren()888 void DvDirModelHistoryNode::refreshChildren() {
889   m_children.clear();
890   m_childrenValid = true;
891   History *h      = History::instance();
892   for (int i = 0; i < h->getDayCount(); i++) {
893     const History::Day *day = h->getDay(i);
894     DvDirModelNode *child =
895         new DvDirModelDayNode(this, ::to_wstring(day->getDate()));
896     addChild(child);
897   }
898 }
899 
900 //-----------------------------------------------------------------------------
901 
getPixmap(bool isOpen) const902 QPixmap DvDirModelHistoryNode::getPixmap(bool isOpen) const {
903   QIcon icon            = createQIcon("history");
904   static QPixmap pixmap = icon.pixmap(16);
905   return pixmap;
906 }
907 
908 //=============================================================================
909 //
910 // DvDirModelMyComputerNode [My Computer]
911 //
912 //-----------------------------------------------------------------------------
913 
DvDirModelMyComputerNode(DvDirModelNode * parent)914 DvDirModelMyComputerNode::DvDirModelMyComputerNode(DvDirModelNode *parent)
915     : DvDirModelNode(parent, L"My Computer") {
916   // setIcon(QFileIconProvider().icon(QFileIconProvider::Computer));
917   m_nodeType = "MyComputer";
918 }
919 
920 //-----------------------------------------------------------------------------
921 
refreshChildren()922 void DvDirModelMyComputerNode::refreshChildren() {
923   m_childrenValid = true;
924   if (!m_children.empty()) clearPointerContainer(m_children);
925 
926   TFilePathSet fps = TSystem::getDisks();
927 
928 #ifdef MACOSX
929   fps.push_back(TFilePath("/Volumes/"));
930 #endif
931 
932   TFilePathSet::iterator it;
933   for (it = fps.begin(); it != fps.end(); ++it) {
934     DvDirModelNode *child =
935         new DvDirModelFileFolderNode(this, it->getWideString(), *it);
936     addChild(child);
937   }
938 }
939 
940 //-----------------------------------------------------------------------------
941 
getPixmap(bool isOpen) const942 QPixmap DvDirModelMyComputerNode::getPixmap(bool isOpen) const {
943   QIcon icon            = createQIcon("my_computer");
944   static QPixmap pixmap = icon.pixmap(16);
945   return pixmap;
946 }
947 
948 //=============================================================================
949 //
950 // DvDirModelNetworkNode [Network]
951 //
952 //-----------------------------------------------------------------------------
953 
DvDirModelNetworkNode(DvDirModelNode * parent)954 DvDirModelNetworkNode::DvDirModelNetworkNode(DvDirModelNode *parent)
955     : DvDirModelNode(parent, L"Network") {
956   m_nodeType = "Network";
957 }
958 
959 //-----------------------------------------------------------------------------
960 
refreshChildren()961 void DvDirModelNetworkNode::refreshChildren() {
962   m_childrenValid = true;
963 
964   if (!m_children.empty()) clearPointerContainer(m_children);
965 
966 #ifdef _WIN32
967 
968   // Enumerate network nodes
969   HANDLE enumHandle;
970   DWORD err =
971       WNetOpenEnum(RESOURCE_CONTEXT, RESOURCETYPE_DISK, 0, NULL, &enumHandle);
972   assert(err == NO_ERROR);
973 
974   DWORD count = -1,
975         bufferSize =
976             16 << 10;  // Use 16 kB as recommended on Windows references
977   LPNETRESOURCE buffer = (LPNETRESOURCE)malloc(bufferSize);
978 
979   do {
980     // Get the next bunch of network devices
981     memset(buffer, 0, bufferSize);
982     err = WNetEnumResource(enumHandle, &count, buffer, &bufferSize);
983 
984     if (err == NO_ERROR) {
985       for (int i = 0; i < count; ++i) {
986         // Only list disk-type devices - in any case, the remote (UNC) name
987         // should exist
988         if (buffer[i].dwType == RESOURCETYPE_DISK && buffer[i].lpRemoteName) {
989           std::wstring wstr(buffer[i].lpRemoteName);
990 
991           // Build a NOT PEEKING folder node. This is important since network
992           // access is SLOW.
993           DvDirModelFileFolderNode *child =
994               new DvDirModelFileFolderNode(this, wstr, TFilePath(wstr));
995           child->setPeeking(false);
996 
997           addChild(child);
998         }
999       }
1000     } else if (err != ERROR_NO_MORE_ITEMS)
1001       break;  // Excluding NO_ERROR and ERROR_NO_MORE_ITEMS, quit
1002 
1003   } while (err != ERROR_NO_MORE_ITEMS);
1004 
1005   err = WNetCloseEnum(enumHandle);
1006   assert(err == NO_ERROR);
1007 
1008   free(buffer);
1009 
1010 #endif
1011 }
1012 
1013 //-----------------------------------------------------------------------------
1014 
getPixmap(bool isOpen) const1015 QPixmap DvDirModelNetworkNode::getPixmap(bool isOpen) const {
1016   QIcon icon            = createQIcon("network");
1017   static QPixmap pixmap = icon.pixmap(16);
1018   return pixmap;
1019 }
1020 
1021 //=============================================================================
1022 //
1023 // DvDirModelRootNode [Root]
1024 //
1025 //-----------------------------------------------------------------------------
1026 
DvDirModelRootNode()1027 DvDirModelRootNode::DvDirModelRootNode()
1028     : DvDirModelNode(0, L"Root")
1029     , m_myComputerNode(0)
1030     , m_networkNode(0)
1031     , m_sandboxProjectNode(0) {
1032   m_nodeType = "Root";
1033 }
1034 
1035 //-----------------------------------------------------------------------------
1036 
add(std::wstring name,const TFilePath & path)1037 void DvDirModelRootNode::add(std::wstring name, const TFilePath &path) {
1038   DvDirModelNode *child = new DvDirModelFileFolderNode(this, name, path);
1039   child->setRow((int)m_children.size());
1040   m_children.push_back(child);
1041 }
1042 
1043 //-----------------------------------------------------------------------------
1044 
refreshChildren()1045 void DvDirModelRootNode::refreshChildren() {
1046   m_childrenValid = true;
1047   if (m_children.empty()) {
1048     addChild(m_myComputerNode = new DvDirModelMyComputerNode(this));
1049 
1050 #ifdef _WIN32
1051     addChild(m_networkNode = new DvDirModelNetworkNode(this));
1052 #endif
1053     DvDirModelSpecialFileFolderNode *child;
1054     child = new DvDirModelSpecialFileFolderNode(this, L"My Documents",
1055                                                 getMyDocumentsPath());
1056     child->setPixmap(recolorPixmap(
1057         svgToPixmap(getIconThemePath("actions/16/my_documents.svg"))));
1058     m_specialNodes.push_back(child);
1059     addChild(child);
1060 
1061     child =
1062         new DvDirModelSpecialFileFolderNode(this, L"Desktop", getDesktopPath());
1063     child->setPixmap(
1064         recolorPixmap(svgToPixmap(getIconThemePath("actions/16/desktop.svg"))));
1065     m_specialNodes.push_back(child);
1066     addChild(child);
1067 
1068     child = new DvDirModelSpecialFileFolderNode(
1069         this, L"Library", ToonzFolder::getLibraryFolder());
1070     child->setPixmap(
1071         recolorPixmap(svgToPixmap(getIconThemePath("actions/16/library.svg"))));
1072     m_specialNodes.push_back(child);
1073     addChild(child);
1074 
1075     addChild(new DvDirModelHistoryNode(this));
1076 
1077     TProjectManager *pm = TProjectManager::instance();
1078     std::vector<TFilePath> projectRoots;
1079     pm->getProjectRoots(projectRoots);
1080 
1081     int i;
1082     for (i = 0; i < (int)projectRoots.size(); i++) {
1083       TFilePath projectRoot = projectRoots[i];
1084       std::wstring roothDir = projectRoot.getWideString();
1085       DvDirModelSpecialFileFolderNode *projectRootNode =
1086           new DvDirModelSpecialFileFolderNode(
1087               this, L"Project root (" + roothDir + L")", projectRoot);
1088       projectRootNode->setPixmap(QPixmap(recolorPixmap(svgToPixmap(
1089           getIconThemePath("actions/18/folder_project_root.svg")))));
1090       m_projectRootNodes.push_back(projectRootNode);
1091       addChild(projectRootNode);
1092     }
1093 
1094     TFilePath sandboxProjectPath = pm->getSandboxProjectFolder();
1095     m_sandboxProjectNode = new DvDirModelProjectNode(this, sandboxProjectPath);
1096     addChild(m_sandboxProjectNode);
1097 
1098     // SVN Repositories
1099     QList<SVNRepository> repositories =
1100         VersionControl::instance()->getRepositories();
1101     int count = repositories.size();
1102     for (int i = 0; i < count; i++) {
1103       SVNRepository repo                = repositories.at(i);
1104       DvDirVersionControlRootNode *node = new DvDirVersionControlRootNode(
1105           this, repo.m_name.toStdWString(),
1106           TFilePath(repo.m_localPath.toStdWString()));
1107       node->setRepositoryPath(repo.m_repoPath.toStdWString());
1108       node->setLocalPath(repo.m_localPath.toStdWString());
1109       node->setUserName(repo.m_username.toStdWString());
1110       node->setPassword(repo.m_password.toStdWString());
1111       m_versionControlNodes.push_back(node);
1112       addChild(node);
1113     }
1114 
1115     // scenefolder node (access to the parent folder of the current scene file)
1116     m_sceneFolderNode =
1117         new DvDirModelSceneFolderNode(this, L"Scene Folder", TFilePath());
1118     m_sceneFolderNode->setPixmap(QPixmap(":Resources/clapboard.png"));
1119   }
1120 }
1121 
1122 //-----------------------------------------------------------------------------
getNodeByPath(const TFilePath & path)1123 DvDirModelNode *DvDirModelRootNode::getNodeByPath(const TFilePath &path) {
1124   // Check first for version control nodes
1125   DvDirModelNode *node = 0;
1126   int i;
1127 
1128   // search in #1 the project folders, #2 sandbox, #3 other folders in file
1129   // system
1130 
1131   // check for the scene folder if it is the first priority
1132   Preferences::PathAliasPriority priority =
1133       Preferences::instance()->getPathAliasPriority();
1134   if (priority == Preferences::SceneFolderAlias &&
1135       !m_sceneFolderNode->getPath().isEmpty()) {
1136     node = m_sceneFolderNode->getNodeByPath(path);
1137     if (node) return node;
1138   }
1139 
1140   // path could be a project, under some project root
1141   for (i = 0; i < (int)m_projectRootNodes.size(); i++) {
1142     node = m_projectRootNodes[i]->getNodeByPath(path);
1143     if (node) return node;
1144     // search in the project folders
1145     for (int j = 0; j < m_projectRootNodes[i]->getChildCount(); j++) {
1146       DvDirModelProjectNode *projectNode =
1147           dynamic_cast<DvDirModelProjectNode *>(
1148               m_projectRootNodes[i]->getChild(j));
1149       if (projectNode) {
1150         // for the normal folder in the project folder
1151         node = projectNode->getNodeByPath(path);
1152         if (node) return node;
1153 
1154         if (projectNode->isCurrent()) {
1155           // for the aliases in the project folder ("+drawings" etc.)
1156           for (int k = 0; k < projectNode->getChildCount(); k++) {
1157             node = projectNode->getChild(k)->getNodeByPath(path);
1158             if (node) return node;
1159           }
1160         }
1161       } else  // for the normal folder in the project root
1162       {
1163         node = m_projectRootNodes[i]->getChild(j)->getNodeByPath(path);
1164         if (node) return node;
1165       }
1166     }
1167   }
1168 
1169   // it could be on a repository, under version control
1170   for (i = 0; i < (int)m_versionControlNodes.size(); i++) {
1171     node = m_versionControlNodes[i]->getNodeByPath(path);
1172     if (node) return node;
1173   }
1174 
1175   // or it could be the sandbox project or in the sandbox project
1176   if (m_sandboxProjectNode && m_sandboxProjectNode->getPath() == path)
1177     return m_sandboxProjectNode;
1178   if (m_sandboxProjectNode) {
1179     for (i = 0; i < m_sandboxProjectNode->getChildCount(); i++) {
1180       DvDirModelNode *node =
1181           m_sandboxProjectNode->getChild(i)->getNodeByPath(path);
1182       if (node) return node;
1183     }
1184   }
1185 
1186   // check for the scene folder if it is the second priority
1187   if (priority == Preferences::ProjectFolderAliases &&
1188       !m_sceneFolderNode->getPath().isEmpty()) {
1189     node = m_sceneFolderNode->getNodeByPath(path);
1190     if (node) return node;
1191   }
1192 
1193   // check for the special folders (My Documents / Desktop / Library)
1194   for (DvDirModelSpecialFileFolderNode *specialNode : m_specialNodes) {
1195     DvDirModelNode *node = specialNode->getNodeByPath(path);
1196     if (node) return node;
1197   }
1198 
1199   // it could be a regular folder, somewhere in the file system
1200   if (m_myComputerNode) {
1201     for (i = 0; i < m_myComputerNode->getChildCount(); i++) {
1202       DvDirModelNode *node = m_myComputerNode->getChild(i)->getNodeByPath(path);
1203       if (node) return node;
1204     }
1205   }
1206 
1207   // it could be a network folder
1208   if (m_networkNode) {
1209     for (i = 0; i < m_networkNode->getChildCount(); i++) {
1210       DvDirModelNode *node = m_networkNode->getChild(i)->getNodeByPath(path);
1211       if (node) return node;
1212     }
1213   }
1214 
1215   return 0;
1216 }
1217 
1218 //-----------------------------------------------------------------------------
1219 // update the path of sceneLocationNode
1220 
setSceneLocation(const TFilePath & path)1221 void DvDirModelRootNode::setSceneLocation(const TFilePath &path) {
1222   if (path == m_sceneFolderNode->getPath()) return;
1223   m_sceneFolderNode->setPath(path);
1224 
1225   Preferences::PathAliasPriority priority =
1226       Preferences::instance()->getPathAliasPriority();
1227   if (priority == Preferences::ProjectFolderOnly) return;
1228 
1229   updateSceneFolderNodeVisibility();
1230 }
1231 
1232 //-----------------------------------------------------------------------------
1233 
updateSceneFolderNodeVisibility(bool forceHide)1234 void DvDirModelRootNode::updateSceneFolderNodeVisibility(bool forceHide) {
1235   bool show = (forceHide) ? false : !m_sceneFolderNode->getPath().isEmpty();
1236   if (show && m_sceneFolderNode->getRow() == -1) {
1237     int row = getChildCount();
1238     DvDirModel::instance()->notifyBeginInsertRows(QModelIndex(), row, row + 1);
1239     addChild(m_sceneFolderNode);
1240     DvDirModel::instance()->notifyEndInsertRows();
1241   } else if (!show && m_sceneFolderNode->getRow() != -1) {
1242     int row = m_sceneFolderNode->getRow();
1243     DvDirModel::instance()->notifyBeginRemoveRows(QModelIndex(), row, row + 1);
1244     // remove the last child of the root node
1245     m_children.erase(m_children.begin() + row, m_children.begin() + row + 1);
1246     DvDirModel::instance()->notifyEndRemoveRows();
1247     m_sceneFolderNode->setRow(-1);
1248   }
1249 }
1250 
1251 //=============================================================================
1252 //
1253 // DvDirModel
1254 //
1255 //-----------------------------------------------------------------------------
1256 
DvDirModel()1257 DvDirModel::DvDirModel() {
1258   m_root = new DvDirModelRootNode();
1259   m_root->refreshChildren();
1260 
1261   // when the scene switched, update the path of the scene location node
1262   TSceneHandle *sceneHandle = TApp::instance()->getCurrentScene();
1263   bool ret                  = true;
1264   ret = ret && connect(sceneHandle, SIGNAL(sceneSwitched()), this,
1265                        SLOT(onSceneSwitched()));
1266   ret = ret && connect(sceneHandle, SIGNAL(nameSceneChanged()), this,
1267                        SLOT(onSceneSwitched()));
1268   ret = ret && connect(sceneHandle, SIGNAL(preferenceChanged(const QString &)),
1269                        this, SLOT(onPreferenceChanged(const QString &)));
1270   assert(ret);
1271 
1272   onSceneSwitched();
1273 }
1274 
1275 //-----------------------------------------------------------------------------
1276 
~DvDirModel()1277 DvDirModel::~DvDirModel() { delete m_root; }
1278 
1279 //-----------------------------------------------------------------------------
1280 
onFolderChanged(const TFilePath & path)1281 void DvDirModel::onFolderChanged(const TFilePath &path) { refreshFolder(path); }
1282 
1283 //-----------------------------------------------------------------------------
1284 
refresh(const QModelIndex & index)1285 void DvDirModel::refresh(const QModelIndex &index) {
1286   if (!index.isValid()) return;
1287   DvDirModelNode *node = getNode(index);
1288   if (!node || node->getChildCount() < 1) return;
1289   emit layoutAboutToBeChanged();
1290   emit beginRemoveRows(index, 0, node->getChildCount() - 1);
1291   node->refreshChildren();
1292   emit endRemoveRows();
1293   emit layoutChanged();
1294 }
1295 
1296 //-----------------------------------------------------------------------------
1297 
refreshFolder(const TFilePath & folderPath,const QModelIndex & i)1298 void DvDirModel::refreshFolder(const TFilePath &folderPath,
1299                                const QModelIndex &i) {
1300   DvDirModelNode *node = getNode(i);
1301   if (!node) return;
1302   if (node->isFolder(folderPath))
1303     refresh(i);
1304   else if (node->areChildrenValid()) {
1305     int count = rowCount(i);
1306     int r;
1307     for (r = 0; r < count; r++) refreshFolder(folderPath, index(r, 0, i));
1308   }
1309 }
1310 
1311 //-----------------------------------------------------------------------------
1312 
refreshFolderChild(const QModelIndex & i)1313 void DvDirModel::refreshFolderChild(const QModelIndex &i) {
1314   DvDirModelNode *node = getNode(i);
1315   if (!node || !node->areChildrenValid()) return;
1316 
1317   if (node->isFolder() || dynamic_cast<DvDirModelMyComputerNode *>(node))
1318     refresh(i);
1319   int count = rowCount(i);
1320   int r;
1321   for (r = 0; r < count; r++) refreshFolderChild(index(r, 0, i));
1322 }
1323 
1324 //-----------------------------------------------------------------------------
1325 
getNode(const QModelIndex & index) const1326 DvDirModelNode *DvDirModel::getNode(const QModelIndex &index) const {
1327   if (index.isValid())
1328     return static_cast<DvDirModelNode *>(index.internalPointer());
1329   else
1330     return m_root;
1331 }
1332 
1333 //-----------------------------------------------------------------------------
1334 
index(int row,int column,const QModelIndex & parent) const1335 QModelIndex DvDirModel::index(int row, int column,
1336                               const QModelIndex &parent) const {
1337   if (column != 0) return QModelIndex();
1338   DvDirModelNode *parentNode       = m_root;
1339   if (parent.isValid()) parentNode = getNode(parent);
1340   if (row < 0 || row >= parentNode->getChildCount()) return QModelIndex();
1341   DvDirModelNode *node = parentNode->getChild(row);
1342   return createIndex(row, column, node);
1343 }
1344 
1345 //-----------------------------------------------------------------------------
1346 
parent(const QModelIndex & index) const1347 QModelIndex DvDirModel::parent(const QModelIndex &index) const {
1348   if (!index.isValid()) return QModelIndex();
1349   DvDirModelNode *node       = getNode(index);
1350   DvDirModelNode *parentNode = node->getParent();
1351   if (!parentNode || parentNode == m_root)
1352     return QModelIndex();
1353   else
1354     return createIndex(parentNode->getRow(), 0, parentNode);
1355 }
1356 
1357 //-----------------------------------------------------------------------------
1358 
childByName(const QModelIndex & parent,const std::wstring & name) const1359 QModelIndex DvDirModel::childByName(const QModelIndex &parent,
1360                                     const std::wstring &name) const {
1361   if (!parent.isValid()) return QModelIndex();
1362   DvDirModelNode *parentNode = getNode(parent);
1363   if (!parentNode) return QModelIndex();
1364   int row = parentNode->rowByName(name);
1365   if (row < 0 || row >= parentNode->getChildCount()) return QModelIndex();
1366   DvDirModelNode *childNode = parentNode->getChild(row);
1367   return createIndex(row, 0, childNode);
1368 }
1369 
1370 //-----------------------------------------------------------------------------
1371 
rowCount(const QModelIndex & parent) const1372 int DvDirModel::rowCount(const QModelIndex &parent) const {
1373   DvDirModelNode *node = getNode(parent);
1374   return node->getChildCount();
1375 }
1376 
1377 //-----------------------------------------------------------------------------
1378 
hasChildren(const QModelIndex & parent) const1379 bool DvDirModel::hasChildren(const QModelIndex &parent) const {
1380   DvDirModelNode *node = getNode(parent);
1381   return node->hasChildren();
1382 }
1383 
1384 //-----------------------------------------------------------------------------
1385 
data(const QModelIndex & index,int role) const1386 QVariant DvDirModel::data(const QModelIndex &index, int role) const {
1387   if (!index.isValid()) return QVariant();
1388   DvDirModelNode *node = getNode(index);
1389   if (role == Qt::DisplayRole || role == Qt::EditRole)
1390     return QString::fromStdWString(node->getName());
1391   else if (role == Qt::DecorationRole) {
1392     return QVariant();
1393   } else if (role == Qt::ForegroundRole) {
1394     if (!node || !node->isRenameEnabled())
1395       return QBrush(Qt::blue);
1396     else
1397       return QVariant();
1398   } else
1399     return QVariant();
1400 }
1401 
1402 //-----------------------------------------------------------------------------
1403 
flags(const QModelIndex & index) const1404 Qt::ItemFlags DvDirModel::flags(const QModelIndex &index) const {
1405   Qt::ItemFlags flags = QAbstractItemModel::flags(index);
1406   if (index.isValid()) {
1407     DvDirModelNode *node = getNode(index);
1408     if (node && node->isRenameEnabled()) flags |= Qt::ItemIsEditable;
1409   }
1410   return flags;
1411 }
1412 
1413 //-----------------------------------------------------------------------------
1414 /*! used only for name / rename of items
1415  */
setData(const QModelIndex & index,const QVariant & value,int role)1416 bool DvDirModel::setData(const QModelIndex &index, const QVariant &value,
1417                          int role) {
1418   if (!index.isValid()) return false;
1419   DvDirModelNode *node = getNode(index);
1420   if (!node || !node->isRenameEnabled()) return false;
1421   QString newName = value.toString();
1422   if (newName == "") return false;
1423   if (!node->setName(newName.toStdWString())) return false;
1424   emit dataChanged(index, index);
1425   return true;
1426 }
1427 
1428 //-----------------------------------------------------------------------------
1429 
removeRows(int row,int count,const QModelIndex & parentIndex)1430 bool DvDirModel::removeRows(int row, int count,
1431                             const QModelIndex &parentIndex) {
1432   if (!parentIndex.isValid()) return false;
1433   DvDirModelNode *node = getNode(parentIndex);
1434   if (!node) return false;
1435   emit beginRemoveRows(parentIndex, row, row + count - 1);
1436   node->removeChildren(row, count);
1437   emit endRemoveRows();
1438   return true;
1439 }
1440 
1441 //-----------------------------------------------------------------------------
1442 
getIndexByPath(const TFilePath & path) const1443 QModelIndex DvDirModel::getIndexByPath(const TFilePath &path) const {
1444   return getIndexByNode(m_root->getNodeByPath(path));
1445 }
1446 
1447 //-----------------------------------------------------------------------------
1448 
getIndexByNode(DvDirModelNode * node) const1449 QModelIndex DvDirModel::getIndexByNode(DvDirModelNode *node) const {
1450   if (!node) return QModelIndex();
1451   return createIndex(node->getRow(), 0, node);
1452 }
1453 
1454 //-----------------------------------------------------------------------------
1455 
getCurrentProjectIndex() const1456 QModelIndex DvDirModel::getCurrentProjectIndex() const {
1457   return getIndexByPath(
1458       TProjectManager::instance()->getCurrentProjectPath().getParentDir());
1459 }
1460 
1461 //-----------------------------------------------------------------------------
1462 
instance()1463 DvDirModel *DvDirModel::instance() {
1464   static DvDirModel _instance;
1465   return &_instance;
1466 }
1467 
1468 //-----------------------------------------------------------------------------
1469 
onSceneSwitched()1470 void DvDirModel::onSceneSwitched() {
1471   DvDirModelRootNode *rootNode = dynamic_cast<DvDirModelRootNode *>(m_root);
1472   if (rootNode) {
1473     ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
1474     if (scene) {
1475       if (scene->isUntitled())
1476         rootNode->setSceneLocation(TFilePath());
1477       else
1478         rootNode->setSceneLocation(scene->getScenePath().getParentDir());
1479     }
1480   }
1481 }
1482 
1483 //-----------------------------------------------------------------------------
1484 
onPreferenceChanged(const QString & prefName)1485 void DvDirModel::onPreferenceChanged(const QString &prefName) {
1486   if (prefName != "PathAliasPriority") return;
1487 
1488   Preferences::PathAliasPriority priority =
1489       Preferences::instance()->getPathAliasPriority();
1490   DvDirModelRootNode *rootNode = dynamic_cast<DvDirModelRootNode *>(m_root);
1491   if (rootNode)
1492     rootNode->updateSceneFolderNodeVisibility(priority ==
1493                                               Preferences::ProjectFolderOnly);
1494 }
1495