mainwindow.cpp 62.5 KB
Newer Older
1 2
/*************************************************************************
 *  Copyright (C) 2008, 2009, 2010, 2012 by Volker Lanz <vl@fidra.de>    *
3
 *  Copyright (C) 2015 by Teo Mrnjavac <teo@kde.org>                     *
Andrius Štikonas's avatar
Andrius Štikonas committed
4
 *  Copyright (C) 2016 by Andrius Štikonas <andrius@stikonas.eu>         *
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *                                                                       *
 *  This program is free software; you can redistribute it and/or        *
 *  modify it under the terms of the GNU General Public License as       *
 *  published by the Free Software Foundation; either version 3 of       *
 *  the License, or (at your option) any later version.                  *
 *                                                                       *
 *  This program is distributed in the hope that it will be useful,      *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of       *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
 *  GNU General Public License for more details.                         *
 *                                                                       *
 *  You should have received a copy of the GNU General Public License    *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.*
 *************************************************************************/
Volker Lanz's avatar
Volker Lanz committed
19 20 21

#include "gui/mainwindow.h"
#include "gui/infopane.h"
22
#include "gui/applyprogressdialog.h"
23
#include "gui/scanprogressdialog.h"
24
#include "gui/createpartitiontabledialog.h"
25 26
#include "gui/createvolumegroupdialog.h"
#include "gui/resizevolumegroupdialog.h"
27 28
#include "gui/filesystemsupportdialog.h"
#include "gui/devicepropsdialog.h"
29
#include "gui/smartdialog.h"
Volker Lanz's avatar
Volker Lanz committed
30

31 32
#include "config/configureoptionsdialog.h"

33 34
#include <backend/corebackendmanager.h>
#include <backend/corebackend.h>
35

36 37 38
#include <core/device.h>
#include <core/partitionalignment.h>
#include <core/smartstatus.h>
39

40 41
#include <ops/operation.h>
#include <ops/createpartitiontableoperation.h>
42
#include <ops/createvolumegroupoperation.h>
Chantara Tith's avatar
Chantara Tith committed
43
#include <ops/resizevolumegroupoperation.h>
44
#include <ops/removevolumegroupoperation.h>
45
#include <ops/deactivatevolumegroupoperation.h>
46 47 48 49 50 51 52
#include <ops/resizeoperation.h>
#include <ops/copyoperation.h>
#include <ops/deleteoperation.h>
#include <ops/newoperation.h>
#include <ops/backupoperation.h>
#include <ops/restoreoperation.h>
#include <ops/checkoperation.h>
53

54 55
#include <fs/filesystem.h>
#include <fs/filesystemfactory.h>
56
#include <fs/luks.h>
57

58
#include <util/externalcommand.h>
59
#include <util/helpers.h>
60 61
#include <util/guihelpers.h>
#include <util/report.h>
62

63
#include <QApplication>
Volker Lanz's avatar
Volker Lanz committed
64
#include <QCloseEvent>
65
#include <QCollator>
66
#include <QDateTime>
67 68
#include <QFile>
#include <QFileDialog>
69
#include <QtGlobal>
Andrius Štikonas's avatar
Andrius Štikonas committed
70
#include <QMenu>
71
#include <QPointer>
Andrius Štikonas's avatar
Andrius Štikonas committed
72
#include <QPushButton>
73
#include <QReadLocker>
74
#include <QRegularExpression>
75 76
#include <QStatusBar>
#include <QTemporaryFile>
77
#include <QTextStream>
Volker Lanz's avatar
Volker Lanz committed
78

79
#include <KAboutApplicationDialog>
Andrius Štikonas's avatar
Andrius Štikonas committed
80
#include <KActionCollection>
81 82
#include <KMessageBox>
#include <KAboutData>
Andrius Štikonas's avatar
Andrius Štikonas committed
83
#include <KIconLoader>
84
#include <KLocalizedString>
Andrius Štikonas's avatar
Andrius Štikonas committed
85
#include <KXMLGUIFactory>
86 87 88
#include <KJobUiDelegate>
#include <KIO/CopyJob>
#include <KIO/Job>
Andrius Štikonas's avatar
Andrius Štikonas committed
89
#include <KJobWidgets>
90
#include "config.h"
91

Volker Lanz's avatar
Volker Lanz committed
92
/** Creates a new MainWindow instance.
93
    @param parent the parent widget
Volker Lanz's avatar
Volker Lanz committed
94
*/
Volker Lanz's avatar
Volker Lanz committed
95
MainWindow::MainWindow(QWidget* parent) :
96 97 98 99 100 101 102 103
    KXmlGuiWindow(parent),
    Ui::MainWindowBase(),
    m_OperationStack(new OperationStack(this)),
    m_OperationRunner(new OperationRunner(this, operationStack())),
    m_DeviceScanner(new DeviceScanner(this, operationStack())),
    m_ApplyProgressDialog(new ApplyProgressDialog(this, operationRunner())),
    m_ScanProgressDialog(new ScanProgressDialog(this)),
    m_StatusText(new QLabel(this))
Volker Lanz's avatar
Volker Lanz committed
104
{
105 106
    setupObjectNames();
    setupUi(this);
Andrius Štikonas's avatar
Andrius Štikonas committed
107 108 109
    connect(&m_ListDevices->listDevices(), &QListWidget::customContextMenuRequested, this, &MainWindow::listDevicesContextMenuRequested);
    connect(&m_TreeLog->treeLog(), &QTreeWidget::customContextMenuRequested, this, &MainWindow::treeLogContextMenuRequested);
    connect(&m_ListOperations->listOperations(), &QListWidget::customContextMenuRequested, this, &MainWindow::listOperationsContextMenuRequested);
110
    init();
111
}
112

113 114
void MainWindow::setupObjectNames()
{
115 116 117 118 119
    m_OperationStack->setObjectName(QStringLiteral("m_OperationStack"));
    m_OperationRunner->setObjectName(QStringLiteral("m_OperationRunner"));
    m_DeviceScanner->setObjectName(QStringLiteral("m_DeviceScanner"));
    m_ApplyProgressDialog->setObjectName(QStringLiteral("m_ApplyProgressDialog"));
    m_ScanProgressDialog->setObjectName(QStringLiteral("m_ScanProgressDialog"));
120 121
}

