1 
2 
3 #include "versioncontrol.h"
4 #include "versioncontrolgui.h"
5 
6 #include "toonzqt/gutil.h"
7 #include "toonzqt/dvdialog.h"
8 #include "toonzqt/icongenerator.h"
9 #include "toonz/sceneresources.h"
10 #include "toonz/toonzscene.h"
11 #include "toonz/levelset.h"
12 #include "toonz/txshsimplelevel.h"
13 #include "toonz/txshlevelhandle.h"
14 #include "toonz/txshleveltypes.h"
15 #include "toonz/palettecontroller.h"
16 #include "toonz/tpalettehandle.h"
17 #include "toonz/tscenehandle.h"
18 #include "toonz/tproject.h"
19 #include "tsystem.h"
20 #include "tenv.h"
21 #include "tapp.h"
22 #include "permissionsmanager.h"
23 
24 #include <QFile>
25 #include <QDir>
26 #include <QSettings>
27 #include <QRegExp>
28 
29 namespace {
30 
getFrameRange(const TFilePath & path,unsigned int & from,unsigned int & to)31 bool getFrameRange(const TFilePath &path, unsigned int &from,
32                    unsigned int &to) {
33   // Set frame range
34   if (path.getDots() == "..") {
35     TFilePath dir = path.getParentDir();
36     QDir qDir(QString::fromStdWString(dir.getWideString()));
37     QString levelName =
38         QRegExp::escape(QString::fromStdWString(path.getWideName()));
39     QString levelType = QString::fromStdString(path.getType());
40     QString exp(levelName + ".[0-9]{1,4}." + levelType);
41     QRegExp regExp(exp);
42     QStringList list        = qDir.entryList(QDir::Files);
43     QStringList levelFrames = list.filter(regExp);
44 
45     from = -1;
46     to   = -1;
47     for (int i = 0; i < levelFrames.size(); i++) {
48       TFilePath frame = dir + TFilePath(levelFrames[i].toStdWString());
49       if (frame.isEmpty() || !frame.isAbsolute()) continue;
50       TFileStatus filestatus(frame);
51       if (filestatus.isWritable()) {
52         if (from == -1)
53           from = i;
54         else
55           to = i;
56       } else if (from != -1 && to != -1)
57         break;
58     }
59 
60     if (from != -1 && to != -1)
61       return true;
62     else
63       return false;
64   }
65   return false;
66 }
67 
68 }  // namespace
69 
70 //=============================================================================
71 // VersionControlThread
72 //-----------------------------------------------------------------------------
73 
VersionControlThread(QObject * parent)74 VersionControlThread::VersionControlThread(QObject *parent)
75     : QThread(parent)
76     , m_abort(false)
77     , m_restart(false)
78     , m_getStatus(false)
79     , m_readOutputOnDone(true)
80     , m_process(0) {}
81 
82 //-----------------------------------------------------------------------------
83 
~VersionControlThread()84 VersionControlThread::~VersionControlThread() {
85   m_mutex.lock();
86   m_abort = true;
87   m_condition.wakeOne();
88   m_mutex.unlock();
89 
90   wait();
91 }
92 
93 //-----------------------------------------------------------------------------
94 
run()95 void VersionControlThread::run() {
96   forever {
97     m_mutex.lock();
98     QString workingDir = m_workingDir;
99     QString binary     = m_binary;
100     QStringList args   = m_args;
101 
102     // Add Username and Password (if exists)
103 
104     VersionControl *vc = VersionControl::instance();
105 
106     QString userName = vc->getUserName();
107     QString password = vc->getPassword();
108 
109     if (!userName.isEmpty() || !password.isEmpty()) {
110       args << QString("--username") << userName << QString("--password")
111            << password;
112     }
113 
114     // Add "non-interactive" and "trust-server-cert" global options
115     args << "--non-interactive";
116     args << "--trust-server-cert";
117 
118     QString executablePath                = vc->getExecutablePath();
119     if (!executablePath.isEmpty()) binary = executablePath + "/" + m_binary;
120 
121     m_mutex.unlock();
122 
123     if (m_abort) {
124       if (m_process) {
125         m_process->close();
126         disconnect(m_process, SIGNAL(readyReadStandardOutput()), this,
127                    SLOT(onStandardOutputReady()));
128         delete m_process;
129         m_process = 0;
130       }
131       return;
132     }
133 
134     if (m_process) {
135       m_process->close();
136       disconnect(m_process, SIGNAL(readyReadStandardOutput()), this,
137                  SLOT(onStandardOutputReady()));
138       delete m_process;
139       m_process = 0;
140     }
141 
142     m_process = new QProcess;
143     if (!workingDir.isEmpty() && QFile::exists(workingDir))
144       m_process->setWorkingDirectory(workingDir);
145 
146     QStringList env = QProcess::systemEnvironment();
147     env << "LC_MESSAGES=en_EN";  // Add this environment variables to fix the
148                                  // language to English
149     m_process->setEnvironment(env);
150 
151     if (!m_readOutputOnDone)
152       connect(m_process, SIGNAL(readyReadStandardOutput()), this,
153               SLOT(onStandardOutputReady()));
154 
155     m_process->start(binary, args);
156     if (!m_process->waitForStarted()) {
157       QString err = QString("Unable to launch \"%1\": %2")
158                         .arg(binary, m_process->errorString());
159       emit error(err);
160       return;
161     }
162 
163     m_process->closeWriteChannel();
164 
165     // Wait until users press cancel (every 1 sec I check for abort..
166     while (!m_process->waitForFinished(1000)) {
167       if (m_abort) {
168         m_process->kill();
169         m_process->waitForFinished();
170         return;
171       }
172     }
173 
174     if (m_process->exitStatus() != QProcess::NormalExit) {
175       QString err = QString("\"%1\" crashed.").arg(binary);
176       emit error(err);
177       return;
178     }
179 
180     if (m_process->exitCode()) {
181       emit error(QString::fromUtf8(m_process->readAllStandardError().data()));
182       return;
183     }
184 
185     if (m_getStatus) {
186       emit statusRetrieved(
187           QString::fromUtf8(m_process->readAllStandardOutput().data()));
188       m_getStatus = false;
189     } else
190       emit done(QString::fromUtf8(m_process->readAllStandardOutput().data()));
191 
192     m_mutex.lock();
193     if (!m_restart) m_condition.wait(&m_mutex);
194     m_restart = false;
195     m_mutex.unlock();
196   }
197 }
198 
199 //-----------------------------------------------------------------------------
200 
onStandardOutputReady()201 void VersionControlThread::onStandardOutputReady() {
202   QString str = QString::fromUtf8(m_process->readAllStandardOutput().data());
203   if (str.isEmpty()) return;
204   emit outputRetrieved(str);
205 }
206 
207 //-----------------------------------------------------------------------------
208 
executeCommand(const QString & workingDir,const QString & binary,const QStringList & args,bool readOutputOnDone)209 void VersionControlThread::executeCommand(const QString &workingDir,
210                                           const QString &binary,
211                                           const QStringList &args,
212                                           bool readOutputOnDone) {
213   QMutexLocker locker(&m_mutex);
214 
215   m_readOutputOnDone = readOutputOnDone;
216   m_workingDir       = workingDir;
217   m_binary           = binary;
218   m_args             = args;
219 
220   if (m_binary.isEmpty()) return;
221 
222   if (!isRunning()) {
223     start(QThread::NormalPriority);
224   } else {
225     m_restart = true;
226     m_condition.wakeOne();
227   }
228 }
229 
230 //-----------------------------------------------------------------------------
231 
getSVNStatus(const QString & path,bool showUpdates,bool nonRecursive,bool depthInfinity)232 void VersionControlThread::getSVNStatus(const QString &path, bool showUpdates,
233                                         bool nonRecursive, bool depthInfinity) {
234   QMutexLocker locker(&m_mutex);
235 
236   m_workingDir = path;
237   m_binary     = QString("svn");
238   QStringList args;
239   args << "status";
240   if (showUpdates)
241     args << "-vu";
242   else
243     args << "-v";
244   if (nonRecursive) args << "--non-recursive";
245   if (depthInfinity)
246     args << "--depth"
247          << "infinity";
248 
249   args << "--xml";
250   m_args = args;
251 
252   m_getStatus        = true;
253   m_readOutputOnDone = true;
254 
255   if (!isRunning()) {
256     start(QThread::NormalPriority);
257   } else {
258     m_restart = true;
259     m_condition.wakeOne();
260   }
261 }
262 
263 //-----------------------------------------------------------------------------
264 
getSVNStatus(const QString & path,const QStringList & files,bool showUpdates,bool nonRecursive,bool depthInfinity)265 void VersionControlThread::getSVNStatus(const QString &path,
266                                         const QStringList &files,
267                                         bool showUpdates, bool nonRecursive,
268                                         bool depthInfinity) {
269   QMutexLocker locker(&m_mutex);
270   m_workingDir = path;
271   m_binary     = QString("svn");
272   QStringList args;
273   args << "status";
274   if (showUpdates)
275     args << "-vu";
276   else
277     args << "-v";
278 
279   int filesCount = files.size();
280   for (int i = 0; i < filesCount; i++) {
281     QString f = files.at(i);
282     if (!f.isEmpty()) args << f;
283   }
284   if (nonRecursive) args << "--non-recursive";
285   if (depthInfinity)
286     args << "--depth"
287          << "infinity";
288 
289   args << "--xml";
290   m_args = args;
291 
292   m_getStatus        = true;
293   m_readOutputOnDone = true;
294 
295   if (!isRunning()) {
296     start(QThread::NormalPriority);
297   } else {
298     m_restart = true;
299     m_condition.wakeOne();
300   }
301 }
302 
303 //=============================================================================
304 // VersionControlManager
305 //-----------------------------------------------------------------------------
306 
VersionControlManager()307 VersionControlManager::VersionControlManager()
308     : m_scene(0), m_levelSet(0), m_isRunning(false), m_deleteLater(false) {
309   connect(&m_thread, SIGNAL(error(const QString &)), this,
310           SLOT(onError(const QString &)));
311 }
312 
313 //-----------------------------------------------------------------------------
314 
instance()315 VersionControlManager *VersionControlManager::instance() {
316   static VersionControlManager _instance;
317   return &_instance;
318 }
319 
320 //-----------------------------------------------------------------------------
setVersionControlCredentials(QString currentPath)321 static void setVersionControlCredentials(QString currentPath) {
322   VersionControl *vc                = VersionControl::instance();
323   QList<SVNRepository> repositories = vc->getRepositories();
324   int repoCount                     = repositories.size();
325   for (int i = 0; i < repoCount; i++) {
326     SVNRepository r = repositories.at(i);
327     if (!currentPath.startsWith(r.m_localPath)) continue;
328     vc->setUserName(r.m_username);
329     vc->setPassword(r.m_password);
330     return;
331   }
332 }
333 
setFrameRange(TLevelSet * levelSet,bool deleteLater)334 void VersionControlManager::setFrameRange(TLevelSet *levelSet,
335                                           bool deleteLater) {
336   if (levelSet->getLevelCount() == 0) return;
337 
338   if (!m_isRunning) {
339     m_scene       = 0;  // Just to be sure
340     m_levelSet    = levelSet;
341     m_deleteLater = deleteLater;
342 
343     QStringList args;
344     args << "proplist";
345 
346     bool checkVersionControl = false;
347     bool filesAddedToArgs    = false;
348 
349     for (int i = 0; i < levelSet->getLevelCount(); i++) {
350       TXshLevel *level    = levelSet->getLevel(i);
351       TXshSimpleLevel *sl = level->getSimpleLevel();
352 
353       if (sl && !checkVersionControl) {
354         TFilePath parentDir =
355             sl->getScene()->decodeFilePath(sl->getPath().getParentDir());
356         if (VersionControl::instance()->isFolderUnderVersionControl(
357                 toQString(parentDir))) {
358           checkVersionControl = true;
359           setVersionControlCredentials(toQString(parentDir));
360         }
361       }
362 
363       if (sl && sl->isReadOnly()) {
364         if (!m_scene) m_scene = sl->getScene();
365 
366         if (sl->getType() == PLI_XSHLEVEL || sl->getType() == TZP_XSHLEVEL) {
367           filesAddedToArgs = true;
368           args << toQString(m_scene->decodeFilePath(level->getPath()));
369         } else if (sl->getType() == OVL_XSHLEVEL) {
370           unsigned int from;
371           unsigned int to;
372           bool ret = getFrameRange(m_scene->decodeFilePath(level->getPath()),
373                                    from, to);
374           if (ret)
375             sl->setEditableRange(
376                 from, to,
377                 VersionControl::instance()->getUserName().toStdWString());
378         }
379       }
380     }
381 
382     if (!checkVersionControl || !filesAddedToArgs) {
383       if (m_deleteLater) {
384         m_deleteLater = false;
385         for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
386           TXshLevel *l = m_levelSet->getLevel(i);
387           m_levelSet->removeLevel(l);
388         }
389         delete m_levelSet;
390         m_levelSet = 0;
391       }
392       m_scene = 0;
393       return;
394     }
395 
396     args << "--xml";
397     args << "-v";
398 
399     TFilePath path     = m_scene->getScenePath();
400     path               = m_scene->decodeFilePath(path);
401     QString workingDir = toQString(path.getParentDir());
402 
403     m_thread.disconnect(SIGNAL(done(const QString &)));
404     connect(&m_thread, SIGNAL(done(const QString &)), this,
405             SLOT(onFrameRangeDone(const QString)));
406     m_thread.executeCommand(workingDir, "svn", args, true);
407     m_isRunning = true;
408   }
409 }
410 
411 //-----------------------------------------------------------------------------
412 
onFrameRangeDone(const QString & text)413 void VersionControlManager::onFrameRangeDone(const QString &text) {
414   m_isRunning = false;
415 
416   SVNPartialLockReader lockReader(text);
417   QList<SVNPartialLock> list = lockReader.getPartialLock();
418   if (list.isEmpty()) {
419     if (m_deleteLater) {
420       m_deleteLater = false;
421 
422       for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
423         TXshLevel *l        = m_levelSet->getLevel(i);
424         TXshSimpleLevel *sl = l->getSimpleLevel();
425         if (sl) sl->clearEditableRange();
426         m_levelSet->removeLevel(l);
427       }
428 
429       delete m_levelSet;
430       m_levelSet = 0;
431     }
432     m_scene = 0;
433     return;
434   }
435 
436   int listSize = list.count();
437   for (int l = 0; l < listSize; l++) {
438     SVNPartialLock pl = list.at(l);
439 
440     QList<SVNPartialLockInfo> lockInfos = pl.m_partialLockList;
441 
442     TFilePath currentPath = TFilePath(pl.m_fileName.toStdWString());
443 
444     TXshSimpleLevel *level;
445     for (int i = 0; i < m_levelSet->getLevelCount(); i++) {
446       TXshLevel *l        = m_levelSet->getLevel(i);
447       TFilePath levelPath = m_scene->decodeFilePath(l->getPath());
448       if (levelPath == currentPath) {
449         level = l->getSimpleLevel();
450         break;
451       }
452     }
453 
454     setVersionControlCredentials(toQString(currentPath));
455 
456     QString username = VersionControl::instance()->getUserName();
457     QString hostName = TSystem::getHostName();
458 
459     int count = lockInfos.size();
460     for (int i = 0; i < count; i++) {
461       SVNPartialLockInfo info = lockInfos.at(i);
462       if (info.m_userName == username && info.m_hostName == hostName) {
463         level->setEditableRange(info.m_from - 1, info.m_to - 1,
464                                 info.m_userName.toStdWString());
465         invalidateIcons(level, level->getEditableRange());
466         TApp *app = TApp::instance();
467         if (app->getCurrentLevel()->getLevel() == level) {
468           app->getPaletteController()->getCurrentLevelPalette()->setPalette(
469               level->getPalette());
470           app->getCurrentLevel()->notifyLevelChange();
471         }
472         break;
473       }
474     }
475   }
476 
477   if (m_deleteLater) {
478     m_deleteLater = false;
479 
480     for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
481       TXshLevel *l = m_levelSet->getLevel(i);
482       m_levelSet->removeLevel(l);
483     }
484 
485     delete m_levelSet;
486     m_levelSet = 0;
487   }
488   m_scene = 0;
489 }
490 
491 //-----------------------------------------------------------------------------
492 
onError(const QString & text)493 void VersionControlManager::onError(const QString &text) {
494   m_isRunning = false;
495   if (m_deleteLater) {
496     m_deleteLater = false;
497     for (int i = m_levelSet->getLevelCount() - 1; i >= 0; i--) {
498       TXshLevel *l = m_levelSet->getLevel(i);
499       m_levelSet->removeLevel(l);
500     }
501     delete m_levelSet;
502     m_levelSet = 0;
503   }
504   m_scene = 0;
505   DVGui::warning(text);
506 }
507 
508 //=============================================================================
509 // VersionControl
510 //-----------------------------------------------------------------------------
511 
VersionControl()512 VersionControl::VersionControl()
513     : m_userName(), m_password(), m_executablePath() {}
514 
515 //-----------------------------------------------------------------------------
516 
instance()517 VersionControl *VersionControl::instance() {
518   static VersionControl _instance;
519   return &_instance;
520 }
521 
522 //-----------------------------------------------------------------------------
523 
init()524 void VersionControl::init() {
525   QString configFileName = QString::fromStdWString(
526       TFilePath(TEnv::getConfigDir() + "versioncontrol.xml").getWideString());
527 
528   if (QFile::exists(configFileName)) {
529     QFile file(configFileName);
530     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
531       QString content = QString(file.readAll());
532       SVNConfigReader reader(content);
533       m_repositories   = reader.getRepositories();
534       m_executablePath = reader.getSVNPath();
535     }
536   }
537 }
538 
539 //-----------------------------------------------------------------------------
540 
testSetup()541 bool VersionControl::testSetup() {
542   QString configFileName = QString::fromStdWString(
543       TFilePath(TEnv::getConfigDir() + "versioncontrol.xml").getWideString());
544 
545   int repositoriesCount = 0;
546   QString path;
547   if (QFile::exists(configFileName)) {
548     QFile file(configFileName);
549     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
550       QString content = QString(file.readAll());
551       SVNConfigReader reader(content);
552       repositoriesCount = reader.getRepositories().size();
553       path              = reader.getSVNPath();
554     }
555   }
556 
557   // Test configuration file
558   if (repositoriesCount == 0) {
559     DVGui::error(
560         tr("The version control configuration file is empty or wrongly "
561            "defined.\nPlease refer to the user guide for details."));
562     return false;
563   }
564 
565 // Test if svnPath is correct (adding another "/" before binary name doesn't
566 // create any issue both on Windows and on Mac
567 #ifdef MACOSX
568   if (!path.isEmpty() && !QFile::exists(path + "/svn"))
569 #else
570   if (!path.isEmpty() && !QFile::exists(path + "/svn.exe"))
571 #endif
572   {
573     DVGui::error(tr(
574         "The version control client application specified on the configuration "
575         "file cannot be found.\nPlease refer to the user guide for details."));
576     return false;
577   }
578 
579   // Try to run svn executable, if the path is not specified in the config file,
580   // to test if exist
581   // In the meantime check for svn version
582   if (path.isEmpty()) {
583     QProcess p;
584 
585     QStringList env = QProcess::systemEnvironment();
586     env << "LC_MESSAGES=en_EN";  // Add this environment variables to fix the
587                                  // language to English
588     p.setEnvironment(env);
589 
590     p.start("svn", QStringList("--version"));
591 
592     if (!p.waitForStarted()) {
593       DVGui::error(
594           tr("The version control client application is not installed on your "
595              "computer.\nSubversion 1.5 or later is required.\nPlease refer to "
596              "the user guide for details."));
597       return false;
598     }
599 
600     if (!p.waitForFinished()) {
601       DVGui::error(
602           tr("The version control client application is not installed on your "
603              "computer.\nSubversion 1.5 or later is required.\nPlease refer to "
604              "the user guide for details."));
605       return false;
606     }
607 
608     QString output(p.readAllStandardOutput());
609 
610     QStringList list = output.split("\n");
611 
612     if (!list.isEmpty()) {
613       QString firstLine = list.first();
614       firstLine         = firstLine.remove("svn, version ");
615 
616       double version = firstLine.left(3).toDouble();
617       if (version <= 1.5) {
618         DVGui::warning(
619             tr("The version control client application installed on your "
620                "computer needs to be updated, otherwise some features may not "
621                "be available.\nSubversion 1.5 or later is required.\nPlease "
622                "refer to the user guide for details."));
623         return true;
624       }
625     }
626   }
627   return true;
628 }
629 
630 //-----------------------------------------------------------------------------
isFolderUnderVersionControl(const QString & folderPath)631 bool VersionControl::isFolderUnderVersionControl(const QString &folderPath) {
632   QDir dir(folderPath);
633   if (dir.entryList(QDir::AllDirs | QDir::Hidden).contains(".svn")) return true;
634   // For SVN 1.7 and greater, check parent directories to see if it's under
635   // version control
636   while (dir.cdUp()) {
637     if (dir.entryList(QDir::AllDirs | QDir::Hidden).contains(".svn"))
638       return true;
639   }
640 
641   return false;
642 }
643 
644 //-----------------------------------------------------------------------------
645 
commit(QWidget * parent,const QString & workingDir,const QStringList & filesToCommit,bool folderOnly,int sceneIconAdded)646 void VersionControl::commit(QWidget *parent, const QString &workingDir,
647                             const QStringList &filesToCommit, bool folderOnly,
648                             int sceneIconAdded) {
649   SVNCommitDialog *dialog = new SVNCommitDialog(
650       parent, workingDir, filesToCommit, folderOnly, sceneIconAdded);
651   connect(dialog, SIGNAL(done(const QStringList &)), this,
652           SIGNAL(commandDone(const QStringList &)));
653   dialog->show();
654   dialog->raise();
655 }
656 
657 //-----------------------------------------------------------------------------
658 
revert(QWidget * parent,const QString & workingDir,const QStringList & files,bool folderOnly,int sceneIconAdded)659 void VersionControl::revert(QWidget *parent, const QString &workingDir,
660                             const QStringList &files, bool folderOnly,
661                             int sceneIconAdded) {
662   SVNRevertDialog *dialog = new SVNRevertDialog(parent, workingDir, files,
663                                                 folderOnly, sceneIconAdded);
664   connect(dialog, SIGNAL(done(const QStringList &)), this,
665           SIGNAL(commandDone(const QStringList &)));
666   dialog->show();
667   dialog->raise();
668 }
669 
670 //-----------------------------------------------------------------------------
671 
update(QWidget * parent,const QString & workingDir,const QStringList & filesToUpdate,int sceneIconsCounts,bool folderOnly,bool updateToRevision,bool nonRecursive)672 void VersionControl::update(QWidget *parent, const QString &workingDir,
673                             const QStringList &filesToUpdate,
674                             int sceneIconsCounts, bool folderOnly,
675                             bool updateToRevision, bool nonRecursive) {
676   SVNUpdateDialog *dialog =
677       new SVNUpdateDialog(parent, workingDir, filesToUpdate, sceneIconsCounts,
678                           folderOnly, updateToRevision, nonRecursive);
679   connect(dialog, SIGNAL(done(const QStringList &)), this,
680           SIGNAL(commandDone(const QStringList &)));
681   dialog->show();
682   dialog->raise();
683 }
684 
685 //-----------------------------------------------------------------------------
686 
updateAndLock(QWidget * parent,const QString & workingDir,const QStringList & files,int workingRevision,int sceneIconAdded)687 void VersionControl::updateAndLock(QWidget *parent, const QString &workingDir,
688                                    const QStringList &files,
689                                    int workingRevision, int sceneIconAdded) {
690   SVNUpdateAndLockDialog *dialog = new SVNUpdateAndLockDialog(
691       parent, workingDir, files, workingRevision, sceneIconAdded);
692   connect(dialog, SIGNAL(done(const QStringList &)), this,
693           SIGNAL(commandDone(const QStringList &)));
694   dialog->show();
695   dialog->raise();
696 }
697 
698 //-----------------------------------------------------------------------------
699 
lock(QWidget * parent,const QString & workingDir,const QStringList & filesToLock,int sceneIconAdded)700 void VersionControl::lock(QWidget *parent, const QString &workingDir,
701                           const QStringList &filesToLock, int sceneIconAdded) {
702   SVNLockDialog *dialog =
703       new SVNLockDialog(parent, workingDir, filesToLock, true, sceneIconAdded);
704   connect(dialog, SIGNAL(done(const QStringList &)), this,
705           SIGNAL(commandDone(const QStringList &)));
706   dialog->show();
707   dialog->raise();
708 }
709 
710 //-----------------------------------------------------------------------------
711 
unlock(QWidget * parent,const QString & workingDir,const QStringList & filesToUnlock,int sceneIconAdded)712 void VersionControl::unlock(QWidget *parent, const QString &workingDir,
713                             const QStringList &filesToUnlock,
714                             int sceneIconAdded) {
715   SVNLockDialog *dialog = new SVNLockDialog(parent, workingDir, filesToUnlock,
716                                             false, sceneIconAdded);
717   connect(dialog, SIGNAL(done(const QStringList &)), this,
718           SIGNAL(commandDone(const QStringList &)));
719   dialog->show();
720   dialog->raise();
721 }
722 
723 //-----------------------------------------------------------------------------
724 
lockFrameRange(QWidget * parent,const QString & workingDir,const QString & file,int frameCount)725 void VersionControl::lockFrameRange(QWidget *parent, const QString &workingDir,
726                                     const QString &file, int frameCount) {
727   SVNLockFrameRangeDialog *dialog =
728       new SVNLockFrameRangeDialog(parent, workingDir, file, frameCount);
729   connect(dialog, SIGNAL(done(const QStringList &)), this,
730           SIGNAL(commandDone(const QStringList &)));
731   dialog->show();
732   dialog->raise();
733 }
734 
735 //-----------------------------------------------------------------------------
736 
lockFrameRange(QWidget * parent,const QString & workingDir,const QStringList & files)737 void VersionControl::lockFrameRange(QWidget *parent, const QString &workingDir,
738                                     const QStringList &files) {
739   SVNLockMultiFrameRangeDialog *dialog =
740       new SVNLockMultiFrameRangeDialog(parent, workingDir, files);
741   connect(dialog, SIGNAL(done(const QStringList &)), this,
742           SIGNAL(commandDone(const QStringList &)));
743   dialog->show();
744   dialog->raise();
745 }
746 
747 //-----------------------------------------------------------------------------
748 
unlockFrameRange(QWidget * parent,const QString & workingDir,const QString & file)749 void VersionControl::unlockFrameRange(QWidget *parent,
750                                       const QString &workingDir,
751                                       const QString &file) {
752   SVNUnlockFrameRangeDialog *dialog =
753       new SVNUnlockFrameRangeDialog(parent, workingDir, file);
754   connect(dialog, SIGNAL(done(const QStringList &)), this,
755           SIGNAL(commandDone(const QStringList &)));
756   dialog->show();
757   dialog->raise();
758 }
759 
760 //-----------------------------------------------------------------------------
761 
unlockFrameRange(QWidget * parent,const QString & workingDir,const QStringList & files)762 void VersionControl::unlockFrameRange(QWidget *parent,
763                                       const QString &workingDir,
764                                       const QStringList &files) {
765   SVNUnlockMultiFrameRangeDialog *dialog =
766       new SVNUnlockMultiFrameRangeDialog(parent, workingDir, files);
767   connect(dialog, SIGNAL(done(const QStringList &)), this,
768           SIGNAL(commandDone(const QStringList &)));
769   dialog->show();
770   dialog->raise();
771 }
772 
773 //-----------------------------------------------------------------------------
774 
showFrameRangeLockInfo(QWidget * parent,const QString & workingDir,const QString & file)775 void VersionControl::showFrameRangeLockInfo(QWidget *parent,
776                                             const QString &workingDir,
777                                             const QString &file) {
778   SVNFrameRangeLockInfoDialog *dialog =
779       new SVNFrameRangeLockInfoDialog(parent, workingDir, file);
780   dialog->show();
781   dialog->raise();
782 }
783 
784 //-----------------------------------------------------------------------------
785 
showFrameRangeLockInfo(QWidget * parent,const QString & workingDir,const QStringList & files)786 void VersionControl::showFrameRangeLockInfo(QWidget *parent,
787                                             const QString &workingDir,
788                                             const QStringList &files) {
789   SVNMultiFrameRangeLockInfoDialog *dialog =
790       new SVNMultiFrameRangeLockInfoDialog(parent, workingDir, files);
791   dialog->show();
792   dialog->raise();
793 }
794 
795 //-----------------------------------------------------------------------------
796 
commitFrameRange(QWidget * parent,const QString & workingDir,const QString & file)797 void VersionControl::commitFrameRange(QWidget *parent,
798                                       const QString &workingDir,
799                                       const QString &file) {
800   SVNCommitFrameRangeDialog *dialog =
801       new SVNCommitFrameRangeDialog(parent, workingDir, file);
802   connect(dialog, SIGNAL(done(const QStringList &)), this,
803           SIGNAL(commandDone(const QStringList &)));
804   dialog->show();
805   dialog->raise();
806 }
807 
808 //-----------------------------------------------------------------------------
809 
revertFrameRange(QWidget * parent,const QString & workingDir,const QString & file,const QString & tempFileName)810 void VersionControl::revertFrameRange(QWidget *parent,
811                                       const QString &workingDir,
812                                       const QString &file,
813                                       const QString &tempFileName) {
814   SVNRevertFrameRangeDialog *dialog =
815       new SVNRevertFrameRangeDialog(parent, workingDir, file, tempFileName);
816   connect(dialog, SIGNAL(done(const QStringList &)), this,
817           SIGNAL(commandDone(const QStringList &)));
818   dialog->show();
819   dialog->raise();
820 }
821 
822 //-----------------------------------------------------------------------------
823 
deleteFiles(QWidget * parent,const QString & workingDir,const QStringList & filesToDelete,int sceneIconAdded)824 void VersionControl::deleteFiles(QWidget *parent, const QString &workingDir,
825                                  const QStringList &filesToDelete,
826                                  int sceneIconAdded) {
827   SVNDeleteDialog *dialog = new SVNDeleteDialog(
828       parent, workingDir, filesToDelete, false, sceneIconAdded);
829   connect(dialog, SIGNAL(done(const QStringList &)), this,
830           SIGNAL(commandDone(const QStringList &)));
831   dialog->show();
832   dialog->raise();
833 }
834 
835 //-----------------------------------------------------------------------------
836 
deleteFolder(QWidget * parent,const QString & workingDir,const QString & folderName)837 void VersionControl::deleteFolder(QWidget *parent, const QString &workingDir,
838                                   const QString &folderName) {
839   SVNDeleteDialog *dialog =
840       new SVNDeleteDialog(parent, workingDir, QStringList(folderName), true, 0);
841   connect(dialog, SIGNAL(done(const QStringList &)), this,
842           SIGNAL(commandDone(const QStringList &)));
843   dialog->show();
844   dialog->raise();
845 }
846 
847 //-----------------------------------------------------------------------------
848 
cleanupFolder(QWidget * parent,const QString & workingDir)849 void VersionControl::cleanupFolder(QWidget *parent, const QString &workingDir) {
850   SVNCleanupDialog *dialog = new SVNCleanupDialog(parent, workingDir);
851   dialog->show();
852   dialog->raise();
853 }
854 
855 //-----------------------------------------------------------------------------
856 
purgeFolder(QWidget * parent,const QString & workingDir)857 void VersionControl::purgeFolder(QWidget *parent, const QString &workingDir) {
858   SVNPurgeDialog *dialog = new SVNPurgeDialog(parent, workingDir);
859   dialog->show();
860   dialog->raise();
861 }
862 
863 //-----------------------------------------------------------------------------
864 
getSceneContents(const QString & wokingDir,const QString & sceneFileName)865 QStringList VersionControl::getSceneContents(const QString &wokingDir,
866                                              const QString &sceneFileName) {
867   QStringList sceneContents;
868 
869   TFilePath scenePath =
870       TFilePath(wokingDir.toStdWString()) + sceneFileName.toStdWString();
871   if (!TFileStatus(scenePath).doesExist()) return sceneContents;
872 
873   ToonzScene scene;
874   try {
875     scene.load(scenePath);
876   } catch (...) {
877   }
878   std::vector<TXshLevel *> levels;
879   scene.getLevelSet()->listLevels(levels);
880   std::vector<TXshLevel *>::iterator it;
881   for (it = levels.begin(); it != levels.end(); ++it) {
882     TFilePath levelPath = scene.decodeFilePath((*it)->getPath());
883 
884     if (levelPath.getDots() == "..") {
885       TFilePath dir = levelPath.getParentDir();
886       QDir qDir(QString::fromStdWString(dir.getWideString()));
887       QString levelName =
888           QRegExp::escape(QString::fromStdWString(levelPath.getWideName()));
889       QString levelType = QString::fromStdString(levelPath.getType());
890       QString exp(levelName + ".[0-9]{1,4}." + levelType);
891       QRegExp regExp(exp);
892       QStringList list = qDir.entryList(QDir::Files);
893       list             = list.filter(regExp);
894       for (int i = 0; i < list.size(); i++) {
895         QString fileName = list.at(i);
896         sceneContents.append(toQString(dir + fileName.toStdWString()));
897       }
898     } else
899       sceneContents.append(toQString(levelPath));
900 
901     TFilePathSet fpset;
902     TXshSimpleLevel::getFiles(levelPath, fpset);
903 
904     TFilePathSet::iterator it;
905     for (it = fpset.begin(); it != fpset.end(); ++it)
906       sceneContents.append(toQString(scene.decodeFilePath((*it))));
907   }
908 
909   return sceneContents;
910 }
911 
912 //-----------------------------------------------------------------------------
913 
getCurrentSceneContents() const914 QStringList VersionControl::getCurrentSceneContents() const {
915   QStringList contents;
916   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
917   if (!scene) return QStringList();
918 
919   std::vector<TXshLevel *> levels;
920   scene->getLevelSet()->listLevels(levels);
921   std::vector<TXshLevel *>::iterator it;
922   for (it = levels.begin(); it != levels.end(); ++it) {
923     TFilePath levelPath = scene->decodeFilePath((*it)->getPath());
924     contents.append(toQString(levelPath));
925 
926     TFilePathSet fpset;
927     TXshSimpleLevel::getFiles(levelPath, fpset);
928 
929     TFilePathSet::iterator it;
930     for (it = fpset.begin(); it != fpset.end(); ++it)
931       contents.append(toQString(scene->decodeFilePath((*it))));
932   }
933   return contents;
934 }
935