File indexing completed on 2024-04-28 09:45:20

0001 //**************************************************************************
0002 //   Copyright 2006 - 2022 Martin Koller, kollix@aon.at
0003 //
0004 //   This program is free software; you can redistribute it and/or modify
0005 //   it under the terms of the GNU General Public License as published by
0006 //   the Free Software Foundation, version 2 of the License
0007 //
0008 //**************************************************************************
0009 #include <MainWindow.hxx>
0010 #include <Selector.hxx>
0011 #include <Archiver.hxx>
0012 #include <MainWidget.hxx>
0013 #include <SettingsDialog.hxx>
0014 
0015 #include <QSplitter>
0016 #include <QSpinBox>
0017 #include <QToolTip>
0018 #include <QTimer>
0019 #include <QCursor>
0020 #include <QCheckBox>
0021 #include <QMenu>
0022 #include <QAction>
0023 #include <QUrl>
0024 #include <QApplication>
0025 #include <QFileDialog>
0026 #include <QHeaderView>
0027 
0028 #include <KXMLGUIFactory>
0029 #include <KStandardAction>
0030 #include <KActionCollection>
0031 #include <KToggleAction>
0032 #include <KRecentFilesAction>
0033 #include <KMessageBox>
0034 #include <kio/global.h>
0035 #include <KStringHandler>
0036 #include <KStatusNotifierItem>
0037 #include <KSharedConfig>
0038 #include <KConfigGroup>
0039 #include <KShortcutsDialog>
0040 
0041 //#include <iostream>
0042 //using namespace std;
0043 //--------------------------------------------------------------------------------
0044 
0045 MainWindow::MainWindow()
0046 {
0047   new Archiver(this);
0048 
0049   quitAction = KStandardAction::quit(this, SLOT(maybeQuit()), actionCollection());
0050   KStandardAction::keyBindings(guiFactory(), &KXMLGUIFactory::showConfigureShortcutsDialog, actionCollection());
0051 
0052 
0053   QAction *action;
0054 
0055   action = actionCollection()->addAction(QStringLiteral("newProfile"), this, SLOT(newProfile()));
0056   action->setText(i18n("New Profile"));
0057   action->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0058 
0059   action = actionCollection()->addAction(QStringLiteral("loadProfile"), this, SLOT(loadProfile()));
0060   action->setText(i18n("Load Profile..."));
0061   action->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
0062 
0063   action = actionCollection()->addAction(QStringLiteral("saveProfile"), this, SLOT(saveProfile()));
0064   action->setText(i18n("Save Profile"));
0065   action->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0066 
0067   action = actionCollection()->addAction(QStringLiteral("saveProfileAs"), this, SLOT(saveProfileAs()));
0068   action->setText(i18n("Save Profile As..."));
0069   action->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as")));
0070 
0071   action = actionCollection()->addAction(QStringLiteral("profileSettings"), this, SLOT(profileSettings()));
0072   action->setText(i18n("Profile Settings..."));
0073 
0074   action = actionCollection()->addAction(QStringLiteral("enableAllMessages"), this, SLOT(enableAllMessages()));
0075   action->setText(i18n("Enable All Messages"));
0076 
0077   KToggleAction *docked = new KToggleAction(i18n("Dock in System Tray"), this);
0078   actionCollection()->addAction(QStringLiteral("dockInSysTray"), docked);
0079   connect(docked, &QAction::toggled, this, &MainWindow::dockInSysTray);
0080 
0081   KToggleAction *showHidden = new KToggleAction(i18n("Show Hidden Files"), this);
0082   actionCollection()->addAction(QStringLiteral("showHiddenFiles"), showHidden);
0083   connect(showHidden, &QAction::toggled, this, &MainWindow::showHiddenFiles);
0084 
0085   recentFiles = KStandardAction::openRecent(this, SLOT(recentProfileSelected(const QUrl &)), actionCollection());
0086   recentFiles->setObjectName(QStringLiteral("recentProfiles"));
0087   recentFiles->loadEntries(KSharedConfig::openConfig()->group(QString()));
0088 
0089   createGUI();
0090 
0091   splitter = new QSplitter(Qt::Horizontal, this);
0092 
0093   selector = new Selector(splitter, actionCollection());
0094 
0095   mainWidget = new MainWidget(splitter);
0096   mainWidget->setSelector(selector);
0097   splitter->setCollapsible(splitter->indexOf(mainWidget), false);
0098 
0099   setCentralWidget(splitter);
0100 
0101   splitter->restoreState(KSharedConfig::openConfig()->group(QStringLiteral("geometry")).readEntry<QByteArray>("splitter", QByteArray()));
0102   selector->header()->restoreState(KSharedConfig::openConfig()->group(QStringLiteral("geometry")).readEntry<QByteArray>("tree", QByteArray()));
0103 
0104   // save/restore window settings and size
0105   setAutoSaveSettings();
0106 
0107   connect(Archiver::instance, &Archiver::totalFilesChanged, this, &MainWindow::changeSystrayTip);
0108   connect(Archiver::instance, &Archiver::logging, this, &MainWindow::loggingSlot);
0109   connect(Archiver::instance, &Archiver::inProgress, this, &MainWindow::inProgress);
0110 
0111   startBackupAction = actionCollection()->addAction(QStringLiteral("startBackup"), mainWidget, SLOT(startBackup()));
0112   startBackupAction->setIcon(QIcon::fromTheme(QStringLiteral("kbackup_start")));
0113   startBackupAction->setText(i18n("Start Backup"));
0114 
0115   cancelBackupAction = actionCollection()->addAction(QStringLiteral("cancelBackup"), Archiver::instance, SLOT(cancel()));
0116   cancelBackupAction->setText(i18n("Cancel Backup"));
0117   cancelBackupAction->setIcon(QIcon::fromTheme(QStringLiteral("kbackup_cancel")));
0118   cancelBackupAction->setEnabled(false);
0119 
0120   showHidden->setChecked(KSharedConfig::openConfig()->group(QStringLiteral("settings")).readEntry<bool>("showHiddenFiles", false));
0121   showHiddenFiles(showHidden->isChecked());
0122   docked->setChecked(KSharedConfig::openConfig()->group(QStringLiteral("settings")).readEntry<bool>("dockInSysTray", false));
0123   dockInSysTray(docked->isChecked());
0124 
0125   // for convenience, open the tree at the HOME directory
0126   selector->openHomeDir();
0127 
0128   changeSystrayTip();
0129 }
0130 
0131 //--------------------------------------------------------------------------------
0132 
0133 void MainWindow::runBackup()
0134 {
0135   autorun = true;
0136   QTimer::singleShot(0, mainWidget, &MainWidget::startBackup);
0137 }
0138 
0139 //--------------------------------------------------------------------------------
0140 
0141 bool MainWindow::stopAllowed()
0142 {
0143   if ( Archiver::instance->isInProgress() )
0144   {
0145     if ( KMessageBox::warningTwoActions(this,
0146             i18n("There is a backup in progress. Do you want to abort it?"), i18n("Backup in Progress"), KGuiItem(i18n("Abort")), KStandardGuiItem::cancel()) == KMessageBox::SecondaryAction )
0147       return false;
0148 
0149     Archiver::instance->cancel();
0150   }
0151 
0152   KSharedConfig::openConfig()->group(QStringLiteral("geometry")).writeEntry("splitter", splitter->saveState());
0153   KSharedConfig::openConfig()->group(QStringLiteral("geometry")).writeEntry("tree", selector->header()->saveState());
0154 
0155   return true;
0156 }
0157 
0158 //--------------------------------------------------------------------------------
0159 
0160 void MainWindow::maybeQuit()
0161 {
0162   if ( stopAllowed() )
0163     qApp->quit();
0164 }
0165 
0166 //--------------------------------------------------------------------------------
0167 
0168 bool MainWindow::queryClose()
0169 {
0170   if ( qApp->isSavingSession() || !sysTray )
0171     return stopAllowed();
0172 
0173   hide();
0174   return false;
0175 }
0176 
0177 //--------------------------------------------------------------------------------
0178 
0179 void MainWindow::recentProfileSelected(const QUrl &url)
0180 {
0181   loadProfile(url.path());
0182 }
0183 
0184 //--------------------------------------------------------------------------------
0185 
0186 void MainWindow::loadProfile()
0187 {
0188   QString fileName = QFileDialog::getOpenFileName(this, i18n("Select Profile"), QString(),
0189                                                   i18n("KBackup Profile (*.kbp)"));
0190 
0191   if ( fileName.isEmpty() ) return;
0192 
0193   loadProfile(fileName);
0194 }
0195 
0196 //--------------------------------------------------------------------------------
0197 
0198 void MainWindow::loadProfile(const QString &fileName, bool adaptTreeWidth)
0199 {
0200   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0201 
0202   QStringList includes, excludes;
0203   QString error;
0204 
0205   if ( ! Archiver::instance->loadProfile(fileName, includes, excludes, error) )
0206   {
0207     QApplication::restoreOverrideCursor();
0208 
0209     KMessageBox::error(this,
0210                 i18n("Could not open profile '%1' for reading: %2",
0211                      fileName,
0212                      error),
0213                 i18n("Open failed"));
0214     return;
0215   }
0216 
0217   setLoadedProfile(fileName);
0218 
0219   // now fill the Selector tree with those settings
0220   selector->setBackupList(includes, excludes);
0221 
0222   mainWidget->getTargetLineEdit()->setText(Archiver::instance->getTarget().toDisplayString(QUrl::PreferLocalFile));
0223 
0224   if ( adaptTreeWidth )
0225     selector->resizeColumnToContents(0);
0226 
0227   QApplication::restoreOverrideCursor();
0228 }
0229 
0230 //--------------------------------------------------------------------------------
0231 
0232 void MainWindow::saveProfileAs()
0233 {
0234   QString fileName = QFileDialog::getSaveFileName(this, i18n("Select Profile"), QString(),
0235                                                   i18n("KBackup Profile (*.kbp)"));
0236 
0237   if ( fileName.isEmpty() ) return;
0238 
0239   saveProfile(fileName);
0240 }
0241 
0242 //--------------------------------------------------------------------------------
0243 
0244 void MainWindow::saveProfile(QString fileName)
0245 {
0246   if ( fileName.isEmpty() )
0247     fileName = loadedProfile;
0248 
0249   if ( fileName.isEmpty() )
0250   {
0251     fileName = QFileDialog::getSaveFileName(this, i18n("Select Profile"), QString(),
0252                                             i18n("KBackup Profile (*.kbp)"));
0253 
0254     if ( fileName.isEmpty() ) return;
0255   }
0256 
0257   QFile file(fileName);
0258 
0259   if ( file.exists() && (fileName != loadedProfile) )
0260   {
0261     if ( KMessageBox::warningTwoActions(this,
0262                 i18n("The profile '%1' does already exist.\n"
0263                      "Do you want to overwrite it?",
0264                      fileName),
0265                 i18n("Profile exists"), KStandardGuiItem::save(), KStandardGuiItem::discard()) == KMessageBox::SecondaryAction )
0266       return;
0267   }
0268 
0269   QStringList includes, excludes;
0270   selector->getBackupList(includes, excludes);
0271   QString error;
0272 
0273   Archiver::instance->setTarget(QUrl(mainWidget->getTargetLineEdit()->text()));
0274 
0275   // when manually saving a profile, we need to restart the backup (incremental) cycle
0276   // since e.g. adding a new dir would otherwise not save its content on the next run
0277   Archiver::instance->resetBackupCycle();
0278 
0279   if ( ! Archiver::instance->saveProfile(fileName, includes, excludes, error) )
0280   {
0281     KMessageBox::error(this,
0282                 i18n("Could not open profile '%1' for writing: %2",
0283                      fileName,
0284                      error),
0285                 i18n("Open failed"));
0286     return;
0287   }
0288 
0289   setLoadedProfile(fileName);
0290 }
0291 
0292 //--------------------------------------------------------------------------------
0293 
0294 void MainWindow::profileSettings()
0295 {
0296   SettingsDialog dialog(this);
0297 
0298   dialog.ui.prefix->setText(Archiver::instance->getFilePrefix());
0299   dialog.setMaxMB(Archiver::instance->getMaxSliceMBs());
0300   dialog.ui.numBackups->setValue(Archiver::instance->getKeptBackups());
0301   dialog.ui.mediaNeedsChange->setChecked(Archiver::instance->getMediaNeedsChange());
0302   dialog.ui.compressFiles->setChecked(Archiver::instance->getCompressFiles());
0303   dialog.ui.fullBackupInterval->setValue(Archiver::instance->getFullBackupInterval());
0304   dialog.ui.filter->setText(Archiver::instance->getFilter());
0305   dialog.ui.dirFilter->setPlainText(Archiver::instance->getDirFilter());
0306 
0307   if ( dialog.exec() == QDialog::Accepted )
0308   {
0309     Archiver::instance->setFilePrefix(dialog.ui.prefix->text().trimmed());
0310     Archiver::instance->setMaxSliceMBs(dialog.ui.maxSliceSize->value());
0311     Archiver::instance->setKeptBackups(dialog.ui.numBackups->value());
0312     Archiver::instance->setMediaNeedsChange(dialog.ui.mediaNeedsChange->isChecked());
0313     Archiver::instance->setCompressFiles(dialog.ui.compressFiles->isChecked());
0314     Archiver::instance->setFullBackupInterval(dialog.ui.fullBackupInterval->value());
0315     Archiver::instance->setFilter(dialog.ui.filter->text());
0316     Archiver::instance->setDirFilter(dialog.ui.dirFilter->toPlainText());
0317   }
0318 }
0319 
0320 //--------------------------------------------------------------------------------
0321 
0322 void MainWindow::newProfile()
0323 {
0324   Archiver::instance->setFilePrefix(QString());  // back to default
0325   Archiver::instance->setMaxSliceMBs(Archiver::UNLIMITED);
0326   Archiver::instance->setMediaNeedsChange(true);
0327   Archiver::instance->setTarget(QUrl());
0328   Archiver::instance->setKeptBackups(Archiver::UNLIMITED);
0329   Archiver::instance->setFullBackupInterval(1);
0330   Archiver::instance->setFilter(QString());
0331   Archiver::instance->setDirFilter(QString());
0332 
0333   // clear selection
0334   QStringList includes, excludes;
0335   selector->setBackupList(includes, excludes);
0336 
0337   mainWidget->getTargetLineEdit()->setText(QString());
0338 
0339   setLoadedProfile(QString());
0340 }
0341 
0342 //--------------------------------------------------------------------------------
0343 
0344 void MainWindow::loggingSlot(const QString &message)
0345 {
0346   lastLog = message;
0347   changeSystrayTip();
0348 }
0349 
0350 //--------------------------------------------------------------------------------
0351 
0352 void MainWindow::changeSystrayTip()
0353 {
0354   if ( !sysTray )
0355     return;
0356 
0357   QString text = qApp->applicationDisplayName() + QLatin1String(" - ") +
0358                  i18n("Files: %1 Size: %2 MB\n%3",
0359                     Archiver::instance->getTotalFiles(),
0360                     QString::number(Archiver::instance->getTotalBytes() / 1024.0 / 1024.0, 'f', 2),
0361                     KStringHandler::csqueeze(lastLog, 60));
0362 
0363   sysTray->setToolTip(QStringLiteral("kbackup"), QStringLiteral("kbackup"), text);
0364 }
0365 
0366 //--------------------------------------------------------------------------------
0367 
0368 void MainWindow::inProgress(bool runs)
0369 {
0370   if ( runs )
0371   {
0372     if ( sysTray )
0373     {
0374       /*
0375       QMovie *movie = KIconLoader::global()->loadMovie("kbackup_runs", KIconLoader::Panel);
0376       if ( movie )
0377       {
0378         sysTray->setMovie(movie);
0379         movie->start();
0380       }
0381       */
0382       sysTray->setIconByName(QStringLiteral("kbackup_runs"));
0383       sysTray->setStatus(KStatusNotifierItem::Active);
0384     }
0385 
0386     startBackupAction->setEnabled(false);
0387     cancelBackupAction->setEnabled(true);
0388   }
0389   else
0390   {
0391     if ( sysTray )
0392     {
0393       /*
0394       if ( sysTray->movie() )
0395         const_cast<QMovie*>(sysTray->movie())->stop();  // why does it return a const pointer ? :-(
0396 
0397       sysTray->setIcon(sysTray->loadIcon("kbackup"));
0398       */
0399       sysTray->setIconByName(QStringLiteral("kbackup"));
0400       sysTray->setStatus(KStatusNotifierItem::Passive);
0401     }
0402 
0403     startBackupAction->setEnabled(true);
0404     cancelBackupAction->setEnabled(false);
0405 
0406     if ( autorun )
0407       qApp->quit();
0408   }
0409 }
0410 
0411 //--------------------------------------------------------------------------------
0412 
0413 void MainWindow::dockInSysTray(bool checked)
0414 {
0415   KSharedConfig::openConfig()->group(QStringLiteral("settings")).writeEntry("dockInSysTray", checked);
0416   KSharedConfig::openConfig()->group(QStringLiteral("settings")).sync();
0417 
0418   if ( checked )
0419   {
0420     // system tray icon
0421     delete sysTray;
0422     sysTray = new KStatusNotifierItem(this);
0423     sysTray->setStandardActionsEnabled(false);
0424 
0425     sysTray->contextMenu()->addAction(startBackupAction);
0426     sysTray->contextMenu()->addAction(cancelBackupAction);
0427     sysTray->contextMenu()->addAction(quitAction);
0428 
0429     if ( Archiver::instance->isInProgress() )
0430     {
0431       sysTray->setStatus(KStatusNotifierItem::Active);
0432       sysTray->setIconByName(QStringLiteral("kbackup_runs"));
0433     }
0434     else
0435     {
0436       sysTray->setStatus(KStatusNotifierItem::Passive);
0437       sysTray->setIconByName(QStringLiteral("kbackup"));
0438     }
0439   }
0440   else
0441   {
0442     delete sysTray;
0443     sysTray = nullptr;
0444   }
0445 }
0446 
0447 //--------------------------------------------------------------------------------
0448 
0449 void MainWindow::showHiddenFiles(bool checked)
0450 {
0451   KSharedConfig::openConfig()->group(QStringLiteral("settings")).writeEntry("showHiddenFiles", checked);
0452   KSharedConfig::openConfig()->group(QStringLiteral("settings")).sync();
0453 
0454   selector->setShowHiddenFiles(checked);
0455 }
0456 
0457 //--------------------------------------------------------------------------------
0458 
0459 void MainWindow::enableAllMessages()
0460 {
0461   KMessageBox::enableAllMessages();
0462 }
0463 
0464 //--------------------------------------------------------------------------------
0465 
0466 void MainWindow::setLoadedProfile(const QString &name)
0467 {
0468   loadedProfile = name;
0469   Archiver::instance->setLoadedProfile(name);
0470   setCaption(name);
0471 
0472   if ( !name.isEmpty() )
0473   {
0474     QUrl url;
0475     url.setPath(name);
0476     recentFiles->addUrl(url);
0477     recentFiles->saveEntries(KSharedConfig::openConfig()->group(QString()));
0478     KSharedConfig::openConfig()->group(QString()).sync();
0479   }
0480 }
0481 
0482 //--------------------------------------------------------------------------------
0483 
0484 #include "moc_MainWindow.cpp"