122 123
void MainWindow::init()
{
124
    treeLog().init();
125

126
    connect(GlobalLog::instance(), &GlobalLog::newMessage, &treeLog(), &TreeLog::onNewLogMessage);
Volker Lanz's avatar
Volker Lanz committed
127

128 129 130
    setupActions();
    setupStatusBar();
    setupConnections();
Volker Lanz's avatar
Volker Lanz committed
131

132 133
    listDevices().setActionCollection(actionCollection());
    listOperations().setActionCollection(actionCollection());
134

135
    setupGUI();
Volker Lanz's avatar
Volker Lanz committed
136

137
    loadConfig();
Volker Lanz's avatar
Volker Lanz committed
138

139
    show();
Andrius Štikonas's avatar
Andrius Štikonas committed
140
    ExternalCommand::setParentWidget(this);
141 142
    pmWidget().init(&operationStack());

143
    scanDevices();
Volker Lanz's avatar
Volker Lanz committed
144 145 146 147
}

void MainWindow::closeEvent(QCloseEvent* event)
{
148 149 150 151 152 153 154 155 156
    if (applyProgressDialog().isVisible()) {
        event->ignore();
        return;
    }

    if (operationStack().size() > 0) {
        if (KMessageBox::warningContinueCancel(this,
                                               xi18ncp("@info", "<para>Do you really want to quit the application?</para><para>There is still an operation pending.</para>",
                                                       "<para>Do you really want to quit the application?</para><para>There are still %1 operations pending.</para>", operationStack().size()),
Andrius Štikonas's avatar
Andrius Štikonas committed
157
                                               xi18nc("@title:window", "Discard Pending Operations and Quit?"),
158 159 160 161 162 163 164 165 166 167
                                               KGuiItem(xi18nc("@action:button", "Quit <application>%1</application>", QGuiApplication::applicationDisplayName()), QStringLiteral("arrow-right")),
                                               KStandardGuiItem::cancel(), QStringLiteral("reallyQuit")) == KMessageBox::Cancel) {
            event->ignore();
            return;
        }
    }

    saveConfig();

    KXmlGuiWindow::closeEvent(event);
168
    ExternalCommand::stopHelper();
Andrius Štikonas's avatar
Andrius Štikonas committed
169
    delete m_ApplyProgressDialog;
Volker Lanz's avatar
Volker Lanz committed
170 171 172 173
}

void MainWindow::changeEvent(QEvent* event)
{
174
    if ((event->type() == QEvent::ActivationChange || event->type() == QEvent::WindowStateChange) && event->spontaneous() && isActiveWindow()) {
175
        QWidget* w = nullptr;
176 177 178 179 180 181

        if (applyProgressDialog().isVisible())
            w = &applyProgressDialog();
        else if (scanProgressDialog().isVisible())
            w = &scanProgressDialog();

182
        if (w != nullptr) {
183 184 185 186 187 188 189
            w->activateWindow();
            w->raise();
            w->setFocus();
        }
    }

    KXmlGuiWindow::changeEvent(event);
Volker Lanz's avatar
Volker Lanz committed
190 191 192 193
}

void MainWindow::setupActions()
{
194
    // File actions
195
    KStandardAction::quit(this, &MainWindow::close, actionCollection());
196 197

    // Edit actions
198 199
    QAction* undoOperation = actionCollection()->addAction(QStringLiteral("undoOperation"));
    connect(undoOperation, &QAction::triggered, this, &MainWindow::onUndoOperation);
200
    undoOperation->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
201 202 203
    undoOperation->setText(xi18nc("@action:inmenu", "Undo"));
    undoOperation->setToolTip(xi18nc("@info:tooltip", "Undo the last operation"));
    undoOperation->setStatusTip(xi18nc("@info:status", "Remove the last operation from the list."));
204
    actionCollection()->setDefaultShortcut(undoOperation, QKeySequence(Qt::CTRL + Qt::Key_Z));
205 206
    undoOperation->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")).pixmap(IconSize(KIconLoader::Toolbar)));

207 208
    QAction* clearAllOperations = actionCollection()->addAction(QStringLiteral("clearAllOperations"));
    connect(clearAllOperations, &QAction::triggered, this, &MainWindow::onClearAllOperations);
209
    clearAllOperations->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
210 211 212
    clearAllOperations->setText(xi18nc("@action:inmenu clear the list of operations", "Clear"));
    clearAllOperations->setToolTip(xi18nc("@info:tooltip", "Clear all operations"));
    clearAllOperations->setStatusTip(xi18nc("@info:status", "Empty the list of pending operations."));
213 214
    clearAllOperations->setIcon(QIcon::fromTheme(QStringLiteral("dialog-cancel")).pixmap(IconSize(KIconLoader::Toolbar)));

215 216
    QAction* applyAllOperations = actionCollection()->addAction(QStringLiteral("applyAllOperations"));
    connect(applyAllOperations, &QAction::triggered, this, &MainWindow::onApplyAllOperations);
217
    applyAllOperations->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
218 219 220
    applyAllOperations->setText(xi18nc("@action:inmenu apply all operations", "Apply"));
    applyAllOperations->setToolTip(xi18nc("@info:tooltip", "Apply all operations"));
    applyAllOperations->setStatusTip(xi18nc("@info:status", "Apply the pending operations in the list."));
221 222 223
    applyAllOperations->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")).pixmap(IconSize(KIconLoader::Toolbar)));

    // Device actions
224 225
    QAction* createNewPartitionTable = actionCollection()->addAction(QStringLiteral("createNewPartitionTable"));
    connect(createNewPartitionTable, &QAction::triggered, this, &MainWindow::onCreateNewPartitionTable);
226
    createNewPartitionTable->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
227 228 229
    createNewPartitionTable->setText(xi18nc("@action:inmenu", "New Partition Table"));
    createNewPartitionTable->setToolTip(xi18nc("@info:tooltip", "Create a new partition table"));
    createNewPartitionTable->setStatusTip(xi18nc("@info:status", "Create a new and empty partition table on a device."));
230
    actionCollection()->setDefaultShortcut(createNewPartitionTable, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_N));
231 232
    createNewPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear")).pixmap(IconSize(KIconLoader::Toolbar)));

233 234
    QAction* exportPartitionTable = actionCollection()->addAction(QStringLiteral("exportPartitionTable"));
    connect(exportPartitionTable, &QAction::triggered, this, &MainWindow::onExportPartitionTable);
235
    exportPartitionTable->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
236 237 238
    exportPartitionTable->setText(xi18nc("@action:inmenu", "Export Partition Table"));
    exportPartitionTable->setToolTip(xi18nc("@info:tooltip", "Export a partition table"));
    exportPartitionTable->setStatusTip(xi18nc("@info:status", "Export the device's partition table to a text file."));
239 240
    exportPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("document-export")).pixmap(IconSize(KIconLoader::Toolbar)));

