1 
2 
3 #include "svnupdatedialog.h"
4 
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "versioncontrolwidget.h"
8 
9 // TnzQt includes
10 #include "toonzqt/gutil.h"
11 
12 // TnzLib includes
13 #include "toonz/txshsimplelevel.h"
14 #include "toonz/toonzscene.h"
15 
16 // Qt includes
17 #include <QWidget>
18 #include <QPushButton>
19 #include <QScrollBar>
20 #include <QBoxLayout>
21 #include <QLabel>
22 #include <QMovie>
23 #include <QTextEdit>
24 #include <QCheckBox>
25 #include <QRegExp>
26 #include <QDir>
27 #include <QMainWindow>
28 
29 //=============================================================================
30 // SVNUpdateDialog
31 //-----------------------------------------------------------------------------
32 
SVNUpdateDialog(QWidget * parent,const QString & workingDir,const QStringList & files,int sceneIconsCount,bool isFolderOnly,bool updateToRevision,bool nonRecursive)33 SVNUpdateDialog::SVNUpdateDialog(QWidget *parent, const QString &workingDir,
34                                  const QStringList &files, int sceneIconsCount,
35                                  bool isFolderOnly, bool updateToRevision,
36                                  bool nonRecursive)
37     : Dialog(TApp::instance()->getMainWindow(), true, false)
38     , m_updateSceneContentsCheckBox(0)
39     , m_workingDir(workingDir)
40     , m_files(files)
41     , m_updateToRevision(updateToRevision)
42     , m_nonRecursive(nonRecursive)
43     , m_sceneIconsCount(sceneIconsCount)
44     , m_someSceneIsMissing(false) {
45   setModal(false);
46   setMinimumSize(300, 180);
47   setAttribute(Qt::WA_DeleteOnClose, true);
48   setWindowTitle(tr("Version Control: Update"));
49 
50   QWidget *container = new QWidget;
51 
52   QVBoxLayout *mainLayout = new QVBoxLayout;
53   mainLayout->setAlignment(Qt::AlignHCenter);
54   mainLayout->setMargin(0);
55 
56   QHBoxLayout *hLayout = new QHBoxLayout;
57 
58   m_waitingLabel      = new QLabel;
59   QMovie *waitingMove = new QMovie(":Resources/waiting.gif");
60   waitingMove->setParent(this);
61 
62   m_waitingLabel->setMovie(waitingMove);
63   waitingMove->setCacheMode(QMovie::CacheAll);
64   waitingMove->start();
65 
66   m_textLabel = new QLabel(tr("Getting repository status..."));
67 
68   hLayout->addStretch();
69   hLayout->addWidget(m_waitingLabel);
70   hLayout->addWidget(m_textLabel);
71   hLayout->addStretch();
72 
73   mainLayout->addLayout(hLayout);
74 
75   m_output = new QTextEdit;
76   m_output->setTextInteractionFlags(Qt::NoTextInteraction);
77   m_output->setReadOnly(true);
78   m_output->hide();
79 
80   mainLayout->addWidget(m_output);
81 
82   m_conflictWidget = new ConflictWidget;
83   m_conflictWidget->hide();
84 
85   mainLayout->addWidget(m_conflictWidget);
86 
87   m_dateChooserWidget = new DateChooserWidget;
88   m_dateChooserWidget->hide();
89 
90   mainLayout->addWidget(m_dateChooserWidget);
91 
92   if (!isFolderOnly) {
93     QHBoxLayout *checkBoxLayout = new QHBoxLayout;
94     checkBoxLayout->setMargin(0);
95     m_updateSceneContentsCheckBox = new QCheckBox(this);
96     m_updateSceneContentsCheckBox->setChecked(false);
97     m_updateSceneContentsCheckBox->setText(tr("Get Scene Contents"));
98     m_updateSceneContentsCheckBox->hide();
99     connect(m_updateSceneContentsCheckBox, SIGNAL(toggled(bool)), this,
100             SLOT(onUpdateSceneContentsToggled(bool)));
101 
102     checkBoxLayout->addStretch();
103     checkBoxLayout->addWidget(m_updateSceneContentsCheckBox);
104     checkBoxLayout->addStretch();
105 
106     mainLayout->addSpacing(10);
107     mainLayout->addLayout(checkBoxLayout);
108   }
109 
110   container->setLayout(mainLayout);
111 
112   beginHLayout();
113   addWidget(container, false);
114   endHLayout();
115 
116   m_updateButton = new QPushButton(tr("Update"));
117   m_updateButton->hide();
118   if (m_updateToRevision)
119     connect(m_updateButton, SIGNAL(clicked()), this,
120             SLOT(onUpdateToRevisionButtonClicked()));
121   else
122     connect(m_updateButton, SIGNAL(clicked()), this,
123             SLOT(onUpdateButtonClicked()));
124 
125   m_closeButton = new QPushButton(tr("Close"));
126   m_closeButton->setEnabled(false);
127   connect(m_closeButton, SIGNAL(clicked()), this, SLOT(close()));
128 
129   m_cancelButton = new QPushButton(tr("Cancel"));
130   connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
131 
132   addButtonBarWidget(m_updateButton, m_closeButton, m_cancelButton);
133 
134   // 0. Connect for svn errors (that may occurs everythings)
135   connect(&m_thread, SIGNAL(error(const QString &)), this,
136           SLOT(onError(const QString &)));
137 
138   // 1. Getting status
139   connect(&m_thread, SIGNAL(statusRetrieved(const QString &)), this,
140           SLOT(onStatusRetrieved(const QString &)));
141   m_thread.getSVNStatus(m_workingDir, m_files, true, false, true);
142 }
143 
144 //-----------------------------------------------------------------------------
145 
onStatusRetrieved(const QString & xmlResponse)146 void SVNUpdateDialog::onStatusRetrieved(const QString &xmlResponse) {
147   m_waitingLabel->hide();
148 
149   SVNStatusReader sr(xmlResponse);
150   m_status = sr.getStatus();
151 
152   checkFiles();
153 
154   // Check if a scene files is found
155   int fileSize = m_filesToUpdate.size();
156   for (int i = 0; i < fileSize; i++) {
157     if (m_filesToUpdate.at(i).endsWith(".tnz")) {
158       if (fileSize == 1)
159         m_textLabel->setText(
160             tr("%1 items to update.")
161                 .arg(m_filesToUpdate.size() + m_sceneResources.size()));
162       else
163         m_textLabel->setText(tr("%1 items to update.")
164                                  .arg(m_filesToUpdate.size() +
165                                       m_sceneResources.size() -
166                                       m_sceneIconsCount));
167       if (m_updateSceneContentsCheckBox && !m_someSceneIsMissing)
168         m_updateSceneContentsCheckBox->show();
169       m_updateButton->show();
170       m_closeButton->hide();
171       return;
172     }
173   }
174 
175   if (m_updateToRevision) {
176     if (m_filesWithConflict.count() > 0) {
177       m_textLabel->setText(
178           tr("Some items are currently modified in your working copy.\nPlease "
179              "commit or revert changes first."));
180       m_textLabel->show();
181       switchToCloseButton();
182       return;
183     }
184 
185     m_textLabel->setText(tr("Update to:"));
186     m_textLabel->show();
187     m_dateChooserWidget->show();
188     m_updateButton->show();
189     m_closeButton->hide();
190 
191     adjustSize();
192   } else {
193     // Resolve first files with conflict
194     if (m_filesWithConflict.count() > 0) {
195       m_textLabel->setText(tr("Some conflict found. Select.."));
196       m_textLabel->show();
197       m_updateButton->setEnabled(false);
198       m_updateButton->show();
199       m_closeButton->hide();
200 
201       QStringList fileswithConflict;
202       int fileswithConflictCount = m_filesWithConflict.size();
203       for (int i = 0; i < fileswithConflictCount; i++)
204         fileswithConflict.append(m_filesWithConflict.at(i));
205       m_conflictWidget->setFiles(fileswithConflict);
206       m_conflictWidget->show();
207       adjustSize();
208       connect(m_conflictWidget, SIGNAL(allConflictSetted()), this,
209               SLOT(onConflictSetted()));
210     } else
211       updateFiles();
212   }
213 }
214 
215 //-----------------------------------------------------------------------------
216 
checkFiles()217 void SVNUpdateDialog::checkFiles() {
218   int statusCount = m_status.size();
219   for (int i = 0; i < statusCount; i++) {
220     SVNStatus s = m_status.at(i);
221     if (s.m_path == "." || s.m_path == "..") continue;
222     if ((m_updateToRevision && s.m_item == "modified") ||
223         (s.m_item == "modified" && s.m_repoStatus == "modified"))
224       m_filesWithConflict.prepend(s.m_path);
225     else if (s.m_item == "none" || s.m_item == "missing" ||
226              s.m_repoStatus == "modified" || s.m_repoStatus == "modified") {
227       if (s.m_path.endsWith(".tnz") &&
228           (s.m_item == "missing" ||
229            (s.m_item == "none" && s.m_repoStatus == "added"))) {
230         TFilePath scenePath =
231             TFilePath(m_workingDir.toStdWString()) + s.m_path.toStdWString();
232         TFilePath iconPath = ToonzScene::getIconPath(scenePath);
233         QDir dir(m_workingDir);
234 #ifdef MACOSX
235         m_filesToUpdate.append(dir.relativeFilePath(toQString(iconPath)));
236 #else
237         m_filesToUpdate.append(
238             dir.relativeFilePath(toQString(iconPath)).replace("/", "\\"));
239 #endif
240         m_sceneIconsCount++;
241         m_someSceneIsMissing = true;
242       }
243       if (m_files.count() == 1 || m_files.contains(s.m_path))
244         m_filesToUpdate.prepend(s.m_path);
245     }
246   }
247 }
248 
249 //-----------------------------------------------------------------------------
250 
updateFiles()251 void SVNUpdateDialog::updateFiles() {
252   if (m_filesToUpdate.count() == 0) {
253     QString msg = QString(tr("No items to update."));
254     m_textLabel->setText(msg);
255     m_textLabel->show();
256     switchToCloseButton();
257     return;
258   }
259 
260   setMinimumSize(300, 200);
261 
262   if (m_updateSceneContentsCheckBox) m_updateSceneContentsCheckBox->hide();
263   m_updateButton->setEnabled(false);
264   m_waitingLabel->hide();
265   m_textLabel->hide();
266   m_output->show();
267 
268   QStringList args;
269   args << "update";
270 
271   int filesCount = m_filesToUpdate.count();
272   for (int i = 0; i < filesCount; i++) {
273     QString file = m_filesToUpdate.at(i);
274     if (!file.isEmpty()) args << file;
275   }
276 
277   int resourceCount = m_sceneResources.size();
278   for (int i = 0; i < resourceCount; i++) args << m_sceneResources.at(i);
279 
280   if (m_nonRecursive) args << "--non-recursive";
281 
282   m_thread.disconnect(SIGNAL(done(const QString &)));
283   connect(&m_thread, SIGNAL(outputRetrieved(const QString &)),
284           SLOT(addOutputText(const QString &)));
285   connect(&m_thread, SIGNAL(done(const QString &)),
286           SLOT(onUpdateDone(const QString &)));
287   m_thread.executeCommand(m_workingDir, "svn", args, false);
288 }
289 
290 //-----------------------------------------------------------------------------
291 
switchToCloseButton()292 void SVNUpdateDialog::switchToCloseButton() {
293   if (m_updateSceneContentsCheckBox) m_updateSceneContentsCheckBox->hide();
294   m_cancelButton->hide();
295   m_updateButton->hide();
296   m_closeButton->show();
297   m_closeButton->setEnabled(true);
298 }
299 
300 //-----------------------------------------------------------------------------
301 
addOutputText(const QString & text)302 void SVNUpdateDialog::addOutputText(const QString &text) {
303   QRegExp regExp("Updated to revision (\\d+)\\.");
304   QString temp = text;
305   temp.remove(regExp);
306 
307   QStringList split = temp.split(QRegExp("\\r\\n|\\n"));
308 
309   for (int i = 0; i < split.size(); i++) {
310     QString s = split.at(i);
311     if (!s.isEmpty()) m_output->insertPlainText(s + "\n");
312   }
313   QScrollBar *scrollBar = m_output->verticalScrollBar();
314   scrollBar->setValue(scrollBar->maximum());
315 }
316 
317 //-----------------------------------------------------------------------------
318 
onUpdateToRevisionButtonClicked()319 void SVNUpdateDialog::onUpdateToRevisionButtonClicked() {
320   m_textLabel->hide();
321   m_cancelButton->hide();
322   m_updateButton->hide();
323   m_dateChooserWidget->hide();
324   m_output->show();
325 
326   QStringList args;
327   args << "update";
328 
329   // Pay attention: I have to perform update of the whole selected files
330   // (that could become updatable "looking in the past")
331   int filesCount = m_files.count();
332   for (int i = 0; i < filesCount; i++) {
333     QString file = m_files.at(i);
334     if (!file.isEmpty()) args << file;
335   }
336   QString revisionString = m_dateChooserWidget->getRevisionString();
337 
338   args << "-r" << revisionString;
339 
340   connect(&m_thread, SIGNAL(outputRetrieved(const QString &)),
341           SLOT(addOutputText(const QString &)));
342   connect(&m_thread, SIGNAL(done(const QString &)),
343           SLOT(onUpdateDone(const QString &)));
344   m_thread.executeCommand(m_workingDir, "svn", args, false);
345 }
346 
347 //-----------------------------------------------------------------------------
348 
onUpdateButtonClicked()349 void SVNUpdateDialog::onUpdateButtonClicked() {
350   // Update to mine
351   QStringList files = m_conflictWidget->getFilesWithOption(0);
352   if (files.size() > 0) {
353     m_waitingLabel->show();
354     m_textLabel->setText(tr("Updating items..."));
355     QStringList args;
356     args << "update";
357     args.append(files);
358     args << "--accept"
359          << "mine-full";
360 
361     m_thread.disconnect(SIGNAL(done(const QString &)));
362     connect(&m_thread, SIGNAL(done(const QString &)),
363             SLOT(onUpdateToMineDone()));
364     m_thread.executeCommand(m_workingDir, "svn", args);
365   } else
366     onUpdateToMineDone();
367 }
368 
369 //-----------------------------------------------------------------------------
370 
onUpdateToMineDone()371 void SVNUpdateDialog::onUpdateToMineDone() {
372   // Update to theirs
373   QStringList files = m_conflictWidget->getFilesWithOption(1);
374   if (files.size() > 0) {
375     m_waitingLabel->show();
376     m_textLabel->setText(tr("Updating to their items..."));
377     QStringList args;
378     args << "update";
379     args.append(files);
380     args << "--accept"
381          << "theirs-full";
382 
383     m_thread.disconnect(SIGNAL(done(const QString &)));
384     connect(&m_thread, SIGNAL(done(const QString &)),
385             SLOT(onConflictResolved()));
386     m_thread.executeCommand(m_workingDir, "svn", args);
387   } else
388     onConflictResolved();
389 }
390 
391 //-----------------------------------------------------------------------------
392 
onConflictResolved()393 void SVNUpdateDialog::onConflictResolved() {
394   m_conflictWidget->hide();
395   updateFiles();
396 }
397 
398 //-----------------------------------------------------------------------------
399 
onError(const QString & errorString)400 void SVNUpdateDialog::onError(const QString &errorString) {
401   m_waitingLabel->hide();
402   if (m_output->isVisible())
403     addOutputText(errorString);
404   else {
405     m_textLabel->setText(errorString);
406     m_textLabel->show();
407   }
408   switchToCloseButton();
409 }
410 
411 //-----------------------------------------------------------------------------
412 
onUpdateDone(const QString & text)413 void SVNUpdateDialog::onUpdateDone(const QString &text) {
414   addOutputText(text);
415 
416   QStringList files;
417   for (int i = 0; i < m_filesToUpdate.size(); i++)
418     files.append(m_filesToUpdate.at(i));
419   emit done(files);
420 
421   switchToCloseButton();
422 }
423 
424 //-----------------------------------------------------------------------------
425 
onConflictSetted()426 void SVNUpdateDialog::onConflictSetted() { m_updateButton->setEnabled(true); }
427 
428 //-----------------------------------------------------------------------------
429 
onUpdateSceneContentsToggled(bool checked)430 void SVNUpdateDialog::onUpdateSceneContentsToggled(bool checked) {
431   if (!checked)
432     m_sceneResources.clear();
433   else {
434     VersionControl *vc = VersionControl::instance();
435 
436     int fileSize = m_filesToUpdate.count();
437     for (int i = 0; i < fileSize; i++) {
438       QString fileName = m_filesToUpdate.at(i);
439       if (fileName.endsWith(".tnz"))
440         m_sceneResources.append(vc->getSceneContents(m_workingDir, fileName));
441     }
442   }
443 
444   if (m_filesToUpdate.size() == 1)
445     m_textLabel->setText(
446         tr("%1 items to update.")
447             .arg(m_filesToUpdate.size() + m_sceneResources.size()));
448   else
449     m_textLabel->setText(tr("%1 items to update.")
450                              .arg(m_filesToUpdate.size() +
451                                   m_sceneResources.size() - m_sceneIconsCount));
452 }
453