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"