241 242
    QAction* importPartitionTable = actionCollection()->addAction(QStringLiteral("importPartitionTable"));
    connect(importPartitionTable, &QAction::triggered, this, &MainWindow::onImportPartitionTable);
243
    importPartitionTable->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
244 245 246
    importPartitionTable->setText(xi18nc("@action:inmenu", "Import Partition Table"));
    importPartitionTable->setToolTip(xi18nc("@info:tooltip", "Import a partition table"));
    importPartitionTable->setStatusTip(xi18nc("@info:status", "Import a partition table from a text file."));
247 248
    importPartitionTable->setIcon(QIcon::fromTheme(QStringLiteral("document-import")).pixmap(IconSize(KIconLoader::Toolbar)));

249 250 251
    QAction* removeVolumeGroup = actionCollection()->addAction(QStringLiteral("removeVolumeGroup"));
    connect(removeVolumeGroup, &QAction::triggered, this, &MainWindow::onRemoveVolumeGroup);
    removeVolumeGroup->setEnabled(false);
252
    removeVolumeGroup->setVisible(false);
253 254 255
    removeVolumeGroup->setText(i18nc("@action:inmenu", "Remove Volume Group"));
    removeVolumeGroup->setToolTip(i18nc("@info:tooltip", "Remove selected Volume Group"));
    removeVolumeGroup->setStatusTip(i18nc("@info:status", "Remove selected Volume Group."));
256 257 258 259 260 261
    //actionCollection()->setDefaultShortcut(removeVolumeGroup, QKeySequence(/*SHORTCUT KEY HERE*/));
    removeVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")).pixmap(IconSize(KIconLoader::Toolbar)));

    QAction* resizeVolumeGroup = actionCollection()->addAction(QStringLiteral("resizeVolumeGroup"));
    connect(resizeVolumeGroup, &QAction::triggered, this, &MainWindow::onResizeVolumeGroup);
    resizeVolumeGroup->setEnabled(false);
262
    resizeVolumeGroup->setVisible(false);
263 264 265
    resizeVolumeGroup->setText(i18nc("@action:inmenu", "Resize Volume Group"));
    resizeVolumeGroup->setToolTip(i18nc("@info:tooltip", "Resize selected Volume Group"));
    resizeVolumeGroup->setStatusTip(i18nc("@info:status", "Extend or reduce selected Volume Group."));
266 267 268
    //actionCollection()->setDefaultShortcut(resizeVolumeGroup, QKeySequence(/*SHORTCUT KEY HERE*/));
    resizeVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")).pixmap(IconSize(KIconLoader::Toolbar)));

269 270 271
    QAction* deactivateVolumeGroup = actionCollection()->addAction(QStringLiteral("deactivateVolumeGroup"));
    connect(deactivateVolumeGroup, &QAction::triggered, this, &MainWindow::onDeactivateVolumeGroup);
    deactivateVolumeGroup->setEnabled(false);
272
    deactivateVolumeGroup->setVisible(false);
273 274 275
    deactivateVolumeGroup->setText(i18nc("@action:inmenu", "Deactivate Volume Group"));
    deactivateVolumeGroup->setToolTip(i18nc("@info:tooltip", "Deactivate selected Volume Group"));
    deactivateVolumeGroup->setStatusTip(i18nc("@info:status", "Deactivate selected Volume Group before unplugging removable devices."));
276
    deactivateVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("media-eject")).pixmap(IconSize(KIconLoader::Toolbar)));
277

278 279
    QAction* smartStatusDevice = actionCollection()->addAction(QStringLiteral("smartStatusDevice"));
    connect(smartStatusDevice, &QAction::triggered, this, &MainWindow::onSmartStatusDevice);
280
    smartStatusDevice->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
281 282 283
    smartStatusDevice->setText(xi18nc("@action:inmenu", "SMART Status"));
    smartStatusDevice->setToolTip(xi18nc("@info:tooltip", "Show SMART status"));
    smartStatusDevice->setStatusTip(xi18nc("@info:status", "Show the device's SMART status if supported"));
284

285
    QAction* propertiesDevice = actionCollection()->addAction(QStringLiteral("propertiesDevice"));
286
    connect(propertiesDevice, &QAction::triggered, [this] {onPropertiesDevice({});});
287
    propertiesDevice->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
288 289 290
    propertiesDevice->setText(xi18nc("@action:inmenu", "Properties"));
    propertiesDevice->setToolTip(xi18nc("@info:tooltip", "Show device properties dialog"));
    propertiesDevice->setStatusTip(xi18nc("@info:status", "View and modify device properties"));
291 292 293
    propertiesDevice->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")).pixmap(IconSize(KIconLoader::Toolbar)));

    // Partition actions
294 295
    QAction* newPartition = actionCollection()->addAction(QStringLiteral("newPartition"));
    connect(newPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onNewPartition);
296
    newPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
297 298 299
    newPartition->setText(xi18nc("@action:inmenu create a new partition", "New"));
    newPartition->setToolTip(xi18nc("@info:tooltip", "New partition"));
    newPartition->setStatusTip(xi18nc("@info:status", "Create a new partition."));
300
    actionCollection()->setDefaultShortcut(newPartition, QKeySequence(Qt::CTRL + Qt::Key_N));
301 302
    newPartition->setIcon(QIcon::fromTheme(QStringLiteral("document-new")).pixmap(IconSize(KIconLoader::Toolbar)));

303 304
    QAction* resizePartition = actionCollection()->addAction(QStringLiteral("resizePartition"));
    connect(resizePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onResizePartition);
305
    resizePartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
306 307 308
    resizePartition->setText(xi18nc("@action:inmenu", "Resize/Move"));
    resizePartition->setToolTip(xi18nc("@info:tooltip", "Resize or move partition"));
    resizePartition->setStatusTip(xi18nc("@info:status", "Shrink, grow or move an existing partition."));
309
    actionCollection()->setDefaultShortcut(resizePartition, QKeySequence(Qt::CTRL + Qt::Key_R));
310 311
    resizePartition->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")).pixmap(IconSize(KIconLoader::Toolbar)));

312 313
    QAction* deletePartition = actionCollection()->addAction(QStringLiteral("deletePartition"));
    connect(deletePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onDeletePartition);
314
    deletePartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
315 316 317
    deletePartition->setText(xi18nc("@action:inmenu", "Delete"));
    deletePartition->setToolTip(xi18nc("@info:tooltip", "Delete partition"));
    deletePartition->setStatusTip(xi18nc("@info:status", "Delete a partition."));
