1 
2 
3 #include "overwritepopup.h"
4 
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "tsystem.h"
8 
9 // TnzQt includes
10 #include "toonzqt/checkbox.h"
11 #include "toonzqt/dvdialog.h"
12 #include "toonzqt/lineedit.h"
13 #include "toonzqt/gutil.h"
14 
15 // TnzLib includes
16 #include "toonz/toonzscene.h"
17 
18 // TnzCore includes
19 #include "tfilepath.h"
20 
21 // Qt includes
22 #include <QLabel>
23 #include <QPushButton>
24 #include <QRadioButton>
25 #include <QButtonGroup>
26 #include <QApplication>
27 #include <QMainWindow>
28 
29 //************************************************************************************
30 //    OverwriteDialog::ExistsFunc  implementation
31 //************************************************************************************
32 
conflictString(const TFilePath & fp) const33 QString OverwriteDialog::DecodeFileExistsFunc::conflictString(
34     const TFilePath &fp) const {
35   return OverwriteDialog::tr(
36              "File \"%1\" already exists.\nWhat do you want to do?")
37       .arg(toQString(fp));
38 }
39 
40 //----------------------------------------------------------------------------------
41 
operator ()(const TFilePath & fp) const42 bool OverwriteDialog::DecodeFileExistsFunc::operator()(
43     const TFilePath &fp) const {
44   return TSystem::doesExistFileOrLevel(m_scene->decodeFilePath(fp));
45 }
46 
47 //************************************************************************************
48 //    OverwriteDialog  implementation
49 //************************************************************************************
50 
OverwriteDialog()51 OverwriteDialog::OverwriteDialog()
52     : DVGui::Dialog(TApp::instance()->getMainWindow(), true) {
53   setModal(true);
54   setWindowTitle(tr("Warning!"));
55 
56   QButtonGroup *buttonGroup = new QButtonGroup(this);
57   buttonGroup->setExclusive(true);
58   bool ret = connect(buttonGroup, SIGNAL(buttonClicked(int)), this,
59                      SLOT(onButtonClicked(int)));
60 
61   beginVLayout();
62 
63   m_label = new QLabel(this);
64   addWidget(m_label);
65 
66   m_keep = new QRadioButton(tr("Keep existing file"), this);
67   buttonGroup->addButton(m_keep);
68   addWidget(m_keep);
69 
70   m_overwrite = new QRadioButton(
71       tr("Overwrite the existing file with the new one"), this);
72   buttonGroup->addButton(m_overwrite);
73   addWidget(m_overwrite);
74 
75   m_rename =
76       new QRadioButton(tr("Rename the new file adding the suffix"), this);
77   buttonGroup->addButton(m_rename);
78 
79   m_suffix = new DVGui::LineEdit("_1", this);
80   m_suffix->setFixedWidth(25);
81   m_suffix->setEnabled(false);
82 
83   QHBoxLayout *boxLayout = new QHBoxLayout();
84   boxLayout->setMargin(0);
85   boxLayout->setSpacing(0);
86   boxLayout->addWidget(m_rename);
87   boxLayout->addWidget(m_suffix);
88   boxLayout->setAlignment(m_rename, Qt::AlignLeft);
89   boxLayout->setAlignment(m_suffix, Qt::AlignLeft);
90   addLayout(boxLayout);
91 
92   endVLayout();
93 
94   m_okBtn = new QPushButton(QString(tr("Apply")), this);
95   ret     = ret && connect(m_okBtn, SIGNAL(clicked()), this, SLOT(accept()));
96   addButtonBarWidget(m_okBtn);
97 
98   m_okToAllBtn = new QPushButton(QString(tr("Apply to All")), this);
99   ret =
100       ret && connect(m_okToAllBtn, SIGNAL(clicked()), this, SLOT(applyToAll()));
101   addButtonBarWidget(m_okToAllBtn);
102 
103   m_cancelBtn = new QPushButton(QString(tr("Cancel")), this);
104   ret = ret && connect(m_cancelBtn, SIGNAL(clicked()), this, SLOT(cancel()));
105   addButtonBarWidget(m_cancelBtn);
106 
107   assert(ret);
108 
109   reset();
110   m_keep->setChecked(true);
111 }
112 
113 //----------------------------------------------------------------------------------
114 
reset()115 void OverwriteDialog::reset() {
116   m_choice        = KEEP_OLD;
117   m_applyToAll    = false;
118   m_cancelPressed = false;
119 }
120 
121 //----------------------------------------------------------------------------------
122 
getSuffix()123 std::wstring OverwriteDialog::getSuffix() {
124   return m_suffix->text().toStdWString();
125 }
126 
127 //----------------------------------------------------------------------------------
128 
applyToAll()129 void OverwriteDialog::applyToAll() {
130   m_applyToAll = true;
131   accept();
132 }
133 
134 //----------------------------------------------------------------------------------
135 
cancel()136 void OverwriteDialog::cancel() {
137   m_cancelPressed = true;
138   reject();
139 }
140 
141 //----------------------------------------------------------------------------------
142 
onButtonClicked(int)143 void OverwriteDialog::onButtonClicked(int) {
144   m_suffix->setEnabled(m_rename->isChecked());
145 }
146 
147 //----------------------------------------------------------------------------------
148 
addSuffix(const TFilePath & src) const149 TFilePath OverwriteDialog::addSuffix(const TFilePath &src) const {
150   return src.withName(src.getWideName() + m_suffix->text().toStdWString());
151 }
152 
153 //----------------------------------------------------------------------------------
154 
execute(TFilePath & filePath,const ExistsFunc & exists,Resolution acceptedRes,Flags flags)155 OverwriteDialog::Resolution OverwriteDialog::execute(TFilePath &filePath,
156                                                      const ExistsFunc &exists,
157                                                      Resolution acceptedRes,
158                                                      Flags flags) {
159   typedef QRadioButton *OverwriteDialog::*RadioRes;
160   static const RadioRes radios[3] = {&OverwriteDialog::m_keep,
161                                      &OverwriteDialog::m_overwrite,
162                                      &OverwriteDialog::m_rename};
163 
164   struct locals {
165     static inline int idx(Resolution res) {
166       int r = 0;
167       while (r < 3 && !(res & (1 << r))) ++r;
168       return r;
169     }
170   };
171 
172   TFilePath writePath = filePath;
173 
174   // Deal with the case where "Apply To All" was already clicked
175   if (m_applyToAll && (m_choice & acceptedRes)) {
176     if (m_choice == RENAME) writePath = addSuffix(writePath);
177 
178     if (m_choice != RENAME || !exists(writePath))
179       return filePath = writePath, m_choice;
180   }
181 
182   reset();
183 
184   // Find a compatible suffix to be displayed
185   if (acceptedRes & RENAME)
186     for (int i = 1; exists(addSuffix(filePath)); ++i)
187       m_suffix->setText("_" + QString::number(i));
188 
189   // Show the various dialog components as specified
190   m_overwrite->setVisible(acceptedRes & OVERWRITE);
191   m_keep->setVisible(acceptedRes & KEEP_OLD);
192   m_rename->setVisible(acceptedRes & RENAME);
193   m_suffix->setVisible(acceptedRes & RENAME);
194   m_okToAllBtn->setVisible(flags & APPLY_TO_ALL_FLAG);
195 
196   // Ensure that there is a checked button among the accepted resolutions
197   for (int r = 0; r < 3; ++r)
198     if ((this->*radios[r])->isChecked()) {
199       if (!(acceptedRes & (1 << r)))
200         (this->*radios[locals::idx(acceptedRes)])->setChecked(true);
201       break;
202     }
203 
204   // Prompt the dialog to let the user decide resolution
205   while (true) {
206     // Build text to be displayed
207     m_label->setText(exists.conflictString(writePath));
208 
209     // Execute dialog
210     int retCode = exec();
211 
212     m_choice = (retCode == QDialog::Rejected)
213                    ? CANCELED
214                    : m_overwrite->isChecked()
215                          ? OVERWRITE
216                          : m_rename->isChecked() ? RENAME : KEEP_OLD;
217 
218     if (m_choice == RENAME) {
219       if (exists(writePath = addSuffix(filePath))) continue;
220     }
221 
222     break;
223   }
224 
225   return filePath = writePath, m_choice;
226 }
227 
228 //----------------------------------------------------------------------------------
229 
execute(ToonzScene * scene,const TFilePath & srcLevelPath,bool multiload)230 std::wstring OverwriteDialog::execute(ToonzScene *scene,
231                                       const TFilePath &srcLevelPath,
232                                       bool multiload) {
233   TFilePath levelPath       = srcLevelPath;
234   TFilePath actualLevelPath = scene->decodeFilePath(levelPath);
235   if (!TSystem::doesExistFileOrLevel(actualLevelPath))
236     return levelPath.getWideName();
237 
238   if (m_applyToAll && m_choice == RENAME) {
239     levelPath       = addSuffix(levelPath);
240     actualLevelPath = scene->decodeFilePath(levelPath);
241   }
242   if (m_applyToAll) {
243     if (m_choice != RENAME || !TSystem::doesExistFileOrLevel(actualLevelPath))
244       return levelPath.getWideName();
245   }
246 
247   m_label->setText(tr("File %1 already exists.\nWhat do you want to do?")
248                        .arg(toQString(levelPath)));
249   // find a compatible suffix
250   if (TSystem::doesExistFileOrLevel(actualLevelPath)) {
251     int i = 0;
252     while (TSystem::doesExistFileOrLevel(
253         scene->decodeFilePath(addSuffix(srcLevelPath)))) {
254       m_suffix->setText("_" + QString::number(++i));
255     }
256   }
257 
258   if (multiload)
259     m_okToAllBtn->show();
260   else
261     m_okToAllBtn->hide();
262 
263   // there could be a WaitCursor cursor
264   QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
265   raise();
266   exec();
267   QApplication::restoreOverrideCursor();
268 
269   if (m_rename->isChecked()) {
270     if (m_suffix->text() == "") {
271       DVGui::warning(tr("The suffix field is empty. Please specify a suffix."));
272       return execute(scene, srcLevelPath, multiload);
273     }
274     levelPath       = addSuffix(srcLevelPath);
275     actualLevelPath = scene->decodeFilePath(levelPath);
276     if (TSystem::doesExistFileOrLevel(actualLevelPath)) {
277       DVGui::warning(
278           tr("File %1 exists as well; please choose a different suffix.")
279               .arg(toQString(levelPath)));
280       return execute(scene, srcLevelPath, multiload);
281     }
282     m_choice = RENAME;
283   } else if (m_overwrite->isChecked())
284     m_choice = OVERWRITE;
285   else {
286     assert(m_keep->isChecked());
287     m_choice = KEEP_OLD;
288   }
289 
290   return levelPath.getWideName();
291 }
292