1 #include "transfer_dialog.h"
2 #include "list_of_job_options.h"
3 #include "utils.h"
4 
TransferDialog(bool isDownload,bool isDrop,const QString & remote,const QDir & path,bool isFolder,QWidget * parent,JobOptions * task,bool editMode)5 TransferDialog::TransferDialog(bool isDownload, bool isDrop,
6                                const QString &remote, const QDir &path,
7                                bool isFolder, QWidget *parent, JobOptions *task,
8                                bool editMode)
9     : QDialog(parent), mIsDownload(isDownload), mIsFolder(isFolder),
10       mIsEditMode(editMode), mJobOptions(task) {
11   ui.setupUi(this);
12   resize(0, 0);
13   setWindowTitle(isDownload ? "Download" : "Upload");
14 
15   QStyle *style = qApp->style();
16   ui.buttonSourceFile->setIcon(style->standardIcon(QStyle::SP_FileIcon));
17   ui.buttonSourceFolder->setIcon(style->standardIcon(QStyle::SP_DirIcon));
18   ui.buttonDest->setIcon(style->standardIcon(QStyle::SP_DirIcon));
19 
20   ui.buttonDefaultSource->setIcon(style->standardIcon(QStyle::SP_DirHomeIcon));
21   ui.buttonDefaultDest->setIcon(style->standardIcon(QStyle::SP_DirHomeIcon));
22 
23   if (!mIsEditMode) {
24     QPushButton *dryRun =
25         ui.buttonBox->addButton("&Dry run", QDialogButtonBox::AcceptRole);
26     ui.buttonBox->addButton("&Run", QDialogButtonBox::AcceptRole);
27     QObject::connect(dryRun, &QPushButton::clicked, this,
28                      [=]() { mDryRun = true; });
29   }
30 
31   QPushButton *saveTask = ui.buttonBox->addButton(
32       "&Save task", QDialogButtonBox::ButtonRole::ActionRole);
33 
34   QObject::connect(
35       ui.buttonBox->button(QDialogButtonBox::RestoreDefaults),
36       &QPushButton::clicked, this, [=]() {
37         ui.cbSyncDelete->setCurrentIndex(0);
38         // set combobox tooltips
39         ui.cbSyncDelete->setItemData(0, "--delete-during", Qt::ToolTipRole);
40         ui.cbSyncDelete->setItemData(1, "--delete-after", Qt::ToolTipRole);
41         ui.cbSyncDelete->setItemData(2, "--delete-before", Qt::ToolTipRole);
42         ui.checkSkipNewer->setChecked(false);
43         ui.checkSkipNewer->setChecked(false);
44         ui.checkSkipExisting->setChecked(false);
45         ui.checkCompare->setChecked(true);
46         ui.cbCompare->setCurrentIndex(0);
47         // set combobox tooltips
48         ui.cbCompare->setItemData(0, "default", Qt::ToolTipRole);
49         ui.cbCompare->setItemData(1, "--checksum", Qt::ToolTipRole);
50         ui.cbCompare->setItemData(2, "--ignore-size", Qt::ToolTipRole);
51         ui.cbCompare->setItemData(3, "--size-only", Qt::ToolTipRole);
52         ui.cbCompare->setItemData(4, "--checksum --ignore-size",
53                                   Qt::ToolTipRole);
54         //      ui.checkVerbose->setChecked(false);
55         ui.checkSameFilesystem->setChecked(false);
56         ui.checkDontUpdateModified->setChecked(false);
57         ui.spinTransfers->setValue(4);
58         ui.spinCheckers->setValue(8);
59         ui.textBandwidth->clear();
60         ui.textMinSize->clear();
61         ui.textMinAge->clear();
62         ui.textMaxAge->clear();
63         ui.spinMaxDepth->setValue(0);
64         ui.spinConnectTimeout->setValue(60);
65         ui.spinIdleTimeout->setValue(300);
66         ui.spinRetries->setValue(3);
67         ui.spinLowLevelRetries->setValue(10);
68         ui.checkDeleteExcluded->setChecked(false);
69         ui.textExclude->clear();
70         auto settings = GetSettings();
71         if (isDownload) {
72           // download
73           ui.textExtra->setText(
74               settings->value("Settings/defaultDownloadOptions").toString());
75         } else {
76           // upload
77           ui.textExtra->setText(
78               settings->value("Settings/defaultUploadOptions").toString());
79         }
80       });
81 
82   ui.buttonBox->button(QDialogButtonBox::RestoreDefaults)->click();
83 
84   QObject::connect(saveTask, &QPushButton::clicked, this, [=]() {
85     // validate before saving task...
86     if (ui.textDescription->text().isEmpty()) {
87       QMessageBox::warning(this, "Warning",
88                            "Please enter task description to Save!");
89       ui.textDescription->setFocus(Qt::FocusReason::OtherFocusReason);
90       return;
91     }
92     // even though the below does not match the condition on the Run buttons
93     // it SEEMS like blanking either one would be a problem, right?
94     if (ui.textDest->text().isEmpty() || ui.textSource->text().isEmpty()) {
95       QMessageBox::warning(this, "Error",
96                            "Invalid Task, source and destination required!");
97       return;
98     }
99     JobOptions *jobo = getJobOptions();
100     ListOfJobOptions::getInstance()->Persist(jobo);
101     // always close on save
102     // if (mIsEditMode)
103     this->close();
104   });
105 
106   QObject::connect(ui.buttonBox, &QDialogButtonBox::accepted, this,
107                    &QDialog::accept);
108   QObject::connect(ui.buttonBox, &QDialogButtonBox::rejected, this,
109                    &QDialog::reject);
110 
111   QObject::connect(ui.buttonSourceFile, &QToolButton::clicked, this, [=]() {
112     QString file = QFileDialog::getOpenFileName(this, "Choose file to upload");
113     if (!file.isEmpty()) {
114       ui.textSource->setText(QDir::toNativeSeparators(file));
115       ui.textDest->setText(remote + ":" + path.path());
116     }
117   });
118 
119   QObject::connect(ui.buttonSourceFolder, &QToolButton::clicked, this, [=]() {
120     auto settings = GetSettings();
121     QString last_used_source_folder =
122         (settings->value("Settings/lastUsedSourceFolder").toString());
123     QString folder = QFileDialog::getExistingDirectory(
124         this, "Choose folder to upload", last_used_source_folder,
125         QFileDialog::ShowDirsOnly);
126     if (!folder.isEmpty()) {
127       // store new folder in lastUsedSourceFolder
128       settings->setValue("Settings/lastUsedSourceFolder", folder);
129       ui.textSource->setText(QDir::toNativeSeparators(folder));
130       ui.textDest->setText(remote + ":" +
131                            path.filePath(QFileInfo(folder).fileName()));
132     }
133   });
134 
135   QObject::connect(ui.buttonDefaultSource, &QToolButton::clicked, this, [=]() {
136     auto settings = GetSettings();
137     QString default_folder =
138         (settings->value("Settings/defaultUploadDir").toString());
139     // store default folder in lastUsedSourceFolder
140     settings->setValue("Settings/lastUsedSourceFolder", default_folder);
141     ui.textSource->setText(QDir::toNativeSeparators(default_folder));
142     if (!default_folder.isEmpty()) {
143       ui.textDest->setText(remote + ":" +
144                            path.filePath(QFileInfo(default_folder).fileName()));
145     } else {
146       ui.textDest->setText(remote + ":" + path.path());
147     };
148   });
149 
150   QObject::connect(ui.buttonDest, &QToolButton::clicked, this, [=]() {
151     auto settings = GetSettings();
152     QString last_used_dest_folder =
153         (settings->value("Settings/lastUsedDestFolder").toString());
154     QString folder = QFileDialog::getExistingDirectory(
155         this, "Choose destination folder", last_used_dest_folder,
156         QFileDialog::ShowDirsOnly);
157     if (!folder.isEmpty()) {
158       // store new folder in lastUsedDestFolder
159       settings->setValue("Settings/lastUsedDestFolder", folder);
160       if (isFolder) {
161         ui.textDest->setText(
162             QDir::toNativeSeparators(folder + "/" + path.dirName()));
163       } else {
164         ui.textDest->setText(QDir::toNativeSeparators(folder));
165       }
166     }
167   });
168 
169   QObject::connect(ui.buttonDefaultDest, &QToolButton::clicked, this, [=]() {
170     auto settings = GetSettings();
171     QString default_folder =
172         (settings->value("Settings/defaultDownloadDir").toString());
173     // store default_folder in lastUsedDestFolder
174     settings->setValue("Settings/lastUsedDestFolder", default_folder);
175     if (!default_folder.isEmpty()) {
176       if (isFolder) {
177         ui.textDest->setText(
178             QDir::toNativeSeparators(default_folder + "/" + path.dirName()));
179       } else {
180         ui.textDest->setText(QDir::toNativeSeparators(default_folder));
181       }
182     } else {
183       ui.textDest->setText("");
184     };
185   });
186 
187   auto settings = GetSettings();
188   settings->beginGroup("Transfer");
189   ReadSettings(settings.get(), this);
190   settings->endGroup();
191 
192   ui.buttonSourceFile->setVisible(!isDownload);
193   ui.buttonSourceFolder->setVisible(!isDownload);
194   ui.buttonDefaultSource->setVisible(!isDownload);
195 
196   ui.buttonDest->setVisible(isDownload);
197   ui.buttonDefaultDest->setVisible(isDownload);
198 
199   // Info only - should not be edited
200   // would be nice to display it only for Google Drive - todo
201   ui.checkisDriveSharedWithMe->setDisabled(true);
202 
203   ui.checkisDriveSharedWithMe->setChecked(
204       settings->value("Settings/driveShared", false).toBool());
205   // always clear for new jobs
206   ui.textDescription->clear();
207 
208   if (mIsEditMode && mJobOptions != nullptr) {
209     // it's not really valid for only one of these things to be true.
210     // when operating on an existing instance i.e. a saved task,
211     // changing the dest or src seems to have problems so we
212     // will not allow it.  simple enough, and better, to make a
213     // new task for different pairings anyway.  that will make
214     // a lot more sense when/if scheduling and history are added...
215     ui.buttonSourceFile->setVisible(false);
216     ui.buttonSourceFolder->setVisible(false);
217     ui.buttonDefaultSource->setVisible(false);
218 
219     ui.buttonDest->setVisible(false);
220     ui.buttonDefaultDest->setVisible(false);
221 
222     ui.textDest->setDisabled(true);
223     ui.textSource->setDisabled(true);
224     putJobOptions();
225   } else {
226 
227     // set source and destination using defaults
228     if (isDownload) {
229       // download
230       ui.textExtra->setText(
231           settings->value("Settings/defaultDownloadOptions").toString());
232       ui.textSource->setText(remote + ":" + path.path());
233       QString folder;
234       QString default_folder =
235           (settings->value("Settings/defaultDownloadDir").toString());
236       QString last_used_dest_folder =
237           (settings->value("Settings/lastUsedDestFolder").toString());
238 
239       if (last_used_dest_folder.isEmpty()) {
240         folder = default_folder;
241       } else {
242         folder = last_used_dest_folder;
243       };
244 
245       if (!folder.isEmpty()) {
246         if (isFolder) {
247           ui.textDest->setText(
248               QDir::toNativeSeparators(folder + "/" + path.dirName()));
249         } else {
250           ui.textDest->setText(QDir::toNativeSeparators(folder));
251         }
252       }
253 
254     } else {
255       // upload
256       ui.textExtra->setText(
257           settings->value("Settings/defaultUploadOptions").toString());
258       QString folder;
259       QString default_folder =
260           (settings->value("Settings/defaultUploadDir").toString());
261       QString last_used_source_folder =
262           (settings->value("Settings/lastUsedSourceFolder").toString());
263 
264       if (last_used_source_folder.isEmpty()) {
265         folder = default_folder;
266       } else {
267         folder = last_used_source_folder;
268       };
269 
270       // if upload initiated from drag and drop we dont use default upload
271       // folder
272       if (!isDrop) {
273         ui.textSource->setText(QDir::toNativeSeparators(folder));
274         if (!folder.isEmpty()) {
275           ui.textDest->setText(remote + ":" +
276                                path.filePath(QFileInfo(folder).fileName()));
277         } else {
278           ui.textDest->setText(remote + ":" + path.path());
279         }
280       } else {
281         // when dropping to root folder
282         if (path.path() == ".") {
283           ui.textDest->setText(remote + ":");
284         } else {
285           ui.textDest->setText(remote + ":" + path.path());
286         }
287       };
288     };
289   }
290 }
291 
~TransferDialog()292 TransferDialog::~TransferDialog() {
293   if (result() == QDialog::Accepted) {
294     auto settings = GetSettings();
295     settings->beginGroup("Transfer");
296     WriteSettings(settings.get(), this);
297     settings->remove("textSource");
298     settings->remove("textDest");
299     settings->endGroup();
300   }
301 }
302 
setSource(const QString & path)303 void TransferDialog::setSource(const QString &path) {
304   ui.textSource->setText(QDir::toNativeSeparators(path));
305 }
306 
getMode() const307 QString TransferDialog::getMode() const {
308   if (ui.rbCopy->isChecked()) {
309     return "Copy";
310   } else if (ui.rbMove->isChecked()) {
311     return "Move";
312   } else if (ui.rbSync->isChecked()) {
313     return "Sync";
314   }
315 
316   return QString();
317 }
318 
getSource() const319 QString TransferDialog::getSource() const { return ui.textSource->text(); }
320 
getDest() const321 QString TransferDialog::getDest() const { return ui.textDest->text(); }
322 
getOptions()323 QStringList TransferDialog::getOptions() {
324   JobOptions *jobo = getJobOptions();
325   QStringList newWay = jobo->getOptions();
326   return newWay;
327 }
328 
329 /*
330  * Apply the displayed/edited values on the UI to the
331  * JobOptions object.
332  *
333  * This needs to be edited whenever options are added or changed.
334  */
getJobOptions()335 JobOptions *TransferDialog::getJobOptions() {
336   if (mJobOptions == nullptr)
337     mJobOptions = new JobOptions(mIsDownload);
338 
339   if (ui.rbCopy->isChecked()) {
340     mJobOptions->operation = JobOptions::Copy;
341     mJobOptions->sync = false;
342   } else if (ui.rbMove->isChecked()) {
343     mJobOptions->operation = JobOptions::Move;
344     mJobOptions->sync = false;
345   } else if (ui.rbSync->isChecked()) {
346     mJobOptions->operation = JobOptions::Sync;
347   }
348 
349   mJobOptions->dryRun = mDryRun;
350   ;
351 
352   if (ui.rbSync->isChecked()) {
353     mJobOptions->sync = true;
354     switch (ui.cbSyncDelete->currentIndex()) {
355     case 0:
356       mJobOptions->syncTiming = JobOptions::During;
357       break;
358     case 1:
359       mJobOptions->syncTiming = JobOptions::After;
360       break;
361     case 2:
362       mJobOptions->syncTiming = JobOptions::Before;
363       break;
364     }
365   }
366 
367   mJobOptions->skipNewer = ui.checkSkipNewer->isChecked();
368   mJobOptions->skipExisting = ui.checkSkipExisting->isChecked();
369 
370   if (ui.checkCompare->isChecked()) {
371     mJobOptions->compare = true;
372     switch (ui.cbCompare->currentIndex()) {
373     case 0:
374       mJobOptions->compareOption = JobOptions::SizeAndModTime;
375       break;
376     case 1:
377       mJobOptions->compareOption = JobOptions::Checksum;
378       break;
379     case 2:
380       mJobOptions->compareOption = JobOptions::IgnoreSize;
381       break;
382     case 3:
383       mJobOptions->compareOption = JobOptions::SizeOnly;
384       break;
385     case 4:
386       mJobOptions->compareOption = JobOptions::ChecksumIgnoreSize;
387       break;
388     }
389   } else {
390     mJobOptions->compare = false;
391   };
392 
393   //    mJobOptions->verbose = ui.checkVerbose->isChecked();
394   mJobOptions->sameFilesystem = ui.checkSameFilesystem->isChecked();
395   mJobOptions->dontUpdateModified = ui.checkDontUpdateModified->isChecked();
396 
397   mJobOptions->transfers = ui.spinTransfers->text();
398   mJobOptions->checkers = ui.spinCheckers->text();
399   mJobOptions->bandwidth = ui.textBandwidth->text();
400   mJobOptions->minSize = ui.textMinSize->text();
401   mJobOptions->minAge = ui.textMinAge->text();
402   mJobOptions->maxAge = ui.textMaxAge->text();
403   mJobOptions->maxDepth = ui.spinMaxDepth->value();
404 
405   mJobOptions->connectTimeout = ui.spinConnectTimeout->text();
406   mJobOptions->idleTimeout = ui.spinIdleTimeout->text();
407   mJobOptions->retries = ui.spinRetries->text();
408   mJobOptions->lowLevelRetries = ui.spinLowLevelRetries->text();
409   mJobOptions->deleteExcluded = ui.checkDeleteExcluded->isChecked();
410 
411   mJobOptions->excluded = ui.textExclude->toPlainText().trimmed();
412   mJobOptions->extra = ui.textExtra->text().trimmed();
413 
414   mJobOptions->source = ui.textSource->text();
415   mJobOptions->dest = ui.textDest->text();
416   mJobOptions->isFolder = mIsFolder;
417 
418   mJobOptions->description = ui.textDescription->text();
419   //   auto settings = GetSettings();
420   //   mJobOptions->DriveSharedWithMe = settings->value("Settings/driveShared",
421   //   false).toBool();
422 
423   if (mIsEditMode)
424     mJobOptions->DriveSharedWithMe = ui.checkisDriveSharedWithMe->isChecked();
425   else {
426     auto settings = GetSettings();
427     mJobOptions->DriveSharedWithMe =
428         settings->value("Settings/driveShared", false).toBool();
429   };
430 
431   return mJobOptions;
432 }
433 
434 /*
435  * Apply the JobOptions object to the displayed widget values.
436  *
437  * It could be "better" to use a two-way binding mechanism, but
438  * if used that should be global to the app; and anyway doing
439  * it this old primitive way makes it easier when the user wants
440  * to not save changes...
441  */
putJobOptions()442 void TransferDialog::putJobOptions() {
443   ui.rbCopy->setChecked(mJobOptions->operation == JobOptions::Copy);
444   ui.rbMove->setChecked(mJobOptions->operation == JobOptions::Move);
445   ui.rbSync->setChecked(mJobOptions->operation == JobOptions::Sync);
446 
447   mDryRun = mJobOptions->dryRun;
448   ui.rbSync->setChecked(mJobOptions->sync);
449 
450   ui.cbSyncDelete->setCurrentIndex((int)mJobOptions->syncTiming);
451   // set combobox tooltips
452   ui.cbSyncDelete->setItemData(0, "--delete-during", Qt::ToolTipRole);
453   ui.cbSyncDelete->setItemData(1, "--delete-after", Qt::ToolTipRole);
454   ui.cbSyncDelete->setItemData(2, "--delete-before", Qt::ToolTipRole);
455 
456   ui.checkSkipNewer->setChecked(mJobOptions->skipNewer);
457   ui.checkSkipExisting->setChecked(mJobOptions->skipExisting);
458 
459   ui.checkCompare->setChecked(mJobOptions->compare);
460 
461   ui.cbCompare->setCurrentIndex(mJobOptions->compareOption);
462   // set combobox tooltips
463   ui.cbCompare->setItemData(0, "default", Qt::ToolTipRole);
464   ui.cbCompare->setItemData(1, "--checksum", Qt::ToolTipRole);
465   ui.cbCompare->setItemData(2, "--ignore-size", Qt::ToolTipRole);
466   ui.cbCompare->setItemData(3, "--size-only", Qt::ToolTipRole);
467   ui.cbCompare->setItemData(4, "--checksum --ignore-size", Qt::ToolTipRole);
468   // ui.checkVerbose->setChecked(mJobOptions->verbose);
469   ui.checkSameFilesystem->setChecked(mJobOptions->sameFilesystem);
470   ui.checkDontUpdateModified->setChecked(mJobOptions->dontUpdateModified);
471 
472   ui.spinTransfers->setValue(mJobOptions->transfers.toInt());
473   ui.spinCheckers->setValue(mJobOptions->checkers.toInt());
474   ui.textBandwidth->setText(mJobOptions->bandwidth);
475   ui.textMinSize->setText(mJobOptions->minSize);
476   ui.textMinAge->setText(mJobOptions->minAge);
477   ui.textMaxAge->setText(mJobOptions->maxAge);
478   ui.spinMaxDepth->setValue(mJobOptions->maxDepth);
479 
480   ui.spinConnectTimeout->setValue(mJobOptions->connectTimeout.toInt());
481   ui.spinIdleTimeout->setValue(mJobOptions->idleTimeout.toInt());
482   ui.spinRetries->setValue(mJobOptions->retries.toInt());
483   ui.spinLowLevelRetries->setValue(mJobOptions->lowLevelRetries.toInt());
484   ui.checkDeleteExcluded->setChecked(mJobOptions->deleteExcluded);
485 
486   ui.textExclude->setPlainText(mJobOptions->excluded);
487   ui.textExtra->setText(mJobOptions->extra);
488 
489   ui.textSource->setText(mJobOptions->source);
490   ui.textDest->setText(mJobOptions->dest);
491   ui.textDescription->setText(mJobOptions->description);
492   // DDBB
493   ui.checkisDriveSharedWithMe->setChecked(mJobOptions->DriveSharedWithMe);
494 }
495 
done(int r)496 void TransferDialog::done(int r) {
497   if (r == QDialog::Accepted) {
498     if (mIsDownload) {
499       if (ui.textDest->text().isEmpty()) {
500         QMessageBox::warning(this, "Warning", "Please enter destination!");
501         return;
502       }
503     } else {
504       if (ui.textSource->text().isEmpty()) {
505         QMessageBox::warning(this, "Warning", "Please enter source!");
506         return;
507       }
508     }
509   }
510   QDialog::done(r);
511 }
512