318
    actionCollection()->setDefaultShortcut(deletePartition, QKeySequence::Delete);
319 320
    deletePartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")).pixmap(IconSize(KIconLoader::Toolbar)));

321 322
    QAction* shredPartition = actionCollection()->addAction(QStringLiteral("shredPartition"));
    connect(shredPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onShredPartition);
323
    shredPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
324 325 326
    shredPartition->setText(xi18nc("@action:inmenu", "Shred"));
    shredPartition->setToolTip(xi18nc("@info:tooltip", "Shred partition"));
    shredPartition->setStatusTip(xi18nc("@info:status", "Shred a partition so that its contents cannot be restored."));
327
    actionCollection()->setDefaultShortcut(shredPartition, QKeySequence(Qt::SHIFT + Qt::Key_Delete));
328 329
    shredPartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete-shred")).pixmap(IconSize(KIconLoader::Toolbar)));

330 331
    QAction* copyPartition = actionCollection()->addAction(QStringLiteral("copyPartition"));
    connect(copyPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onCopyPartition);
332
    copyPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
333 334 335
    copyPartition->setText(xi18nc("@action:inmenu", "Copy"));
    copyPartition->setToolTip(xi18nc("@info:tooltip", "Copy partition"));
    copyPartition->setStatusTip(xi18nc("@info:status", "Copy an existing partition."));
336
    actionCollection()->setDefaultShortcut(copyPartition, QKeySequence(Qt::CTRL + Qt::Key_C));
337 338
    copyPartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")).pixmap(IconSize(KIconLoader::Toolbar)));

339 340
    QAction* pastePartition = actionCollection()->addAction(QStringLiteral("pastePartition"));
    connect(pastePartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onPastePartition);
341
    pastePartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
342 343 344
    pastePartition->setText(xi18nc("@action:inmenu", "Paste"));
    pastePartition->setToolTip(xi18nc("@info:tooltip", "Paste partition"));
    pastePartition->setStatusTip(xi18nc("@info:status", "Paste a copied partition."));
345
    actionCollection()->setDefaultShortcut(pastePartition, QKeySequence(Qt::CTRL + Qt::Key_V));
346 347
    pastePartition->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste")).pixmap(IconSize(KIconLoader::Toolbar)));

348 349
    QAction* editMountPoint = actionCollection()->addAction(QStringLiteral("editMountPoint"));
    connect(editMountPoint, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onEditMountPoint);
350
    editMountPoint->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
351 352 353
    editMountPoint->setText(xi18nc("@action:inmenu", "Edit Mount Point"));
    editMountPoint->setToolTip(xi18nc("@info:tooltip", "Edit mount point"));
    editMountPoint->setStatusTip(xi18nc("@info:status", "Edit a partition's mount point and options."));
354

355 356
    QAction* mountPartition = actionCollection()->addAction(QStringLiteral("mountPartition"));
    connect(mountPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onMountPartition);
357
    mountPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
358 359 360
    mountPartition->setText(xi18nc("@action:inmenu", "Mount"));
    mountPartition->setToolTip(xi18nc("@info:tooltip", "Mount or unmount partition"));
    mountPartition->setStatusTip(xi18nc("@info:status", "Mount or unmount a partition."));
361

362 363
    QAction* decryptPartition = actionCollection()->addAction(QStringLiteral("decryptPartition"));
    connect(decryptPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onDecryptPartition);
364
    decryptPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
365 366 367
    decryptPartition->setText(xi18nc("@action:inmenu", "Unlock"));
    decryptPartition->setToolTip(xi18nc("@info:tooltip", "Unlock or lock encrypted partition"));
    decryptPartition->setStatusTip(xi18nc("@info:status", "Unlock or lock encrypted partition."));
368

369 370
    QAction* checkPartition = actionCollection()->addAction(QStringLiteral("checkPartition"));
    connect(checkPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onCheckPartition);
371
    checkPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
372 373 374
    checkPartition->setText(xi18nc("@action:inmenu", "Check"));
    checkPartition->setToolTip(xi18nc("@info:tooltip", "Check partition"));
    checkPartition->setStatusTip(xi18nc("@info:status", "Check a filesystem on a partition for errors."));
375 376
    checkPartition->setIcon(QIcon::fromTheme(QStringLiteral("flag")).pixmap(IconSize(KIconLoader::Toolbar)));

377 378
    QAction* propertiesPartition = actionCollection()->addAction(QStringLiteral("propertiesPartition"));
    connect(propertiesPartition, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onPropertiesPartition);
379
    propertiesPartition->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
380 381 382
    propertiesPartition->setText(xi18nc("@action:inmenu", "Properties"));
    propertiesPartition->setToolTip(xi18nc("@info:tooltip", "Show partition properties dialog"));
    propertiesPartition->setStatusTip(xi18nc("@info:status", "View and modify partition properties (label, partition flags, etc.)"));
383 384
    propertiesPartition->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")).pixmap(IconSize(KIconLoader::Toolbar)));

385 386
    QAction* backup = actionCollection()->addAction(QStringLiteral("backupPartition"));
    connect(backup, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onBackupPartition);
387
    backup->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
388 389 390
    backup->setText(xi18nc("@action:inmenu", "Backup"));
    backup->setToolTip(xi18nc("@info:tooltip", "Backup partition"));
    backup->setStatusTip(xi18nc("@info:status", "Backup a partition to an image file."));
391 392
    backup->setIcon(QIcon::fromTheme(QStringLiteral("document-export")).pixmap(IconSize(KIconLoader::Toolbar)));

393 394
    QAction* restore = actionCollection()->addAction(QStringLiteral("restorePartition"));
    connect(restore, &QAction::triggered, &pmWidget(), &PartitionManagerWidget::onRestorePartition);
395
    restore->setEnabled(false);
Andrius Štikonas's avatar
Andrius Štikonas committed
396 397 398
    restore->setText(xi18nc("@action:inmenu", "Restore"));
    restore->setToolTip(xi18nc("@info:tooltip", "Restore partition"));
    restore->setStatusTip(xi18nc("@info:status", "Restore a partition from an image file."));
399 400
    restore->setIcon(QIcon::fromTheme(QStringLiteral("document-import")).pixmap(IconSize(KIconLoader::Toolbar)));

401 402 403 404
    // Tools actions
    QAction* createVolumeGroup = actionCollection()->addAction(QStringLiteral("createVolumeGroup"));
    connect(createVolumeGroup, &QAction::triggered, this, &MainWindow::onCreateNewVolumeGroup);
    createVolumeGroup->setEnabled(false);
