1 //**************************************************************************
2 // Copyright 2006 - 2017 Martin Koller, kollix@aon.at
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 2 of the License
7 //
8 //**************************************************************************
9 #include <MainWindow.hxx>
10 #include <Selector.hxx>
11 #include <Archiver.hxx>
12 #include <MainWidget.hxx>
13 #include <SettingsDialog.hxx>
14
15 #include <QSplitter>
16 #include <QSpinBox>
17 #include <QToolTip>
18 #include <QTimer>
19 #include <QCursor>
20 #include <QCheckBox>
21 #include <QMenu>
22 #include <QAction>
23 #include <QUrl>
24 #include <QApplication>
25 #include <QFileDialog>
26 #include <QHeaderView>
27
28 #include <KXMLGUIFactory>
29 #include <KStandardAction>
30 #include <KActionCollection>
31 #include <KToggleAction>
32 #include <KRecentFilesAction>
33 #include <KMessageBox>
34 #include <kio/global.h>
35 #include <KStringHandler>
36 #include <KStatusNotifierItem>
37 #include <KSharedConfig>
38 #include <KConfigGroup>
39 #include <KShortcutsDialog>
40
41 //#include <iostream>
42 //using namespace std;
43 //--------------------------------------------------------------------------------
44
MainWindow()45 MainWindow::MainWindow()
46 {
47 new Archiver(this);
48
49 quitAction = KStandardAction::quit(this, SLOT(maybeQuit()), actionCollection());
50 KStandardAction::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
51
52
53 QAction *action;
54
55 action = actionCollection()->addAction(QStringLiteral("newProfile"), this, SLOT(newProfile()));
56 action->setText(i18n("New Profile"));
57 action->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
58
59 action = actionCollection()->addAction(QStringLiteral("loadProfile"), this, SLOT(loadProfile()));
60 action->setText(i18n("Load Profile..."));
61 action->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
62
63 action = actionCollection()->addAction(QStringLiteral("saveProfile"), this, SLOT(saveProfile()));
64 action->setText(i18n("Save Profile"));
65 action->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
66
67 action = actionCollection()->addAction(QStringLiteral("saveProfileAs"), this, SLOT(saveProfileAs()));
68 action->setText(i18n("Save Profile As..."));
69 action->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
70
71 action = actionCollection()->addAction(QStringLiteral("profileSettings"), this, SLOT(profileSettings()));
72 action->setText(i18n("Profile Settings..."));
73
74 action = actionCollection()->addAction(QStringLiteral("enableAllMessages"), this, SLOT(enableAllMessages()));
75 action->setText(i18n("Enable All Messages"));
76
77 KToggleAction *docked = new KToggleAction(i18n("Dock in System Tray"), this);
78 actionCollection()->addAction(QStringLiteral("dockInSysTray"), docked);
79 connect(docked, &QAction::toggled, this, &MainWindow::dockInSysTray);
80
81 KToggleAction *showHidden = new KToggleAction(i18n("Show Hidden Files"), this);
82 actionCollection()->addAction(QStringLiteral("showHiddenFiles"), showHidden);
83 connect(showHidden, &QAction::toggled, this, &MainWindow::showHiddenFiles);
84
85 recentFiles = KStandardAction::openRecent(this, SLOT(recentProfileSelected(const QUrl &)), actionCollection());
86 recentFiles->setObjectName(QStringLiteral("recentProfiles"));
87 recentFiles->loadEntries(KSharedConfig::openConfig()->group(""));
88
89 createGUI();
90
91 splitter = new QSplitter(Qt::Horizontal, this);
92
93 selector = new Selector(splitter, actionCollection());
94
95 mainWidget = new MainWidget(splitter);
96 mainWidget->setSelector(selector);
97 splitter->setCollapsible(splitter->indexOf(mainWidget), false);
98
99 setCentralWidget(splitter);
100
101 splitter->restoreState(KSharedConfig::openConfig()->group("geometry").readEntry<QByteArray>("splitter", QByteArray()));
102 selector->header()->restoreState(KSharedConfig::openConfig()->group("geometry").readEntry<QByteArray>("tree", QByteArray()));
103
104 // save/restore window settings and size
105 setAutoSaveSettings();
106
107 connect(Archiver::instance, &Archiver::totalFilesChanged, this, &MainWindow::changeSystrayTip);
108 connect(Archiver::instance, &Archiver::logging, this, &MainWindow::loggingSlot);
109 connect(Archiver::instance, &Archiver::inProgress, this, &MainWindow::inProgress);
110
111 startBackupAction = actionCollection()->addAction(QStringLiteral("startBackup"), mainWidget, SLOT(startBackup()));
112 startBackupAction->setIcon(QIcon::fromTheme(QStringLiteral("kbackup_start")));
113 startBackupAction->setText(i18n("Start Backup"));
114
115 cancelBackupAction = actionCollection()->addAction(QStringLiteral("cancelBackup"), Archiver::instance, SLOT(cancel()));
116 cancelBackupAction->setText(i18n("Cancel Backup"));
117 cancelBackupAction->setIcon(QIcon::fromTheme(QStringLiteral("kbackup_cancel")));
118 cancelBackupAction->setEnabled(false);
119
120 showHidden->setChecked(KSharedConfig::openConfig()->group("settings").readEntry<bool>("showHiddenFiles", false));
121 showHiddenFiles(showHidden->isChecked());
122 docked->setChecked(KSharedConfig::openConfig()->group("settings").readEntry<bool>("dockInSysTray", false));
123 dockInSysTray(docked->isChecked());
124
125 // for convenience, open the tree at the HOME directory
126 selector->openHomeDir();
127
128 changeSystrayTip();
129 }
130
131 //--------------------------------------------------------------------------------
132
runBackup()133 void MainWindow::runBackup()
134 {
135 autorun = true;
136 QTimer::singleShot(0, mainWidget, &MainWidget::startBackup);
137 }
138
139 //--------------------------------------------------------------------------------
140
stopAllowed()141 bool MainWindow::stopAllowed()
142 {
143 if ( Archiver::instance->isInProgress() )
144 {
145 if ( KMessageBox::warningYesNo(this,
146 i18n("There is a backup in progress. Do you want to abort it?")) == KMessageBox::No )
147 return false;
148
149 Archiver::instance->cancel();
150 }
151
152 KSharedConfig::openConfig()->group("geometry").writeEntry("splitter", splitter->saveState());
153 KSharedConfig::openConfig()->group("geometry").writeEntry("tree", selector->header()->saveState());
154
155 return true;
156 }
157
158 //--------------------------------------------------------------------------------
159
maybeQuit()160 void MainWindow::maybeQuit()
161 {
162 if ( stopAllowed() )
163 qApp->quit();
164 }
165
166 //--------------------------------------------------------------------------------
167
queryClose()168 bool MainWindow::queryClose()
169 {
170 if ( qApp->isSavingSession() || !sysTray )
171 return stopAllowed();
172
173 hide();
174 return false;
175 }
176
177 //--------------------------------------------------------------------------------
178
recentProfileSelected(const QUrl & url)179 void MainWindow::recentProfileSelected(const QUrl &url)
180 {
181 loadProfile(url.path());
182 }
183
184 //--------------------------------------------------------------------------------
185
loadProfile()186 void MainWindow::loadProfile()
187 {
188 QString fileName = QFileDialog::getOpenFileName(this, i18n("Select Profile"), QString(),
189 i18n("KBackup Profile (*.kbp)"));
190
191 if ( fileName.isEmpty() ) return;
192
193 loadProfile(fileName);
194 }
195
196 //--------------------------------------------------------------------------------
197
loadProfile(const QString & fileName,bool adaptTreeWidth)198 void MainWindow::loadProfile(const QString &fileName, bool adaptTreeWidth)
199 {
200 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
201
202 QStringList includes, excludes;
203 QString error;
204
205 if ( ! Archiver::instance->loadProfile(fileName, includes, excludes, error) )
206 {
207 QApplication::restoreOverrideCursor();
208
209 KMessageBox::error(this,
210 i18n("Could not open profile '%1' for reading: %2",
211 fileName,
212 error),
213 i18n("Open failed"));
214 return;
215 }
216
217 setLoadedProfile(fileName);
218
219 // now fill the Selector tree with those settings
220 selector->setBackupList(includes, excludes);
221
222 mainWidget->getTargetLineEdit()->setText(Archiver::instance->getTarget().toDisplayString(QUrl::PreferLocalFile));
223
224 if ( adaptTreeWidth )
225 selector->resizeColumnToContents(0);
226
227 QApplication::restoreOverrideCursor();
228 }
229
230 //--------------------------------------------------------------------------------
231
saveProfileAs()232 void MainWindow::saveProfileAs()
233 {
234 QString fileName = QFileDialog::getSaveFileName(this, i18n("Select Profile"), QString(),
235 i18n("KBackup Profile (*.kbp)"));
236
237 if ( fileName.isEmpty() ) return;
238
239 saveProfile(fileName);
240 }
241
242 //--------------------------------------------------------------------------------
243
saveProfile(QString fileName)244 void MainWindow::saveProfile(QString fileName)
245 {
246 if ( fileName.isEmpty() )
247 fileName = loadedProfile;
248
249 if ( fileName.isEmpty() )
250 {
251 fileName = QFileDialog::getSaveFileName(this, i18n("Select Profile"), QString(),
252 i18n("KBackup Profile (*.kbp)"));
253
254 if ( fileName.isEmpty() ) return;
255 }
256
257 QFile file(fileName);
258
259 if ( file.exists() && (fileName != loadedProfile) )
260 {
261 if ( KMessageBox::warningYesNo(this,
262 i18n("The profile '%1' does already exist.\n"
263 "Do you want to overwrite it?",
264 fileName),
265 i18n("Profile exists")) == KMessageBox::No )
266 return;
267 }
268
269 QStringList includes, excludes;
270 selector->getBackupList(includes, excludes);
271 QString error;
272
273 Archiver::instance->setTarget(QUrl(mainWidget->getTargetLineEdit()->text()));
274
275 if ( ! Archiver::instance->saveProfile(fileName, includes, excludes, error) )
276 {
277 KMessageBox::error(this,
278 i18n("Could not open profile '%1' for writing: %2",
279 fileName,
280 error),
281 i18n("Open failed"));
282 return;
283 }
284
285 setLoadedProfile(fileName);
286 }
287
288 //--------------------------------------------------------------------------------
289
profileSettings()290 void MainWindow::profileSettings()
291 {
292 SettingsDialog dialog(this);
293
294 dialog.ui.prefix->setText(Archiver::instance->getFilePrefix());
295 dialog.setMaxMB(Archiver::instance->getMaxSliceMBs());
296 dialog.ui.numBackups->setValue(Archiver::instance->getKeptBackups());
297 dialog.ui.mediaNeedsChange->setChecked(Archiver::instance->getMediaNeedsChange());
298 dialog.ui.compressFiles->setChecked(Archiver::instance->getCompressFiles());
299 dialog.ui.fullBackupInterval->setValue(Archiver::instance->getFullBackupInterval());
300 dialog.ui.filter->setText(Archiver::instance->getFilter());
301 dialog.ui.dirFilter->setPlainText(Archiver::instance->getDirFilter());
302
303 if ( dialog.exec() == QDialog::Accepted )
304 {
305 Archiver::instance->setFilePrefix(dialog.ui.prefix->text().trimmed());
306 Archiver::instance->setMaxSliceMBs(dialog.ui.maxSliceSize->value());
307 Archiver::instance->setKeptBackups(dialog.ui.numBackups->value());
308 Archiver::instance->setMediaNeedsChange(dialog.ui.mediaNeedsChange->isChecked());
309 Archiver::instance->setCompressFiles(dialog.ui.compressFiles->isChecked());
310 Archiver::instance->setFullBackupInterval(dialog.ui.fullBackupInterval->value());
311 Archiver::instance->setFilter(dialog.ui.filter->text());
312 Archiver::instance->setDirFilter(dialog.ui.dirFilter->toPlainText());
313 }
314 }
315
316 //--------------------------------------------------------------------------------
317
newProfile()318 void MainWindow::newProfile()
319 {
320 Archiver::instance->setFilePrefix(QString()); // back to default
321 Archiver::instance->setMaxSliceMBs(Archiver::UNLIMITED);
322 Archiver::instance->setMediaNeedsChange(true);
323 Archiver::instance->setTarget(QUrl());
324 Archiver::instance->setKeptBackups(Archiver::UNLIMITED);
325 Archiver::instance->setFullBackupInterval(1);
326 Archiver::instance->setFilter(QString());
327 Archiver::instance->setDirFilter(QString());
328
329 // clear selection
330 QStringList includes, excludes;
331 selector->setBackupList(includes, excludes);
332
333 mainWidget->getTargetLineEdit()->setText(QString());
334
335 setLoadedProfile(QString());
336 }
337
338 //--------------------------------------------------------------------------------
339
loggingSlot(const QString & message)340 void MainWindow::loggingSlot(const QString &message)
341 {
342 lastLog = message;
343 changeSystrayTip();
344 }
345
346 //--------------------------------------------------------------------------------
347
changeSystrayTip()348 void MainWindow::changeSystrayTip()
349 {
350 if ( !sysTray )
351 return;
352
353 QString text = qApp->applicationDisplayName() + QLatin1String(" - ") +
354 i18n("Files: %1 Size: %2 MB\n%3",
355 Archiver::instance->getTotalFiles(),
356 QString::number(Archiver::instance->getTotalBytes() / 1024.0 / 1024.0, 'f', 2),
357 KStringHandler::csqueeze(lastLog, 60));
358
359 sysTray->setToolTip(QStringLiteral("kbackup"), QStringLiteral("kbackup"), text);
360 }
361
362 //--------------------------------------------------------------------------------
363
inProgress(bool runs)364 void MainWindow::inProgress(bool runs)
365 {
366 if ( runs )
367 {
368 if ( sysTray )
369 {
370 /*
371 QMovie *movie = KIconLoader::global()->loadMovie("kbackup_runs", KIconLoader::Panel);
372 if ( movie )
373 {
374 sysTray->setMovie(movie);
375 movie->start();
376 }
377 */
378 sysTray->setIconByName(QStringLiteral("kbackup_runs"));
379 sysTray->setStatus(KStatusNotifierItem::Active);
380 }
381
382 startBackupAction->setEnabled(false);
383 cancelBackupAction->setEnabled(true);
384 }
385 else
386 {
387 if ( sysTray )
388 {
389 /*
390 if ( sysTray->movie() )
391 const_cast<QMovie*>(sysTray->movie())->stop(); // why does it return a const pointer ? :-(
392
393 sysTray->setIcon(sysTray->loadIcon("kbackup"));
394 */
395 sysTray->setIconByName(QStringLiteral("kbackup"));
396 sysTray->setStatus(KStatusNotifierItem::Passive);
397 }
398
399 startBackupAction->setEnabled(true);
400 cancelBackupAction->setEnabled(false);
401
402 if ( autorun )
403 qApp->quit();
404 }
405 }
406
407 //--------------------------------------------------------------------------------
408
dockInSysTray(bool checked)409 void MainWindow::dockInSysTray(bool checked)
410 {
411 KSharedConfig::openConfig()->group("settings").writeEntry("dockInSysTray", checked);
412 KSharedConfig::openConfig()->group("settings").sync();
413
414 if ( checked )
415 {
416 // system tray icon
417 delete sysTray;
418 sysTray = new KStatusNotifierItem(this);
419 sysTray->setStandardActionsEnabled(false);
420
421 sysTray->contextMenu()->addAction(startBackupAction);
422 sysTray->contextMenu()->addAction(cancelBackupAction);
423 sysTray->contextMenu()->addAction(quitAction);
424
425 if ( Archiver::instance->isInProgress() )
426 {
427 sysTray->setStatus(KStatusNotifierItem::Active);
428 sysTray->setIconByName(QStringLiteral("kbackup_runs"));
429 }
430 else
431 {
432 sysTray->setStatus(KStatusNotifierItem::Passive);
433 sysTray->setIconByName(QStringLiteral("kbackup"));
434 }
435 }
436 else
437 {
438 delete sysTray;
439 sysTray = nullptr;
440 }
441 }
442
443 //--------------------------------------------------------------------------------
444
showHiddenFiles(bool checked)445 void MainWindow::showHiddenFiles(bool checked)
446 {
447 KSharedConfig::openConfig()->group("settings").writeEntry("showHiddenFiles", checked);
448 KSharedConfig::openConfig()->group("settings").sync();
449
450 selector->setShowHiddenFiles(checked);
451 }
452
453 //--------------------------------------------------------------------------------
454
enableAllMessages()455 void MainWindow::enableAllMessages()
456 {
457 KMessageBox::enableAllMessages();
458 }
459
460 //--------------------------------------------------------------------------------
461
setLoadedProfile(const QString & name)462 void MainWindow::setLoadedProfile(const QString &name)
463 {
464 loadedProfile = name;
465 Archiver::instance->setLoadedProfile(name);
466 setCaption(name);
467
468 if ( !name.isEmpty() )
469 {
470 QUrl url;
471 url.setPath(name);
472 recentFiles->addUrl(url);
473 recentFiles->saveEntries(KSharedConfig::openConfig()->group(""));
474 KSharedConfig::openConfig()->group("").sync();
475 }
476 }
477
478 //--------------------------------------------------------------------------------
479