diff --git a/src/gui/editmountpointdialog.cpp b/src/gui/editmountpointdialog.cpp index 329e870..f80b112 100644 --- a/src/gui/editmountpointdialog.cpp +++ b/src/gui/editmountpointdialog.cpp @@ -1,84 +1,89 @@ /************************************************************************* * Copyright (C) 2009, 2010 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "gui/editmountpointdialog.h" #include "gui/editmountpointdialogwidget.h" #include #include #include #include #include #include #include #include EditMountPointDialog::EditMountPointDialog(QWidget* parent, Partition& p) : QDialog(parent), m_Partition(p), m_DialogWidget(new EditMountPointDialogWidget(this, partition())) { QVBoxLayout *mainLayout = new QVBoxLayout(this); setLayout(mainLayout); mainLayout->addWidget(&widget()); setWindowTitle(xi18nc("@title:window", "Edit mount point for %1", p.deviceNode())); KConfigGroup kcg(KSharedConfig::openConfig(), "editMountPointDialog"); restoreGeometry(kcg.readEntry("Geometry", QByteArray())); QDialogButtonBox* dbb = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this ); mainLayout->addWidget(dbb); connect(dbb, &QDialogButtonBox::accepted, - this, &EditMountPointDialog::accept); + this, [=] () {accept_(Edit);} ); connect(dbb, &QDialogButtonBox::rejected, this, &EditMountPointDialog::reject); + connect(widget().m_ButtonRemove, &QPushButton::clicked, this, [=] () {accept_(Remove);} ); } -/** Destroys an EditMOuntOptionsDialog instance */ +/** Destroys an EditMountOptionsDialog instance */ EditMountPointDialog::~EditMountPointDialog() { KConfigGroup kcg(KSharedConfig::openConfig(), "editMountPointDialog"); kcg.writeEntry("Geometry", saveGeometry()); } -void EditMountPointDialog::accept() +void EditMountPointDialog::accept_(MountPointAction action) { if (KMessageBox::warningContinueCancel(this, xi18nc("@info", "Are you sure you want to save the changes you made to the system table file /etc/fstab?" "This will overwrite the existing file on your hard drive now. This can not be undone."), xi18nc("@title:window", "Really save changes?"), KGuiItem(xi18nc("@action:button", "Save changes"), QStringLiteral("arrow-right")), KStandardGuiItem::cancel(), QStringLiteral("reallyWriteMountPoints")) == KMessageBox::Cancel) return; - - widget().acceptChanges(); - if (writeMountpoints(widget().fstabEntries())) - partition().setMountPoint(widget().editPath().text()); + if(action == Remove) + widget().removeMountPoint(); + else if (action == Edit) + widget().acceptChanges(); + if (writeMountpoints(widget().fstabEntries())) { + if (action == Edit) + partition().setMountPoint(widget().editPath().text()); + } else KMessageBox::sorry(this, xi18nc("@info", "Could not save mount points to file /etc/fstab."), xi18nc("@title:window", "Error While Saving Mount Points")); QDialog::accept(); } diff --git a/src/gui/editmountpointdialog.h b/src/gui/editmountpointdialog.h index 0b64243..b3574df 100644 --- a/src/gui/editmountpointdialog.h +++ b/src/gui/editmountpointdialog.h @@ -1,55 +1,61 @@ /************************************************************************* * Copyright (C) 2009, 2010 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(EDITMOUNTPOINTDIALOG_H) #define EDITMOUNTPOINTDIALOG_H #include class EditMountPointDialogWidget; class Partition; class QWidget; class QString; +enum MountPointAction +{ + Remove = 1, + Edit = 2 +}; + class EditMountPointDialog : public QDialog { public: EditMountPointDialog(QWidget* parent, Partition& p); ~EditMountPointDialog(); protected: EditMountPointDialogWidget& widget() { return *m_DialogWidget; } - void accept() override; + void accept_(MountPointAction action); private: Partition& partition() { return m_Partition; } private: Partition& m_Partition; EditMountPointDialogWidget* m_DialogWidget; }; #endif diff --git a/src/gui/editmountpointdialogwidget.cpp b/src/gui/editmountpointdialogwidget.cpp index 73b0d4c..7591a83 100644 --- a/src/gui/editmountpointdialogwidget.cpp +++ b/src/gui/editmountpointdialogwidget.cpp @@ -1,197 +1,213 @@ /************************************************************************* * Copyright (C) 2009, 2010 by Volker Lanz * * Copyright (C) 2016, 2017 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "gui/editmountpointdialogwidget.h" #include "gui/editmountoptionsdialog.h" #include #include #include #include #include #include #include #include #include #include -EditMountPointDialogWidget::EditMountPointDialogWidget(QWidget* parent, const Partition& p) : +EditMountPointDialogWidget::EditMountPointDialogWidget(QWidget* parent, Partition& p) : QWidget(parent), m_Partition(p) { m_fstabEntries = readFstabEntries(); setupUi(this); m_deviceNode = partition().deviceNode(); if (partition().roles().has(PartitionRole::Luks) && partition().fileSystem().type() != FileSystem::Luks) { const FS::luks* luksFs = dynamic_cast(&partition().fileSystem()); m_deviceNode = luksFs->mapperName(); } labelName().setText(m_deviceNode); labelType().setText(partition().fileSystem().name()); bool entryFound = false; for (auto &e : m_fstabEntries) { QString canonicalEntryPath = QFileInfo(e.deviceNode()).canonicalFilePath(); QString canonicalDevicePath = QFileInfo(m_deviceNode).canonicalFilePath(); if (canonicalEntryPath == canonicalDevicePath) { // FIXME fix multiple mountpoints entryFound = true; entry = &e; } } if (!entryFound) { FileSystem::Type type = partition().fileSystem().type(); QString fsName; switch (type) { case FileSystem::LinuxSwap: fsName = QStringLiteral("swap"); break; case FileSystem::Fat16: case FileSystem::Fat32: fsName = QStringLiteral("vfat"); break; default: fsName = partition().fileSystem().name(); } m_fstabEntries.append(FstabEntry(m_deviceNode, QString(), fsName, QString())); entry = &m_fstabEntries.last(); } editPath().setText(entry->mountPoint()); spinDumpFreq().setValue(entry->dumpFreq()); spinPassNumber().setValue(entry->passNumber()); switch (entry->entryType()) { case FstabEntryType::uuid: radioUUID().setChecked(true); break; case FstabEntryType::label: radioLabel().setChecked(true); break; case FstabEntryType::partuuid: radioUUID().setChecked(true); break; case FstabEntryType::partlabel: radioLabel().setChecked(true); break; case FstabEntryType::deviceNode: radioDeviceNode().setChecked(true); break; case FstabEntryType::comment: break; } boxOptions()[QStringLiteral("ro")] = m_CheckReadOnly; boxOptions()[QStringLiteral("users")] = m_CheckUsers; boxOptions()[QStringLiteral("noauto")] = m_CheckNoAuto; boxOptions()[QStringLiteral("noatime")] = m_CheckNoAtime; boxOptions()[QStringLiteral("nodiratime")] = m_CheckNoDirAtime; boxOptions()[QStringLiteral("sync")] = m_CheckSync; boxOptions()[QStringLiteral("noexec")] = m_CheckNoExec; boxOptions()[QStringLiteral("relatime")] = m_CheckRelAtime; setupOptions(entry->options()); if (partition().fileSystem().uuid().isEmpty()) { radioUUID().setEnabled(false); if (radioUUID().isChecked()) radioDeviceNode().setChecked(true); } if (partition().fileSystem().label().isEmpty()) { radioLabel().setEnabled(false); if (radioLabel().isChecked()) radioDeviceNode().setChecked(true); } connect(m_ButtonMore, &QPushButton::clicked, this, &EditMountPointDialogWidget::buttonMoreClicked); connect(m_ButtonSelect, &QPushButton::clicked, this, &EditMountPointDialogWidget::buttonSelectClicked); } EditMountPointDialogWidget::~EditMountPointDialogWidget() { } void EditMountPointDialogWidget::setupOptions(const QStringList& options) { QStringList optTmpList; for (const auto &o : options) { if (boxOptions().find(o) != boxOptions().end()) boxOptions()[o]->setChecked(true); else optTmpList.append(o); } m_Options = optTmpList.join(QLatin1Char(',')); } void EditMountPointDialogWidget::buttonSelectClicked(bool) { const QString s = QFileDialog::getExistingDirectory(this, editPath().text()); if (!s.isEmpty()) editPath().setText(s); } +void EditMountPointDialogWidget::removeMountPoint() +{ + int i=0; + for (const auto &e : fstabEntries()) { + if(e.fsSpec().contains(partition().deviceNode()) || e.fsSpec().contains(partition().fileSystem().uuid()) || + e.fsSpec().contains(partition().fileSystem().label()) || e.fsSpec().contains(partition().label()) || e.fsSpec().contains(partition().uuid()) ) + { + fstabEntries().removeAt(i); + partition().setMountPoint(QString()); + i--; + } + i++; + } + +} + void EditMountPointDialogWidget::buttonMoreClicked(bool) { QPointer dlg = new EditMountOptionsDialog(this, m_Options.split(QLatin1Char(','))); if (dlg->exec() == QDialog::Accepted) setupOptions(dlg->options()); delete dlg; } QStringList EditMountPointDialogWidget::options() const { QStringList optList = m_Options.split(QLatin1Char(','), QString::SkipEmptyParts); const auto keys = boxOptions(); for (const auto &s : keys) if (s.second->isChecked()) optList.append(s.first); return optList; } void EditMountPointDialogWidget::acceptChanges() { entry->setDumpFreq(spinDumpFreq().value()); entry->setPassNumber(spinPassNumber().value()); entry->setMountPoint(editPath().text()); entry->setOptions(options()); if (radioUUID().isChecked() && !partition().fileSystem().uuid().isEmpty()) entry->setFsSpec(QStringLiteral("UUID=") + partition().fileSystem().uuid()); else if (radioLabel().isChecked() && !partition().fileSystem().label().isEmpty()) entry->setFsSpec(QStringLiteral("LABEL=") + partition().fileSystem().label()); else entry->setFsSpec(m_deviceNode); } diff --git a/src/gui/editmountpointdialogwidget.h b/src/gui/editmountpointdialogwidget.h index 905f238..da54790 100644 --- a/src/gui/editmountpointdialogwidget.h +++ b/src/gui/editmountpointdialogwidget.h @@ -1,107 +1,112 @@ /************************************************************************* * Copyright (C) 2009, 2010 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 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #if !defined(EDITMOUNTPOINTDIALOGWIDGET_H) #define EDITMOUNTPOINTDIALOGWIDGET_H #include "ui_editmountpointdialogwidgetbase.h" #include #include #include #include class Partition; class QFile; class QSpinBox; class QCheckBox; class QLineEdit; class QPushButton; class QStringList; class EditMountPointDialogWidget : public QWidget, public Ui::EditMountPointDialogWidgetBase { public: - EditMountPointDialogWidget(QWidget* parent, const Partition& p); + EditMountPointDialogWidget(QWidget* parent, Partition& p); ~EditMountPointDialogWidget(); QPushButton& buttonMore() { return *m_ButtonMore; } QLabel& labelName() { return *m_LabelNameValue; } QLineEdit& editPath() { return *m_EditPath; } QSpinBox& spinDumpFreq() { return *m_SpinDumpFreq; } QSpinBox& spinPassNumber() { return *m_SpinPassNumber; } QLabel& labelType() { return *m_LabelTypeValue; } QStringList options() const; QRadioButton& radioUUID() { return *m_RadioUUID; } QRadioButton& radioLabel() { return *m_RadioLabel; } QRadioButton& radioDeviceNode() { return *m_RadioDeviceNode; } FstabEntryList& fstabEntries() { return m_fstabEntries; } void acceptChanges(); + void removeMountPoint(); bool writeMountpoints(const QString& filename); protected: void buttonSelectClicked(bool); void buttonMoreClicked(bool); private: void setupOptions(const QStringList& options); std::map& boxOptions() { return m_BoxOptions; } const std::map& boxOptions() const { return m_BoxOptions; } + Partition& partition() { + return m_Partition; + } + const Partition& partition() const { return m_Partition; } private: FstabEntryList m_fstabEntries; FstabEntry *entry; - const Partition& m_Partition; + Partition& m_Partition; QString m_Options; QString m_deviceNode; std::map m_BoxOptions; }; #endif diff --git a/src/gui/editmountpointdialogwidgetbase.ui b/src/gui/editmountpointdialogwidgetbase.ui index 11f432d..e83a459 100644 --- a/src/gui/editmountpointdialogwidgetbase.ui +++ b/src/gui/editmountpointdialogwidgetbase.ui @@ -1,320 +1,333 @@ EditMountPointDialogWidgetBase 0 0 - 534 - 353 + 600 + 374 - - - - Path: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - m_EditPath - - - - + 3 0 - - - - - 1 - 0 - - + + - Select... + Path: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + m_EditPath Type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Options: Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing Read-only Users can mount and unmount No automatic mount No update of file access times Synchronous access No update of directory access times No binary execution Update access times relative to modification Qt::Horizontal 195 22 More... - Dump Frequency: + Dump &Frequency: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter m_SpinDumpFreq Qt::Horizontal 70 23 - Pass Number: + Pass &Number: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter m_SpinPassNumber - - - - Qt::Horizontal - - - - 135 - 23 - - - - 75 true Qt::AlignCenter Qt::Horizontal Qt::Vertical QSizePolicy::Fixed 20 10 Qt::Vertical QSizePolicy::Fixed 20 10 Qt::Vertical QSizePolicy::Fixed 20 20 - Device Node + De&vice Node true - UUID + UU&ID - Label + &Label Identify by: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Qt::Horizontal + + + + 135 + 23 + + + + + + + + + 1 + 0 + + + + Select... + + + + + + + + 1 + 0 + + + + Remove + + + diff --git a/src/gui/partitionmanagerwidget.cpp b/src/gui/partitionmanagerwidget.cpp index 82cf4e5..806fea2 100644 --- a/src/gui/partitionmanagerwidget.cpp +++ b/src/gui/partitionmanagerwidget.cpp @@ -1,804 +1,800 @@ /************************************************************************* * Copyright (C) 2008-2010 by Volker Lanz * * Copyright (C) 2015 by Teo Mrnjavac * * Copyright (C) 2016 by Andrius Štikonas * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* *************************************************************************/ #include "gui/partitionmanagerwidget.h" #include "gui/partpropsdialog.h" #include "gui/resizedialog.h" #include "gui/newdialog.h" #include "gui/applyprogressdialog.h" #include "gui/insertdialog.h" #include "gui/editmountpointdialog.h" #include "util/guihelpers.h" #include #include #include #include #include #include #include #include #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 { Q_DISABLE_COPY(PartitionTreeWidgetItem) 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 */ PartitionManagerWidget::PartitionManagerWidget(QWidget* parent) : QWidget(parent), Ui::PartitionManagerWidgetBase(), m_OperationStack(nullptr), m_SelectedDevice(nullptr), m_ClipboardPartition(nullptr) { setupUi(this); treePartitions().header()->setStretchLastSection(false); treePartitions().header()->setContextMenuPolicy(Qt::CustomContextMenu); } PartitionManagerWidget::~PartitionManagerWidget() { saveConfig(); } void PartitionManagerWidget::init(OperationStack* ostack) { m_OperationStack = ostack; // TODO: shouldn't this also go to the main window class? FileSystemFactory::init(); loadConfig(); setupConnections(); } void PartitionManagerWidget::loadConfig() { QList colWidths = Config::treePartitionColumnWidths(); QList colPositions = Config::treePartitionColumnPositions(); QList colVisible = Config::treePartitionColumnVisible(); QHeaderView* header = treePartitions().header(); for (int i = 0; i < treePartitions().columnCount(); i++) { if (colPositions[0] != -1 && colPositions.size() > i) header->moveSection(header->visualIndex(i), colPositions[i]); if (colVisible[0] != -1 && colVisible.size() > i) treePartitions().setColumnHidden(i, colVisible[i] == 0); if (colWidths[0] != -1 && colWidths.size() > i) treePartitions().setColumnWidth(i, colWidths[i]); } } void PartitionManagerWidget::saveConfig() const { QList colWidths; QList colPositions; QList colVisible; for (int i = 0; i < treePartitions().columnCount(); i++) { colPositions.append(treePartitions().header()->visualIndex(i)); colVisible.append(treePartitions().isColumnHidden(i) ? 0 : 1); colWidths.append(treePartitions().columnWidth(i)); } Config::setTreePartitionColumnPositions(colPositions); Config::setTreePartitionColumnVisible(colVisible); Config::setTreePartitionColumnWidths(colWidths); Config::self()->save(); } void PartitionManagerWidget::setupConnections() { connect(treePartitions().header(), &QHeaderView::customContextMenuRequested, this, &PartitionManagerWidget::onHeaderContextMenu); } void PartitionManagerWidget::clear() { setSelectedDevice(nullptr); setClipboardPartition(nullptr); treePartitions().clear(); partTableWidget().clear(); } void PartitionManagerWidget::setSelectedPartition(const Partition* p) { if (p == nullptr) { treePartitions().setCurrentItem(nullptr); emit selectedPartitionChanged(nullptr); updatePartitions(); } else partTableWidget().setActivePartition(p); } Partition* PartitionManagerWidget::selectedPartition() { if (selectedDevice() == nullptr || selectedDevice()->partitionTable() == nullptr || partTableWidget().activeWidget() == nullptr) return nullptr; - // 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)); + return partTableWidget().activeWidget()->partition(); } void PartitionManagerWidget::setSelectedDevice(const QString& deviceNode) { QReadLocker lockDevices(&operationStack().lock()); const auto previewDevices = operationStack().previewDevices(); for (const auto &d : previewDevices) { if (d->deviceNode() == deviceNode) { setSelectedDevice(d); return; } } setSelectedDevice(nullptr); } void PartitionManagerWidget::setSelectedDevice(Device* d) { m_SelectedDevice = d; setSelectedPartition(nullptr); } static QTreeWidgetItem* createTreeWidgetItem(const Partition& p) { QTreeWidgetItem* item = new PartitionTreeWidgetItem(&p); int i = 0; item->setText(i++, p.deviceNode()); if (p.roles().has(PartitionRole::Luks) && (p.fileSystem().name() != p.fileSystem().nameForType(FileSystem::Luks) && p.fileSystem().name() != p.fileSystem().nameForType(FileSystem::Luks2))) item->setText(i, xi18nc("@item:intable Encrypted file systems, e.g. btrfs[Encrypted]", "%1 [Encrypted]", p.fileSystem().name())); else item->setText(i, p.fileSystem().name()); item->setIcon(i, createFileSystemColor(p.fileSystem().type(), 14)); i++; item->setText(i, p.mountPoint()); if (p.isMounted()) item->setIcon(i, QIcon::fromTheme(QStringLiteral("object-locked")).pixmap(IconSize(KIconLoader::Panel))); i++; item->setText(i++, p.fileSystem().label()); item->setText(i++, p.fileSystem().uuid()); item->setText(i++, p.label()); item->setText(i++, p.uuid()); item->setText(i++, Capacity::formatByteSize(p.capacity())); item->setText(i++, Capacity::formatByteSize(p.used())); item->setText(i++, Capacity::formatByteSize(p.available())); item->setText(i++, QLocale().toString(p.firstSector())); item->setText(i++, QLocale().toString(p.lastSector())); item->setText(i++, QLocale().toString(p.length())); item->setText(i++, PartitionTable::flagNames(p.activeFlags()).join(QStringLiteral(", "))); item->setSizeHint(0, QSize(0, 32)); return item; } void PartitionManagerWidget::updatePartitions() { if (selectedDevice() == nullptr) return; treePartitions().clear(); partTableWidget().clear(); partTableWidget().setPartitionTable(selectedDevice()->partitionTable()); QTreeWidgetItem* deviceItem = new QTreeWidgetItem(); QFont font; font.setBold(true); font.setWeight(75); deviceItem->setFont(0, font); deviceItem->setText(0, selectedDevice()->prettyName()); deviceItem->setIcon(0, QIcon::fromTheme(selectedDevice()->iconName()).pixmap(IconSize(KIconLoader::Desktop))); deviceItem->setSizeHint(0, QSize(0, 32)); treePartitions().addTopLevelItem(deviceItem); if (selectedDevice()->partitionTable() != nullptr) { const auto children = selectedDevice()->partitionTable()->children(); for (const auto * p : children) { QTreeWidgetItem* item = createTreeWidgetItem(*p); for (const auto &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() : nullptr); } else partTableWidget().setActiveWidget(nullptr); } void PartitionManagerWidget::on_m_TreePartitions_itemDoubleClicked(QTreeWidgetItem* item, int) { if (item == treePartitions().topLevelItem(0)) { if (selectedDevice() != nullptr) emit deviceDoubleClicked(selectedDevice()); } else { if (selectedPartition() != nullptr) emit partitionDoubleClicked(selectedPartition()); } } void PartitionManagerWidget::onHeaderContextMenu(const QPoint& p) { showColumnsContextMenu(p, treePartitions()); } void PartitionManagerWidget::on_m_PartTableWidget_itemSelectionChanged(PartWidget* item) { if (item == nullptr) { treePartitions().setCurrentItem(nullptr); emit selectedPartitionChanged(nullptr); return; } const Partition* p = item->partition(); Q_ASSERT(p); if (p) { QList findResult = treePartitions().findItems(p->deviceNode(), Qt::MatchFixedString | Qt::MatchRecursive, 0); for (const auto &treeWidgetItem : findResult) { const PartitionTreeWidgetItem* ptwItem = dynamic_cast(treeWidgetItem); if (ptwItem && ptwItem->partition() == p) { treePartitions().setCurrentItem(treeWidgetItem); break; } } } emit selectedPartitionChanged(p); } void PartitionManagerWidget::on_m_PartTableWidget_customContextMenuRequested(const QPoint& pos) { emit contextMenuRequested(partTableWidget().mapToGlobal(pos)); } void PartitionManagerWidget::on_m_PartTableWidget_itemDoubleClicked() { if (selectedPartition()) emit partitionDoubleClicked(selectedPartition()); } void PartitionManagerWidget::on_m_TreePartitions_customContextMenuRequested(const QPoint& pos) { emit contextMenuRequested(treePartitions().viewport()->mapToGlobal(pos)); } void PartitionManagerWidget::onPropertiesPartition() { if (selectedPartition()) { Partition& p = *selectedPartition(); Q_ASSERT(selectedDevice()); QPointer dlg = new PartPropsDialog(this, *selectedDevice(), p); if (dlg->exec() == QDialog::Accepted) { if (dlg->newFileSystemType() != p.fileSystem().type() || dlg->forceRecreate()) operationStack().push(new CreateFileSystemOperation(*selectedDevice(), p, dlg->newFileSystemType())); if (dlg->newLabel() != p.fileSystem().label()) operationStack().push(new SetFileSystemLabelOperation(p, dlg->newLabel())); if (dlg->newFlags() != p.activeFlags()) operationStack().push(new SetPartFlagsOperation(*selectedDevice(), p, dlg->newFlags())); } delete dlg; } } void PartitionManagerWidget::onMountPartition() { Partition* p = selectedPartition(); Q_ASSERT(p); if (p == nullptr) { qWarning() << "no partition selected"; return; } Report report(nullptr); if (p->canMount()) { if (!p->mount(report)) KMessageBox::detailedSorry(this, xi18nc("@info", "The file system on partition %1 could not be mounted.", p->deviceNode()), QStringLiteral("
%1
").arg(report.toText()), xi18nc("@title:window", "Could Not Mount File System.")); } else if (p->canUnmount()) { if (!p->unmount(report)) KMessageBox::detailedSorry(this, xi18nc("@info", "The file system on partition %1 could not be unmounted.", p->deviceNode()), QStringLiteral("
%1
").arg(report.toText()), xi18nc("@title:window", "Could Not Unmount File System.")); } if (p->roles().has(PartitionRole::Logical)) { Partition* parent = dynamic_cast(p->parent()); Q_ASSERT(parent); if (parent != nullptr) parent->checkChildrenMounted(); else qWarning() << "parent is null"; } updatePartitions(); } void PartitionManagerWidget::onDecryptPartition() { Partition* p = selectedPartition(); Q_ASSERT(p); if (p == nullptr) { qWarning() << "no partition selected"; return; } if (!p->roles().has(PartitionRole::Luks)) return; const FileSystem& fsRef = p->fileSystem(); FS::luks* luksFs = const_cast(dynamic_cast(&fsRef)); if (!luksFs) return; if (luksFs->canCryptOpen(p->partitionPath())) { if (!luksFs->cryptOpen(this, p->partitionPath())) KMessageBox::detailedSorry(this, xi18nc("@info", "The encrypted file system on partition " "%1 could not be " "unlocked.", p->deviceNode()), QString(), xi18nc("@title:window", "Could Not Unlock Encrypted File System.")); } else if (luksFs->canCryptClose(p->partitionPath())) { if (!luksFs->cryptClose(p->partitionPath())) KMessageBox::detailedSorry(this, xi18nc("@info", "The encrypted file system on partition " "%1 could not be " "locked.", p->deviceNode()), QString(), xi18nc("@title:window", "Could Not Lock Encrypted File System.")); } updatePartitions(); } void PartitionManagerWidget::onEditMountPoint() { Partition* p = selectedPartition(); Q_ASSERT(p); if (p == nullptr) return; QPointer dlg = new EditMountPointDialog(this, *p); if (dlg->exec() == QDialog::Accepted) updatePartitions(); delete dlg; } 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, xi18ncp("@info", "There is already one primary partition on this device. This is the maximum number its partition table type 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 type can handle." "You cannot create, paste or restore a primary partition on it before you delete an existing one.", d.partitionTable()->numPrimaries()), xi18nc("@title:window", "Too Many Primary Partitions.")); return true; } return false; } void PartitionManagerWidget::onNewPartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } Q_ASSERT(selectedDevice()->partitionTable()); if (selectedDevice()->partitionTable() == nullptr) { qWarning() << "partition table on selected device is null"; return; } if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) return; Partition* newPartition = NewOperation::createNew(*selectedPartition(), static_cast(Config::defaultFileSystem())); QPointer dlg = new NewDialog(this, *selectedDevice(), *newPartition, selectedDevice()->partitionTable()->childRoles(*selectedPartition())); if (dlg->exec() == QDialog::Accepted) operationStack().push(new NewOperation(*selectedDevice(), newPartition)); else delete newPartition; delete dlg; } void PartitionManagerWidget::onDeletePartition(bool shred) { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } if (selectedPartition()->roles().has(PartitionRole::Logical)) { Q_ASSERT(selectedPartition()->parent()); if (selectedPartition()->parent() == nullptr) { qWarning() << "parent of selected partition is null."; return; } if (selectedPartition()->number() > 0 && selectedPartition()->parent()->highestMountedChild() > selectedPartition()->number()) { KMessageBox::sorry(this, xi18nc("@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()), xi18nc("@title:window", "Cannot Delete Partition.")); return; } } if (clipboardPartition() == selectedPartition()) { if (KMessageBox::warningContinueCancel(this, xi18nc("@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."), xi18nc("@title:window", "Really Delete Partition in the Clipboard?"), KGuiItem(xi18nc("@action:button", "Delete It"), QStringLiteral("arrow-right")), KStandardGuiItem::cancel(), QStringLiteral("reallyDeleteClipboardPartition")) == KMessageBox::Cancel) return; setClipboardPartition(nullptr); } if (shred && Config::shredSource() == Config::EnumShredSource::random) operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::RandomShred)); else if (shred && Config::shredSource() == Config::EnumShredSource::zeros) operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::ZeroShred)); else operationStack().push(new DeleteOperation(*selectedDevice(), selectedPartition(), DeleteOperation::NoShred)); } void PartitionManagerWidget::onShredPartition() { onDeletePartition(true); } void PartitionManagerWidget::onResizePartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } Q_ASSERT(selectedDevice()->partitionTable()); if (selectedDevice()->partitionTable() == nullptr) { qWarning() << "partition table on selected device is null"; return; } // we cannot work with selectedPartition() here because opening and closing the dialog will // clear the selection, so we'll lose the partition after the dialog's been exec'd Partition& p = *selectedPartition(); qint64 freeBefore = selectedDevice()->partitionTable()->freeSectorsBefore(p); qint64 freeAfter = selectedDevice()->partitionTable()->freeSectorsAfter(p); if (selectedDevice()->type() == Device::LVM_Device) { freeBefore = 0; freeAfter = selectedDevice()->partitionTable()->freeSectors(); } QPointer dlg = new ResizeDialog(this, *selectedDevice(), p, p.firstSector() - freeBefore, p.lastSector() + freeAfter); if (dlg->exec() == QDialog::Accepted) { if (dlg->resizedFirstSector() == p.firstSector() && dlg->resizedLastSector() == p.lastSector()) Log(Log::information) << xi18nc("@info:status", "Partition %1 has the same position and size after resize/move. Ignoring operation.", p.deviceNode()); else operationStack().push(new ResizeOperation(*selectedDevice(), p, dlg->resizedFirstSector(), dlg->resizedLastSector())); } if (p.roles().has(PartitionRole::Extended)) { // Even if the user dismissed the resize dialog we must update the partitions // if it's an extended partition: // The dialog has to remove and create unallocated children if the user resizes // an extended partition. We can't know if that has happened, so to avoid // any problems (like, the user resized an extended and then canceled, which would // lead to the unallocated children having the wrong size) do this now. updatePartitions(); } delete dlg; } void PartitionManagerWidget::onCopyPartition() { Q_ASSERT(selectedPartition()); if (selectedPartition() == nullptr) { qWarning() << "selected partition: " << selectedPartition(); return; } setClipboardPartition(selectedPartition()); Log() << xi18nc("@info:status", "Partition %1 has been copied to the clipboard.", selectedPartition()->deviceNode()); } void PartitionManagerWidget::onPastePartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } if (clipboardPartition() == nullptr) { qWarning() << "no partition in the clipboard."; return; } if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) return; Device* dSource = operationStack().findDeviceForPartition(clipboardPartition()); Q_ASSERT(dSource); if (dSource == nullptr) { qWarning() << "source partition is null."; return; } Partition* copiedPartition = CopyOperation::createCopy(*selectedPartition(), *clipboardPartition()); if (showInsertDialog(*copiedPartition, clipboardPartition()->length())) operationStack().push(new CopyOperation(*selectedDevice(), copiedPartition, *dSource, clipboardPartition())); else delete copiedPartition; } bool PartitionManagerWidget::showInsertDialog(Partition& insertedPartition, qint64 sourceLength) { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "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 PartitionTable::alignPartition() work correctly. selectedPartition()->parent()->reparent(insertedPartition); if (!overwrite) { QPointer dlg = new InsertDialog(this, *selectedDevice(), insertedPartition, *selectedPartition()); int result = dlg->exec(); delete dlg; if (result != QDialog::Accepted) return false; } else if (KMessageBox::warningContinueCancel(this, xi18nc("@info", "You are about to lose all data on partition " "%1." "Overwriting one partition with another (or with an image file) will " "destroy all data on this target partition." "If you continue now and apply the resulting operation in the main " "window, all data currently stored on %1 will " "unrecoverably be overwritten.", selectedPartition()->deviceNode()), xi18nc("@title:window", "Really Overwrite Existing Partition?"), KGuiItem(xi18nc("@action:button", "Overwrite Partition"), QStringLiteral("arrow-right")), KStandardGuiItem::cancel(), QStringLiteral("reallyOverwriteExistingPartition")) == KMessageBox::Cancel) return false; if (insertedPartition.length() < sourceLength) { if (overwrite) KMessageBox::error(this, xi18nc("@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."), xi18nc("@title:window", "Target Not Large Enough")); else KMessageBox::sorry(this, xi18nc("@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 are correctly aligned " "or when copying a primary partition into an extended partition."), xi18nc("@title:window", "Cannot Create Target Partition.")); return false; } return true; } void PartitionManagerWidget::onCheckPartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } operationStack().push(new CheckOperation(*selectedDevice(), *selectedPartition())); } void PartitionManagerWidget::onBackupPartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } QString fileName = QFileDialog::getSaveFileName(this); if (fileName.isEmpty()) return; operationStack().push(new BackupOperation(*selectedDevice(), *selectedPartition(), fileName)); } void PartitionManagerWidget::onRestorePartition() { Q_ASSERT(selectedDevice()); Q_ASSERT(selectedPartition()); if (selectedDevice() == nullptr || selectedPartition() == nullptr) { qWarning() << "selected device: " << selectedDevice() << ", selected partition: " << selectedPartition(); return; } if (checkTooManyPartitions(this, *selectedDevice(), *selectedPartition())) return; QString fileName = QFileDialog::getOpenFileName(this); // 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, xi18nc("@info", "The file system in the image file %1 is too large to be restored to the selected partition.", fileName), xi18nc("@title:window", "Not Enough Space to Restore File System.")); delete restorePartition; return; } if (showInsertDialog(*restorePartition, restorePartition->length())) operationStack().push(new RestoreOperation(*selectedDevice(), restorePartition, fileName)); else delete restorePartition; } }