405
    createVolumeGroup->setText(i18nc("@action:inmenu", "New Volume Group"));
406 407 408 409 410
    createVolumeGroup->setToolTip(i18nc("@info:tooltip", "Create a new LVM Volume Group"));
    createVolumeGroup->setStatusTip(i18nc("@info:status", "Create a new LVM Volume Group as a device."));
    actionCollection()->setDefaultShortcut(createVolumeGroup, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_L));
    createVolumeGroup->setIcon(QIcon::fromTheme(QStringLiteral("document-new")).pixmap(IconSize(KIconLoader::Toolbar)));

411 412
    QAction* fileSystemSupport = actionCollection()->addAction(QStringLiteral("fileSystemSupport"));
    connect(fileSystemSupport, &QAction::triggered, this, &MainWindow::onFileSystemSupport);
Andrius Štikonas's avatar
Andrius Štikonas committed
413 414 415
    fileSystemSupport->setText(xi18nc("@action:inmenu", "File System Support"));
    fileSystemSupport->setToolTip(xi18nc("@info:tooltip", "View file system support information"));
    fileSystemSupport->setStatusTip(xi18nc("@info:status", "Show information about supported file systems."));
416

417 418 419 420 421 422 423 424
    QAction* refreshDevices = actionCollection()->addAction(QStringLiteral("refreshDevices"));
    connect(refreshDevices, &QAction::triggered, this, &MainWindow::onRefreshDevices);
    refreshDevices->setText(xi18nc("@action:inmenu refresh list of devices", "Refresh Devices"));
    refreshDevices->setToolTip(xi18nc("@info:tooltip", "Refresh all devices"));
    refreshDevices->setStatusTip(xi18nc("@info:status", "Renew the devices list."));
    actionCollection()->setDefaultShortcut(refreshDevices, Qt::Key_F5);
    refreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")).pixmap(IconSize(KIconLoader::Toolbar)));

425
    // Settings Actions
426 427 428 429 430
    actionCollection()->addAction(QStringLiteral("toggleDockDevices"), dockDevices().toggleViewAction());
    actionCollection()->addAction(QStringLiteral("toggleDockOperations"), dockOperations().toggleViewAction());
    actionCollection()->addAction(QStringLiteral("toggleDockInformation"), dockInformation().toggleViewAction());
    actionCollection()->addAction(QStringLiteral("toggleDockLog"), dockLog().toggleViewAction());

431
    KStandardAction::preferences(this, &MainWindow::onConfigureOptions, actionCollection());
432 433

    // Log Actions
434 435
    QAction* clearLog = actionCollection()->addAction(QStringLiteral("clearLog"));
    connect(clearLog, &QAction::triggered, &treeLog(), &TreeLog::onClearLog);
Andrius Štikonas's avatar
Andrius Štikonas committed
436 437 438
    clearLog->setText(xi18nc("@action:inmenu", "Clear Log"));
    clearLog->setToolTip(xi18nc("@info:tooltip", "Clear the log output"));
    clearLog->setStatusTip(xi18nc("@info:status", "Clear the log output panel."));
439 440
    clearLog->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-list")).pixmap(IconSize(KIconLoader::Toolbar)));

441 442
    QAction* saveLog = actionCollection()->addAction(QStringLiteral("saveLog"));
    connect(saveLog, &QAction::triggered, &treeLog(), &TreeLog::onSaveLog);
Andrius Štikonas's avatar
Andrius Štikonas committed
443 444 445
    saveLog->setText(xi18nc("@action:inmenu", "Save Log"));
    saveLog->setToolTip(xi18nc("@info:tooltip", "Save the log output"));
    saveLog->setStatusTip(xi18nc("@info:status", "Save the log output to a file."));
446
    saveLog->setIcon(QIcon::fromTheme(QStringLiteral("document-save")).pixmap(IconSize(KIconLoader::Toolbar)));
447 448 449 450 451 452 453 454

    // Help Actions
    QAction* aboutKPMcore = actionCollection()->addAction(QStringLiteral("aboutKPMcore"));
    connect(aboutKPMcore, &QAction::triggered, this, &MainWindow::onShowAboutKPMcore);
    aboutKPMcore->setText(xi18nc("@action:inmenu", "About KPMcore Library"));
    aboutKPMcore->setToolTip(xi18nc("@info:tooltip", "Show About KPMcore dialog"));
    aboutKPMcore->setStatusTip(xi18nc("@info:status", "Show About KPMcore dialog."));
    aboutKPMcore->setIcon(QIcon::fromTheme(QStringLiteral("partitionmanager")).pixmap(IconSize(KIconLoader::Toolbar)));
Volker Lanz's avatar
Volker Lanz committed
455 456 457 458
}

void MainWindow::setupConnections()
{
459
    connect(&listDevices(), &ListDevices::selectionChanged, &pmWidget(), qOverload<const QString&>(&PartitionManagerWidget::setSelectedDevice));
460
    connect(&listDevices(), &ListDevices::deviceDoubleClicked, this, &MainWindow::onPropertiesDevice);
Volker Lanz's avatar
Volker Lanz committed
461 462 463 464
}

void MainWindow::setupStatusBar()
{
465
    statusBar()->addWidget(&statusText());
Volker Lanz's avatar
Volker Lanz committed
466 467 468 469
}

void MainWindow::loadConfig()
{
470 471 472 473
    if (Config::firstRun()) {
        dockLog().setVisible(false);
        dockInformation().setVisible(false);
    }
474
    PartitionAlignment::setSectorAlignment(Config::sectorAlignment());
Volker Lanz's avatar
Volker Lanz committed
475 476 477 478
}

void MainWindow::saveConfig() const
{
479 480
    Config::setFirstRun(false);
    Config::self()->save();
Volker Lanz's avatar
Volker Lanz committed
481 482
}

483 484
void MainWindow::enableActions()
{
485 486
    actionCollection()->action(QStringLiteral("createNewPartitionTable"))
            ->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice()));
487
    actionCollection()->action(QStringLiteral("createNewPartitionTable"))
488 489
            ->setVisible(pmWidget().selectedDevice() && (pmWidget().selectedDevice()->type() == Device::Type::Disk_Device ||
                                                         pmWidget().selectedDevice()->type() == Device::Type::SoftwareRAID_Device));
490 491 492 493 494 495 496
    actionCollection()->action(QStringLiteral("exportPartitionTable"))
            ->setEnabled(pmWidget().selectedDevice() &&
                         pmWidget().selectedDevice()->partitionTable() &&
                         operationStack().size() == 0);
    actionCollection()->action(QStringLiteral("importPartitionTable"))
            ->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice()));
    actionCollection()->action(QStringLiteral("smartStatusDevice"))
