diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff045e2..8ed0b2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,7 +26,7 @@ file(GLOB partitionmanagerprivate_SRCS gui/*.cpp ) -file(GLOB partitionmanagerprivate_UIFILES gui/*.ui) +file(GLOB partitionmanagerprivate_UIFILES gui/*.ui kcm/*.ui) kde4_add_ui_files(partitionmanagerprivate_SRCS ${partitionmanagerprivate_UIFILES}) @@ -85,3 +85,22 @@ if(PARTMAN_KPART) endif(PARTMAN_KPART) +############################################ + +option(PARTMAN_KCM "Build a kcm for KDE Partition Manager" ON) + +if(PARTMAN_KCM) + file(GLOB partitionmanagerkcm_SRCS kcm/partitionmanagerkcm.cpp) + + kde4_add_plugin(kcm_partitionmanager ${partitionmanagerkcm_SRCS}) + + target_link_libraries(kcm_partitionmanager + ${KDE4_KDEUI_LIBS} + partitionmanagerprivate + ) + + install(TARGETS kcm_partitionmanager DESTINATION ${PLUGIN_INSTALL_DIR}) + install(FILES kcm/kcm_partitionmanager.desktop DESTINATION ${SERVICES_INSTALL_DIR}) + +endif(PARTMAN_KCM) + diff --git a/src/gui/listdevices.cpp b/src/gui/listdevices.cpp new file mode 100644 index 0000000..656c785 --- /dev/null +++ b/src/gui/listdevices.cpp @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "gui/listdevices.h" + +#include "gui/partitionmanagerwidget.h" + +#include "core/device.h" + +#include "util/globallog.h" +#include "util/capacity.h" + +#include +#include + +/** Creates a new ListDevices instance. + @param parent the parent widget +*/ +ListDevices::ListDevices(QWidget* parent) : + QWidget(parent), + Ui::ListDevicesBase(), + m_ActionCollection(NULL), + m_PartitionManagerWidget(NULL) +{ + setupUi(this); +} + +void ListDevices::updateDevices() +{ + listDevices().clear(); + + foreach(const Device* d, pmWidget().previewDevices()) + { + const QString shortText = d->deviceNode() + " (" + Capacity(*d).toString() + ')'; + const QString longText = d->deviceNode() + " (" + Capacity(*d).toString() + ", " + d->name() + ')'; + QListWidgetItem* item = new QListWidgetItem(SmallIcon("drive-harddisk"), shortText); + item->setToolTip(longText); + listDevices().addItem(item); + } +} + +void ListDevices::on_m_ListDevices_itemSelectionChanged() +{ + int idx = -1; + + if (listDevices().selectedItems().size() == 1) + idx = listDevices().row(listDevices().selectedItems()[0]); + + Device* d = NULL; + if (idx >= 0 && idx < pmWidget().previewDevices().size()) + d = pmWidget().previewDevices()[idx]; + + emit selectionChanged(d); +} + +void ListDevices::on_m_ListDevices_customContextMenuRequested(const QPoint& pos) +{ + Q_ASSERT(actionCollection()); + + KMenu deviceMenu; + deviceMenu.addAction(actionCollection()->action("createNewPartitionTable")); + deviceMenu.exec(listDevices().viewport()->mapToGlobal(pos)); +} diff --git a/src/gui/listdevices.h b/src/gui/listdevices.h new file mode 100644 index 0000000..743bf2b --- /dev/null +++ b/src/gui/listdevices.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#if !defined(LISTDEVICES__H) + +#define LISTDEVICES__H + +#include "util/libpartitionmanagerexport.h" + +#include "ui_listdevicesbase.h" + +#include + +#include + +class Device; +class QPoint; +class PartitionManagerWidget; +class KActionCollection; + +/** @brief A list of devices. + @author vl@fidra.de +*/ +class LIBPARTITIONMANAGERPRIVATE_EXPORT ListDevices : public QWidget, public Ui::ListDevicesBase +{ + Q_OBJECT + + public: + ListDevices(QWidget* parent); + + signals: + void selectionChanged(Device*); + + public: + void init(KActionCollection* coll, PartitionManagerWidget* pm_widget) { m_ActionCollection = coll; m_PartitionManagerWidget = pm_widget; } + + public slots: + void updateDevices(); + + protected: + QListWidget& listDevices() { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + const QListWidget& listDevices() const { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + + PartitionManagerWidget& pmWidget() { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + const PartitionManagerWidget& pmWidget() const { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + + KActionCollection* actionCollection() { return m_ActionCollection; } + + protected slots: + void on_m_ListDevices_itemSelectionChanged(); + void on_m_ListDevices_customContextMenuRequested(const QPoint& pos); + + private: + KActionCollection* m_ActionCollection; + PartitionManagerWidget* m_PartitionManagerWidget; +}; + +#endif + diff --git a/src/gui/listdevicesbase.ui b/src/gui/listdevicesbase.ui new file mode 100644 index 0000000..4d87b44 --- /dev/null +++ b/src/gui/listdevicesbase.ui @@ -0,0 +1,33 @@ + + ListDevicesBase + + + + 0 + 0 + 255 + 396 + + + + Form + + + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + + + + + + diff --git a/src/gui/listoperations.cpp b/src/gui/listoperations.cpp new file mode 100644 index 0000000..db2a2c1 --- /dev/null +++ b/src/gui/listoperations.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "gui/listoperations.h" + +#include "gui/partitionmanagerwidget.h" + +#include "ops/operation.h" + +#include "util/globallog.h" +#include "util/capacity.h" + +#include +#include + +/** Creates a new ListOperations instance. + @param parent the parent widget +*/ +ListOperations::ListOperations(QWidget* parent) : + QWidget(parent), + Ui::ListOperationsBase(), + m_ActionCollection(NULL), + m_PartitionManagerWidget(NULL) +{ + setupUi(this); +} + +void ListOperations::updateOperations() +{ + listOperations().clear(); + + foreach (const Operation* op, pmWidget().operations()) + { + QListWidgetItem* item = new QListWidgetItem(SmallIcon(op->iconName()), op->description()); + item->setToolTip(op->description()); + listOperations().addItem(item); + } + + listOperations().scrollToBottom(); +} + +void ListOperations::on_m_ListOperations_customContextMenuRequested(const QPoint& pos) +{ + Q_ASSERT(actionCollection()); + + KMenu opsMenu; + + opsMenu.addAction(actionCollection()->action("undoOperation")); + opsMenu.addAction(actionCollection()->action("clearAllOperations")); + opsMenu.addAction(actionCollection()->action("applyAllOperations")); + + opsMenu.exec(listOperations().viewport()->mapToGlobal(pos)); +} + diff --git a/src/gui/listoperations.h b/src/gui/listoperations.h new file mode 100644 index 0000000..43de41f --- /dev/null +++ b/src/gui/listoperations.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#if !defined(LISTOPERATIONS__H) + +#define LISTOPERATIONS__H + +#include "util/libpartitionmanagerexport.h" + +#include "ui_listoperationsbase.h" + +#include + +#include + +class Operation; +class QPoint; +class PartitionManagerWidget; +class KActionCollection; + +/** @brief A list of pending operations. + + @author vl@fidra.de +*/ +class LIBPARTITIONMANAGERPRIVATE_EXPORT ListOperations : public QWidget, public Ui::ListOperationsBase +{ + Q_OBJECT + + public: + ListOperations(QWidget* parent); + + public: + void init(KActionCollection* coll, PartitionManagerWidget* pm_widget) { m_ActionCollection = coll; m_PartitionManagerWidget = pm_widget; } + + protected: + KActionCollection* actionCollection() { return m_ActionCollection; } + + PartitionManagerWidget& pmWidget() { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + const PartitionManagerWidget& pmWidget() const { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + + QListWidget& listOperations() { Q_ASSERT(m_ListOperations); return *m_ListOperations; } + const QListWidget& listOperations() const { Q_ASSERT(m_ListOperations); return *m_ListOperations; } + + protected slots: + void on_m_ListOperations_customContextMenuRequested(const QPoint& pos); + void updateOperations(); + + private: + KActionCollection* m_ActionCollection; + PartitionManagerWidget* m_PartitionManagerWidget; +}; + +#endif + diff --git a/src/gui/listoperationsbase.ui b/src/gui/listoperationsbase.ui new file mode 100644 index 0000000..ee55972 --- /dev/null +++ b/src/gui/listoperationsbase.ui @@ -0,0 +1,45 @@ + + ListOperationsBase + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Qt::CustomContextMenu + + + Qt::ScrollBarAlwaysOff + + + false + + + true + + + QAbstractItemView::NoSelection + + + QListView::Adjust + + + true + + + + + + + + diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 4112c2c..67bf71f 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2008,2009 by Volker Lanz * * * * 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 * @@ -77,16 +77,6 @@ #include -class PartitionTreeWidgetItem : public QTreeWidgetItem -{ - public: - PartitionTreeWidgetItem(const Partition* p) : QTreeWidgetItem(), m_Partition(p) {} - const Partition* partition() const { return m_Partition; } - - private: - const Partition* m_Partition; -}; - /** Creates a new MainWindow instance. @param parent the parent widget @param coll an action collection if used as a KPart @@ -94,13 +84,8 @@ class PartitionTreeWidgetItem : public QTreeWidgetItem MainWindow::MainWindow(QWidget* parent, KActionCollection* coll) : KXmlGuiWindow(parent), Ui::MainWindowBase(), - m_LibParted(), - m_OperationStack(), - m_OperationRunner(operationStack()), m_StatusText(new QLabel(this)), m_InfoPane(new InfoPane(this)), - m_ClipboardPartition(NULL), - m_ProgressDialog(new ProgressDialog(this, operationRunner())), m_ActionCollection(coll) { setupUi(this); @@ -115,6 +100,10 @@ void MainWindow::init() setupStatusBar(); setupConnections(); + listDevices().init(actionCollection(), &pmWidget()); + listOperations().init(actionCollection(), &pmWidget()); + pmWidget().init(actionCollection()); + // If we were called with an action collection we're supposed to be a KPart, so don't // create the GUI in that case. if (m_ActionCollection != NULL) @@ -122,29 +111,24 @@ void MainWindow::init() else setupGUI(ToolBar | Keys | StatusBar | Save | Create); - FileSystemFactory::init(); - loadConfig(); dockInformation().setWidget(&infoPane()); - treePartitions().header()->setStretchLastSection(false); - - scanDevices(); } void MainWindow::closeEvent(QCloseEvent* event) { - if (progressDialog().isVisible()) + if (pmWidget().progressDialog().isVisible()) { event->ignore(); return; } - if (operationStack().size() > 0) + if (pmWidget().numPendingOperations() > 0) { if (KMessageBox::warningContinueCancel(this, i18ncp("@info", "Do you really want to quit the application?There is still an operation pending.", - "Do you really want to quit the application?There are still %1 operations pending.", operationStack().size()), + "Do you really want to quit the application?There are still %1 operations pending.", pmWidget().numPendingOperations()), i18nc("@title:window", "Discard Pending Operations and Quit?"), KGuiItem(i18nc("@action:button", "&Quit %1", KGlobal::mainComponent().aboutData()->programName())), KStandardGuiItem::cancel(), "reallyQuit") == KMessageBox::Cancel) @@ -161,156 +145,33 @@ void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::changeEvent(QEvent* event) { - if ((event->type() == QEvent::ActivationChange || event->type() == QEvent::WindowStateChange) && event->spontaneous() && isActiveWindow() && progressDialog().isVisible()) + if ((event->type() == QEvent::ActivationChange || event->type() == QEvent::WindowStateChange) && event->spontaneous() && isActiveWindow() && pmWidget().progressDialog().isVisible()) { - progressDialog().activateWindow(); - progressDialog().raise(); + pmWidget().progressDialog().activateWindow(); + pmWidget().progressDialog().raise(); } KXmlGuiWindow::changeEvent(event); } -KActionCollection* MainWindow::actionCollection() const -{ - return m_ActionCollection != NULL ? m_ActionCollection : KXmlGuiWindow::actionCollection(); -} - void MainWindow::setupActions() { // File actions KStandardAction::quit(this, SLOT(close()), actionCollection()); - // Edit actions - KAction* undoOperation = actionCollection()->addAction("undoOperation", this, SLOT(onUndoOperation())); - undoOperation->setEnabled(false); - undoOperation->setText(i18nc("@action:inmenu", "Undo")); - undoOperation->setToolTip(i18nc("@info:tooltip", "Undo the last operation")); - undoOperation->setStatusTip(i18nc("@info:status", "Remove the last operation from the list.")); - undoOperation->setShortcut(Qt::CTRL | Qt::Key_Z); - undoOperation->setIcon(BarIcon("edit-undo")); - - KAction* clearAllOperations = actionCollection()->addAction("clearAllOperations", this, SLOT(onClearAllOperations())); - clearAllOperations->setEnabled(false); - clearAllOperations->setText(i18nc("@action:inmenu clear the list of operations", "Clear")); - clearAllOperations->setToolTip(i18nc("@info:tooltip", "Clear all operations")); - clearAllOperations->setStatusTip(i18nc("@info:status", "Empty the list of pending operations.")); - clearAllOperations->setIcon(BarIcon("dialog-cancel")); - - KAction* applyAllOperations = actionCollection()->addAction("applyAllOperations", this, SLOT(onApplyAllOperations())); - applyAllOperations->setEnabled(false); - applyAllOperations->setText(i18nc("@action:inmenu apply all operations", "Apply")); - applyAllOperations->setToolTip(i18nc("@info:tooltip", "Apply all operations")); - applyAllOperations->setStatusTip(i18nc("@info:status", "Apply the pending operations in the list.")); - applyAllOperations->setIcon(BarIcon("dialog-ok-apply")); - // View actions actionCollection()->addAction("toggleDockDevices", dockDevices().toggleViewAction()); actionCollection()->addAction("toggleDockOperations", dockOperations().toggleViewAction()); actionCollection()->addAction("toggleDockInformation", dockInformation().toggleViewAction()); actionCollection()->addAction("toggleDockLog", dockLog().toggleViewAction()); - - KAction* fileSystemSupport = actionCollection()->addAction("fileSystemSupport", this, SLOT(onFileSystemSupport())); - fileSystemSupport->setText(i18nc("@action:inmenu", "File System Support")); - fileSystemSupport->setToolTip(i18nc("@info:tooltip", "View file system support information")); - fileSystemSupport->setStatusTip(i18nc("@info:status", "Show information about supported file systems.")); - - // Device actions - KAction* refreshDevices = actionCollection()->addAction("refreshDevices", this, SLOT(onRefreshDevices())); - refreshDevices->setText(i18nc("@action:inmenu refresh list of devices", "Refresh Devices")); - refreshDevices->setToolTip(i18nc("@info:tooltip", "Refresh all devices")); - refreshDevices->setStatusTip(i18nc("@info:status", "Renew the devices list.")); - refreshDevices->setShortcut(Qt::Key_F5); - refreshDevices->setIcon(BarIcon("view-refresh")); - - KAction* createNewPartitionTable = actionCollection()->addAction("createNewPartitionTable", this, SLOT(onCreateNewPartitionTable())); - createNewPartitionTable->setEnabled(false); - createNewPartitionTable->setText(i18nc("@action:inmenu", "Create New Partition Table")); - createNewPartitionTable->setToolTip(i18nc("@info:tooltip", "Create new partition table")); - createNewPartitionTable->setStatusTip(i18nc("@info:status", "Create a new and empty partition table on a device.")); - createNewPartitionTable->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_N); - createNewPartitionTable->setIcon(BarIcon("edit-clear")); - - // Partition actions - KAction* newPartition = actionCollection()->addAction("newPartition", this, SLOT(onNewPartition())); - newPartition->setEnabled(false); - newPartition->setText(i18nc("@action:inmenu create a new partition", "New")); - newPartition->setToolTip(i18nc("@info:tooltip", "New partition")); - newPartition->setStatusTip(i18nc("@info:status", "Create a new partition.")); - newPartition->setShortcut(Qt::CTRL | Qt::Key_N); - newPartition->setIcon(BarIcon("document-new")); - - KAction* resizePartition = actionCollection()->addAction("resizePartition", this, SLOT(onResizePartition())); - resizePartition->setEnabled(false); - resizePartition->setText(i18nc("@action:inmenu", "Resize/Move")); - resizePartition->setToolTip(i18nc("@info:tooltip", "Resize or move partition")); - resizePartition->setStatusTip(i18nc("@info:status", "Shrink, grow or move an existing partition.")); - resizePartition->setShortcut(Qt::CTRL | Qt::Key_R); - resizePartition->setIcon(BarIcon("arrow-right-double")); - - KAction* deletePartition = actionCollection()->addAction("deletePartition", this, SLOT(onDeletePartition())); - deletePartition->setEnabled(false); - deletePartition->setText(i18nc("@action:inmenu", "Delete")); - deletePartition->setToolTip(i18nc("@info:tooltip", "Delete partition")); - deletePartition->setStatusTip(i18nc("@info:status", "Delete a partition.")); - deletePartition->setShortcut(Qt::Key_Delete); - deletePartition->setIcon(BarIcon("edit-delete-shred")); - - KAction* copyPartition = actionCollection()->addAction("copyPartition", this, SLOT(onCopyPartition())); - copyPartition->setEnabled(false); - copyPartition->setText(i18nc("@action:inmenu", "Copy")); - copyPartition->setToolTip(i18nc("@info:tooltip", "Copy partition")); - copyPartition->setStatusTip(i18nc("@info:status", "Copy an existing partition.")); - copyPartition->setShortcut(Qt::CTRL | Qt::Key_C); - copyPartition->setIcon(BarIcon("edit-copy")); - - KAction* pastePartition = actionCollection()->addAction("pastePartition", this, SLOT(onPastePartition())); - pastePartition->setEnabled(false); - pastePartition->setText(i18nc("@action:inmenu", "Paste")); - pastePartition->setToolTip(i18nc("@info:tooltip", "Paste partition")); - pastePartition->setStatusTip(i18nc("@info:status", "Paste a copied partition.")); - pastePartition->setShortcut(Qt::CTRL | Qt::Key_V); - pastePartition->setIcon(BarIcon("edit-paste")); - - KAction* mountPartition = actionCollection()->addAction("mountPartition", this, SLOT(onMountPartition())); - mountPartition->setEnabled(false); - mountPartition->setText(i18nc("@action:inmenu", "Mount")); - mountPartition->setToolTip(i18nc("@info:tooltip", "Mount or unmount partition")); - mountPartition->setStatusTip(i18nc("@info:status", "Mount or unmount a partition.")); - - KAction* checkPartition = actionCollection()->addAction("checkPartition", this, SLOT(onCheckPartition())); - checkPartition->setEnabled(false); - checkPartition->setText(i18nc("@action:inmenu", "Check")); - checkPartition->setToolTip(i18nc("@info:tooltip", "Check partition")); - checkPartition->setStatusTip(i18nc("@info:status", "Check a filesystem on a partition for errors.")); - checkPartition->setIcon(BarIcon("flag")); - - KAction* propertiesPartition = actionCollection()->addAction("propertiesPartition", this, SLOT(onPropertiesPartition())); - propertiesPartition->setEnabled(false); - propertiesPartition->setText(i18nc("@action:inmenu", "Properties")); - propertiesPartition->setToolTip(i18nc("@info:tooltip", "Show properties dialog")); - propertiesPartition->setStatusTip(i18nc("@info:status", "View and modify partition properties (label, partition flags, etc.)")); - propertiesPartition->setIcon(BarIcon("document-properties")); - - KAction* backup = actionCollection()->addAction("backupPartition", this, SLOT(onBackupPartition())); - backup->setEnabled(false); - backup->setText(i18nc("@action:inmenu", "Backup")); - backup->setToolTip(i18nc("@info:tooltip", "Backup partition")); - backup->setStatusTip(i18nc("@info:status", "Backup a partition to an image file.")); - backup->setIcon(BarIcon("document-export")); - - KAction* restore = actionCollection()->addAction("restorePartition", this, SLOT(onRestorePartition())); - restore->setEnabled(false); - restore->setText(i18nc("@action:inmenu", "Restore")); - restore->setToolTip(i18nc("@info:tooltip", "Restore partition")); - restore->setStatusTip(i18nc("@info:status", "Restore a partition from an image file.")); - restore->setIcon(BarIcon("document-import")); } void MainWindow::setupConnections() { - connect(&partTableWidget(), SIGNAL(itemActivated(const PartWidget*)), actionCollection()->action("propertiesPartition"), SLOT(trigger())); - connect(&progressDialog(), SIGNAL(finished(int)), SLOT(onFinished())); - connect(this, SIGNAL(devicesChanged()), SLOT(scanDevices())); + connect(&pmWidget(), SIGNAL(devicesChanged()), SLOT(updateDevices())); + connect(&pmWidget(), SIGNAL(operationsChanged()), &listOperations(), SLOT(updateOperations())); + connect(&pmWidget(), SIGNAL(statusChanged()), SLOT(updateStatusBar())); + connect(&pmWidget(), SIGNAL(selectionChanged(const Partition*)), SLOT(updateSelection(const Partition*))); } void MainWindow::setupStatusBar() @@ -321,12 +182,6 @@ void MainWindow::setupStatusBar() void MainWindow::loadConfig() { - QList colWidths = Config::treePartitionColumnWidths(); - - if (!colWidths.isEmpty() && colWidths[0] != -1) - for (int i = 0; i < colWidths.size(); i++) - treePartitions().setColumnWidth(i, colWidths[i]); - bool firstRun = Config::firstRun(); if (firstRun) @@ -339,866 +194,71 @@ void MainWindow::loadConfig() void MainWindow::saveConfig() const { - QList colWidths; - for(int i = 0; i < treePartitions().columnCount(); i++) - colWidths.append(treePartitions().columnWidth(i)); - Config::setTreePartitionColumnWidths(colWidths); - Config::setFirstRun(false); - Config::self()->writeConfig(); } void MainWindow::updateStatusBar() { - statusText().setText(i18ncp("@info:status", "One pending operation", "%1 pending operations", operationStack().size())); -} - -void MainWindow::scanDevices() -{ - log() << i18nc("@info/plain", "Rescanning devices..."); - - KApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - - const qint64 selectedDeviceIdx = selectedDevice() ? listDevices().currentRow() : -1; - - listDevices().clearSelection(); - setClipboardPartition(NULL); - partTableWidget().clear(); - - libParted().scanDevices(operationStack()); - - setupDevicesList(); - updatePartitions(); - updateOperations(); - updateStatusBar(); - - if (selectedDeviceIdx > -1 && selectedDeviceIdx < listDevices().count()) - listDevices().setCurrentRow(selectedDeviceIdx); - - log() << i18nc("@info/plain", "Rescan finished."); - KApplication::restoreOverrideCursor(); -} - -void MainWindow::setupDevicesList() -{ - listDevices().clear(); - - foreach(const Device* d, operationStack().previewDevices()) - { - const QString shortText = d->deviceNode() + " (" + Capacity(*d).toString() + ')'; - const QString longText = d->deviceNode() + " (" + Capacity(*d).toString() + ", " + d->name() + ')'; - QListWidgetItem* item = new QListWidgetItem(SmallIcon("drive-harddisk"), shortText); - item->setToolTip(longText); - listDevices().addItem(item); - } - - enableActions(); -} - -void MainWindow::enableActions() -{ - actionCollection()->action("createNewPartitionTable")->setEnabled(CreatePartitionTableOperation::canCreate(selectedDevice())); - - const Partition* part = selectedPartition(); - - const bool readOnly = selectedDevice() == NULL || selectedDevice()->partitionTable() == NULL || selectedDevice()->partitionTable()->isReadOnly(); - - actionCollection()->action("newPartition")->setEnabled(!readOnly && NewOperation::canCreateNew(part)); - const bool canResize = ResizeOperation::canGrow(part) || ResizeOperation::canShrink(part) || ResizeOperation::canMove(part); - actionCollection()->action("resizePartition")->setEnabled(!readOnly && canResize); - actionCollection()->action("copyPartition")->setEnabled(CopyOperation::canCopy(part)); - actionCollection()->action("deletePartition")->setEnabled(!readOnly && DeleteOperation::canDelete(part)); - actionCollection()->action("pastePartition")->setEnabled(!readOnly && CopyOperation::canPaste(part, clipboardPartition())); - actionCollection()->action("propertiesPartition")->setEnabled(part != NULL); - - actionCollection()->action("mountPartition")->setEnabled(part && (part->canMount() || part->canUnmount())); - - if (part != NULL) - actionCollection()->action("mountPartition")->setText(part->isMounted() ? part->fileSystem().unmountTitle() : part->fileSystem().mountTitle() ); - - actionCollection()->action("checkPartition")->setEnabled(!readOnly && CheckOperation::canCheck(part)); - - actionCollection()->action("undoOperation")->setEnabled(operationStack().size() > 0); - actionCollection()->action("clearAllOperations")->setEnabled(operationStack().size() > 0); - actionCollection()->action("applyAllOperations")->setEnabled(operationStack().size() > 0 && geteuid() == 0); - - actionCollection()->action("backupPartition")->setEnabled(BackupOperation::canBackup(part)); - actionCollection()->action("restorePartition")->setEnabled(RestoreOperation::canRestore(part)); -} - -void MainWindow::on_m_ListDevices_itemClicked() -{ - treePartitions().setCurrentItem(NULL); - enableActions(); - updatePartitions(); + statusText().setText(i18ncp("@info:status", "One pending operation", "%1 pending operations", pmWidget().numPendingOperations())); } void MainWindow::updateDevices() { - enableActions(); - updatePartitions(); + listDevices().updateDevices(); - if (selectedDevice()) - infoPane().showDevice(*selectedDevice()); + if (pmWidget().selectedDevice()) + infoPane().showDevice(*pmWidget().selectedDevice()); else infoPane().clear(); + + updateWindowTitle(); } -void MainWindow::on_m_ListDevices_itemSelectionChanged() +void MainWindow::on_m_ListDevices_selectionChanged(Device* d) { - updateDevices(); + pmWidget().setSelectedDevice(d); + updateSelection(NULL); } -void MainWindow::updateOperations() -{ - listOperations().clear(); - - foreach (const Operation* op, operationStack().operations()) - { - QListWidgetItem* item = new QListWidgetItem(SmallIcon(op->iconName()), op->description()); - item->setToolTip(op->description()); - listOperations().addItem(item); - } - - listOperations().scrollToBottom(); -} - -static QTreeWidgetItem* createTreeWidgetItem(const Partition& p) -{ - QTreeWidgetItem* item = new PartitionTreeWidgetItem(&p); - - item->setText(0, p.deviceNode()); - item->setText(1, p.fileSystem().name()); - item->setText(2, p.mountPoints().join(", ")); - if (p.isMounted()) - item->setIcon(2, SmallIcon("object-locked")); - item->setText(3, p.fileSystem().label()); - item->setText(4, Capacity(p).toString()); - item->setText(5, Capacity(p, Capacity::Used).toString()); - item->setText(6, PartitionTable::flagNames(p.activeFlags()).join(", ")); - - return item; -} +// void MainWindow::updateOperations() +// { +// listOperations().clear(); +// +// foreach (const Operation* op, pmWidget().operations()) +// { +// QListWidgetItem* item = new QListWidgetItem(SmallIcon(op->iconName()), op->description()); +// item->setToolTip(op->description()); +// listOperations().addItem(item); +// } +// +// listOperations().scrollToBottom(); +// } void MainWindow::updateWindowTitle() { QString title; - if (selectedDevice()) - title = selectedDevice()->deviceNode() + " - "; + if (pmWidget().selectedDevice()) + title = pmWidget().selectedDevice()->deviceNode() + " - "; title += KGlobal::mainComponent().aboutData()->programName() + ' ' + KGlobal::mainComponent().aboutData()->version(); setWindowTitle(title); } -void MainWindow::updatePartitions() +void MainWindow::updateSelection(const Partition* p) { - treePartitions().clear(); - partTableWidget().clear(); - updateWindowTitle(); - - if (selectedDevice() == NULL) - return; - - partTableWidget().setPartitionTable(selectedDevice()->partitionTable()); - - QTreeWidgetItem* deviceItem = new QTreeWidgetItem(); - deviceItem->setText(0, selectedDevice()->name()); - deviceItem->setIcon(0, SmallIcon("drive-harddisk")); - treePartitions().addTopLevelItem(deviceItem); - - if (selectedDevice()->partitionTable() != NULL) - { - foreach(const Partition* p, selectedDevice()->partitionTable()->children()) - { - QTreeWidgetItem* item = createTreeWidgetItem(*p); - - foreach(const Partition* child, p->children()) - { - QTreeWidgetItem* childItem = createTreeWidgetItem(*child); - item->addChild(childItem); - } - - deviceItem->addChild(item); - item->setExpanded(true); - } - } - - treePartitions().setFirstItemColumnSpanned(deviceItem, true); - deviceItem->setExpanded(true); - deviceItem->setFlags(Qt::ItemIsEnabled); - - partTableWidget().update(); -} - -void MainWindow::on_m_TreePartitions_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem*) -{ - if (current) - { - const PartitionTreeWidgetItem* ptwItem = dynamic_cast(current); - partTableWidget().setActivePartition(ptwItem ? ptwItem->partition() : NULL); - } + if (p) + infoPane().showPartition(*p); + else if (pmWidget().selectedDevice()) + infoPane().showDevice(*pmWidget().selectedDevice()); else - partTableWidget().setActiveWidget(NULL); + infoPane().clear(); updateWindowTitle(); } -void MainWindow::on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int) -{ - // if the activated item is the device item, don't do anything - if (item == treePartitions().topLevelItem(0)) - return; - - actionCollection()->action("propertiesPartition")->trigger(); -} - -Partition* MainWindow::selectedPartition() -{ - if (selectedDevice() == NULL || selectedDevice()->partitionTable() == NULL || partTableWidget().activeWidget() == NULL || partTableWidget().activeWidget()->partition() == NULL) - return NULL; - - // The active partition we get from PartTableWidget is const; we need non-const. - // So take the first sector and find the partition in the selected device's - // partition table. - const Partition* activePartition = partTableWidget().activeWidget()->partition(); - return selectedDevice()->partitionTable()->findPartitionBySector(activePartition->firstSector(), PartitionRole(PartitionRole::Any)); -} - -Device* MainWindow::selectedDevice() -{ - if (listDevices().selectedItems().size() != 1) - return NULL; - - int idx = listDevices().row(listDevices().selectedItems()[0]); - - if (idx < 0 || idx >= operationStack().previewDevices().size()) - return NULL; - - return operationStack().previewDevices()[idx]; -} - -void MainWindow::on_m_PartTableWidget_itemSelectionChanged(PartWidget* item) -{ - enableActions(); - - if (item == NULL) - { - if (selectedDevice()) - infoPane().showDevice(*selectedDevice()); - else - infoPane().clear(); - - treePartitions().setCurrentItem(NULL); - return; - } - - const Partition* p = item->partition(); - - Q_ASSERT(p); - - QList findResult = treePartitions().findItems(p->deviceNode(), Qt::MatchFixedString | Qt::MatchRecursive, 0); - - for (int idx = 0; idx < findResult.size(); idx++) - { - const PartitionTreeWidgetItem* ptwItem = dynamic_cast(findResult[idx]); - - if (ptwItem && ptwItem->partition() == p) - { - treePartitions().setCurrentItem(findResult[idx]); - break; - } - } - - infoPane().showPartition(*p); -} - -void MainWindow::on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos) -{ - showPartitionContextMenu(partTableWidget().mapToGlobal(pos)); -} - -void MainWindow::on_m_TreePartitions_customContextMenuRequested(const QPoint& pos) -{ - showPartitionContextMenu(treePartitions().viewport()->mapToGlobal(pos)); -} - -void MainWindow::showPartitionContextMenu(const QPoint& pos) -{ - if (selectedPartition() == NULL) - return; - - KMenu partitionMenu; - - partitionMenu.addAction(actionCollection()->action("newPartition")); - partitionMenu.addAction(actionCollection()->action("resizePartition")); - partitionMenu.addAction(actionCollection()->action("deletePartition")); - partitionMenu.addSeparator(); - partitionMenu.addAction(actionCollection()->action("copyPartition")); - partitionMenu.addAction(actionCollection()->action("pastePartition")); - partitionMenu.addSeparator(); - partitionMenu.addAction(actionCollection()->action("mountPartition")); - partitionMenu.addSeparator(); - partitionMenu.addAction(actionCollection()->action("checkPartition")); - partitionMenu.addSeparator(); - partitionMenu.addAction(actionCollection()->action("propertiesPartition")); - - partitionMenu.exec(pos); - statusBar()->clearMessage(); -} - -void MainWindow::on_m_ListDevices_customContextMenuRequested(const QPoint& pos) -{ - on_m_ListDevices_itemClicked(); - - KMenu deviceMenu; - - deviceMenu.addAction(actionCollection()->action("createNewPartitionTable")); - - deviceMenu.exec(listDevices().viewport()->mapToGlobal(pos)); - statusBar()->clearMessage(); -} - -void MainWindow::on_m_ListOperations_customContextMenuRequested(const QPoint& pos) -{ - KMenu opsMenu; - - opsMenu.addAction(actionCollection()->action("undoOperation")); - opsMenu.addAction(actionCollection()->action("clearAllOperations")); - opsMenu.addAction(actionCollection()->action("applyAllOperations")); - - opsMenu.exec(listOperations().viewport()->mapToGlobal(pos)); - statusBar()->clearMessage(); -} - -void MainWindow::onPropertiesPartition() -{ - if (selectedPartition()) - { - Q_ASSERT(selectedDevice()); - - PartPropsDialog dlg(this, *selectedDevice(), *selectedPartition()); - - if (dlg.exec() == KDialog::Accepted) - { - if (dlg.newFileSystemType() != selectedPartition()->fileSystem().type() || dlg.forceRecreate()) - operationStack().push(new CreateFileSystemOperation(*selectedDevice(), *selectedPartition(), dlg.newFileSystemType())); - - if (dlg.newLabel() != selectedPartition()->fileSystem().label()) - operationStack().push(new SetFileSystemLabelOperation(*selectedPartition(), dlg.newLabel())); - - if (dlg.newFlags() != selectedPartition()->activeFlags()) - operationStack().push(new SetPartFlagsOperation(*selectedDevice(), *selectedPartition(), dlg.newFlags())); - - updatePartitions(); - updateOperations(); - updateStatusBar(); - } - } -} - -void MainWindow::onMountPartition() -{ - Partition* p = selectedPartition(); - Report report(NULL); - - if (p && p->canMount()) - { - if (!p->mount(report)) - KMessageBox::detailedSorry(this, i18nc("@info", "The file system on partition %1 could not be mounted.", p->deviceNode()), QString("
%1
").arg(report.toText()), i18nc("@title:window", "Could Not Mount File System.")); - } - else if (p && p->canUnmount()) - { - if (!p->unmount(report)) - KMessageBox::detailedSorry(this, i18nc("@info", "The file system on partition %1 could not be unmounted.", p->deviceNode()), QString("
%1
").arg(report.toText()), i18nc("@title:window", "Could Not Unmount File System.")); - } - - if (p->roles().has(PartitionRole::Logical)) - { - Partition* parent = dynamic_cast(p->parent()); - - Q_ASSERT(parent); - - if (parent != NULL) - parent->checkChildrenMounted(); - else - kWarning() << "parent is null"; - } - - enableActions(); - updatePartitions(); -} - -void MainWindow::onFinished() -{ - partTableWidget().setUpdatesEnabled(true); - -#if !defined(NDEBUG) && 0 - operationStack().clearOperations(); - updatePartitions(); - updateOperations(); - enableActions(); -#else - devicesChanged(); -#endif -} - -static bool checkTooManyPartitions(QWidget* parent, const Device& d, const Partition& p) -{ - Q_ASSERT(d.partitionTable()); - - if (p.roles().has(PartitionRole::Unallocated) && d.partitionTable()->numPrimaries() >= d.partitionTable()->maxPrimaries() && !p.roles().has(PartitionRole::Logical)) - { - KMessageBox::sorry(parent, i18ncp("@info", - "There is already 1 primary partition on this device. This is the maximum number its partition table can handle." - "You cannot create, paste or restore a primary partition on it before you delete an existing one.", - "There are already %1 primary partitions on this device. This is the maximum number its partition table can handle." - "You cannot create, paste or restore a primary partition on it before you delete an existing one.", - d.partitionTable()->numPrimaries()), i18nc("@title:window", "Too Many Primary Partitions.")); - return true; - } - - return false; -} - -void MainWindow::onNewPartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - Q_ASSERT(selectedDevice()->partitionTable()); - - if (selectedDevice()->partitionTable() == NULL) - { - kWarning() << "partition table on selected device is null"; - return; - } - - if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) - return; - - Partition* newPartition = NewOperation::createNew(*selectedPartition()); - - NewDialog dlg(this, *selectedDevice(), *newPartition, selectedDevice()->partitionTable()->childRoles(*selectedPartition())); - if (dlg.exec() == KDialog::Accepted) - { - PartitionTable::snap(*selectedDevice(), *newPartition); - operationStack().push(new NewOperation(*selectedDevice(), newPartition)); - updatePartitions(); - updateStatusBar(); - updateOperations(); - } - else - delete newPartition; -} - -void MainWindow::onDeletePartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - if (selectedPartition()->roles().has(PartitionRole::Logical)) - { - Q_ASSERT(selectedPartition()->parent()); - - if (selectedPartition()->parent() == NULL) - { - kWarning() << "parent of selected partition is null."; - return; - } - - if (selectedPartition()->parent()->highestMountedChild() > selectedPartition()->number()) - { - KMessageBox::sorry(this, - i18nc("@info", - "The partition %1 cannot currently be deleted because one or more partitions with higher logical numbers are still mounted." - "Please unmount all partitions with higher logical numbers than %2 first.", - selectedPartition()->deviceNode(), selectedPartition()->number()), - i18nc("@title:window", "Cannot Delete Partition.")); - - return; - } - } - - if (clipboardPartition() == selectedPartition()) - { - if (KMessageBox::warningContinueCancel(this, - i18nc("@info", - "Do you really want to delete the partition that is currently in the clipboard? " - "It will no longer be available for pasting after it has been deleted."), - i18nc("@title:window", "Really Delete Partition in the Clipboard?"), - KGuiItem(i18nc("@action:button", "&Delete It")), - KStandardGuiItem::cancel(), "reallyDeleteClipboardPartition") == KMessageBox::Cancel) - return; - - setClipboardPartition(NULL); - } - - operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition())); - updatePartitions(); - updateStatusBar(); - updateOperations(); -} - -void MainWindow::onResizePartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - Q_ASSERT(selectedDevice()->partitionTable()); - - if (selectedDevice()->partitionTable() == NULL) - { - kWarning() << "partition table on selected device is null"; - return; - } - - const qint64 freeBefore = selectedDevice()->partitionTable()->freeSectorsBefore(*selectedPartition()); - const qint64 freeAfter = selectedDevice()->partitionTable()->freeSectorsAfter(*selectedPartition()); - - Partition resizedPartition(*selectedPartition()); - ResizeDialog dlg(this, *selectedDevice(), resizedPartition, freeBefore, freeAfter); - - if (dlg.exec() == KDialog::Accepted && dlg.isModified()) - { - PartitionTable::snap(*selectedDevice(), resizedPartition, selectedPartition()); - - if (resizedPartition.firstSector() == selectedPartition()->firstSector() && resizedPartition.lastSector() == selectedPartition()->lastSector()) - log(log::information) << i18nc("@info/plain", "Partition %1 has the same position and size after resize/move. Ignoring operation.", selectedPartition()->deviceNode()); - else - { - operationStack().push(new ResizeOperation(*selectedDevice(), *selectedPartition(), resizedPartition.firstSector(), resizedPartition.lastSector())); - - updatePartitions(); - updateStatusBar(); - updateOperations(); - } - } -} - -void MainWindow::onCopyPartition() -{ - Q_ASSERT(selectedPartition()); - - if (selectedPartition() == NULL) - { - kWarning() << "selected partition: " << selectedPartition(); - return; - } - - setClipboardPartition(selectedPartition()); - log() << i18nc("@info/plain", "Partition %1 has been copied to the clipboard.", selectedPartition()->deviceNode()); - - enableActions(); -} - -void MainWindow::onPastePartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - if (clipboardPartition() == NULL) - { - kWarning() << "no partition in the clipboard."; - return; - } - - if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) - return; - - Device* dSource = operationStack().findDeviceForPartition(clipboardPartition()); - - Q_ASSERT(dSource); - - if (dSource == NULL) - { - kWarning() << "source partition is null."; - return; - } - - Partition* copiedPartition = CopyOperation::createCopy(*selectedPartition(), *clipboardPartition()); - - if (showInsertDialog(*copiedPartition, clipboardPartition()->length())) - { - operationStack().push(new CopyOperation(*selectedDevice(), copiedPartition, *dSource, clipboardPartition())); - updatePartitions(); - updateStatusBar(); - updateOperations(); - } - else - delete copiedPartition; -} - -bool MainWindow::showInsertDialog(Partition& insertPartition, qint64 sourceLength) -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return false; - } - - const bool overwrite = !selectedPartition()->roles().has(PartitionRole::Unallocated); - - // Make sure the inserted partition has the right parent and logical or primary set. Only then - // can Device::snap() work correctly. - selectedPartition()->parent()->reparent(insertPartition); - - if (!overwrite) - { - InsertDialog dlg(this, *selectedDevice(), insertPartition, *selectedPartition()); - if (dlg.exec() != KDialog::Accepted) - return false; - PartitionTable::snap(*selectedDevice(), insertPartition, selectedPartition()); - } - - if (insertPartition.length() < sourceLength) - { - if (overwrite) - KMessageBox::error(this, i18nc("@info", - "The selected partition is not large enough to hold the source partition or the backup file." - "Pick another target or resize this partition so it is as large as the source."), i18nc("@title:window", "Target Not Large Enough")); - else - KMessageBox::sorry(this, i18nc("@info", - "It is not possible to create the target partition large enough to hold the source." - "This may happen if not all partitions on a device start and end on cylinder boundaries " - "or when copying a primary partition into an extended partition."), - i18nc("@title:window", "Cannot Create Target Partition.")); - return false; - } - - return true; -} - -void MainWindow::onCreateNewPartitionTable() -{ - Q_ASSERT(selectedDevice()); - - if (selectedDevice() == NULL) - { - kWarning() << "selected device is null."; - return; - } - - if (KMessageBox::warningContinueCancel(this, - i18nc("@info", - "Do you really want to create a new partition table on the following device?" - "%1 (%2)" - "This will destroy all data on the device.", selectedDevice()->deviceNode(), selectedDevice()->name()), - i18nc("@title:window", "Destroy All Data on Device?"), - KGuiItem(i18nc("@action:button", "&Create New Partition Table")), - KStandardGuiItem::cancel()) == KMessageBox::Continue) - { - operationStack().push(new CreatePartitionTableOperation(*selectedDevice())); - - updateDevices(); - updatePartitions(); - updateStatusBar(); - updateOperations(); - enableActions(); - } -} - -void MainWindow::onRefreshDevices() -{ - if (operationStack().size() == 0 || KMessageBox::warningContinueCancel(this, - i18nc("@info", - "Do you really want to rescan the devices?" - "This will also clear the list of pending operations."), - i18nc("@title:window", "Really Rescan the Devices?"), - KGuiItem(i18nc("@action:button", "&Rescan Devices")), - KStandardGuiItem::cancel(), "reallyRescanDevices") == KMessageBox::Continue) - { - scanDevices(); - } -} - -void MainWindow::onUndoOperation() -{ - log() << i18nc("@info/plain", "Undoing operation: %1", operationStack().operations().last()->description()); - operationStack().pop(); - - updateDevices(); - updatePartitions(); - updateOperations(); - updateStatusBar(); - enableActions(); -} - -void MainWindow::onClearAllOperations() -{ - if (KMessageBox::warningContinueCancel(this, - i18nc("@info", "Do you really want to clear the list of pending operations?"), - i18nc("@title:window", "Clear Pending Operations?"), - KGuiItem(i18nc("@action:button", "&Clear Pending Operations")), - KStandardGuiItem::cancel(), "reallyClearPendingOperations") == KMessageBox::Continue) - { - log() << i18nc("@info/plain", "Clearing the list of pending operations."); - operationStack().clearOperations(); - - updateDevices(); - updatePartitions(); - updateOperations(); - updateStatusBar(); - enableActions(); - } -} - -void MainWindow::onApplyAllOperations() -{ - QStringList opList; - - foreach (const Operation* op, operationStack().operations()) - opList.append(op->description()); - - if (KMessageBox::warningContinueCancelList(this, - i18nc("@info", - "Do you really want to apply the pending operations listed below?" - "This will permanently modify your disks."), - opList, i18nc("@title:window", "Apply Pending Operations?"), - KGuiItem(i18nc("@action:button", "&Apply Pending Operations")), - KStandardGuiItem::cancel()) == KMessageBox::Continue) - { - log() << i18nc("@info/plain", "Applying operations..."); - - progressDialog().show(); - - operationRunner().setReport(&progressDialog().report()); - - partTableWidget().setUpdatesEnabled(false); - - // 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); - } - - updatePartitions(); - - operationRunner().start(); - } -} - -void MainWindow::onCheckPartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - operationStack().push(new CheckOperation(*selectedDevice(), *selectedPartition())); - - updatePartitions(); - updateStatusBar(); - updateOperations(); -} - -void MainWindow::onBackupPartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - QString fileName = KFileDialog::getSaveFileName(KUrl("kfiledialog://backupPartition")); -// QString fileName = "/tmp/backuptest.img"; - - if (fileName.isEmpty()) - return; - - if (!QFile::exists(fileName) || KMessageBox::warningContinueCancel(this, i18nc("@info", "Do you want to overwrite the existing file %1?", fileName), i18nc("@title:window", "Overwrite Existing File?"), KGuiItem(i18nc("@action:button", "&Overwrite File")), KStandardGuiItem::cancel()) == KMessageBox::Continue) - { - operationStack().push(new BackupOperation(*selectedDevice(), *selectedPartition(), fileName)); - updatePartitions(); - updateStatusBar(); - updateOperations(); - } -} - -void MainWindow::onRestorePartition() -{ - Q_ASSERT(selectedDevice()); - Q_ASSERT(selectedPartition()); - - if (selectedDevice() == NULL || selectedPartition() == NULL) - { - kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); - return; - } - - if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) - return; - - QString fileName = KFileDialog::getOpenFileName(KUrl("kfiledialog://backupPartition")); -// QString fileName = "/tmp/backuptest.img"; - - if (!fileName.isEmpty() && QFile::exists(fileName)) - { - Partition* restorePartition = RestoreOperation::createRestorePartition(*selectedDevice(), *selectedPartition()->parent(), selectedPartition()->firstSector(), fileName); - - if (restorePartition->length() > selectedPartition()->length()) - { - KMessageBox::error(this, i18nc("@info", "The file system in the image file %1 is too large to be restored to the selected partition.", fileName), i18nc("@title:window", "Not Enough Space to Restore File System.")); - delete restorePartition; - return; - } - - if (showInsertDialog(*restorePartition, restorePartition->length())) - { - operationStack().push(new RestoreOperation(*selectedDevice(), restorePartition, fileName)); - - updatePartitions(); - updateStatusBar(); - updateOperations(); - } - else - delete restorePartition; - } -} - -void MainWindow::onFileSystemSupport() -{ - FileSystemSupportDialog dlg(this); - dlg.exec(); -} - void MainWindow::onNewLogMessage(log::Level logLevel, const QString& s) { static const char* icons[] = diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index 49ea890..bfea282 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2008 by Volker Lanz * + * Copyright (C) 2008,2009 by Volker Lanz * * * * 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 * @@ -25,10 +25,6 @@ #include "ui_mainwindowbase.h" -#include "core/libparted.h" -#include "core/operationrunner.h" -#include "core/operationstack.h" - #include "util/globallog.h" #include @@ -40,7 +36,6 @@ class InfoPane; class QCloseEvent; class QEvent; class Device; -class ProgressDialog; class KActionCollection; /** @brief The application's main window. @@ -54,44 +49,26 @@ class LIBPARTITIONMANAGERPRIVATE_EXPORT MainWindow : public KXmlGuiWindow, publi public: MainWindow(QWidget* parent = NULL, KActionCollection* coll = NULL); - signals: - void devicesChanged(); - protected: void setupActions(); void setupConnections(); void setupStatusBar(); - void setupDevicesList(); void loadConfig(); void saveConfig() const; void updateWindowTitle(); - void updateStatusBar(); - void updateOperations(); - void enableActions(); - void showPartitionContextMenu(const QPoint& pos); - void updateDevices(); - void updatePartitions(); - bool showInsertDialog(Partition& insertPartition, qint64 sourceLength); - - Device* selectedDevice(); - Partition* selectedPartition(); - - KActionCollection* actionCollection() const; + KActionCollection* actionCollection() const { return m_ActionCollection != NULL ? m_ActionCollection : KXmlGuiWindow::actionCollection(); } InfoPane& infoPane() { Q_ASSERT(m_InfoPane); return *m_InfoPane; } - PartTableWidget& partTableWidget() { Q_ASSERT(m_PartTableWidget); return *m_PartTableWidget; } - const PartTableWidget& partTableWidget() const { Q_ASSERT(m_PartTableWidget); return *m_PartTableWidget; } + PartitionManagerWidget& pmWidget() { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + const PartitionManagerWidget& pmWidget() const { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } - QListWidget& listDevices() { Q_ASSERT(m_ListDevices); return *m_ListDevices; } - const QListWidget& listDevices() const { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + ListDevices& listDevices() { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + const ListDevices& listDevices() const { Q_ASSERT(m_ListDevices); return *m_ListDevices; } - QListWidget& listOperations() { Q_ASSERT(m_ListOperations); return *m_ListOperations; } - const QListWidget& listOperations() const { Q_ASSERT(m_ListOperations); return *m_ListOperations; } - - QTreeWidget& treePartitions() { Q_ASSERT(m_TreePartitions); return *m_TreePartitions; } - const QTreeWidget& treePartitions() const { Q_ASSERT(m_TreePartitions); return *m_TreePartitions; } + ListOperations& listOperations() { Q_ASSERT(m_ListOperations); return *m_ListOperations; } + const ListOperations& listOperations() const { Q_ASSERT(m_ListOperations); return *m_ListOperations; } QDockWidget& dockInformation() { Q_ASSERT(m_DockInformation); return *m_DockInformation; } const QDockWidget& dockInformation() const { Q_ASSERT(m_DockInformation); return *m_DockInformation; } @@ -108,68 +85,25 @@ class LIBPARTITIONMANAGERPRIVATE_EXPORT MainWindow : public KXmlGuiWindow, publi QTreeWidget& treeLog() { Q_ASSERT(m_TreeLog); return *m_TreeLog; } const QTreeWidget& treeLog() const { Q_ASSERT(m_TreeLog); return *m_TreeLog; } - Partition* clipboardPartition() { return m_ClipboardPartition; } - const Partition* clipboardPartition() const { return m_ClipboardPartition; } - void setClipboardPartition(Partition* p) { m_ClipboardPartition = p; } - - LibParted& libParted() { return m_LibParted; } - const LibParted& libParted() const { return m_LibParted; } - - ProgressDialog& progressDialog() { Q_ASSERT(m_ProgressDialog); return *m_ProgressDialog; } - - OperationRunner& operationRunner() { return m_OperationRunner; } - const OperationRunner& operationRunner() const { return m_OperationRunner; } - - OperationStack& operationStack() { return m_OperationStack; } - const OperationStack& operationStack() const { return m_OperationStack; } - QLabel& statusText() { Q_ASSERT(m_StatusText); return *m_StatusText; } const QLabel& statusText() const { Q_ASSERT(m_StatusText); return *m_StatusText; } protected slots: - void on_m_ListDevices_itemSelectionChanged(); - void on_m_ListDevices_customContextMenuRequested(const QPoint& pos); - void on_m_ListOperations_customContextMenuRequested(const QPoint& pos); - void on_m_TreePartitions_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); - void on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos); - void on_m_TreePartitions_customContextMenuRequested(const QPoint& pos); - void on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int); - void on_m_PartTableWidget_itemSelectionChanged(PartWidget* item); - void on_m_ListDevices_itemClicked(); - - void onPropertiesPartition(); - void onMountPartition(); - void onNewPartition(); - void onDeletePartition(); - void onResizePartition(); - void onCopyPartition(); - void onPastePartition(); - void onCheckPartition(); - void onCreateNewPartitionTable(); - void onRefreshDevices(); - void onUndoOperation(); - void onClearAllOperations(); - void onApplyAllOperations(); - void onFileSystemSupport(); - void onBackupPartition(); - void onRestorePartition(); + void on_m_ListDevices_selectionChanged(Device* d); void closeEvent(QCloseEvent*); void changeEvent(QEvent* event); void onNewLogMessage(log::Level logLevel, const QString& s); - void onFinished(); - void scanDevices(); void init(); + void updateDevices(); + void updateStatusBar(); +// void updateOperations(); + void updateSelection(const Partition* p); private: - LibParted m_LibParted; - OperationStack m_OperationStack; - OperationRunner m_OperationRunner; QLabel* m_StatusText; InfoPane* m_InfoPane; - Partition* m_ClipboardPartition; - ProgressDialog* m_ProgressDialog; KActionCollection* m_ActionCollection; }; diff --git a/src/gui/mainwindowbase.ui b/src/gui/mainwindowbase.ui index 10568de..a578d1a 100644 --- a/src/gui/mainwindowbase.ui +++ b/src/gui/mainwindowbase.ui @@ -6,7 +6,7 @@ 0 0 1007 - 696 + 684 @@ -15,84 +15,7 @@ - - - - - - 0 - 0 - - - - - 0 - 80 - - - - - 16777215 - 80 - - - - Qt::CustomContextMenu - - - - - - - Qt::CustomContextMenu - - - true - - - false - - - false - - - - Partition - - - - - Type - - - - - Mount Point - - - - - Label - - - - - Size - - - - - Used - - - - - Flags - - - - - + @@ -109,17 +32,7 @@ - - - - 0 - 0 - - - - Qt::CustomContextMenu - - + @@ -137,32 +50,9 @@ - - - Qt::CustomContextMenu - - - Qt::ScrollBarAlwaysOff - - - false - - - true - - - QAbstractItemView::NoSelection - - - QListView::Adjust - - - true - - + - m_ListOperations @@ -222,12 +112,12 @@ - Time + Time - Message + Message @@ -238,9 +128,21 @@ - PartTableWidget + PartitionManagerWidget QWidget -
gui/parttablewidget.h
+
gui/partitionmanagerwidget.h
+ 1 +
+ + ListDevices + QWidget +
gui/listdevices.h
+ 1 +
+ + ListOperations + QWidget +
gui/listoperations.h
1
diff --git a/src/gui/partitionmanagerwidget.cpp b/src/gui/partitionmanagerwidget.cpp new file mode 100644 index 0000000..3ca3ca2 --- /dev/null +++ b/src/gui/partitionmanagerwidget.cpp @@ -0,0 +1,1032 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "gui/partitionmanagerwidget.h" +#include "gui/partwidget.h" +#include "gui/partpropsdialog.h" +#include "gui/resizedialog.h" +#include "gui/infopane.h" +#include "gui/newdialog.h" +#include "gui/filesystemsupportdialog.h" +#include "gui/progressdialog.h" +#include "gui/insertdialog.h" + +#include "core/partition.h" +#include "core/device.h" +#include "core/operationstack.h" +#include "core/partitiontable.h" +#include "core/operationrunner.h" + +#include "fs/filesystemfactory.h" + +#include "ops/deleteoperation.h" +#include "ops/resizeoperation.h" +#include "ops/newoperation.h" +#include "ops/copyoperation.h" +#include "ops/createpartitiontableoperation.h" +#include "ops/checkoperation.h" +#include "ops/backupoperation.h" +#include "ops/restoreoperation.h" +#include "ops/setfilesystemlabeloperation.h" +#include "ops/setpartflagsoperation.h" +#include "ops/createfilesystemoperation.h" + +#include "util/globallog.h" +#include "util/capacity.h" +#include "util/report.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +class PartitionTreeWidgetItem : public QTreeWidgetItem +{ + public: + PartitionTreeWidgetItem(const Partition* p) : QTreeWidgetItem(), m_Partition(p) {} + const Partition* partition() const { return m_Partition; } + + private: + const Partition* m_Partition; +}; + +/** Creates a new PartitionManagerWidget instance. + @param parent the parent widget + @param coll an action collection (may be NULL and set later) +*/ +PartitionManagerWidget::PartitionManagerWidget(QWidget* parent, KActionCollection* coll) : + QWidget(parent), + Ui::PartitionManagerWidgetBase(), + m_LibParted(), + m_OperationStack(), + m_OperationRunner(operationStack()), + m_ProgressDialog(new ProgressDialog(this, operationRunner())), + m_ActionCollection(coll), + m_SelectedDevice(NULL), + m_ClipboardPartition(NULL) +{ + setupUi(this); + + treePartitions().header()->setStretchLastSection(false); +} + +PartitionManagerWidget::~PartitionManagerWidget() +{ + saveConfig(); +} + +void PartitionManagerWidget::init(KActionCollection* coll) +{ + Q_ASSERT(coll); + m_ActionCollection = coll; + + FileSystemFactory::init(); + + loadConfig(); + setupActions(); + setupConnections(); + + scanDevices(); +} + +void PartitionManagerWidget::loadConfig() +{ + QList colWidths = Config::treePartitionColumnWidths(); + + if (!colWidths.isEmpty() && colWidths[0] != -1) + for (int i = 0; i < colWidths.size(); i++) + treePartitions().setColumnWidth(i, colWidths[i]); +} + +void PartitionManagerWidget::saveConfig() const +{ + QList colWidths; + for(int i = 0; i < treePartitions().columnCount(); i++) + colWidths.append(treePartitions().columnWidth(i)); + Config::setTreePartitionColumnWidths(colWidths); +} + +void PartitionManagerWidget::setupActions() +{ + // Edit actions + KAction* undoOperation = actionCollection()->addAction("undoOperation", this, SLOT(onUndoOperation())); + undoOperation->setEnabled(false); + undoOperation->setText(i18nc("@action:inmenu", "Undo")); + undoOperation->setToolTip(i18nc("@info:tooltip", "Undo the last operation")); + undoOperation->setStatusTip(i18nc("@info:status", "Remove the last operation from the list.")); + undoOperation->setShortcut(Qt::CTRL | Qt::Key_Z); + undoOperation->setIcon(BarIcon("edit-undo")); + + KAction* clearAllOperations = actionCollection()->addAction("clearAllOperations", this, SLOT(onClearAllOperations())); + clearAllOperations->setEnabled(false); + clearAllOperations->setText(i18nc("@action:inmenu clear the list of operations", "Clear")); + clearAllOperations->setToolTip(i18nc("@info:tooltip", "Clear all operations")); + clearAllOperations->setStatusTip(i18nc("@info:status", "Empty the list of pending operations.")); + clearAllOperations->setIcon(BarIcon("dialog-cancel")); + + KAction* applyAllOperations = actionCollection()->addAction("applyAllOperations", this, SLOT(onApplyAllOperations())); + applyAllOperations->setEnabled(false); + applyAllOperations->setText(i18nc("@action:inmenu apply all operations", "Apply")); + applyAllOperations->setToolTip(i18nc("@info:tooltip", "Apply all operations")); + applyAllOperations->setStatusTip(i18nc("@info:status", "Apply the pending operations in the list.")); + applyAllOperations->setIcon(BarIcon("dialog-ok-apply")); + + // Device actions + KAction* refreshDevices = actionCollection()->addAction("refreshDevices", this, SLOT(onRefreshDevices())); + refreshDevices->setText(i18nc("@action:inmenu refresh list of devices", "Refresh Devices")); + refreshDevices->setToolTip(i18nc("@info:tooltip", "Refresh all devices")); + refreshDevices->setStatusTip(i18nc("@info:status", "Renew the devices list.")); + refreshDevices->setShortcut(Qt::Key_F5); + refreshDevices->setIcon(BarIcon("view-refresh")); + + KAction* createNewPartitionTable = actionCollection()->addAction("createNewPartitionTable", this, SLOT(onCreateNewPartitionTable())); + createNewPartitionTable->setEnabled(false); + createNewPartitionTable->setText(i18nc("@action:inmenu", "Create New Partition Table")); + createNewPartitionTable->setToolTip(i18nc("@info:tooltip", "Create new partition table")); + createNewPartitionTable->setStatusTip(i18nc("@info:status", "Create a new and empty partition table on a device.")); + createNewPartitionTable->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_N); + createNewPartitionTable->setIcon(BarIcon("edit-clear")); + + // Partition actions + KAction* newPartition = actionCollection()->addAction("newPartition", this, SLOT(onNewPartition())); + newPartition->setEnabled(false); + newPartition->setText(i18nc("@action:inmenu create a new partition", "New")); + newPartition->setToolTip(i18nc("@info:tooltip", "New partition")); + newPartition->setStatusTip(i18nc("@info:status", "Create a new partition.")); + newPartition->setShortcut(Qt::CTRL | Qt::Key_N); + newPartition->setIcon(BarIcon("document-new")); + + KAction* resizePartition = actionCollection()->addAction("resizePartition", this, SLOT(onResizePartition())); + resizePartition->setEnabled(false); + resizePartition->setText(i18nc("@action:inmenu", "Resize/Move")); + resizePartition->setToolTip(i18nc("@info:tooltip", "Resize or move partition")); + resizePartition->setStatusTip(i18nc("@info:status", "Shrink, grow or move an existing partition.")); + resizePartition->setShortcut(Qt::CTRL | Qt::Key_R); + resizePartition->setIcon(BarIcon("arrow-right-double")); + + KAction* deletePartition = actionCollection()->addAction("deletePartition", this, SLOT(onDeletePartition())); + deletePartition->setEnabled(false); + deletePartition->setText(i18nc("@action:inmenu", "Delete")); + deletePartition->setToolTip(i18nc("@info:tooltip", "Delete partition")); + deletePartition->setStatusTip(i18nc("@info:status", "Delete a partition.")); + deletePartition->setShortcut(Qt::Key_Delete); + deletePartition->setIcon(BarIcon("edit-delete-shred")); + + KAction* copyPartition = actionCollection()->addAction("copyPartition", this, SLOT(onCopyPartition())); + copyPartition->setEnabled(false); + copyPartition->setText(i18nc("@action:inmenu", "Copy")); + copyPartition->setToolTip(i18nc("@info:tooltip", "Copy partition")); + copyPartition->setStatusTip(i18nc("@info:status", "Copy an existing partition.")); + copyPartition->setShortcut(Qt::CTRL | Qt::Key_C); + copyPartition->setIcon(BarIcon("edit-copy")); + + KAction* pastePartition = actionCollection()->addAction("pastePartition", this, SLOT(onPastePartition())); + pastePartition->setEnabled(false); + pastePartition->setText(i18nc("@action:inmenu", "Paste")); + pastePartition->setToolTip(i18nc("@info:tooltip", "Paste partition")); + pastePartition->setStatusTip(i18nc("@info:status", "Paste a copied partition.")); + pastePartition->setShortcut(Qt::CTRL | Qt::Key_V); + pastePartition->setIcon(BarIcon("edit-paste")); + + KAction* mountPartition = actionCollection()->addAction("mountPartition", this, SLOT(onMountPartition())); + mountPartition->setEnabled(false); + mountPartition->setText(i18nc("@action:inmenu", "Mount")); + mountPartition->setToolTip(i18nc("@info:tooltip", "Mount or unmount partition")); + mountPartition->setStatusTip(i18nc("@info:status", "Mount or unmount a partition.")); + + KAction* checkPartition = actionCollection()->addAction("checkPartition", this, SLOT(onCheckPartition())); + checkPartition->setEnabled(false); + checkPartition->setText(i18nc("@action:inmenu", "Check")); + checkPartition->setToolTip(i18nc("@info:tooltip", "Check partition")); + checkPartition->setStatusTip(i18nc("@info:status", "Check a filesystem on a partition for errors.")); + checkPartition->setIcon(BarIcon("flag")); + + KAction* propertiesPartition = actionCollection()->addAction("propertiesPartition", this, SLOT(onPropertiesPartition())); + propertiesPartition->setEnabled(false); + propertiesPartition->setText(i18nc("@action:inmenu", "Properties")); + propertiesPartition->setToolTip(i18nc("@info:tooltip", "Show properties dialog")); + propertiesPartition->setStatusTip(i18nc("@info:status", "View and modify partition properties (label, partition flags, etc.)")); + propertiesPartition->setIcon(BarIcon("document-properties")); + + KAction* backup = actionCollection()->addAction("backupPartition", this, SLOT(onBackupPartition())); + backup->setEnabled(false); + backup->setText(i18nc("@action:inmenu", "Backup")); + backup->setToolTip(i18nc("@info:tooltip", "Backup partition")); + backup->setStatusTip(i18nc("@info:status", "Backup a partition to an image file.")); + backup->setIcon(BarIcon("document-export")); + + KAction* restore = actionCollection()->addAction("restorePartition", this, SLOT(onRestorePartition())); + restore->setEnabled(false); + restore->setText(i18nc("@action:inmenu", "Restore")); + restore->setToolTip(i18nc("@info:tooltip", "Restore partition")); + restore->setStatusTip(i18nc("@info:status", "Restore a partition from an image file.")); + restore->setIcon(BarIcon("document-import")); + + // View actions + KAction* fileSystemSupport = actionCollection()->addAction("fileSystemSupport", this, SLOT(onFileSystemSupport())); + fileSystemSupport->setText(i18nc("@action:inmenu", "File System Support")); + fileSystemSupport->setToolTip(i18nc("@info:tooltip", "View file system support information")); + fileSystemSupport->setStatusTip(i18nc("@info:status", "Show information about supported file systems.")); +} + +void PartitionManagerWidget::setupConnections() +{ + Q_ASSERT(actionCollection()); + + connect(&partTableWidget(), SIGNAL(itemActivated(const PartWidget*)), actionCollection()->action("propertiesPartition"), SLOT(trigger())); + connect(&progressDialog(), SIGNAL(finished(int)), SLOT(onFinished())); +} + +void PartitionManagerWidget::scanDevices() +{ + log() << i18nc("@info/plain", "Rescanning devices..."); + + KApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + setSelectedDevice(NULL); + setClipboardPartition(NULL); + clear(); + + libParted().scanDevices(operationStack()); + + updatePartitions(); + + log() << i18nc("@info/plain", "Rescan finished."); + KApplication::restoreOverrideCursor(); + + emit selectionChanged(NULL); + emit devicesChanged(); + emit operationsChanged(); + emit statusChanged(); +} + +void PartitionManagerWidget::enableActions() +{ + actionCollection()->action("createNewPartitionTable")->setEnabled(CreatePartitionTableOperation::canCreate(selectedDevice())); + + actionCollection()->action("undoOperation")->setEnabled(numPendingOperations() > 0); + actionCollection()->action("clearAllOperations")->setEnabled(numPendingOperations() > 0); + actionCollection()->action("applyAllOperations")->setEnabled(numPendingOperations() > 0 && geteuid() == 0); + + const bool readOnly = selectedDevice() == NULL || selectedDevice()->partitionTable() == NULL || selectedDevice()->partitionTable()->isReadOnly(); + + const Partition* part = selectedPartition(); + + actionCollection()->action("newPartition")->setEnabled(!readOnly && NewOperation::canCreateNew(part)); + const bool canResize = ResizeOperation::canGrow(part) || ResizeOperation::canShrink(part) || ResizeOperation::canMove(part); + actionCollection()->action("resizePartition")->setEnabled(!readOnly && canResize); + actionCollection()->action("copyPartition")->setEnabled(CopyOperation::canCopy(part)); + actionCollection()->action("deletePartition")->setEnabled(!readOnly && DeleteOperation::canDelete(part)); + actionCollection()->action("pastePartition")->setEnabled(!readOnly && CopyOperation::canPaste(part, clipboardPartition())); + actionCollection()->action("propertiesPartition")->setEnabled(part != NULL); + + actionCollection()->action("mountPartition")->setEnabled(part && (part->canMount() || part->canUnmount())); + + if (part != NULL) + actionCollection()->action("mountPartition")->setText(part->isMounted() ? part->fileSystem().unmountTitle() : part->fileSystem().mountTitle() ); + + actionCollection()->action("checkPartition")->setEnabled(!readOnly && CheckOperation::canCheck(part)); + + actionCollection()->action("backupPartition")->setEnabled(BackupOperation::canBackup(part)); + actionCollection()->action("restorePartition")->setEnabled(RestoreOperation::canRestore(part)); +} + +void PartitionManagerWidget::clear() +{ + treePartitions().clear(); + partTableWidget().clear(); +} + +void PartitionManagerWidget::clearSelection() +{ + treePartitions().setCurrentItem(NULL); + enableActions(); + updatePartitions(); +} + +static QTreeWidgetItem* createTreeWidgetItem(const Partition& p) +{ + QTreeWidgetItem* item = new PartitionTreeWidgetItem(&p); + + item->setText(0, p.deviceNode()); + item->setText(1, p.fileSystem().name()); + item->setText(2, p.mountPoints().join(", ")); + if (p.isMounted()) + item->setIcon(2, SmallIcon("object-locked")); + item->setText(3, p.fileSystem().label()); + item->setText(4, Capacity(p).toString()); + item->setText(5, Capacity(p, Capacity::Used).toString()); + item->setText(6, PartitionTable::flagNames(p.activeFlags()).join(", ")); + + return item; +} + +void PartitionManagerWidget::updatePartitions() +{ + if (selectedDevice() == NULL) + return; + + treePartitions().clear(); + partTableWidget().clear(); + + partTableWidget().setPartitionTable(selectedDevice()->partitionTable()); + + QTreeWidgetItem* deviceItem = new QTreeWidgetItem(); + deviceItem->setText(0, selectedDevice()->name()); + deviceItem->setIcon(0, SmallIcon("drive-harddisk")); + treePartitions().addTopLevelItem(deviceItem); + + if (selectedDevice()->partitionTable() != NULL) + { + foreach(const Partition* p, selectedDevice()->partitionTable()->children()) + { + QTreeWidgetItem* item = createTreeWidgetItem(*p); + + foreach(const Partition* child, p->children()) + { + QTreeWidgetItem* childItem = createTreeWidgetItem(*child); + item->addChild(childItem); + } + + deviceItem->addChild(item); + item->setExpanded(true); + } + } + + treePartitions().setFirstItemColumnSpanned(deviceItem, true); + deviceItem->setExpanded(true); + deviceItem->setFlags(Qt::ItemIsEnabled); + + partTableWidget().update(); +} + +void PartitionManagerWidget::on_m_TreePartitions_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem*) +{ + if (current) + { + const PartitionTreeWidgetItem* ptwItem = dynamic_cast(current); + partTableWidget().setActivePartition(ptwItem ? ptwItem->partition() : NULL); + } + else + partTableWidget().setActiveWidget(NULL); +} + +void PartitionManagerWidget::on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int) +{ + // if the activated item is the device item, don't do anything + if (item == treePartitions().topLevelItem(0)) + return; + + actionCollection()->action("propertiesPartition")->trigger(); +} + +void PartitionManagerWidget::on_m_PartTableWidget_itemSelectionChanged(PartWidget* item) +{ + enableActions(); + + if (item == NULL) + { + treePartitions().setCurrentItem(NULL); + emit selectionChanged(NULL); + return; + } + + const Partition* p = item->partition(); + + Q_ASSERT(p); + + QList findResult = treePartitions().findItems(p->deviceNode(), Qt::MatchFixedString | Qt::MatchRecursive, 0); + + for (int idx = 0; idx < findResult.size(); idx++) + { + const PartitionTreeWidgetItem* ptwItem = dynamic_cast(findResult[idx]); + + if (ptwItem && ptwItem->partition() == p) + { + treePartitions().setCurrentItem(findResult[idx]); + break; + } + } + + emit selectionChanged(p); +} + +void PartitionManagerWidget::on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos) +{ + showPartitionContextMenu(partTableWidget().mapToGlobal(pos)); +} + +void PartitionManagerWidget::on_m_TreePartitions_customContextMenuRequested(const QPoint& pos) +{ + showPartitionContextMenu(treePartitions().viewport()->mapToGlobal(pos)); +} + +void PartitionManagerWidget::showPartitionContextMenu(const QPoint& pos) +{ + Q_ASSERT(actionCollection()); + + if (selectedPartition() == NULL || actionCollection() == NULL) + return; + + KMenu partitionMenu; + + partitionMenu.addAction(actionCollection()->action("newPartition")); + partitionMenu.addAction(actionCollection()->action("resizePartition")); + partitionMenu.addAction(actionCollection()->action("deletePartition")); + partitionMenu.addSeparator(); + partitionMenu.addAction(actionCollection()->action("copyPartition")); + partitionMenu.addAction(actionCollection()->action("pastePartition")); + partitionMenu.addSeparator(); + partitionMenu.addAction(actionCollection()->action("mountPartition")); + partitionMenu.addSeparator(); + partitionMenu.addAction(actionCollection()->action("checkPartition")); + partitionMenu.addSeparator(); + partitionMenu.addAction(actionCollection()->action("propertiesPartition")); + + partitionMenu.exec(pos); +} + +void PartitionManagerWidget::setPartitionTable(const PartitionTable* ptable) +{ + partTableWidget().setPartitionTable(ptable); +} + +void PartitionManagerWidget::setSelection(const Partition* p) +{ + partTableWidget().setActivePartition(p); +} + +quint32 PartitionManagerWidget::numPendingOperations() +{ + return operationStack().size(); +} + +Partition* PartitionManagerWidget::selectedPartition() +{ + if (selectedDevice() == NULL || selectedDevice()->partitionTable() == NULL || partTableWidget().activeWidget() == NULL) + return NULL; + + // The active partition we get from the part table widget is const; we need non-const. + // So take the first sector and find the partition in the selected device's + // partition table. + const Partition* activePartition = partTableWidget().activeWidget()->partition(); + return selectedDevice()->partitionTable()->findPartitionBySector(activePartition->firstSector(), PartitionRole(PartitionRole::Any)); +} + +void PartitionManagerWidget::onPropertiesPartition() +{ + if (selectedPartition()) + { + Q_ASSERT(selectedDevice()); + + PartPropsDialog dlg(this, *selectedDevice(), *selectedPartition()); + + if (dlg.exec() == KDialog::Accepted) + { + if (dlg.newFileSystemType() != selectedPartition()->fileSystem().type() || dlg.forceRecreate()) + operationStack().push(new CreateFileSystemOperation(*selectedDevice(), *selectedPartition(), dlg.newFileSystemType())); + + if (dlg.newLabel() != selectedPartition()->fileSystem().label()) + operationStack().push(new SetFileSystemLabelOperation(*selectedPartition(), dlg.newLabel())); + + if (dlg.newFlags() != selectedPartition()->activeFlags()) + operationStack().push(new SetPartFlagsOperation(*selectedDevice(), *selectedPartition(), dlg.newFlags())); + + updatePartitions(); + emit operationsChanged(); + emit statusChanged(); + } + } +} + +void PartitionManagerWidget::onMountPartition() +{ + Partition* p = selectedPartition(); + Report report(NULL); + + if (p && p->canMount()) + { + if (!p->mount(report)) + KMessageBox::detailedSorry(this, i18nc("@info", "The file system on partition %1 could not be mounted.", p->deviceNode()), QString("
%1
").arg(report.toText()), i18nc("@title:window", "Could Not Mount File System.")); + } + else if (p && p->canUnmount()) + { + if (!p->unmount(report)) + KMessageBox::detailedSorry(this, i18nc("@info", "The file system on partition %1 could not be unmounted.", p->deviceNode()), QString("
%1
").arg(report.toText()), i18nc("@title:window", "Could Not Unmount File System.")); + } + + if (p->roles().has(PartitionRole::Logical)) + { + Partition* parent = dynamic_cast(p->parent()); + + Q_ASSERT(parent); + + if (parent != NULL) + parent->checkChildrenMounted(); + else + kWarning() << "parent is null"; + } + + enableActions(); + updatePartitions(); +} + +static bool checkTooManyPartitions(QWidget* parent, const Device& d, const Partition& p) +{ + Q_ASSERT(d.partitionTable()); + + if (p.roles().has(PartitionRole::Unallocated) && d.partitionTable()->numPrimaries() >= d.partitionTable()->maxPrimaries() && !p.roles().has(PartitionRole::Logical)) + { + KMessageBox::sorry(parent, i18ncp("@info", + "There is already 1 primary partition on this device. This is the maximum number its partition table can handle." + "You cannot create, paste or restore a primary partition on it before you delete an existing one.", + "There are already %1 primary partitions on this device. This is the maximum number its partition table can handle." + "You cannot create, paste or restore a primary partition on it before you delete an existing one.", + d.partitionTable()->numPrimaries()), i18nc("@title:window", "Too Many Primary Partitions.")); + return true; + } + + return false; +} + +void PartitionManagerWidget::onNewPartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + Q_ASSERT(selectedDevice()->partitionTable()); + + if (selectedDevice()->partitionTable() == NULL) + { + kWarning() << "partition table on selected device is null"; + return; + } + + if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) + return; + + Partition* newPartition = NewOperation::createNew(*selectedPartition()); + + NewDialog dlg(this, *selectedDevice(), *newPartition, selectedDevice()->partitionTable()->childRoles(*selectedPartition())); + if (dlg.exec() == KDialog::Accepted) + { + PartitionTable::snap(*selectedDevice(), *newPartition); + operationStack().push(new NewOperation(*selectedDevice(), newPartition)); + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + } + else + delete newPartition; +} + +void PartitionManagerWidget::onDeletePartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + if (selectedPartition()->roles().has(PartitionRole::Logical)) + { + Q_ASSERT(selectedPartition()->parent()); + + if (selectedPartition()->parent() == NULL) + { + kWarning() << "parent of selected partition is null."; + return; + } + + if (selectedPartition()->parent()->highestMountedChild() > selectedPartition()->number()) + { + KMessageBox::sorry(this, + i18nc("@info", + "The partition %1 cannot currently be deleted because one or more partitions with higher logical numbers are still mounted." + "Please unmount all partitions with higher logical numbers than %2 first.", + selectedPartition()->deviceNode(), selectedPartition()->number()), + i18nc("@title:window", "Cannot Delete Partition.")); + + return; + } + } + + if (clipboardPartition() == selectedPartition()) + { + if (KMessageBox::warningContinueCancel(this, + i18nc("@info", + "Do you really want to delete the partition that is currently in the clipboard? " + "It will no longer be available for pasting after it has been deleted."), + i18nc("@title:window", "Really Delete Partition in the Clipboard?"), + KGuiItem(i18nc("@action:button", "&Delete It")), + KStandardGuiItem::cancel(), "reallyDeleteClipboardPartition") == KMessageBox::Cancel) + return; + + setClipboardPartition(NULL); + } + + operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition())); + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); +} + +void PartitionManagerWidget::onResizePartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + Q_ASSERT(selectedDevice()->partitionTable()); + + if (selectedDevice()->partitionTable() == NULL) + { + kWarning() << "partition table on selected device is null"; + return; + } + + const qint64 freeBefore = selectedDevice()->partitionTable()->freeSectorsBefore(*selectedPartition()); + const qint64 freeAfter = selectedDevice()->partitionTable()->freeSectorsAfter(*selectedPartition()); + + Partition resizedPartition(*selectedPartition()); + ResizeDialog dlg(this, *selectedDevice(), resizedPartition, freeBefore, freeAfter); + + if (dlg.exec() == KDialog::Accepted && dlg.isModified()) + { + PartitionTable::snap(*selectedDevice(), resizedPartition, selectedPartition()); + + if (resizedPartition.firstSector() == selectedPartition()->firstSector() && resizedPartition.lastSector() == selectedPartition()->lastSector()) + log(log::information) << i18nc("@info/plain", "Partition %1 has the same position and size after resize/move. Ignoring operation.", selectedPartition()->deviceNode()); + else + { + operationStack().push(new ResizeOperation(*selectedDevice(), *selectedPartition(), resizedPartition.firstSector(), resizedPartition.lastSector())); + + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + } + } +} + +void PartitionManagerWidget::onCopyPartition() +{ + Q_ASSERT(selectedPartition()); + + if (selectedPartition() == NULL) + { + kWarning() << "selected partition: " << selectedPartition(); + return; + } + + setClipboardPartition(selectedPartition()); + log() << i18nc("@info/plain", "Partition %1 has been copied to the clipboard.", selectedPartition()->deviceNode()); + + enableActions(); +} + +void PartitionManagerWidget::onPastePartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + if (clipboardPartition() == NULL) + { + kWarning() << "no partition in the clipboard."; + return; + } + + if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) + return; + + Device* dSource = operationStack().findDeviceForPartition(clipboardPartition()); + + Q_ASSERT(dSource); + + if (dSource == NULL) + { + kWarning() << "source partition is null."; + return; + } + + Partition* copiedPartition = CopyOperation::createCopy(*selectedPartition(), *clipboardPartition()); + + if (showInsertDialog(*copiedPartition, clipboardPartition()->length())) + { + operationStack().push(new CopyOperation(*selectedDevice(), copiedPartition, *dSource, clipboardPartition())); + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + } + else + delete copiedPartition; +} + +bool PartitionManagerWidget::showInsertDialog(Partition& insertPartition, qint64 sourceLength) +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return false; + } + + const bool overwrite = !selectedPartition()->roles().has(PartitionRole::Unallocated); + + // Make sure the inserted partition has the right parent and logical or primary set. Only then + // can Device::snap() work correctly. + selectedPartition()->parent()->reparent(insertPartition); + + if (!overwrite) + { + InsertDialog dlg(this, *selectedDevice(), insertPartition, *selectedPartition()); + if (dlg.exec() != KDialog::Accepted) + return false; + PartitionTable::snap(*selectedDevice(), insertPartition, selectedPartition()); + } + + if (insertPartition.length() < sourceLength) + { + if (overwrite) + KMessageBox::error(this, i18nc("@info", + "The selected partition is not large enough to hold the source partition or the backup file." + "Pick another target or resize this partition so it is as large as the source."), i18nc("@title:window", "Target Not Large Enough")); + else + KMessageBox::sorry(this, i18nc("@info", + "It is not possible to create the target partition large enough to hold the source." + "This may happen if not all partitions on a device start and end on cylinder boundaries " + "or when copying a primary partition into an extended partition."), + i18nc("@title:window", "Cannot Create Target Partition.")); + return false; + } + + return true; +} + +void PartitionManagerWidget::onCreateNewPartitionTable() +{ + Q_ASSERT(selectedDevice()); + + if (selectedDevice() == NULL) + { + kWarning() << "selected device is null."; + return; + } + + if (KMessageBox::warningContinueCancel(this, + i18nc("@info", + "Do you really want to create a new partition table on the following device?" + "%1 (%2)" + "This will destroy all data on the device.", selectedDevice()->deviceNode(), selectedDevice()->name()), + i18nc("@title:window", "Destroy All Data on Device?"), + KGuiItem(i18nc("@action:button", "&Create New Partition Table")), + KStandardGuiItem::cancel()) == KMessageBox::Continue) + { + operationStack().push(new CreatePartitionTableOperation(*selectedDevice())); + + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + enableActions(); + } +} + +void PartitionManagerWidget::onRefreshDevices() +{ + if (numPendingOperations() == 0 || KMessageBox::warningContinueCancel(this, + i18nc("@info", + "Do you really want to rescan the devices?" + "This will also clear the list of pending operations."), + i18nc("@title:window", "Really Rescan the Devices?"), + KGuiItem(i18nc("@action:button", "&Rescan Devices")), + KStandardGuiItem::cancel(), "reallyRescanDevices") == KMessageBox::Continue) + { + scanDevices(); + } +} + +void PartitionManagerWidget::onUndoOperation() +{ + log() << i18nc("@info/plain", "Undoing operation: %1", operationStack().operations().last()->description()); + operationStack().pop(); + + updatePartitions(); + emit operationsChanged(); + emit statusChanged(); + enableActions(); +} + +void PartitionManagerWidget::onClearAllOperations() +{ + if (KMessageBox::warningContinueCancel(this, + i18nc("@info", "Do you really want to clear the list of pending operations?"), + i18nc("@title:window", "Clear Pending Operations?"), + KGuiItem(i18nc("@action:button", "&Clear Pending Operations")), + KStandardGuiItem::cancel(), "reallyClearPendingOperations") == KMessageBox::Continue) + { + log() << i18nc("@info/plain", "Clearing the list of pending operations."); + operationStack().clearOperations(); + + updatePartitions(); + emit operationsChanged(); + emit statusChanged(); + enableActions(); + } +} + +void PartitionManagerWidget::onApplyAllOperations() +{ + QStringList opList; + + foreach (const Operation* op, operationStack().operations()) + opList.append(op->description()); + + if (KMessageBox::warningContinueCancelList(this, + i18nc("@info", + "Do you really want to apply the pending operations listed below?" + "This will permanently modify your disks."), + opList, i18nc("@title:window", "Apply Pending Operations?"), + KGuiItem(i18nc("@action:button", "&Apply Pending Operations")), + KStandardGuiItem::cancel()) == KMessageBox::Continue) + { + log() << i18nc("@info/plain", "Applying operations..."); + + progressDialog().show(); + + operationRunner().setReport(&progressDialog().report()); + + setUpdatesEnabled(false); + + // 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); + } + + updatePartitions(); + + operationRunner().start(); + } +} + +void PartitionManagerWidget::onCheckPartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + operationStack().push(new CheckOperation(*selectedDevice(), *selectedPartition())); + + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); +} + +void PartitionManagerWidget::onBackupPartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + QString fileName = KFileDialog::getSaveFileName(KUrl("kfiledialog://backupPartition")); +// QString fileName = "/tmp/backuptest.img"; + + if (fileName.isEmpty()) + return; + + if (!QFile::exists(fileName) || KMessageBox::warningContinueCancel(this, i18nc("@info", "Do you want to overwrite the existing file %1?", fileName), i18nc("@title:window", "Overwrite Existing File?"), KGuiItem(i18nc("@action:button", "&Overwrite File")), KStandardGuiItem::cancel()) == KMessageBox::Continue) + { + operationStack().push(new BackupOperation(*selectedDevice(), *selectedPartition(), fileName)); + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + } +} + +void PartitionManagerWidget::onRestorePartition() +{ + Q_ASSERT(selectedDevice()); + Q_ASSERT(selectedPartition()); + + if (selectedDevice() == NULL || selectedPartition() == NULL) + { + kWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); + return; + } + + if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) + return; + + QString fileName = KFileDialog::getOpenFileName(KUrl("kfiledialog://backupPartition")); +// QString fileName = "/tmp/backuptest.img"; + + if (!fileName.isEmpty() && QFile::exists(fileName)) + { + Partition* restorePartition = RestoreOperation::createRestorePartition(*selectedDevice(), *selectedPartition()->parent(), selectedPartition()->firstSector(), fileName); + + if (restorePartition->length() > selectedPartition()->length()) + { + KMessageBox::error(this, i18nc("@info", "The file system in the image file %1 is too large to be restored to the selected partition.", fileName), i18nc("@title:window", "Not Enough Space to Restore File System.")); + delete restorePartition; + return; + } + + if (showInsertDialog(*restorePartition, restorePartition->length())) + { + operationStack().push(new RestoreOperation(*selectedDevice(), restorePartition, fileName)); + + updatePartitions(); + emit statusChanged(); + emit operationsChanged(); + } + else + delete restorePartition; + } +} + +void PartitionManagerWidget::onFileSystemSupport() +{ + FileSystemSupportDialog dlg(this); + dlg.exec(); +} + +void PartitionManagerWidget::onFinished() +{ + setUpdatesEnabled(true); + scanDevices(); +} + +void PartitionManagerWidget::setSelectedDevice(Device* d) +{ + m_SelectedDevice = d; + clearSelection(); +} diff --git a/src/gui/partitionmanagerwidget.h b/src/gui/partitionmanagerwidget.h new file mode 100644 index 0000000..e1c928c --- /dev/null +++ b/src/gui/partitionmanagerwidget.h @@ -0,0 +1,151 @@ +/*************************************************************************** + * Copyright (C) 2008,2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#if !defined(CENTRALWIDGET__H) + +#define CENTRALWIDGET__H + +#include "util/libpartitionmanagerexport.h" + +#include "core/libparted.h" +#include "core/operationrunner.h" +#include "core/operationstack.h" + +#include "ui_partitionmanagerwidgetbase.h" + +#include + +class QWidget; +class QLabel; +class PartWidget; +class KActionCollection; +class Device; +class ProgressDialog; + +/** @brief The central widget for the application. + + @author vl@fidra.de +*/ +class LIBPARTITIONMANAGERPRIVATE_EXPORT PartitionManagerWidget : public QWidget, Ui::PartitionManagerWidgetBase +{ + Q_OBJECT + + public: + explicit PartitionManagerWidget(QWidget* parent, KActionCollection* coll = NULL); + virtual ~PartitionManagerWidget(); + + signals: + void devicesChanged(); + void operationsChanged(); + void statusChanged(); + void selectionChanged(const Partition*); + + public: + void init(KActionCollection* coll); + KActionCollection* actionCollection() const { return m_ActionCollection; } + + void clear(); + void clearSelection(); + void setPartitionTable(const PartitionTable* ptable); + void setSelection(const Partition* p); + void enableActions(); + + Device* selectedDevice() { return m_SelectedDevice; } + const Device* selectedDevice() const { return m_SelectedDevice; } + void setSelectedDevice(Device* d); + + Partition* selectedPartition(); + + OperationStack::Devices& previewDevices() { return operationStack().previewDevices(); } + const OperationStack::Devices& previewDevices() const { return operationStack().previewDevices(); } + const OperationStack::Operations& operations() const { return operationStack().operations(); } + + void updatePartitions(); + + Partition* clipboardPartition() { return m_ClipboardPartition; } + const Partition* clipboardPartition() const { return m_ClipboardPartition; } + void setClipboardPartition(Partition* p) { m_ClipboardPartition = p; } + + ProgressDialog& progressDialog() { Q_ASSERT(m_ProgressDialog); return *m_ProgressDialog; } + const ProgressDialog& progressDialog() const { Q_ASSERT(m_ProgressDialog); return *m_ProgressDialog; } + + quint32 numPendingOperations(); + + protected: + void setupActions(); + void setupConnections(); + void showPartitionContextMenu(const QPoint& pos); + void loadConfig(); + void saveConfig() const; + bool showInsertDialog(Partition& insertPartition, qint64 sourceLength); + + PartTableWidget& partTableWidget() { Q_ASSERT(m_PartTableWidget); return *m_PartTableWidget; } + const PartTableWidget& partTableWidget() const { Q_ASSERT(m_PartTableWidget); return *m_PartTableWidget; } + + QTreeWidget& treePartitions() { Q_ASSERT(m_TreePartitions); return *m_TreePartitions; } + const QTreeWidget& treePartitions() const { Q_ASSERT(m_TreePartitions); return *m_TreePartitions; } + + LibParted& libParted() { return m_LibParted; } + const LibParted& libParted() const { return m_LibParted; } + + OperationRunner& operationRunner() { return m_OperationRunner; } + const OperationRunner& operationRunner() const { return m_OperationRunner; } + + OperationStack& operationStack() { return m_OperationStack; } + const OperationStack& operationStack() const { return m_OperationStack; } + + protected slots: + void on_m_TreePartitions_currentItemChanged(QTreeWidgetItem* current, QTreeWidgetItem* previous); + void on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos); + void on_m_TreePartitions_customContextMenuRequested(const QPoint& pos); + void on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int); + void on_m_PartTableWidget_itemSelectionChanged(PartWidget* item); + + void scanDevices(); + + void onPropertiesPartition(); + void onMountPartition(); + void onNewPartition(); + void onDeletePartition(); + void onResizePartition(); + void onCopyPartition(); + void onPastePartition(); + void onCheckPartition(); + void onCreateNewPartitionTable(); + void onRefreshDevices(); + void onUndoOperation(); + void onClearAllOperations(); + void onApplyAllOperations(); + void onFileSystemSupport(); + void onBackupPartition(); + void onRestorePartition(); + void onFinished(); + + private: + LibParted m_LibParted; + OperationStack m_OperationStack; + OperationRunner m_OperationRunner; + ProgressDialog* m_ProgressDialog; + KActionCollection* m_ActionCollection; + Device* m_SelectedDevice; + Partition* m_ClipboardPartition; +}; + +#endif + diff --git a/src/gui/partitionmanagerwidgetbase.ui b/src/gui/partitionmanagerwidgetbase.ui new file mode 100644 index 0000000..41cc4b5 --- /dev/null +++ b/src/gui/partitionmanagerwidgetbase.ui @@ -0,0 +1,104 @@ + + PartitionManagerWidgetBase + + + + 0 + 0 + 634 + 531 + + + + KDE Partition Manager + + + + + + + 0 + 0 + + + + + 0 + 80 + + + + + 16777215 + 80 + + + + Qt::CustomContextMenu + + + + + + + Qt::CustomContextMenu + + + true + + + false + + + false + + + + Partition + + + + + Type + + + + + Mount Point + + + + + Label + + + + + Size + + + + + Used + + + + + Flags + + + + + + + + + PartTableWidget + QWidget +
gui/parttablewidget.h
+ 1 +
+
+ + +
diff --git a/src/kcm/kcm_partitionmanager.desktop b/src/kcm/kcm_partitionmanager.desktop new file mode 100644 index 0000000..c71bd91 --- /dev/null +++ b/src/kcm/kcm_partitionmanager.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Exec=kcmshell4 kcm_partitionmanager +Icon=partitionmanager +Type=Service + +X-KDE-Library=kcm_partitionmanager +#X-KDE-RootOnly=true +X-KDE-ServiceTypes=KCModule +X-KDE-ParentApp=kcontrol +X-KDE-System-Settings-Parent-Category=system +#X-KDE-SubstituteUID=true + +Name=Partition Manager +Comment=Manage disks, partitions and file systems diff --git a/src/kcm/partitionmanagerkcm.cpp b/src/kcm/partitionmanagerkcm.cpp new file mode 100644 index 0000000..0940d06 --- /dev/null +++ b/src/kcm/partitionmanagerkcm.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "kcm/partitionmanagerkcm.h" + +#include "gui/partitionmanagerwidget.h" +#include "gui/listdevices.h" + +#include "util/helpers.h" + +#include +#include +#include +#include + +K_PLUGIN_FACTORY( + PartitionManagerKCMFactory, + registerPlugin(); +) +K_EXPORT_PLUGIN( + PartitionManagerKCMFactory("partitionmanagerkcm") +) + +PartitionManagerKCM::PartitionManagerKCM(QWidget* parent, const QVariantList&) : + KCModule(PartitionManagerKCMFactory::componentData(), parent), + Ui::PartitionManagerKCMBase(), + m_ActionCollection(new KActionCollection(this, PartitionManagerKCMFactory::componentData())) +{ + setupUi(this); + + connect(GlobalLog::instance(), SIGNAL(newMessage(log::Level, const QString&)), SLOT(onNewLogMessage(log::Level, const QString&))); + + // workaround for https://bugs.launchpad.net/kdesudo/+bug/272427 + unblockSigChild(); + registerMetaTypes(); + + setButtons(Apply); + + setupConnections(); + + listDevices().init(actionCollection(), &pmWidget()); + listOperations().init(actionCollection(), &pmWidget()); + pmWidget().init(actionCollection()); + + const char* actionNames[] = + { + "applyAllOperations", + "undoOperation", + "clearAllOperations", + "", +// "refreshDevices", + "createNewPartitionTable", + "", + "newPartition", + "resizePartition", + "deletePartition", + "copyPartition", + "pastePartition", +// "mountPartition", + "checkPartition", + "propertiesPartition", + "backupPartition", + "restorePartition", +// "", +// "fileSystemSupport" + }; + + for(size_t i = 0; i < sizeof(actionNames) / sizeof(actionNames[0]); i++) + if (strlen(actionNames[i]) > 0) + toolBar()->addAction(actionCollection()->action(actionNames[i])); + else + toolBar()->addSeparator(); + + toolBar()->setIconSize(QSize(22, 22)); + toolBar()->setToolButtonStyle(Qt::ToolButtonIconOnly); +} + +void PartitionManagerKCM::onNewLogMessage(log::Level, const QString& s) +{ + kDebug() << s; +} + +void PartitionManagerKCM::load() +{ +} + +void PartitionManagerKCM::save() +{ +} + +void PartitionManagerKCM::setupConnections() +{ + connect(&pmWidget(), SIGNAL(devicesChanged()), &listDevices(), SLOT(updateDevices())); + connect(&pmWidget(), SIGNAL(operationsChanged()), &listOperations(), SLOT(updateOperations())); +} + +void PartitionManagerKCM::on_m_ListDevices_selectionChanged(Device* d) +{ + pmWidget().setSelectedDevice(d); +} diff --git a/src/kcm/partitionmanagerkcm.h b/src/kcm/partitionmanagerkcm.h new file mode 100644 index 0000000..a6c20e2 --- /dev/null +++ b/src/kcm/partitionmanagerkcm.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2009 by Volker Lanz * + * * + * 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 2 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, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#if !defined(PARTMANKCM__H) + +#define PARTMANKCM__H + +#include "ui_partitionmanagerkcmbase.h" + +#include "util/globallog.h" + +#include +#include + +class PartitionManagerWidget; +class ListDevices; +class KActionCollection; +class Device; +class KToolBar; + +class PartitionManagerKCM : public KCModule, public Ui::PartitionManagerKCMBase +{ + Q_OBJECT + + public: + PartitionManagerKCM(QWidget* parent, const QVariantList& args); + virtual ~PartitionManagerKCM() {} + + public: + void load(); + void save(); + + protected: + void setupConnections(); + + PartitionManagerWidget& pmWidget() { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + const PartitionManagerWidget& pmWidget() const { Q_ASSERT(m_PartitionManagerWidget); return *m_PartitionManagerWidget; } + + ListDevices& listDevices() { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + const ListDevices& listDevices() const { Q_ASSERT(m_ListDevices); return *m_ListDevices; } + + ListOperations& listOperations() { Q_ASSERT(m_ListOperations); return *m_ListOperations; } + const ListOperations& listOperations() const { Q_ASSERT(m_ListOperations); return *m_ListOperations; } + + KActionCollection* actionCollection() { return m_ActionCollection; } + + KToolBar* toolBar() { return m_ToolBar; } + + protected slots: + void on_m_ListDevices_selectionChanged(Device* d); + void onNewLogMessage(log::Level logLevel, const QString& s); + + private: + KActionCollection* m_ActionCollection; +}; + + +#endif + diff --git a/src/kcm/partitionmanagerkcmbase.ui b/src/kcm/partitionmanagerkcmbase.ui new file mode 100644 index 0000000..36a3546 --- /dev/null +++ b/src/kcm/partitionmanagerkcmbase.ui @@ -0,0 +1,91 @@ + + PartitionManagerKCMBase + + + + 0 + 0 + 684 + 684 + + + + Form + + + + + + Qt::Horizontal + + + + Qt::Vertical + + + + + 170 + 0 + + + + + + + + + + + + 300 + 32 + + + + + + + + + 0 + 0 + + + + + + + + + + + + + PartitionManagerWidget + QWidget +
gui/partitionmanagerwidget.h
+ 1 +
+ + ListDevices + QWidget +
gui/listdevices.h
+ 1 +
+ + ListOperations + QWidget +
gui/listoperations.h
+ 1 +
+ + KToolBar + QWidget +
ktoolbar.h
+ 1 +
+
+ + +