File indexing completed on 2024-04-28 05:46:33

0001 /*
0002     SPDX-FileCopyrightText: 2008-2012 Volker Lanz <vl@fidra.de>
0003     SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas <andrius@stikonas.eu>
0004     SPDX-FileCopyrightText: 2015 Teo Mrnjavac <teo@kde.org>
0005     SPDX-FileCopyrightText: 2016 Chantara Tith <tith.chantara@gmail.com>
0006     SPDX-FileCopyrightText: 2018 Caio Jordão Carvalho <caiojcarvalho@gmail.com>
0007     SPDX-FileCopyrightText: 2018 René J.V. Bertin <rjvbertin@gmail.com>
0008     SPDX-FileCopyrightText: 2018 Huzaifa Faruqui <huzaifafaruqui@gmail.com>
0009 
0010     SPDX-License-Identifier: GPL-3.0-or-later
0011 */
0012 
0013 #include "gui/mainwindow.h"
0014 #include "gui/infopane.h"
0015 #include "gui/applyprogressdialog.h"
0016 #include "gui/scanprogressdialog.h"
0017 #include "gui/createpartitiontabledialog.h"
0018 #include "gui/createvolumegroupdialog.h"
0019 #include "gui/resizevolumegroupdialog.h"
0020 #include "gui/filesystemsupportdialog.h"
0021 #include "gui/devicepropsdialog.h"
0022 #include "gui/smartdialog.h"
0023 
0024 #include "config/configureoptionsdialog.h"
0025 
0026 #include <backend/corebackendmanager.h>
0027 #include <backend/corebackend.h>
0028 
0029 #include <core/device.h>
0030 #include <core/partitionalignment.h>
0031 #include <core/smartstatus.h>
0032 
0033 #include <ops/operation.h>
0034 #include <ops/createpartitiontableoperation.h>
0035 #include <ops/createvolumegroupoperation.h>
0036 #include <ops/resizevolumegroupoperation.h>
0037 #include <ops/removevolumegroupoperation.h>
0038 #include <ops/deactivatevolumegroupoperation.h>
0039 #include <ops/resizeoperation.h>
0040 #include <ops/copyoperation.h>
0041 #include <ops/deleteoperation.h>
0042 #include <ops/newoperation.h>
0043 #include <ops/backupoperation.h>
0044 #include <ops/restoreoperation.h>
0045 #include <ops/checkoperation.h>
0046 #include <ops/setpartflagsoperation.h>
0047 
0048 #include <fs/filesystem.h>
0049 #include <fs/filesystemfactory.h>
0050 #include <fs/luks.h>
0051 
0052 #include <util/externalcommand.h>
0053 #include <util/helpers.h>
0054 #include <util/guihelpers.h>
0055 #include <util/report.h>
0056 
0057 #include <algorithm>
0058 
0059 #include <QApplication>
0060 #include <QCloseEvent>
0061 #include <QCollator>
0062 #include <QDateTime>
0063 #include <QFile>
0064 #include <QFileDialog>
0065 #include <QtGlobal>
0066 #include <QMenu>
0067 #include <QPointer>
0068 #include <QPushButton>
0069 #include <QReadLocker>
0070 #include <QRegularExpression>
0071 #include <QStatusBar>
0072 #include <QTemporaryFile>
0073 #include <QTextStream>
0074 
0075 #include <PolkitQt1/Authority>
0076 #include <polkitqt1-version.h>
0077 
0078 #include <KAboutApplicationDialog>
0079 #include <KActionCollection>
0080 #include <KMessageBox>
0081 #include <KAboutData>
0082 #include <KLocalizedString>
0083 #include <KXMLGUIFactory>
0084 #include <KJobUiDelegate>
0085 #include <KIO/CopyJob>
0086 #include <KIO/FileCopyJob>
0087 #include <KJobWidgets>
0088 #include "config.h"
0089 
0090 /** Creates a new MainWindow instance.
0091     @param parent the parent widget
0092 */
0093 MainWindow::MainWindow(QWidget* parent) :
0094     KXmlGuiWindow(parent),
0095     Ui::MainWindowBase(),
0096     m_OperationStack(new OperationStack(this)),
0097     m_OperationRunner(new OperationRunner(this, operationStack())),
0098     m_DeviceScanner(new DeviceScanner(this, operationStack())),
0099     m_ApplyProgressDialog(new ApplyProgressDialog(this, operationRunner())),
0100     m_ScanProgressDialog(new ScanProgressDialog(this)),
0101     m_StatusText(new QLabel(this))
0102 {
0103     CoreBackend::isPolkitInstalledCorrectly();
0104 
0105     setupObjectNames();
0106     setupUi(this);
0107     init();
0108 }
0109 
0110 void MainWindow::setupObjectNames()
0111 {
0112     m_OperationStack->setObjectName(QStringLiteral("m_OperationStack"));
0113     m_OperationRunner->setObjectName(QStringLiteral("m_OperationRunner"));
0114     m_DeviceScanner->setObjectName(QStringLiteral("m_DeviceScanner"));
0115     m_ApplyProgressDialog->setObjectName(QStringLiteral("m_ApplyProgressDialog"));
0116     m_ScanProgressDialog->setObjectName(QStringLiteral("m_ScanProgressDialog"));
0117 }
0118 
0119 void MainWindow::setDisallowOtherDevices()
0120 {
0121     // We need to store that we are hiding this for this session only
0122     // but only if it's currently visible (ie, the user didn't select
0123     // that it should be hidden on purpose.
0124     if (m_DockDevices->isVisible() == true) {
0125         Config::self()->setHideDeviceDockWidgetByCmdArgs(true);
0126     }
0127 
0128     // because of how Qt works, the user still can enable the
0129     // dock widget via a mouse click, so we need to also set it to disabled.
0130     // so that the user doesn't select it by mistake.
0131 
0132     m_DockDevices->setVisible(false);
0133     m_DockDevices->setEnabled(false);
0134 }
0135 
0136 void MainWindow::showDevicePanelIfPreviouslyHiddenByDisallowOtherDevices()
0137 {
0138     if (Config::self()->hideDeviceDockWidgetByCmdArgs()) {
0139         m_DockDevices->setVisible(true);
0140         Config::self()->setHideDeviceDockWidgetByCmdArgs(false);
0141     }
0142 }
0143 
0144 void MainWindow::init()
0145 {
0146     treeLog().init();
0147 
0148     setupActions();
0149     setupStatusBar();
0150     setupConnections();
0151 
0152     listDevices().setActionCollection(actionCollection());
0153     listOperations().setActionCollection(actionCollection());
0154 
0155     setupGUI();
0156 
0157     loadConfig();
0158 
0159     // this is done in order to hide the title bar of MessageWidget dock
0160     findChild<QDockWidget*>(QStringLiteral("MessageWidgetDock"))->setTitleBarWidget(new QWidget());
0161 
0162     // HACK: Otherwise the message widget bottom border is not correctly rendered
0163     findChild<QDockWidget*>(QStringLiteral("MessageWidgetDock"))->setContentsMargins(0, 0, 0, 1);
0164 
0165     MessageWidget().hide();
0166     MessageWidget().setText(
0167         i18nc("@info", "Partition Manager requires privileges in order to work. Please refresh the devices and authenticate when prompted."));
0168 
0169     show();
0170     pmWidget().init(&operationStack());
0171 
0172     scanProgressDialog().cancel();
0173     setEnabled(false);
0174     guiFactory()->container(QStringLiteral("selectedDevice"), this)->setEnabled(false);
0175 
0176     askForPermissions();
0177 
0178     if (m_permissionGranted) {
0179         FileSystemFactory::init();
0180         scanDevices();
0181     } else
0182         Q_EMIT showMessageWidget();
0183 
0184     setEnabled(true);
0185 }
0186 
0187 void MainWindow::askForPermissions()
0188 {
0189     PolkitQt1::UnixProcessSubject subject(QApplication::applicationPid());
0190     PolkitQt1::Authority *authority = PolkitQt1::Authority::instance();
0191 
0192     PolkitQt1::Authority::Result result;
0193     QEventLoop e;
0194     connect(authority, &PolkitQt1::Authority::checkAuthorizationFinished, &e,
0195             [&e, &result](PolkitQt1::Authority::Result _result) {
0196                 result = _result;
0197                 e.quit();
0198             });
0199 
0200     authority->checkAuthorization(QStringLiteral("org.kde.kpmcore.externalcommand.init"), subject, PolkitQt1::Authority::AllowUserInteraction);
0201     e.exec();
0202 
0203     if (authority->hasError()) {
0204         qDebug() << "Encountered error while checking authorization, error code:"
0205                  << authority->lastError() << authority->errorDetails();
0206         authority->clearError();
0207     }
0208 
0209     m_permissionGranted = result == PolkitQt1::Authority::Yes;
0210 }
0211 
0212 QMenu *MainWindow::createPopupMenu()
0213 {
0214     auto menu = QMainWindow::createPopupMenu();
0215     auto actions = menu->actions();
0216     QAction *toRemove =
0217         *std::find_if(actions.begin(), actions.end(),
0218                       [](QAction *x) { return x->text().isEmpty(); });
0219     // this is done in order to hide the entry for the MessageWidget dock
0220     menu->removeAction(toRemove);
0221 
0222     return menu;
0223 }
0224 
0225 void MainWindow::closeEvent(QCloseEvent* event)
0226 {
0227     if (applyProgressDialog().isVisible()) {
0228         event->ignore();
0229         return;
0230     }
0231 
0232     if (operationStack().size() > 0) {
0233         if (KMessageBox::warningContinueCancel(this,
0234                                                xi18ncp("@info", "<para>Do you really want to quit the application?</para><para>There is still an operation pending.</para>",
0235                                                        "<para>Do you really want to quit the application?</para><para>There are still %1 operations pending.</para>", operationStack().size()),
0236                                                xi18nc("@title:window", "Discard Pending Operations and Quit?"),
0237                                                KGuiItem(xi18nc("@action:button", "Quit <application>%1</application>", QGuiApplication::applicationDisplayName()), QStringLiteral("arrow-right")),
0238                                                KStandardGuiItem::cancel(), QStringLiteral("reallyQuit")) == KMessageBox::Cancel) {
0239             event->ignore();
0240             return;
0241         }
0242     }
0243 
0244     saveConfig();
0245 
0246     KXmlGuiWindow::closeEvent(event);
0247     delete m_ApplyProgressDialog;
0248 }
0249 
0250 void MainWindow::changeEvent(QEvent* event)
0251 {
0252     if ((event->type() == QEvent::ActivationChange || event->type() == QEvent::WindowStateChange) && event->spontaneous() && isActiveWindow()) {
0253         QWidget* w = nullptr;
0254 
0255         if (applyProgressDialog().isVisible())
0256             w = &applyProgressDialog();
0257         else if (scanProgressDialog().isVisible())
0258             w = &scanProgressDialog();
0259 
0260         if (w != nullptr) {
0261             w->activateWindow();
0262             w->raise();
0263             w->setFocus();
0264         }
0265     }
0266 
0267     KXmlGuiWindow::changeEvent(event);
0268 }
0269 
0270 void MainWindow::setupActions()
0271 {
0272     // File actions
0273     KStandardAction::quit(this, &MainWindow::close, actionCollection());
0274 
0275     // Edit actions
0276     QAction* undoOperation = actionCollection()->addAction(QStringLiteral("undoOperation"));
0277     connect(undoOperation, &QAction::triggered, this, &MainWindow::onUndoOperation);
0278     undoOperation->setEnabled(false);
0279     undoOperation->setText(xi18nc("@action:inmenu", "Undo"));
0280     undoOperation->setToolTip(xi18nc("@info:tooltip", "Undo the last operation"));
0281     undoOperation->setStatusTip(xi18nc("@info:status", "Remove the last operation from the list."));
0282     actionCollection()->setDefaultShortcut(undoOperation, QKeySequence(Qt::CTRL | Qt::Key_Z));
0283     undoOperation->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
0284 
0285     QAction* clearAllOperations = actionCollection()->addAction(QStringLiteral("clearAllOperations"));
0286     connect(clearAllOperations, &QAction::triggered, this, &MainWindow::onClearAllOperations);
0287     clearAllOperations->setEnabled(false);
0288     clearAllOperations->setText(xi18nc("@action:inmenu clear the list of operations", "Clear"));
0289     clearAllOperations->setToolTip(xi18nc("@info:tooltip", "Clear all operations"));
0290     clearAllOperations->setStatusTip(xi18nc("@info:status", "Empty the list of pending operations."));
0291     clearAllOperations->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")));
0292 
0293     QAction* applyAllOperations = actionCollection()->addAction(QStringLiteral("applyAllOperations"));
0294     connect(applyAllOperations, &QAction::triggered, this, &MainWindow::onApplyAllOperations);
0295     applyAllOperations->setEnabled(false);
0296     applyAllOperations->setText(xi18nc("@action:inmenu apply all operations", "Apply"));
0297     applyAllOperations->setToolTip(xi18nc("@info:tooltip", "Apply all operations"));
0298     applyAllOperations->setStatusTip(xi18nc("@info:status", "Apply the pending operations in the list."));
0299     applyAllOperations->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")));
0300 
0301     // Device actions
0302     QAction* createNewPartitionTable = actionCollection()->addAction(QStringLiteral("createNewPartitionTable"));
0303     connect(createNewPartitionTable, &QAction::triggered, this, &MainWindow::onCreateNewPartitionTable);
0304     createNewPartitionTable->setEnabled(false);
0305     createNewPartitionTable->setText(xi18nc("@action:inmenu", "New Partition Table"));
0306     createNewPartitionTable->setToolTip(xi18nc("@info:tooltip", "Create a new partition table"));
0307     createNewPartitionTable->setStatusTip(xi18nc("@info:status", "Create a new and empty partition table on a device."));
0308     actionCollection()->setDefaultShortcut(createNewPartitionTable, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_N));
0309     createNewPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")));
0310 
0311     QAction* exportPartitionTable = actionCollection()->addAction(QStringLiteral("exportPartitionTable"));
0312     connect(exportPartitionTable, &QAction::triggered, this, &MainWindow::onExportPartitionTable);
0313     exportPartitionTable->setEnabled(false);
0314     exportPartitionTable->setText(xi18nc("@action:inmenu", "Export Partition Table"));
0315     exportPartitionTable->setToolTip(xi18nc("@info:tooltip", "Export a partition table"));
0316     exportPartitionTable->setStatusTip(xi18nc("@info:status", "Export the device's partition table to a text file."));
0317     exportPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
0318 
0319     QAction* importPartitionTable = actionCollection()->addAction(QStringLiteral("importPartitionTable"));
0320     connect(importPartitionTable, &QAction::triggered, this, &MainWindow::onImportPartitionTable);
0321     importPartitionTable->setEnabled(false);
0322     importPartitionTable->setText(xi18nc("@action:inmenu", "Import Partition Table"));
0323     importPartitionTable->setToolTip(xi18nc("@info:tooltip", "Import a partition table"));
0324     importPartitionTable->setStatusTip(xi18nc("@info:status", "Import a partition table from a text file."));
0325     importPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
0326 
0327     QAction* removeVolumeGroup = actionCollection()->addAction(QStringLiteral("removeVolumeGroup"));
0328     connect(removeVolumeGroup, &QAction::triggered, this, &MainWindow::onRemoveVolumeGroup);
0329     removeVolumeGroup->setEnabled(false);
0330     removeVolumeGroup->setVisible(false);
0331     removeVolumeGroup->setText(i18nc("@action:inmenu", "Remove Volume Group"));
0332     removeVolumeGroup->setToolTip(i18nc("@info:tooltip", "Remove selected Volume Group"));
0333     removeVolumeGroup->setStatusTip(i18nc("@info:status", "Remove selected Volume Group."));
0334     //actionCollection()->setDefaultShortcut(removeVolumeGroup, QKeySequence(/*SHORTCUT KEY HERE*/));
0335     removeVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0336 
0337     QAction* resizeVolumeGroup = actionCollection()->addAction(QStringLiteral("resizeVolumeGroup"));
0338     connect(resizeVolumeGroup, &QAction::triggered, this, &MainWindow::onResizeVolumeGroup);
0339     resizeVolumeGroup->setEnabled(false);
0340     resizeVolumeGroup->setVisible(false);
0341     resizeVolumeGroup->setText(i18nc("@action:inmenu", "Resize Volume Group"));
0342     resizeVolumeGroup->setToolTip(i18nc("@info:tooltip", "Resize selected Volume Group"));
0343     resizeVolumeGroup->setStatusTip(i18nc("@info:status", "Extend or reduce selected Volume Group."));
0344     //actionCollection()->setDefaultShortcut(resizeVolumeGroup, QKeySequence(/*SHORTCUT KEY HERE*/));
0345     resizeVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
0346 
0347     QAction* deactivateVolumeGroup = actionCollection()->addAction(QStringLiteral("deactivateVolumeGroup"));
0348     connect(deactivateVolumeGroup, &QAction::triggered, this, &MainWindow::onDeactivateVolumeGroup);
0349     deactivateVolumeGroup->setEnabled(false);
0350     deactivateVolumeGroup->setVisible(false);
0351     deactivateVolumeGroup->setText(i18nc("@action:inmenu", "Deactivate Volume Group"));
0352     deactivateVolumeGroup->setToolTip(i18nc("@info:tooltip", "Deactivate selected Volume Group"));
0353     deactivateVolumeGroup->setStatusTip(i18nc("@info:status", "Deactivate selected Volume Group before unplugging removable devices."));
0354     deactivateVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("media-eject")));
0355 
0356     QAction* smartStatusDevice = actionCollection()->addAction(QStringLiteral("smartStatusDevice"));
0357     connect(smartStatusDevice, &QAction::triggered, this, &MainWindow::onSmartStatusDevice);
0358     smartStatusDevice->setEnabled(false);
0359     smartStatusDevice->setText(xi18nc("@action:inmenu", "SMART Status"));
0360     smartStatusDevice->setToolTip(xi18nc("@info:tooltip", "Show SMART status"));
0361     smartStatusDevice->setStatusTip(xi18nc("@info:status", "Show the device's SMART status if supported"));
0362 
0363     QAction* propertiesDevice = actionCollection()->addAction(QStringLiteral("propertiesDevice"));
0364     connect(propertiesDevice, &QAction::triggered, [this] {onPropertiesDevice({});});
0365     propertiesDevice->setEnabled(false);
0366     propertiesDevice->setText(xi18nc("@action:inmenu", "Properties"));
0367     propertiesDevice->setToolTip(xi18nc("@info:tooltip", "Show device properties dialog"));
0368     propertiesDevice->setStatusTip(xi18nc("@info:status", "View and modify device properties"));
0369     propertiesDevice->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0370 
0371     // Partition actions
0372     QAction* newPartition = actionCollection()->addAction(QStringLiteral("newPartition"));
0373     connect(newPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onNewPartition);
0374     newPartition->setEnabled(false);
0375     newPartition->setText(xi18nc("@action:inmenu create a new partition", "New"));
0376     newPartition->setToolTip(xi18nc("@info:tooltip", "New partition"));
0377     newPartition->setStatusTip(xi18nc("@info:status", "Create a new partition."));
0378     actionCollection()->setDefaultShortcut(newPartition, QKeySequence(Qt::CTRL | Qt::Key_N));
0379     newPartition->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0380 
0381     QAction* resizePartition = actionCollection()->addAction(QStringLiteral("resizePartition"));
0382     connect(resizePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onResizePartition);
0383     resizePartition->setEnabled(false);
0384     resizePartition->setText(xi18nc("@action:inmenu", "Resize/Move"));
0385     resizePartition->setToolTip(xi18nc("@info:tooltip", "Resize or move partition"));
0386     resizePartition->setStatusTip(xi18nc("@info:status", "Shrink, grow or move an existing partition."));
0387     actionCollection()->setDefaultShortcut(resizePartition, QKeySequence(Qt::CTRL | Qt::Key_R));
0388     resizePartition->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
0389 
0390     QAction* deletePartition = actionCollection()->addAction(QStringLiteral("deletePartition"));
0391     connect(deletePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onDeletePartition);
0392     deletePartition->setEnabled(false);
0393     deletePartition->setText(xi18nc("@action:inmenu", "Delete"));
0394     deletePartition->setToolTip(xi18nc("@info:tooltip", "Delete partition"));
0395     deletePartition->setStatusTip(xi18nc("@info:status", "Delete a partition."));
0396     actionCollection()->setDefaultShortcut(deletePartition, QKeySequence::Delete);
0397     deletePartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0398 
0399     QAction* shredPartition = actionCollection()->addAction(QStringLiteral("shredPartition"));
0400     connect(shredPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onShredPartition);
0401     shredPartition->setEnabled(false);
0402     shredPartition->setText(xi18nc("@action:inmenu", "Shred"));
0403     shredPartition->setToolTip(xi18nc("@info:tooltip", "Shred partition"));
0404     shredPartition->setStatusTip(xi18nc("@info:status", "Shred a partition so that its contents cannot be restored."));
0405     actionCollection()->setDefaultShortcut(shredPartition, QKeySequence(Qt::SHIFT | Qt::Key_Delete));
0406     shredPartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete-shred")));
0407 
0408     QAction* copyPartition = actionCollection()->addAction(QStringLiteral("copyPartition"));
0409     connect(copyPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onCopyPartition);
0410     copyPartition->setEnabled(false);
0411     copyPartition->setText(xi18nc("@action:inmenu", "Copy"));
0412     copyPartition->setToolTip(xi18nc("@info:tooltip", "Copy partition"));
0413     copyPartition->setStatusTip(xi18nc("@info:status", "Copy an existing partition."));
0414     actionCollection()->setDefaultShortcut(copyPartition, QKeySequence(Qt::CTRL | Qt::Key_C));
0415     copyPartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
0416 
0417     QAction* pastePartition = actionCollection()->addAction(QStringLiteral("pastePartition"));
0418     connect(pastePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onPastePartition);
0419     pastePartition->setEnabled(false);
0420     pastePartition->setText(xi18nc("@action:inmenu", "Paste"));
0421     pastePartition->setToolTip(xi18nc("@info:tooltip", "Paste partition"));
0422     pastePartition->setStatusTip(xi18nc("@info:status", "Paste a copied partition."));
0423     actionCollection()->setDefaultShortcut(pastePartition, QKeySequence(Qt::CTRL | Qt::Key_V));
0424     pastePartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste")));
0425 
0426     QAction* editMountPoint = actionCollection()->addAction(QStringLiteral("editMountPoint"));
0427     connect(editMountPoint, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onEditMountPoint);
0428     editMountPoint->setEnabled(false);
0429     editMountPoint->setText(xi18nc("@action:inmenu", "Edit Mount Point"));
0430     editMountPoint->setToolTip(xi18nc("@info:tooltip", "Edit mount point"));
0431     editMountPoint->setStatusTip(xi18nc("@info:status", "Edit a partition's mount point and options."));
0432 
0433     QAction* mountPartition = actionCollection()->addAction(QStringLiteral("mountPartition"));
0434     connect(mountPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onMountPartition);
0435     mountPartition->setEnabled(false);
0436     mountPartition->setText(xi18nc("@action:inmenu", "Mount"));
0437     mountPartition->setToolTip(xi18nc("@info:tooltip", "Mount or unmount partition"));
0438     mountPartition->setStatusTip(xi18nc("@info:status", "Mount or unmount a partition."));
0439 
0440     QAction* decryptPartition = actionCollection()->addAction(QStringLiteral("decryptPartition"));
0441     connect(decryptPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onDecryptPartition);
0442     decryptPartition->setEnabled(false);
0443     decryptPartition->setText(xi18nc("@action:inmenu", "Unlock"));
0444     decryptPartition->setToolTip(xi18nc("@info:tooltip", "Unlock or lock encrypted partition"));
0445     decryptPartition->setStatusTip(xi18nc("@info:status", "Unlock or lock encrypted partition."));
0446 
0447     QAction* checkPartition = actionCollection()->addAction(QStringLiteral("checkPartition"));
0448     connect(checkPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onCheckPartition);
0449     checkPartition->setEnabled(false);
0450     checkPartition->setText(xi18nc("@action:inmenu", "Check"));
0451     checkPartition->setToolTip(xi18nc("@info:tooltip", "Check partition"));
0452     checkPartition->setStatusTip(xi18nc("@info:status", "Check a filesystem on a partition for errors."));
0453     checkPartition->setIcon(QIcon::fromTheme(QStringLiteral("flag")));
0454 
0455     QAction* propertiesPartition = actionCollection()->addAction(QStringLiteral("propertiesPartition"));
0456     connect(propertiesPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onPropertiesPartition);
0457     propertiesPartition->setEnabled(false);
0458     propertiesPartition->setText(xi18nc("@action:inmenu", "Properties"));
0459     propertiesPartition->setToolTip(xi18nc("@info:tooltip", "Show partition properties dialog"));
0460     propertiesPartition->setStatusTip(xi18nc("@info:status", "View and modify partition properties (label, partition flags, etc.)"));
0461     propertiesPartition->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
0462 
0463     QAction* backup = actionCollection()->addAction(QStringLiteral("backupPartition"));
0464     connect(backup, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onBackupPartition);
0465     backup->setEnabled(false);
0466     backup->setText(xi18nc("@action:inmenu", "Backup"));
0467     backup->setToolTip(xi18nc("@info:tooltip", "Backup partition"));
0468     backup->setStatusTip(xi18nc("@info:status", "Backup a partition to an image file."));
0469     backup->setIcon(QIcon::fromTheme(QStringLiteral("document-export")));
0470 
0471     QAction* restore = actionCollection()->addAction(QStringLiteral("restorePartition"));
0472     connect(restore, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onRestorePartition);
0473     restore->setEnabled(false);
0474     restore->setText(xi18nc("@action:inmenu", "Restore"));
0475     restore->setToolTip(xi18nc("@info:tooltip", "Restore partition"));
0476     restore->setStatusTip(xi18nc("@info:status", "Restore a partition from an image file."));
0477     restore->setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
0478 
0479     // Tools actions
0480     QAction* createVolumeGroup = actionCollection()->addAction(QStringLiteral("createVolumeGroup"));
0481     connect(createVolumeGroup, &QAction::triggered, this, &MainWindow::onCreateNewVolumeGroup);
0482     createVolumeGroup->setEnabled(false);
0483     createVolumeGroup->setText(i18nc("@action:inmenu", "New Volume Group"));
0484     createVolumeGroup->setToolTip(i18nc("@info:tooltip", "Create a new LVM Volume Group"));
0485     createVolumeGroup->setStatusTip(i18nc("@info:status", "Create a new LVM Volume Group as a device."));
0486     actionCollection()->setDefaultShortcut(createVolumeGroup, QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L));
0487     createVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
0488 
0489     QAction* fileSystemSupport = actionCollection()->addAction(QStringLiteral("fileSystemSupport"));
0490     connect(fileSystemSupport, &QAction::triggered, this, &MainWindow::onFileSystemSupport);
0491     fileSystemSupport->setText(xi18nc("@action:inmenu", "File System Support"));
0492     fileSystemSupport->setToolTip(xi18nc("@info:tooltip", "View file system support information"));
0493     fileSystemSupport->setStatusTip(xi18nc("@info:status", "Show information about supported file systems."));
0494 
0495     QAction* refreshDevices = actionCollection()->addAction(QStringLiteral("refreshDevices"));
0496     connect(refreshDevices, &QAction::triggered, this, &MainWindow::onRefreshDevices);
0497     refreshDevices->setText(xi18nc("@action:inmenu refresh list of devices", "Refresh Devices"));
0498     refreshDevices->setToolTip(xi18nc("@info:tooltip", "Refresh all devices"));
0499     refreshDevices->setStatusTip(xi18nc("@info:status", "Renew the devices list."));
0500     actionCollection()->setDefaultShortcut(refreshDevices, Qt::Key_F5);
0501     refreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0502     MessageWidget().addAction(refreshDevices);
0503 
0504     // Settings Actions
0505     actionCollection()->addAction(QStringLiteral("toggleDockDevices"), dockDevices().toggleViewAction());
0506     actionCollection()->addAction(QStringLiteral("toggleDockOperations"), dockOperations().toggleViewAction());
0507     actionCollection()->addAction(QStringLiteral("toggleDockInformation"), dockInformation().toggleViewAction());
0508     actionCollection()->addAction(QStringLiteral("toggleDockLog"), dockLog().toggleViewAction());
0509 
0510     KStandardAction::preferences(this, &MainWindow::onConfigureOptions, actionCollection());
0511 
0512     // Log Actions
0513     QAction* clearLog = actionCollection()->addAction(QStringLiteral("clearLog"));
0514     connect(clearLog, &QAction::triggered, &treeLog(), &TreeLog::onClearLog);
0515     clearLog->setText(xi18nc("@action:inmenu", "Clear Log"));
0516     clearLog->setToolTip(xi18nc("@info:tooltip", "Clear the log output"));
0517     clearLog->setStatusTip(xi18nc("@info:status", "Clear the log output panel."));
0518     clearLog->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-list")));
0519 
0520     QAction* saveLog = actionCollection()->addAction(QStringLiteral("saveLog"));
0521     connect(saveLog, &QAction::triggered, &treeLog(), &TreeLog::onSaveLog);
0522     saveLog->setText(xi18nc("@action:inmenu", "Save Log"));
0523     saveLog->setToolTip(xi18nc("@info:tooltip", "Save the log output"));
0524     saveLog->setStatusTip(xi18nc("@info:status", "Save the log output to a file."));
0525     saveLog->setIcon(QIcon::fromTheme(QStringLiteral("document-save")));
0526 
0527     // Help Actions
0528     QAction* aboutKPMcore = actionCollection()->addAction(QStringLiteral("aboutKPMcore"));
0529     connect(aboutKPMcore, &QAction::triggered, this, &MainWindow::onShowAboutKPMcore);
0530     aboutKPMcore->setText(xi18nc("@action:inmenu", "About KPMcore Library"));
0531     aboutKPMcore->setToolTip(xi18nc("@info:tooltip", "Show About KPMcore dialog"));
0532     aboutKPMcore->setStatusTip(xi18nc("@info:status", "Show About KPMcore dialog."));
0533     aboutKPMcore->setIcon(QIcon::fromTheme(QStringLiteral("partitionmanager")));
0534 }
0535 
0536 void MainWindow::setupConnections()
0537 {
0538     connect(&listDevices(), &ListDevices::selectionChanged,
0539             &pmWidget(), qOverload<const QString&>(&PartitionManagerWidget::setSelectedDevice));
0540 
0541     connect(&listDevices(), &ListDevices::deviceDoubleClicked,
0542             this, &MainWindow::onPropertiesDevice);
0543 
0544     connect(&m_ListDevices->listDevices(), &QListWidget::customContextMenuRequested,
0545             this, &MainWindow::listDevicesContextMenuRequested);
0546     connect(&m_TreeLog->treeLog(), &QTreeWidget::customContextMenuRequested,
0547             this, &MainWindow::treeLogContextMenuRequested);
0548     connect(&m_ListOperations->listOperations(), &QListWidget::customContextMenuRequested,
0549             this, &MainWindow::listOperationsContextMenuRequested);
0550 
0551     connect(GlobalLog::instance(), &GlobalLog::newMessage,
0552             &treeLog(), &TreeLog::onNewLogMessage);
0553 
0554     connect(this, &MainWindow::showMessageWidget, &MessageWidget(),
0555             &KMessageWidget::animatedShow);
0556     connect(this, &MainWindow::hideMessageWidget, &MessageWidget(),
0557             &KMessageWidget::animatedHide);
0558 }
0559 
0560 void MainWindow::setupStatusBar()
0561 {
0562     statusBar()->addWidget(&statusText());
0563 }
0564 
0565 void MainWindow::loadConfig()
0566 {
0567     if (Config::firstRun()) {
0568         dockLog().setVisible(false);
0569         dockInformation().setVisible(false);
0570     }
0571     PartitionAlignment::setSectorAlignment(Config::sectorAlignment());
0572 }
0573 
0574 void MainWindow::saveConfig() const
0575 {
0576     Config::setFirstRun(false);
0577     Config::self()->save();
0578 }
0579 
0580 void MainWindow::enableActions()
0581 {
0582     actionCollection()->action(QStringLiteral("createNewPartitionTable"))
0583             ->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice()));
0584     actionCollection()->action(QStringLiteral("createNewPartitionTable"))
0585             ->setVisible(pmWidget().selectedDevice() && (pmWidget().selectedDevice()->type() == Device::Type::Disk_Device ||
0586                                                          pmWidget().selectedDevice()->type() == Device::Type::SoftwareRAID_Device));
0587     actionCollection()->action(QStringLiteral("exportPartitionTable"))
0588             ->setEnabled(pmWidget().selectedDevice() &&
0589                          pmWidget().selectedDevice()->partitionTable() &&
0590                          operationStack().size() == 0);
0591     actionCollection()->action(QStringLiteral("importPartitionTable"))
0592             ->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice()));
0593     actionCollection()->action(QStringLiteral("smartStatusDevice"))
0594             ->setEnabled(pmWidget().selectedDevice() != nullptr && pmWidget().selectedDevice()->type() == Device::Type::Disk_Device &&
0595                                                         pmWidget().selectedDevice()->smartStatus().isValid());
0596     actionCollection()->action(QStringLiteral("smartStatusDevice"))
0597             ->setVisible(pmWidget().selectedDevice() != nullptr && pmWidget().selectedDevice()->type() == Device::Type::Disk_Device);
0598     actionCollection()->action(QStringLiteral("propertiesDevice"))
0599             ->setEnabled(pmWidget().selectedDevice() != nullptr);
0600 
0601     actionCollection()->action(QStringLiteral("undoOperation"))
0602             ->setEnabled(operationStack().size() > 0);
0603     actionCollection()->action(QStringLiteral("clearAllOperations"))
0604             ->setEnabled(operationStack().size() > 0);
0605     actionCollection()->action(QStringLiteral("applyAllOperations"))
0606             ->setEnabled(operationStack().size() > 0);
0607 
0608     const bool readOnly = pmWidget().selectedDevice() == nullptr ||
0609                           pmWidget().selectedDevice()->partitionTable() == nullptr ||
0610                           pmWidget().selectedDevice()->partitionTable()->isReadOnly();
0611 
0612     actionCollection()->action(QStringLiteral("createVolumeGroup"))
0613             ->setEnabled(CreateVolumeGroupOperation::canCreate());
0614 
0615     bool lvmDevice = pmWidget().selectedDevice() && pmWidget().selectedDevice()->type() == Device::Type::LVM_Device;
0616     bool removable = false;
0617 
0618     if (lvmDevice)
0619         removable = RemoveVolumeGroupOperation::isRemovable(dynamic_cast<LvmDevice*>(pmWidget().selectedDevice()));
0620 
0621     actionCollection()->action(QStringLiteral("removeVolumeGroup"))->setEnabled(removable);
0622     actionCollection()->action(QStringLiteral("removeVolumeGroup"))->setVisible(lvmDevice);
0623 
0624     bool deactivatable = lvmDevice ?
0625         DeactivateVolumeGroupOperation::isDeactivatable(dynamic_cast<LvmDevice*>(pmWidget().selectedDevice())) : false;
0626     actionCollection()->action(QStringLiteral("deactivateVolumeGroup"))->setEnabled(deactivatable);
0627     actionCollection()->action(QStringLiteral("deactivateVolumeGroup"))->setVisible(lvmDevice);
0628 
0629     actionCollection()->action(QStringLiteral("resizeVolumeGroup"))->setEnabled(lvmDevice);
0630     actionCollection()->action(QStringLiteral("resizeVolumeGroup"))->setVisible(lvmDevice);
0631 
0632     const Partition* part = pmWidget().selectedPartition();
0633 
0634     actionCollection()->action(QStringLiteral("newPartition"))
0635             ->setEnabled(!readOnly && NewOperation::canCreateNew(part));
0636 
0637     const bool canResize = ResizeOperation::canGrow(part) ||
0638                            ResizeOperation::canShrink(part) ||
0639                            ResizeOperation::canMove(part);
0640     actionCollection()->action(QStringLiteral("resizePartition"))
0641             ->setEnabled(!readOnly && canResize);
0642 
0643     actionCollection()->action(QStringLiteral("copyPartition"))
0644             ->setEnabled(CopyOperation::canCopy(part));
0645     actionCollection()->action(QStringLiteral("deletePartition"))
0646             ->setEnabled(!readOnly && DeleteOperation::canDelete(part));
0647     actionCollection()->action(QStringLiteral("shredPartition"))
0648             ->setEnabled(!readOnly && DeleteOperation::canDelete(part));
0649     actionCollection()->action(QStringLiteral("pastePartition"))
0650             ->setEnabled(!readOnly && CopyOperation::canPaste(part, pmWidget().clipboardPartition()));
0651     actionCollection()->action(QStringLiteral("propertiesPartition"))
0652             ->setEnabled(part != nullptr);
0653 
0654     actionCollection()->action(QStringLiteral("editMountPoint"))
0655             ->setEnabled(part && !part->isMounted() && part->fileSystem().canMount(part->deviceNode(), QStringLiteral("/")));
0656 
0657     actionCollection()->action(QStringLiteral("mountPartition"))
0658             ->setEnabled(part &&
0659                          (part->canMount() || part->canUnmount()));
0660     if (part != nullptr)
0661         actionCollection()->action(QStringLiteral("mountPartition"))
0662                 ->setText(part->isMounted() ?
0663                           part->fileSystem().unmountTitle() :
0664                           part->fileSystem().mountTitle());
0665 
0666     if (part && part->roles().has(PartitionRole::Luks)) {
0667         const FileSystem& fsRef = part->fileSystem();
0668         const FS::luks* luksFs = dynamic_cast<const FS::luks*>(&fsRef);
0669 
0670         actionCollection()->action(QStringLiteral("decryptPartition"))
0671                 ->setVisible(true);
0672         actionCollection()->action(QStringLiteral("decryptPartition"))
0673                 ->setEnabled(luksFs && !operationStack().contains(part) &&
0674                              (luksFs->canCryptOpen(part->partitionPath()) ||
0675                               luksFs->canCryptClose(part->partitionPath())));
0676         if (luksFs) {
0677             actionCollection()->action(QStringLiteral("decryptPartition"))
0678                     ->setText(luksFs->isCryptOpen() ?
0679                               luksFs->cryptCloseTitle() :
0680                               luksFs->cryptOpenTitle());
0681         }
0682     }
0683     else {
0684         actionCollection()->action(QStringLiteral("decryptPartition"))
0685                 ->setEnabled(false);
0686         actionCollection()->action(QStringLiteral("decryptPartition"))
0687                 ->setVisible(false);
0688     }
0689 
0690 
0691     actionCollection()->action(QStringLiteral("checkPartition"))
0692             ->setEnabled(!readOnly && CheckOperation::canCheck(part));
0693 
0694     actionCollection()->action(QStringLiteral("backupPartition"))
0695             ->setEnabled(BackupOperation::canBackup(part));
0696     actionCollection()->action(QStringLiteral("restorePartition"))
0697             ->setEnabled(RestoreOperation::canRestore(part));
0698 }
0699 
0700 void MainWindow::on_m_ApplyProgressDialog_finished()
0701 {
0702     scanDevices();
0703 }
0704 
0705 void MainWindow::on_m_OperationStack_operationsChanged()
0706 {
0707     listOperations().updateOperations(operationStack().operations());
0708     pmWidget().updatePartitions();
0709     enableActions();
0710 
0711     // this will make sure that the info pane gets updated
0712     on_m_PartitionManagerWidget_selectedPartitionChanged(pmWidget().selectedPartition());
0713 
0714     statusText().setText(xi18ncp("@info:status", "One pending operation", "%1 pending operations", operationStack().size()));
0715 }
0716 
0717 void MainWindow::on_m_OperationStack_devicesChanged()
0718 {
0719     QReadLocker lockDevices(&operationStack().lock());
0720 
0721     listDevices().updateDevices(operationStack().previewDevices());
0722 
0723     if (pmWidget().selectedDevice())
0724         infoPane().showDevice(dockWidgetArea(&dockInformation()), *pmWidget().selectedDevice());
0725     else
0726         infoPane().clear();
0727 
0728     updateWindowTitle();
0729 }
0730 
0731 void MainWindow::on_m_DockInformation_dockLocationChanged(Qt::DockWidgetArea)
0732 {
0733     on_m_PartitionManagerWidget_selectedPartitionChanged(pmWidget().selectedPartition());
0734 }
0735 
0736 void MainWindow::updateWindowTitle()
0737 {
0738     QString title;
0739 
0740     if (pmWidget().selectedDevice())
0741         title = pmWidget().selectedDevice()->deviceNode();
0742 
0743     setWindowTitle(title);
0744 }
0745 
0746 void MainWindow::listOperationsContextMenuRequested(const QPoint& pos)
0747 {
0748     QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("edit"), this));
0749 
0750     if (menu)
0751         menu->exec(m_ListOperations->listOperations().viewport()->mapToGlobal(pos));
0752 }
0753 
0754 void MainWindow::treeLogContextMenuRequested(const QPoint& pos)
0755 {
0756     QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("log"), this));
0757 
0758     if (menu)
0759         menu->exec(m_TreeLog->treeLog().viewport()->mapToGlobal(pos));
0760 }
0761 
0762 void MainWindow::listDevicesContextMenuRequested(const QPoint& pos)
0763 {
0764     QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("device"), this));
0765 
0766     if (menu)
0767         menu->exec(m_ListDevices->listDevices().viewport()->mapToGlobal(pos));
0768 }
0769 
0770 void MainWindow::on_m_PartitionManagerWidget_contextMenuRequested(const QPoint& pos)
0771 {
0772     QMenu* menu = nullptr;
0773 
0774     if (pmWidget().selectedPartition() == nullptr) {
0775         if (pmWidget().selectedDevice() != nullptr)
0776             menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("device"), this));
0777     } else
0778         menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("partition"), this));
0779 
0780     if (menu)
0781         menu->exec(pos);
0782 }
0783 
0784 void MainWindow::on_m_PartitionManagerWidget_deviceDoubleClicked(const Device*)
0785 {
0786     actionCollection()->action(QStringLiteral("propertiesDevice"))->trigger();
0787 }
0788 
0789 void MainWindow::on_m_PartitionManagerWidget_partitionDoubleClicked(const Partition*)
0790 {
0791     actionCollection()->action(QStringLiteral("propertiesPartition"))->trigger();
0792 }
0793 
0794 void MainWindow::on_m_PartitionManagerWidget_selectedPartitionChanged(const Partition* p)
0795 {
0796     if (p)
0797         infoPane().showPartition(dockWidgetArea(&dockInformation()), *p);
0798     else if (pmWidget().selectedDevice())
0799         infoPane().showDevice(dockWidgetArea(&dockInformation()), *pmWidget().selectedDevice());
0800     else
0801         infoPane().clear();
0802 
0803     updateWindowTitle();
0804     enableActions();
0805 }
0806 
0807 void MainWindow::scanDevices()
0808 {
0809     Log(Log::Level::information) << xi18nc("@info:progress", "Using backend plugin: %1 (%2)",
0810                                    CoreBackendManager::self()->backend()->id(),
0811                                    CoreBackendManager::self()->backend()->version());
0812 
0813     Log() << xi18nc("@info:progress", "Scanning devices...");
0814 
0815     // remember the currently selected device's node
0816     setSavedSelectedDeviceNode(pmWidget().selectedDevice() ?  pmWidget().selectedDevice()->deviceNode() : QString());
0817 
0818     pmWidget().clear();
0819 
0820     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0821 
0822     scanProgressDialog().setEnabled(true);
0823     scanProgressDialog().show();
0824 
0825     deviceScanner().start();
0826 }
0827 
0828 void MainWindow::on_m_DeviceScanner_progress(const QString& deviceNode, int percent)
0829 {
0830     scanProgressDialog().setProgress(percent);
0831     scanProgressDialog().setDeviceName(deviceNode);
0832 }
0833 
0834 void MainWindow::on_m_DeviceScanner_finished()
0835 {
0836     QReadLocker lockDevices(&operationStack().lock());
0837 
0838     scanProgressDialog().setProgress(100);
0839 
0840     bool foundDevices = !operationStack().previewDevices().isEmpty();
0841 
0842     guiFactory()
0843         ->container(QStringLiteral("selectedDevice"), this)
0844         ->setEnabled(foundDevices);
0845 
0846     if (foundDevices)
0847         pmWidget().setSelectedDevice(operationStack().previewDevices()[0]);
0848 
0849     pmWidget().updatePartitions();
0850 
0851     Log() << xi18nc("@info:progress", "Scan finished.");
0852     QApplication::restoreOverrideCursor();
0853 
0854     // try to set the seleted device, either from the saved one or just select the
0855     // first device
0856     if (!listDevices().setSelectedDevice(savedSelectedDeviceNode()) && foundDevices)
0857         listDevices().setSelectedDevice(operationStack().previewDevices()[0]->deviceNode());
0858 
0859     updateSeletedDeviceMenu();
0860     checkFileSystemSupport();
0861     Q_EMIT scanFinished();
0862 }
0863 
0864 void MainWindow::updateSeletedDeviceMenu()
0865 {
0866     QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
0867     if (!devicesMenu)
0868         return;
0869 
0870     devicesMenu->clear();
0871 
0872     devicesMenu->setEnabled(!operationStack().previewDevices().isEmpty());
0873 
0874     const auto previewDevices = operationStack().previewDevices();
0875     for (auto const &d : previewDevices) {
0876         QAction* action = new QAction(d->prettyName(), devicesMenu);
0877         action->setCheckable(true);
0878         action->setChecked(d->deviceNode() == pmWidget().selectedDevice()->deviceNode());
0879         action->setData(d->deviceNode());
0880         connect(action, &QAction::triggered, this, &MainWindow::onSelectedDeviceMenuTriggered);
0881         devicesMenu->addAction(action);
0882     }
0883 }
0884 
0885 void MainWindow::onSelectedDeviceMenuTriggered(bool)
0886 {
0887     QAction* action = qobject_cast<QAction*>(sender());
0888     QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
0889 
0890     if (action == nullptr || action->parent() != devicesMenu || !devicesMenu)
0891         return;
0892 
0893     const auto children = devicesMenu->findChildren<QAction*>();
0894     for (auto &entry : children)
0895         entry->setChecked(entry == action);
0896 
0897     listDevices().setSelectedDevice(action->data().toString());
0898 }
0899 
0900 void MainWindow::on_m_ListDevices_selectionChanged(const QString& device_node)
0901 {
0902     QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
0903     if (!devicesMenu)
0904         return;
0905 
0906     const auto children = devicesMenu->findChildren<QAction*>();
0907     for (auto &entry : children)
0908         entry->setChecked(entry->data().toString() == device_node);
0909 }
0910 
0911 void MainWindow::onRefreshDevices()
0912 {
0913     if (operationStack().size() == 0 || KMessageBox::warningContinueCancel(this,
0914             xi18nc("@info",
0915                    "<para>Do you really want to rescan the devices?</para>"
0916                    "<para><warning>This will also clear the list of pending operations.</warning></para>"),
0917             xi18nc("@title:window", "Really Rescan the Devices?"),
0918             KGuiItem(xi18nc("@action:button", "Rescan Devices"), QStringLiteral("arrow-right")),
0919             KStandardGuiItem::cancel(), QStringLiteral("reallyRescanDevices")) == KMessageBox::Continue) {
0920 
0921         if (m_permissionGranted) {
0922             scanDevices();
0923         } else {
0924             askForPermissions();
0925             if (m_permissionGranted) {
0926                   Q_EMIT hideMessageWidget();
0927                   FileSystemFactory::init();
0928                   scanDevices();
0929             }
0930         }
0931     }
0932 }
0933 
0934 void MainWindow::onApplyAllOperations()
0935 {
0936     QStringList opList;
0937 
0938     const auto operations = operationStack().operations();
0939     for (const auto &op : operations)
0940         opList.append(op->description());
0941 
0942     if (KMessageBox::warningContinueCancelList(this,
0943             xi18nc("@info",
0944                    "<para>Do you really want to apply the pending operations listed below?</para>"
0945                    "<para><warning>This will permanently modify your disks.</warning></para>"),
0946             opList, xi18nc("@title:window", "Apply Pending Operations?"),
0947             KGuiItem(xi18nc("@action:button", "Apply Pending Operations"), QStringLiteral("arrow-right")),
0948             KStandardGuiItem::cancel()) == KMessageBox::Continue) {
0949         Log() << xi18nc("@info:status", "Applying operations...");
0950 
0951         applyProgressDialog().show();
0952 
0953         operationRunner().setReport(&applyProgressDialog().report());
0954 
0955         // Undo all operations so the runner has a defined starting point
0956         for (int i = operationStack().operations().size() - 1; i >= 0; i--) {
0957             operationStack().operations()[i]->undo();
0958             operationStack().operations()[i]->setStatus(Operation::StatusNone);
0959         }
0960 
0961         pmWidget().updatePartitions();
0962 
0963         operationRunner().start();
0964     }
0965 }
0966 
0967 void MainWindow::onUndoOperation()
0968 {
0969     Q_ASSERT(operationStack().size() > 0);
0970 
0971     if (operationStack().size() == 0)
0972         return;
0973 
0974     Log() << xi18nc("@info:status", "Undoing operation: %1", operationStack().operations().last()->description());
0975     operationStack().pop();
0976 
0977     // it's possible the undo killed the partition in the clipboard. if there's a partition in the clipboard, try
0978     // to find a device for it (OperationStack::findDeviceForPartition() only compares pointers, so an invalid
0979     // pointer is not a problem). if no device is found, the pointer must be dangling, so clear the clipboard.
0980     if (pmWidget().clipboardPartition() != nullptr && operationStack().findDeviceForPartition(pmWidget().clipboardPartition()) == nullptr)
0981         pmWidget().setClipboardPartition(nullptr);
0982 
0983     pmWidget().updatePartitions();
0984     enableActions();
0985 }
0986 
0987 void MainWindow::onClearAllOperations()
0988 {
0989     if (KMessageBox::warningContinueCancel(this,
0990                                            xi18nc("@info", "Do you really want to clear the list of pending operations?"),
0991                                            xi18nc("@title:window", "Clear Pending Operations?"),
0992                                            KGuiItem(xi18nc("@action:button", "Clear Pending Operations"), QStringLiteral("arrow-right")),
0993                                            KStandardGuiItem::cancel(), QStringLiteral("reallyClearPendingOperations")) == KMessageBox::Continue) {
0994         Log() << xi18nc("@info:status", "Clearing the list of pending operations.");
0995         operationStack().clearOperations();
0996 
0997         pmWidget().updatePartitions();
0998         enableActions();
0999     }
1000 }
1001 
1002 void MainWindow::onCreateNewPartitionTable()
1003 {
1004     Q_ASSERT(pmWidget().selectedDevice());
1005 
1006     if (pmWidget().selectedDevice() == nullptr) {
1007         qWarning() << "selected device is null.";
1008         return;
1009     }
1010 
1011     QPointer<CreatePartitionTableDialog> dlg = new CreatePartitionTableDialog(this, *pmWidget().selectedDevice());
1012 
1013     if (dlg->exec() == QDialog::Accepted)
1014         operationStack().push(new CreatePartitionTableOperation(*pmWidget().selectedDevice(), dlg->type()));
1015 
1016     delete dlg;
1017 }
1018 
1019 void MainWindow::onImportPartitionTable()
1020 {
1021     Q_ASSERT(pmWidget().selectedDevice());
1022 
1023     // TODO: This method looks like should live somewhere completely different.
1024     // It's importing the partition table, why it's not on the partition table source?
1025     const QUrl url = QFileDialog::getOpenFileUrl(this, QStringLiteral("Import Partition Table"));
1026 
1027     if (url.isEmpty())
1028         return;
1029 
1030     QFile file;
1031     if (url.isLocalFile())
1032     {
1033         file.setFileName(url.toLocalFile());
1034         if (!file.open(QFile::ReadOnly))
1035         {
1036             KMessageBox::error(this, xi18nc("@info", "Could not open input file <filename>%1</filename> for import", url.toLocalFile()), xi18nc("@title:window", "Error Importing Partition Table"));
1037             return;
1038         }
1039 
1040     }
1041     else
1042     {
1043         QTemporaryFile tempFile;
1044 
1045         if (!tempFile.open()) {
1046             KMessageBox::error(this, xi18nc("@info", "Could not create temporary file when trying to save to <filename>%1</filename>.", tempFile.fileName()), xi18nc("@title:window", "Error Importing Partition Table"));
1047             return;
1048         }
1049 
1050         KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()));
1051         KJobWidgets::setWindow(job, this);
1052         job->exec();
1053         if (job->error()) {
1054             KMessageBox::error(this, xi18nc("@info", "Could not open input file <filename>%1</filename> for import: %2", url.fileName(), job->errorString()), xi18nc("@title:window", "Error Importing Partition Table"));
1055             return;
1056         }
1057         file.setFileName(tempFile.fileName());
1058         if (!file.open(QFile::ReadOnly))
1059         {
1060             KMessageBox::error(this, xi18nc("@info", "Could not open temporary file <filename>%1</filename> while trying to import from <filename>%2</filename>.", tempFile.fileName(), url.fileName()), xi18nc("@title:window", "Error Importing Partition Table"));
1061             return;
1062         }
1063     }
1064 
1065     Device& device = *pmWidget().selectedDevice();
1066 
1067     QString line;
1068     quint32 lineNo = 0;
1069     bool haveMagic = false;
1070     PartitionTable* ptable = nullptr;
1071     PartitionTable::TableType tableType = PartitionTable::unknownTableType;
1072 
1073     while (!(line = QString::fromUtf8(file.readLine())).isEmpty()) {
1074         lineNo++;
1075         line = line.trimmed();
1076 
1077         if (line.isEmpty())
1078             continue;
1079 
1080         QRegularExpression re(QStringLiteral("(\\d+);(\\d+);(\\d+);(\\w+);(\\w+);(\"\\w*\");(\"[^\"]*\")"));
1081         QRegularExpressionMatch rePartition = re.match(line);
1082         re.setPattern(QStringLiteral("type:\\s\"(.+)\""));
1083         QRegularExpressionMatch reType = re.match(line);
1084         re.setPattern(QStringLiteral("align:\\s\"(cylinder|sector)\""));
1085         QRegularExpressionMatch reAlign = re.match(line);
1086         re.setPattern(QStringLiteral("^##|v(\\d+)|##"));
1087         QRegularExpressionMatch reMagic = re.match(line);
1088 
1089         if (!(haveMagic || reMagic.hasMatch())) {
1090             KMessageBox::error(this, xi18nc("@info", "The import file <filename>%1</filename> does not contain a valid partition table.", url.fileName()), xi18nc("@title:window", "Error While Importing Partition Table"));
1091             return;
1092         } else
1093             haveMagic = true;
1094 
1095         if (line.startsWith(QStringLiteral("#")))
1096             continue;
1097 
1098         if (reType.hasMatch()) {
1099             if (ptable != nullptr) {
1100                 KMessageBox::error(this, xi18nc("@info", "Found more than one partition table type in import file (line %1).", lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1101                 return;
1102             }
1103 
1104             tableType = PartitionTable::nameToTableType(reType.captured(1));
1105 
1106             if (tableType == PartitionTable::unknownTableType) {
1107                 KMessageBox::error(this, xi18nc("@info", "Partition table type \"%1\" is unknown (line %2).", reType.captured(1), lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1108                 return;
1109             }
1110 
1111             if (tableType != PartitionTable::msdos && tableType != PartitionTable::gpt && tableType != PartitionTable::none) {
1112                 KMessageBox::error(this, xi18nc("@info", "Partition table type \"%1\" is not supported for import (line %2).", reType.captured(1), lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1113                 return;
1114             }
1115 
1116             ptable = new PartitionTable(tableType, PartitionTable::defaultFirstUsable(device, tableType), PartitionTable::defaultLastUsable(device, tableType));
1117             operationStack().push(new CreatePartitionTableOperation(device, ptable));
1118         } else if (reAlign.hasMatch()) {
1119             // currently ignored
1120         } else if (rePartition.hasMatch()) {
1121             if (ptable == nullptr) {
1122                 KMessageBox::error(this, xi18nc("@info", "Found partition but no partition table type (line %1).",  lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1123                 return;
1124             }
1125 
1126             qint32 num = rePartition.captured(1).toInt();
1127             qint64 firstSector = rePartition.captured(2).toLongLong();
1128             qint64 lastSector = rePartition.captured(3).toLongLong();
1129             QString fsName = rePartition.captured(4);
1130             QString roleNames = rePartition.captured(5);
1131             QString volumeLabel = rePartition.captured(6).replace(QStringLiteral("\""), QString());
1132             QStringList flags = rePartition.captured(7).replace(QStringLiteral("\""), QString()).split(QStringLiteral(","));
1133 
1134             if (firstSector < ptable->firstUsable() || lastSector > ptable->lastUsable()) {
1135                 KMessageBox::error(this, xi18nc("@info the partition is NOT a device path, just a number", "Partition %1 would be outside the device's boundaries (line %2).", num, lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1136                 return;
1137             }
1138 
1139             if (firstSector >= lastSector) {
1140                 KMessageBox::error(this, xi18nc("@info", "Partition %1 has end before start sector (line %2).", num, lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1141                 return;
1142             }
1143 
1144             PartitionNode* parent = ptable;
1145 
1146             Q_ASSERT(parent);
1147 
1148             PartitionRole role(PartitionRole::None);
1149 
1150             if (roleNames == QStringLiteral("extended"))
1151                 role = PartitionRole(PartitionRole::Extended);
1152             else if (roleNames == QStringLiteral("logical")) {
1153                 role = PartitionRole(PartitionRole::Logical);
1154                 parent = ptable->findPartitionBySector(firstSector, PartitionRole(PartitionRole::Extended));
1155             } else if (roleNames == QStringLiteral("primary"))
1156                 role = PartitionRole(PartitionRole::Primary);
1157 
1158             if (role == PartitionRole(PartitionRole::None)) {
1159                 KMessageBox::error(this, xi18nc("@info the partition is NOT a device path, just a number", "Unrecognized partition role \"%1\" for partition %2 (line %3).", roleNames, num, lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1160                 return;
1161             }
1162 
1163             if (parent == nullptr) {
1164                 KMessageBox::error(this, xi18nc("@info the partition is NOT a device path, just a number", "No parent partition or partition table found for partition %1 (line %2).", num, lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1165                 return;
1166             }
1167 
1168             if (role.has(PartitionRole::Extended) && !PartitionTable::tableTypeSupportsExtended(tableType)) {
1169                 KMessageBox::error(this, xi18nc("@info", "The partition table type \"%1\" does not support extended partitions, but one was found (line %2).", PartitionTable::tableTypeToName(tableType), lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1170                 return;
1171             }
1172 
1173             FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(fsName, { QStringLiteral("C") }), firstSector, lastSector, device.logicalSize());
1174 
1175             if (fs == nullptr) {
1176                 KMessageBox::error(this, xi18nc("@info the partition is NOT a device path, just a number", "Could not create file system \"%1\" for partition %2 (line %3).", fsName, num, lineNo), xi18nc("@title:window", "Error While Importing Partition Table"));
1177                 return;
1178             }
1179 
1180             if (fs->supportSetLabel() != FileSystem::cmdSupportNone && !volumeLabel.isEmpty())
1181                 fs->setLabel(volumeLabel);
1182 
1183             Partition* p = new Partition(parent, device, role, fs, firstSector, lastSector, QString(), PartitionTable::Flag::None, QString(), false, PartitionTable::Flag::None, Partition::State::New);
1184 
1185             operationStack().push(new NewOperation(device, p));
1186 
1187             auto newFlags = PartitionTable::flagsFromList(flags);
1188             if (newFlags != PartitionTable::Flag::None)
1189                 operationStack().push(new SetPartFlagsOperation(device, *p, newFlags));
1190         } else
1191             Log(Log::Level::warning) << xi18nc("@info:status", "Could not parse line %1 from import file. Ignoring it.", lineNo);
1192     }
1193 
1194     if (ptable->type() == PartitionTable::msdos && ptable->isSectorBased(device))
1195         ptable->setType(device, PartitionTable::msdos_sectorbased);
1196 }
1197 
1198 void MainWindow::onExportPartitionTable()
1199 {
1200     Q_ASSERT(pmWidget().selectedDevice());
1201     Q_ASSERT(pmWidget().selectedDevice()->partitionTable());
1202 
1203     const QUrl url = QFileDialog::getSaveFileUrl();
1204 
1205     if (url.isEmpty())
1206         return;
1207 
1208     QTemporaryFile tempFile;
1209 
1210     if (!tempFile.open()) {
1211         KMessageBox::error(this, xi18nc("@info", "Could not create temporary file when trying to save to <filename>%1</filename>.", url.fileName()), xi18nc("@title:window", "Error Exporting Partition Table"));
1212         return;
1213     }
1214 
1215     QTextStream stream(&tempFile);
1216 
1217     stream << "##|v1|## partition table of " << pmWidget().selectedDevice()->deviceNode() << "\n";
1218     stream << "# on " << QLocale::c().toString(QDateTime::currentDateTime()) << "\n";
1219     stream << *pmWidget().selectedDevice()->partitionTable();
1220 
1221     tempFile.close();
1222 
1223     KIO::CopyJob* job = KIO::move(QUrl::fromLocalFile(tempFile.fileName()), url, KIO::HideProgressInfo);
1224     job->exec();
1225     if (job->error())
1226         job->uiDelegate()->showErrorMessage();
1227 }
1228 
1229 void MainWindow::onCreateNewVolumeGroup()
1230 {
1231     QString vgName;
1232     QVector<const Partition*> pvList;
1233     qint32 peSize = 4;
1234     // *NOTE*: vgName & pvList will be modified and validated by the dialog
1235     QPointer<CreateVolumeGroupDialog> dlg = new CreateVolumeGroupDialog(this, vgName, pvList, peSize, operationStack().previewDevices(), operationStack().operations());
1236     if (dlg->exec() == QDialog::Accepted)
1237         operationStack().push(new CreateVolumeGroupOperation(vgName, pvList, peSize));
1238 
1239     delete dlg;
1240 }
1241 
1242 void MainWindow::onResizeVolumeGroup()
1243 {
1244     if (pmWidget().selectedDevice()->type() == Device::Type::LVM_Device) {
1245         LvmDevice* d = dynamic_cast<LvmDevice*>(pmWidget().selectedDevice());
1246 
1247         QVector<const Partition*> pvList;
1248         // *NOTE*: pvList will be modified and validated by the dialog
1249 
1250         QPointer<ResizeVolumeGroupDialog> dlg = new ResizeVolumeGroupDialog(this, d, pvList, operationStack().previewDevices(), operationStack().operations());
1251         if (dlg->exec() == QDialog::Accepted)
1252             operationStack().push(new ResizeVolumeGroupOperation(*d, pvList));
1253 
1254         delete dlg;
1255     }
1256 }
1257 
1258 void MainWindow::onRemoveVolumeGroup()
1259 {
1260     Device* tmpDev = pmWidget().selectedDevice();
1261     if (tmpDev->type() == Device::Type::LVM_Device) {
1262         operationStack().push(new RemoveVolumeGroupOperation(*(dynamic_cast<LvmDevice*>(tmpDev))));
1263     }
1264 }
1265 
1266 void MainWindow::onDeactivateVolumeGroup()
1267 {
1268     Device* tmpDev = pmWidget().selectedDevice();
1269     if (tmpDev->type() == Device::Type::LVM_Device) {
1270         DeactivateVolumeGroupOperation* deactivate = new DeactivateVolumeGroupOperation( *(dynamic_cast<LvmDevice*>(tmpDev)) );
1271         Report* tmpReport = new Report(nullptr);
1272         if (deactivate->execute(*tmpReport)) {
1273             deactivate->preview();
1274             actionCollection()->action(QStringLiteral("resizeVolumeGroup"))->setEnabled(false);
1275             actionCollection()->action(QStringLiteral("deactivateVolumeGroup"))->setEnabled(false);
1276         }
1277         delete tmpReport;
1278         pmWidget().updatePartitions();
1279         enableActions();
1280     }
1281 }
1282 
1283 void MainWindow::onFileSystemSupport()
1284 {
1285     FileSystemSupportDialog dlg(this);
1286     dlg.exec();
1287 }
1288 
1289 void MainWindow::onShowAboutKPMcore()
1290 {
1291     KAboutApplicationDialog dlg(aboutKPMcore(), this);
1292     dlg.exec();
1293 }
1294 
1295 void MainWindow::onSettingsChanged()
1296 {
1297     if (CoreBackendManager::self()->backend()->id() != Config::backend()) {
1298         CoreBackendManager::self()->unload();
1299         // FIXME: if loadBackend() fails to load the configured backend and loads the default
1300         // one instead it also sets the default backend in the config; the config dialog will
1301         // overwrite that again, however, after we're done here.
1302         if (loadBackend()) {
1303             deviceScanner().setupConnections();
1304             scanDevices();
1305             FileSystemFactory::init();
1306         } else
1307             close();
1308     }
1309 
1310     enableActions();
1311     pmWidget().updatePartitions();
1312 
1313     PartitionAlignment::setSectorAlignment(Config::sectorAlignment());
1314 
1315     Q_EMIT settingsChanged();
1316 }
1317 
1318 void MainWindow::onConfigureOptions()
1319 {
1320     if (ConfigureOptionsDialog::showDialog(QStringLiteral("Settings")))
1321         return;
1322 
1323     QPointer<ConfigureOptionsDialog> dlg = new ConfigureOptionsDialog(this, operationStack(), QStringLiteral("Settings"));
1324 
1325     // FIXME: we'd normally use settingsChanged(), according to the kde api docs. however, this
1326     // is emitted each time the user changes any of our own settings (backend, default file system), without
1327     // applying or clicking ok. so the below is the workaround for that.
1328     connect(dlg->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &MainWindow::onSettingsChanged);
1329     connect(dlg->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &MainWindow::onSettingsChanged);
1330 
1331     dlg->show();
1332 }
1333 
1334 void MainWindow::onSmartStatusDevice()
1335 {
1336     Q_ASSERT(pmWidget().selectedDevice());
1337 
1338     if (pmWidget().selectedDevice()) {
1339         QPointer<SmartDialog> dlg = new SmartDialog(this, *pmWidget().selectedDevice());
1340         dlg->exec();
1341         delete dlg;
1342     }
1343 }
1344 
1345 void MainWindow::onPropertiesDevice(const QString&)
1346 {
1347     Q_ASSERT(pmWidget().selectedDevice());
1348 
1349     if (pmWidget().selectedDevice()) {
1350         Device& d = *pmWidget().selectedDevice();
1351 
1352         QPointer<DevicePropsDialog> dlg = new DevicePropsDialog(this, d);
1353         if (dlg->exec() == QDialog::Accepted) {
1354             if (d.partitionTable()->type() == PartitionTable::msdos && dlg->sectorBasedAlignment())
1355                 d.partitionTable()->setType(d, PartitionTable::msdos_sectorbased);
1356             else if (d.partitionTable()->type() == PartitionTable::msdos_sectorbased && dlg->cylinderBasedAlignment())
1357                 d.partitionTable()->setType(d, PartitionTable::msdos);
1358 
1359             on_m_OperationStack_devicesChanged();
1360             pmWidget().updatePartitions();
1361         }
1362 
1363         delete dlg;
1364     }
1365 }
1366 
1367 static KLocalizedString checkSupportInNode(const PartitionNode* parent)
1368 {
1369     if (parent == nullptr)
1370         return KLocalizedString();
1371 
1372     KLocalizedString rval;
1373 
1374     for (const auto &node : parent->children()) {
1375         const Partition* p = dynamic_cast<const Partition*>(node);
1376 
1377         if (p == nullptr)
1378             continue;
1379 
1380         if (node->children().size() > 0 && !rval.isEmpty())
1381             rval = kxi18n("%1%2").subs(rval).subs(checkSupportInNode(node));
1382         else
1383             rval = checkSupportInNode(node);
1384 
1385         // TODO: Don't create HTML tables manually.
1386         if ((!p->fileSystem().supportToolFound() && !p->fileSystem().supportToolName().name.isEmpty()) && !rval.isEmpty())
1387             rval = kxi18n("%1%2").subs(rval).subs(kxi18n("<tr>"
1388                                    "<td>%1</td>"
1389                                    "<td>%2</td>"
1390                                    "<td>%3</td>"
1391                                    "<td><link>%4</link></td>"
1392                                    "</tr>")
1393                  .subs(p->deviceNode())
1394                  .subs(p->fileSystem().name())
1395                  .subs(p->fileSystem().supportToolName().name)
1396                  .subs(p->fileSystem().supportToolName().url.toString()));
1397         else if (!p->fileSystem().supportToolFound() && !p->fileSystem().supportToolName().name.isEmpty())
1398             rval =kxi18n("<tr>"
1399                                    "<td>%1</td>"
1400                                    "<td>%2</td>"
1401                                    "<td>%3</td>"
1402                                    "<td><link>%4</link></td>"
1403                                    "</tr>")
1404                  .subs(p->deviceNode())
1405                  .subs(p->fileSystem().name())
1406                  .subs(p->fileSystem().supportToolName().name)
1407                  .subs(p->fileSystem().supportToolName().url.toString());
1408     }
1409 
1410     return rval;
1411 }
1412 
1413 void MainWindow::checkFileSystemSupport()
1414 {
1415     KLocalizedString supportList, supportInNode;
1416     bool missingSupportTools = false;
1417 
1418     const auto previewDevices = operationStack().previewDevices();
1419     for (auto const &d : previewDevices ) {
1420         supportInNode = checkSupportInNode(d->partitionTable());
1421         if (!supportInNode.isEmpty() && !supportList.isEmpty()) {
1422             missingSupportTools = true;
1423             supportList = kxi18n("%1%2").subs(supportList).subs(supportInNode);
1424         }
1425         else if (!supportInNode.isEmpty()) {
1426             missingSupportTools = true;
1427             supportList = supportInNode;
1428         }
1429     }
1430 
1431     if (missingSupportTools)
1432         // TODO: Don't create HTML manually.
1433         KMessageBox::information(this,
1434                                  xi18nc("@info",
1435                                         "<para>No support tools were found for file systems currently present on hard disks in this computer:</para>"
1436                                         "<table style='margin-top:12px'>"
1437                                         "<tr>"
1438                                         "<td style='font-weight:bold;padding-right:12px;white-space:nowrap;'>Partition</td>"
1439                                         "<td style='font-weight:bold;padding-right:12px;white-space:nowrap;'>File System</td>"
1440                                         "<td style='font-weight:bold;padding-right:12px;white-space:nowrap;'>Support Tools</td>"
1441                                         "<td style='font-weight:bold;padding-right:12px;white-space:nowrap;'>URL</td>"
1442                                         "</tr>"
1443                                         "%1"
1444                                         "</table>"
1445                                         "<para>As long as the support tools for these file systems are not installed you will not be able to modify them.</para>"
1446                                         "<para>You should find packages with these support tools in your distribution's package manager.</para>",
1447                                         supportList),
1448                                  xi18nc("@title:window", "Missing File System Support Packages"),
1449                                  QStringLiteral("showInformationOnMissingFileSystemSupport"), KMessageBox::Notify | KMessageBox::AllowLink);
1450 }
1451 
1452 void MainWindow::setCurrentDeviceByName(const QString& name)
1453 {
1454     // TODO: Port KPartitionManager away from KMessageBox into KMessageWidget.
1455     // TODO: setSelectedDevice from m_ListDevices is not using a device name, but
1456     // just issuing a match query on a string list, this will produce false results.
1457     if (!m_ListDevices->setSelectedDevice(name)) {
1458         KMessageBox::error(this,
1459                            xi18nc("@info device should be inside of /dev", "Unrecognized device \"%1\" ", name),
1460                            xi18nc("@title:window", "Error While Importing Partition Table"));
1461     }
1462 }
1463 
1464 void MainWindow::setCurrentPartitionByName(const QString& partitionName)
1465 {
1466     if (!pmWidget().setCurrentPartitionByName(partitionName)) {
1467         KMessageBox::error(this,
1468                            xi18nc("@info device should be inside of /dev", "Unrecognized partition \"%1\" ", partitionName),
1469                            xi18nc("@title:window", "Error opening partition"));
1470     }
1471 }
1472 
1473 #include "moc_mainwindow.cpp"