497
            ->setEnabled(pmWidget().selectedDevice() != nullptr && pmWidget().selectedDevice()->type() == Device::Type::Disk_Device &&
498
                                                        pmWidget().selectedDevice()->smartStatus().isValid());
499
    actionCollection()->action(QStringLiteral("smartStatusDevice"))
500
            ->setVisible(pmWidget().selectedDevice() != nullptr && pmWidget().selectedDevice()->type() == Device::Type::Disk_Device);
501 502 503 504 505 506 507 508
    actionCollection()->action(QStringLiteral("propertiesDevice"))
            ->setEnabled(pmWidget().selectedDevice() != nullptr);

    actionCollection()->action(QStringLiteral("undoOperation"))
            ->setEnabled(operationStack().size() > 0);
    actionCollection()->action(QStringLiteral("clearAllOperations"))
            ->setEnabled(operationStack().size() > 0);
    actionCollection()->action(QStringLiteral("applyAllOperations"))
509
            ->setEnabled(operationStack().size() > 0);
510 511 512

    const bool readOnly = pmWidget().selectedDevice() == nullptr ||
                          pmWidget().selectedDevice()->partitionTable() == nullptr ||
513
                          pmWidget().selectedDevice()->partitionTable()->isReadOnly();
514

515 516 517
    actionCollection()->action(QStringLiteral("createVolumeGroup"))
            ->setEnabled(CreateVolumeGroupOperation::canCreate());

518
    bool lvmDevice = pmWidget().selectedDevice() && pmWidget().selectedDevice()->type() == Device::Type::LVM_Device;
519
    bool removable = false;
520

521 522 523
    if (lvmDevice)
        removable = RemoveVolumeGroupOperation::isRemovable(dynamic_cast<LvmDevice*>(pmWidget().selectedDevice()));

524
    actionCollection()->action(QStringLiteral("removeVolumeGroup"))->setEnabled(removable);
525
    actionCollection()->action(QStringLiteral("removeVolumeGroup"))->setVisible(lvmDevice);
526

527 528
    bool deactivatable = lvmDevice ?
        DeactivateVolumeGroupOperation::isDeactivatable(dynamic_cast<LvmDevice*>(pmWidget().selectedDevice())) : false;
529
    actionCollection()->action(QStringLiteral("deactivateVolumeGroup"))->setEnabled(deactivatable);
530
    actionCollection()->action(QStringLiteral("deactivateVolumeGroup"))->setVisible(lvmDevice);
531

532 533
    actionCollection()->action(QStringLiteral("resizeVolumeGroup"))->setEnabled(lvmDevice);
    actionCollection()->action(QStringLiteral("resizeVolumeGroup"))->setVisible(lvmDevice);
534

Chantara Tith's avatar
Chantara Tith committed
535 536 537 538 539
    const Partition* part = pmWidget().selectedPartition();

    actionCollection()->action(QStringLiteral("newPartition"))
            ->setEnabled(!readOnly && NewOperation::canCreateNew(part));

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    const bool canResize = ResizeOperation::canGrow(part) ||
                           ResizeOperation::canShrink(part) ||
                           ResizeOperation::canMove(part);
    actionCollection()->action(QStringLiteral("resizePartition"))
            ->setEnabled(!readOnly && canResize);

    actionCollection()->action(QStringLiteral("copyPartition"))
            ->setEnabled(CopyOperation::canCopy(part));
    actionCollection()->action(QStringLiteral("deletePartition"))
            ->setEnabled(!readOnly && DeleteOperation::canDelete(part));
    actionCollection()->action(QStringLiteral("shredPartition"))
            ->setEnabled(!readOnly && DeleteOperation::canDelete(part));
    actionCollection()->action(QStringLiteral("pastePartition"))
            ->setEnabled(!readOnly && CopyOperation::canPaste(part, pmWidget().clipboardPartition()));
    actionCollection()->action(QStringLiteral("propertiesPartition"))
            ->setEnabled(part != nullptr);

    actionCollection()->action(QStringLiteral("editMountPoint"))
558
            ->setEnabled(part && !part->isMounted() && part->fileSystem().canMount(part->deviceNode(), QStringLiteral("/")));
559 560 561 562 563 564 565 566 567 568

    actionCollection()->action(QStringLiteral("mountPartition"))
            ->setEnabled(part &&
                         (part->canMount() || part->canUnmount()));
    if (part != nullptr)
        actionCollection()->action(QStringLiteral("mountPartition"))
                ->setText(part->isMounted() ?
                          part->fileSystem().unmountTitle() :
                          part->fileSystem().mountTitle());

Teo Mrnjavac's avatar
Teo Mrnjavac committed
569 570 571 572
    if (part && part->roles().has(PartitionRole::Luks)) {
        const FileSystem& fsRef = part->fileSystem();
        const FS::luks* luksFs = dynamic_cast<const FS::luks*>(&fsRef);

573 574
        actionCollection()->action(QStringLiteral("decryptPartition"))
                ->setVisible(true);
575
        actionCollection()->action(QStringLiteral("decryptPartition"))
576
                ->setEnabled(luksFs && !operationStack().contains(part) &&
Teo Mrnjavac's avatar
Teo Mrnjavac committed
577 578 579 580 581 582 583
                             (luksFs->canCryptOpen(part->partitionPath()) ||
                              luksFs->canCryptClose(part->partitionPath())));
        if (luksFs) {
            actionCollection()->action(QStringLiteral("decryptPartition"))
                    ->setText(luksFs->isCryptOpen() ?
                              luksFs->cryptCloseTitle() :
                              luksFs->cryptOpenTitle());
584
        }
Teo Mrnjavac's avatar
Teo Mrnjavac committed
585 586
    }
    else {
587
        actionCollection()->action(QStringLiteral("decryptPartition"))
588
                ->setEnabled(false);
589 590
        actionCollection()->action(QStringLiteral("decryptPartition"))
                ->setVisible(false);
591
    }
592

593

594 595
    actionCollection()->action(QStringLiteral("checkPartition"))
            ->setEnabled(!readOnly && CheckOperation::canCheck(part));
596

597 598 599 600
    actionCollection()->action(QStringLiteral("backupPartition"))
            ->setEnabled(BackupOperation::canBackup(part));
    actionCollection()->action(QStringLiteral("restorePartition"))
            ->setEnabled(RestoreOperation::canRestore(part));
601 602 603
}

