File indexing completed on 2024-04-21 05:50:00

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