void MainWindow::on_m_ApplyProgressDialog_finished()
Volker Lanz's avatar
Volker Lanz committed
604
{
605
    scanDevices();
606 607 608 609
}

void MainWindow::on_m_OperationStack_operationsChanged()
{
610 611 612
    listOperations().updateOperations(operationStack().operations());
    pmWidget().updatePartitions();
    enableActions();
613

614 615
    // this will make sure that the info pane gets updated
    on_m_PartitionManagerWidget_selectedPartitionChanged(pmWidget().selectedPartition());
616

Andrius Štikonas's avatar
Andrius Štikonas committed
617
    statusText().setText(xi18ncp("@info:status", "One pending operation", "%1 pending operations", operationStack().size()));
Volker Lanz's avatar
Volker Lanz committed
618 619
}

620
void MainWindow::on_m_OperationStack_devicesChanged()
Volker Lanz's avatar
Volker Lanz committed
621
{
622
    QReadLocker lockDevices(&operationStack().lock());
623

624
    listDevices().updateDevices(operationStack().previewDevices());
625

626 627 628 629
    if (pmWidget().selectedDevice())
        infoPane().showDevice(dockWidgetArea(&dockInformation()), *pmWidget().selectedDevice());
    else
        infoPane().clear();
Volker Lanz's avatar
Volker Lanz committed
630

631
    updateWindowTitle();
Volker Lanz's avatar
Volker Lanz committed
632 633
}

634
void MainWindow::on_m_DockInformation_dockLocationChanged(Qt::DockWidgetArea)
635
{
636
    on_m_PartitionManagerWidget_selectedPartitionChanged(pmWidget().selectedPartition());
637 638
}

Volker Lanz's avatar
Volker Lanz committed
639 640
void MainWindow::updateWindowTitle()
{
641
    QString title;
Volker Lanz's avatar
Volker Lanz committed
642

643 644
    if (pmWidget().selectedDevice())
        title = pmWidget().selectedDevice()->deviceNode();
Volker Lanz's avatar
Volker Lanz committed
645

646
    setWindowTitle(title);
Volker Lanz's avatar
Volker Lanz committed
647 648
}

Andrius Štikonas's avatar
Andrius Štikonas committed
649
void MainWindow::listOperationsContextMenuRequested(const QPoint& pos)
650
{
651
    QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("edit"), this));
652

653
    if (menu)
Andrius Štikonas's avatar
Andrius Štikonas committed
654
        menu->exec(m_ListOperations->listOperations().viewport()->mapToGlobal(pos));
655 656
}

Andrius Štikonas's avatar
Andrius Štikonas committed
657
void MainWindow::treeLogContextMenuRequested(const QPoint& pos)
658
{
659
    QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("log"), this));
660

661
    if (menu)
Andrius Štikonas's avatar
Andrius Štikonas committed
662
        menu->exec(m_TreeLog->treeLog().viewport()->mapToGlobal(pos));
663 664
}

Andrius Štikonas's avatar
Andrius Štikonas committed
665
void MainWindow::listDevicesContextMenuRequested(const QPoint& pos)
666
{
667
    QMenu* menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("device"), this));
668

669
    if (menu)
Andrius Štikonas's avatar
Andrius Štikonas committed
670
        menu->exec(m_ListDevices->listDevices().viewport()->mapToGlobal(pos));
671 672 673 674
}

void MainWindow::on_m_PartitionManagerWidget_contextMenuRequested(const QPoint& pos)
{
675
    QMenu* menu = nullptr;
676

677 678
    if (pmWidget().selectedPartition() == nullptr) {
        if (pmWidget().selectedDevice() != nullptr)
679 680 681 682 683 684
            menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("device"), this));
    } else
        menu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("partition"), this));

    if (menu)
        menu->exec(pos);
685 686 687 688
}

void MainWindow::on_m_PartitionManagerWidget_deviceDoubleClicked(const Device*)
{
689
    actionCollection()->action(QStringLiteral("propertiesDevice"))->trigger();
690 691 692 693
}

void MainWindow::on_m_PartitionManagerWidget_partitionDoubleClicked(const Partition*)
{
694
    actionCollection()->action(QStringLiteral("propertiesPartition"))->trigger();
695 696
}

697
void MainWindow::on_m_PartitionManagerWidget_selectedPartitionChanged(const Partition* p)
Volker Lanz's avatar
Volker Lanz committed
698
{
699 700 701 702 703 704 705 706 707
    if (p)
        infoPane().showPartition(dockWidgetArea(&dockInformation()), *p);
    else if (pmWidget().selectedDevice())
        infoPane().showDevice(dockWidgetArea(&dockInformation()), *pmWidget().selectedDevice());
    else
        infoPane().clear();

    updateWindowTitle();
    enableActions();
Volker Lanz's avatar
Volker Lanz committed
708 709
}

710 711
void MainWindow::scanDevices()
{
712
    Log(Log::Level::information) << xi18nc("@info:progress", "Using backend plugin: %1 (%2)",
713 714
                                   CoreBackendManager::self()->backend()->id(),
                                   CoreBackendManager::self()->backend()->version());
715

Andrius Štikonas's avatar
Andrius Štikonas committed
716
    Log() << xi18nc("@info:progress", "Scanning devices...");
717

718 719
    // remember the currently selected device's node
    setSavedSelectedDeviceNode(pmWidget().selectedDevice() ?  pmWidget().selectedDevice()->deviceNode() : QString());
720

721
    pmWidget().clear();
722

723
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
724

725 726
    scanProgressDialog().setEnabled(true);
    scanProgressDialog().show();
727

728
    deviceScanner().start();
729 730
}

731
void MainWindow::on_m_DeviceScanner_progress(const QString& deviceNode, int percent)
732
{
733
    scanProgressDialog().setProgress(percent);
734
    scanProgressDialog().setDeviceName(deviceNode);
735 736 737 738
}

void MainWindow::on_m_DeviceScanner_finished()
{
739
    QReadLocker lockDevices(&operationStack().lock());
740

741
    scanProgressDialog().setProgress(100);
742

743 744
    if (!operationStack().previewDevices().isEmpty())
        pmWidget().setSelectedDevice(operationStack().previewDevices()[0]);
745

746
    pmWidget().updatePartitions();
747

Andrius Štikonas's avatar
Andrius Štikonas committed
748
    Log() << xi18nc("@info:progress", "Scan finished.");
749
    QApplication::restoreOverrideCursor();
750

751 752 753 754
    // try to set the seleted device, either from the saved one or just select the
    // first device
    if (!listDevices().setSelectedDevice(savedSelectedDeviceNode()) && !operationStack().previewDevices().isEmpty())
        listDevices().setSelectedDevice(operationStack().previewDevices()[0]->deviceNode());
755

756 757
    updateSeletedDeviceMenu();
    checkFileSystemSupport();
758 759 760 761
}

void MainWindow::updateSeletedDeviceMenu()
{
762
    QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
763 764 765
    if (!devicesMenu)
        return;

766 767 768 769
    devicesMenu->clear();

    devicesMenu->setEnabled(!operationStack().previewDevices().isEmpty());

Andrius Štikonas's avatar
Andrius Štikonas committed
770 771
    const auto previewDevices = operationStack().previewDevices();
    for (auto const &d : previewDevices) {
772 773 774 775
        QAction* action = new QAction(d->prettyName(), devicesMenu);
        action->setCheckable(true);
        action->setChecked(d->deviceNode() == pmWidget().selectedDevice()->deviceNode());
        action->setData(d->deviceNode());
776
        connect(action, &QAction::triggered, this, &MainWindow::onSelectedDeviceMenuTriggered);
777 778
        devicesMenu->addAction(action);
    }
779 780 781 782
}

void MainWindow::onSelectedDeviceMenuTriggered(bool)
{
783 784
    QAction* action = qobject_cast<QAction*>(sender());
    QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
785

786
    if (action == nullptr || action->parent() != devicesMenu || !devicesMenu)
787
        return;
788

Andrius Štikonas's avatar
Andrius Štikonas committed
789 790
    const auto children = devicesMenu->findChildren<QAction*>();
    for (auto &entry : children)
791
        entry->setChecked(entry == action);
792

793
    listDevices().setSelectedDevice(action->data().toString());
794 795 796 797
}

void MainWindow::on_m_ListDevices_selectionChanged(const QString& device_node)
{
798
    QMenu* devicesMenu = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("selectedDevice"), this));
799 800
    if (!devicesMenu)
        return;
801

Andrius Štikonas's avatar
Andrius Štikonas committed
802 803
    const auto children = devicesMenu->findChildren<QAction*>();
    for (auto &entry : children)
804
        entry->setChecked(entry->data().toString() == device_node);
805 806 807 808
}

void MainWindow::onRefreshDevices()
{
809 810 811 812
    if (operationStack().size() == 0 || KMessageBox::warningContinueCancel(this,
            xi18nc("@info",
                   "<para>Do you really want to rescan the devices?</para>"
                   "<para><warning>This will also clear the list of pending operations.</warning></para>"),
Andrius Štikonas's avatar
Andrius Štikonas committed
813 814
            xi18nc("@title:window", "Really Rescan the Devices?"),
            KGuiItem(xi18nc("@action:button", "Rescan Devices"), QStringLiteral("arrow-right")),
815 816 817
            KStandardGuiItem::cancel(), QStringLiteral("reallyRescanDevices")) == KMessageBox::Continue) {
        scanDevices();
    }
818 819 820 821
}

void MainWindow::onApplyAllOperations()
{
822
    QStringList opList;
823

Andrius Štikonas's avatar
Andrius Štikonas committed
824 825
    const auto operations = operationStack().operations();
    for (const auto &op : operations)
826
        opList.append(op->description());
827

828 829 830 831
    if (KMessageBox::warningContinueCancelList(this,
            xi18nc("@info",
                   "<para>Do you really want to apply the pending operations listed below?</para>"
                   "<para><warning>This will permanently modify your disks.</warning></para>"),
Andrius Štikonas's avatar
Andrius Štikonas committed
832 833
            opList, xi18nc("@title:window", "Apply Pending Operations?"),
            KGuiItem(xi18nc("@action:button", "Apply Pending Operations"), QStringLiteral("arrow-right")),
834
            KStandardGuiItem::cancel()) == KMessageBox::Continue) {
Andrius Štikonas's avatar
Andrius Štikonas committed
835
        Log() << xi18nc("@info:status", "Applying operations...");
836

837
        applyProgressDialog().show();
838

839
        operationRunner().setReport(&applyProgressDialog().report());
840

841 842 843 844 845
        // Undo all operations so the runner has a defined starting point
        for (int i = operationStack().operations().size() - 1; i >= 0; i--) {
            operationStack().operations()[i]->undo();
            operationStack().operations()[i]->setStatus(Operation::StatusNone);
        }
846

847
        pmWidget().updatePartitions();
848

849 850
        operationRunner().start();
    }
851 852 853 854
}

void MainWindow::onUndoOperation()
{
855
    Q_ASSERT(operationStack().size() > 0);
856

857 858
    if (operationStack().size() == 0)
        return;
859

Andrius Štikonas's avatar
Andrius Štikonas committed
860
    Log() << xi18nc("@info:status", "Undoing operation: %1", operationStack().operations().last()->description());
861
    operationStack().pop();
862

863 864 865
    // it's possible the undo killed the partition in the clipboard. if there's a partition in the clipboard, try
    // to find a device for it (OperationStack::findDeviceForPartition() only compares pointers, so an invalid
    // pointer is not a problem). if no device is found, the pointer must be dangling, so clear the clipboard.
866 867
    if (pmWidget().clipboardPartition() != nullptr && operationStack().findDeviceForPartition(pmWidget().clipboardPartition()) == nullptr)
        pmWidget().setClipboardPartition(nullptr);
868

869 870
    pmWidget().updatePartitions();
    enableActions();
871 872 873 874
}

void MainWindow::onClearAllOperations()
{
875
    if (KMessageBox::warningContinueCancel(this,
Andrius Štikonas's avatar
Andrius Štikonas committed
876 877 878
                                           xi18nc("@info", "Do you really want to clear the list of pending operations?"),
                                           xi18nc("@title:window", "Clear Pending Operations?"),
                                           KGuiItem(xi18nc("@action:button", "Clear Pending Operations"), QStringLiteral("arrow-right")),
879
                                           KStandardGuiItem::cancel(), QStringLiteral("reallyClearPendingOperations")) == KMessageBox::Continue) {
Andrius Štikonas's avatar
Andrius Štikonas committed
880
        Log() << xi18nc("@info:status", "Clearing the list of pending operations.");
881 882 883 884 885
        operationStack().clearOperations();

        pmWidget().updatePartitions();
        enableActions();
    }
886 887 888 889
}

void MainWindow::onCreateNewPartitionTable()
{
890
    Q_ASSERT(pmWidget().selectedDevice());
891

892
    if (pmWidget().selectedDevice() == nullptr) {
893 894 895
        qWarning() << "selected device is null.";
        return;
    }
896

897