partitionmanager/src/gui/partpropsdialog.cpp

388 lines
18 KiB
C++

/*
SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de>
SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2014 Yuri Chornoivan <yurchor@ukr.net>
SPDX-FileCopyrightText: 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "gui/partpropsdialog.h"
#include "gui/partpropswidget.h"
#include <core/partition.h>
#include <core/device.h>
#include <fs/filesystemfactory.h>
#include <util/capacity.h>
#include <util/helpers.h>
#include "util/guihelpers.h"
#include <utility>
#include <QComboBox>
#include <QFontDatabase>
#include <QtGlobal>
#include <QLineEdit>
#include <QLocale>
#include <QPalette>
#include <QPushButton>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
/** Creates a new PartPropsDialog
@param parent pointer to the parent widget
@param d the Device the Partition is on
@param p the Partition to show properties for
*/
PartPropsDialog::PartPropsDialog(QWidget* parent, Device& d, Partition& p) :
QDialog(parent),
m_Device(d),
m_Partition(p),
m_WarnFileSystemChange(false),
m_DialogWidget(new PartPropsWidget(this)),
m_ReadOnly(partition().isMounted() || partition().state() == Partition::State::Copy || partition().state() == Partition::State::Restore || d.partitionTable()->isReadOnly()),
m_ForceRecreate(false)
{
mainLayout = new QVBoxLayout(this);
setLayout(mainLayout);
mainLayout->addWidget(&dialogWidget());
setWindowTitle(xi18nc("@title:window", "Partition properties: <filename>%1</filename>", partition().deviceNode()));
setupDialog();
setupConnections();
KConfigGroup kcg(KSharedConfig::openConfig(), "partPropsDialog");
restoreGeometry(kcg.readEntry<QByteArray>("Geometry", QByteArray()));
}
/** Destroys a PartPropsDialog */
PartPropsDialog::~PartPropsDialog()
{
KConfigGroup kcg(KSharedConfig::openConfig(), "partPropsDialog");
kcg.writeEntry("Geometry", saveGeometry());
}
/** @return the new label */
QString PartPropsDialog::newLabel() const
{
return dialogWidget().label().text();
}
/** @return the new Partition flags */
PartitionTable::Flags PartPropsDialog::newFlags() const
{
PartitionTable::Flags flags;
for (int i = 0; i < dialogWidget().listFlags().count(); i++)
if (dialogWidget().listFlags().item(i)->checkState() == Qt::Checked)
flags |= static_cast<PartitionTable::Flag>(dialogWidget().listFlags().item(i)->data(Qt::UserRole).toInt());
return flags;
}
/** @return the new FileSystem type */
FileSystem::Type PartPropsDialog::newFileSystemType() const
{
return FileSystem::typeForName(dialogWidget().fileSystem().currentText());
}
void PartPropsDialog::setupDialog()
{
dialogButtonBox = new QDialogButtonBox;
okButton = dialogButtonBox->addButton(QDialogButtonBox::Ok);
cancelButton = dialogButtonBox->addButton(QDialogButtonBox::Cancel);
mainLayout->addWidget(dialogButtonBox);
okButton->setEnabled(false);
cancelButton->setFocus();
cancelButton->setDefault(true);
connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &PartPropsDialog::accept);
connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &PartPropsDialog::reject);
dialogWidget().partWidget().init(&partition());
const QString mp = partition().mountPoint().isEmpty()
? xi18nc("@item mountpoint", "(none found)")
: partition().mountPoint();
dialogWidget().mountPoint().setText(mp);
dialogWidget().role().setText(partition().roles().toString());
QString statusText = xi18nc("@label partition state", "idle");
if (partition().isMounted()) {
if (partition().roles().has(PartitionRole::Extended))
statusText = xi18nc("@label partition state", "At least one logical partition is mounted.");
else if (!partition().mountPoint().isEmpty())
statusText = xi18nc("@label partition state", "mounted on <filename>%1</filename>", mp);
else
statusText = xi18nc("@label partition state", "mounted");
}
dialogWidget().status().setText(statusText);
dialogWidget().uuid().setText(partition().fileSystem().uuid().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().fileSystem().uuid());
if(device().partitionTable()->type() == PartitionTable::gpt){
QString PartitionLabel = partition().label().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().label();
QString PartitionUUID = partition().uuid().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().uuid();
dialogWidget().partitionLabel().setText(PartitionLabel);
dialogWidget().partitionUuid().setText(PartitionUUID);
}
else{
dialogWidget().partitionLabel().hide();
dialogWidget().partitionTextLabel().hide();
dialogWidget().partitionUuid().hide();
dialogWidget().partitionTextUuid().hide();
}
setupFileSystemComboBox();
// don't do this before the file system combo box has been set up!
dialogWidget().label().setText(newLabel().isEmpty() ? partition().fileSystem().label() : newLabel());
dialogWidget().capacity().setText(Capacity::formatByteSize(partition().capacity()));
if (Capacity(partition(), Capacity::Type::Available).isValid()) {
const qint64 availPercent = (partition().fileSystem().length() - partition().fileSystem().sectorsUsed()) * 100 / partition().fileSystem().length();
const QString availString = QStringLiteral("%1% - %2")
.arg(availPercent)
.arg(Capacity::formatByteSize(partition().available()));
const QString usedString = QStringLiteral("%1% - %2")
.arg(100 - availPercent)
.arg(Capacity::formatByteSize(partition().used()));
dialogWidget().available().setText(availString);
dialogWidget().used().setText(usedString);
} else {
dialogWidget().available().setText(Capacity::invalidString());
dialogWidget().used().setText(Capacity::invalidString());
}
dialogWidget().firstSector().setText(QLocale().toString(partition().firstSector()));
dialogWidget().lastSector().setText(QLocale().toString(partition().lastSector()));
dialogWidget().numSectors().setText(QLocale().toString(partition().length()));
setupFlagsList();
updateHideAndShow();
setMinimumSize(dialogWidget().size());
resize(dialogWidget().size());
}
void PartPropsDialog::setupFlagsList()
{
int f = 1;
QString s;
while (!(s = PartitionTable::flagName(static_cast<PartitionTable::Flag>(f))).isEmpty()) {
if (partition().availableFlags() & f) {
QListWidgetItem* item = new QListWidgetItem(s);
dialogWidget().listFlags().addItem(item);
item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setData(Qt::UserRole, f);
item->setCheckState((partition().activeFlags() & f) ? Qt::Checked : Qt::Unchecked);
}
f <<= 1;
}
}
void PartPropsDialog::updateHideAndShow()
{
// create a temporary fs for some checks
const FileSystem* fs = FileSystemFactory::create(newFileSystemType(), -1, -1, -1, -1, QString());
if (fs == nullptr || fs->supportSetLabel() == FileSystem::cmdSupportNone) {
dialogWidget().label().setReadOnly(true);
dialogWidget().noSetLabel().setVisible(true);
dialogWidget().noSetLabel().setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
QPalette palette = dialogWidget().noSetLabel().palette();
QColor f = palette.color(QPalette::WindowText);
f.setAlpha(128);
palette.setColor(QPalette::WindowText, f);
dialogWidget().noSetLabel().setPalette(palette);
} else {
dialogWidget().label().setReadOnly(isReadOnly() && !partition().fileSystem().supportSetLabelOnline());
dialogWidget().noSetLabel().setVisible(false);
}
// when do we show the uuid?
const bool showUuid =
partition().state() != Partition::State::New && // not for new partitions
!(fs == nullptr || fs->supportGetUUID() == FileSystem::cmdSupportNone); // not if the FS doesn't support it
dialogWidget().showUuid(showUuid);
delete fs;
// when do we show available and used capacity?
const bool showAvailableAndUsed =
partition().state() != Partition::State::New && // not for new partitions
!partition().roles().has(PartitionRole::Extended) && // neither for extended
!partition().roles().has(PartitionRole::Unallocated) && // or for unallocated
newFileSystemType() != FileSystem::Type::Unformatted; // and not for unformatted file systems
dialogWidget().showAvailable(showAvailableAndUsed);
dialogWidget().showUsed(showAvailableAndUsed);
// when do we show the file system combo box?
const bool showFileSystem =
!partition().roles().has(PartitionRole::Extended) && // not for extended, they have no file system
!partition().roles().has(PartitionRole::Unallocated) && // and not for unallocated: no choice there
// do not show file system comboBox for open luks volumes.
!(partition().roles().has(PartitionRole::Luks) && partition().fileSystem().type() != FileSystem::Type::Luks);
dialogWidget().showFileSystem(showFileSystem);
// when do we show the recreate file system check box?
const bool showCheckRecreate =
showFileSystem && // only if we also show the file system
partition().fileSystem().supportCreate() != FileSystem::cmdSupportNone && // and support creating this file system
partition().fileSystem().type() != FileSystem::Type::Unknown && // and not for unknown file systems
partition().state() != Partition::State::New && // or new partitions
!partition().roles().has(PartitionRole::Luks); // or encrypted filesystems
dialogWidget().showCheckRecreate(showCheckRecreate);
// when do we show the list of partition flags?
const bool showListFlags =
partition().state() != Partition::State::New && // not for new partitions
!partition().roles().has(PartitionRole::Unallocated); // and not for unallocated space
dialogWidget().showListFlags(showListFlags);
dialogWidget().checkRecreate().setEnabled(!isReadOnly());
dialogWidget().listFlags().setEnabled(!isReadOnly());
dialogWidget().fileSystem().setEnabled(!isReadOnly() && !forceRecreate());
}
void PartPropsDialog::setupConnections()
{
connect(&dialogWidget().label(), &QLineEdit::textEdited, [this] (const QString &) {setDirty();});
connect(&dialogWidget().fileSystem(), qOverload<int>(&QComboBox::currentIndexChanged), this, &PartPropsDialog::onFilesystemChanged);
connect(&dialogWidget().checkRecreate(), &QCheckBox::stateChanged, this, &PartPropsDialog::onRecreate);
// We want to enable the OK-button whenever the user checks or unchecks a flag in the flag list.
// But it seems Qt doesn't offer a foolproof way to detect if this has happened: The currentRow/ItemChanged
// signal only means the _current_ item has changed, but not necessarily that it was checked/unchecked. And
// itemClicked alone isn't enough either. We choose to rather enable the OK-button too often than too
// seldom.
connect(&dialogWidget().listFlags(), &QListWidget::itemClicked, this, &PartPropsDialog::setDirty);
connect(&dialogWidget().listFlags(), &QListWidget::currentRowChanged, [this] (int) {setDirty();});
}
void PartPropsDialog::setDirty(void*)
{
okButton->setEnabled(true);
okButton->setDefault(true);
}
void PartPropsDialog::setupFileSystemComboBox()
{
dialogWidget().fileSystem().clear();
QString selected;
QStringList fsNames;
for(const auto &fs : FileSystemFactory::map())
{
// If the partition isn't encrypted, skip the luks FS
if (fs->type() == FileSystem::Type::Luks && partition().fileSystem().type() != FileSystem::Type::Luks)
continue;
if (fs->type() == FileSystem::Type::Luks2 && partition().fileSystem().type() != FileSystem::Type::Luks2)
continue;
if (partition().fileSystem().type() == fs->type() || (fs->supportCreate() != FileSystem::cmdSupportNone &&
partition().capacity() >= fs->minCapacity() && partition().capacity() <= fs->maxCapacity())) {
QString name = fs->name();
if (partition().fileSystem().type() == fs->type())
selected = name;
// If the partition isn't extended, skip the extended FS
if (fs->type() == FileSystem::Type::Extended && !partition().roles().has(PartitionRole::Extended))
continue;
// The user cannot change the filesystem back to "unformatted" once a filesystem has been created.
if (fs->type() == FileSystem::Type::Unformatted) {
// .. but if the file system is unknown to us, show the unformatted option as the currently selected one
if (partition().fileSystem().type() == FileSystem::Type::Unknown) {
name = FileSystem::nameForType(FileSystem::Type::Unformatted);
selected = name;
} else if (partition().fileSystem().type() != FileSystem::Type::Unformatted && partition().state() != Partition::State::New)
continue;
}
fsNames.append(name);
}
}
std::sort(fsNames.begin(), fsNames.end(), caseInsensitiveLessThan);
for (const auto &fsName : std::as_const(fsNames))
dialogWidget().fileSystem().addItem(createFileSystemColor(FileSystem::typeForName(fsName), 8), fsName);
dialogWidget().fileSystem().setCurrentIndex(dialogWidget().fileSystem().findText(selected));
const FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(dialogWidget().fileSystem().currentText()), -1, -1, -1, -1, QString());
dialogWidget().m_EditLabel->setMaxLength(fs->maxLabelLength());
dialogWidget().m_EditLabel->setValidator(fs->labelValidator(dialogWidget().m_EditLabel));
}
void PartPropsDialog::updatePartitionFileSystem()
{
FileSystem* fs = FileSystemFactory::create(newFileSystemType(), partition().firstSector(), partition().lastSector(), partition().sectorSize());
partition().deleteFileSystem();
partition().setFileSystem(fs);
dialogWidget().partWidget().update();
}
void PartPropsDialog::onFilesystemChanged(int)
{
if (partition().state() == Partition::State::New || warnFileSystemChange() || KMessageBox::warningContinueCancel(this,
xi18nc("@info", "<para><warning>You are about to lose all data on partition <filename>%1</filename>.</warning></para>"
"<para>Changing the file system on a partition already on disk will erase all its contents. If you continue now and apply the resulting operation in the main window, all data on <filename>%1</filename> will unrecoverably be lost.</para>", partition().deviceNode()),
xi18nc("@title:window", "Really Recreate <filename>%1</filename> with File System %2?", partition().deviceNode(), dialogWidget().fileSystem().currentText()),
KGuiItem(xi18nc("@action:button", "Change the File System"), QStringLiteral("arrow-right")),
KGuiItem(xi18nc("@action:button", "Do Not Change the File System"), QStringLiteral("dialog-cancel")), QStringLiteral("reallyChangeFileSystem")) == KMessageBox::Continue) {
setDirty();
updateHideAndShow();
setWarnFileSystemChange();
updatePartitionFileSystem();
const FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(dialogWidget().fileSystem().currentText()), -1, -1, -1, -1, QString());
dialogWidget().m_EditLabel->setMaxLength(fs->maxLabelLength());
dialogWidget().m_EditLabel->setValidator(fs->labelValidator(dialogWidget().m_EditLabel));
} else {
dialogWidget().fileSystem().disconnect(this);
setupFileSystemComboBox();
connect(&dialogWidget().fileSystem(), qOverload<int>(&QComboBox::currentIndexChanged), this, &PartPropsDialog::onFilesystemChanged);
}
}
void PartPropsDialog::onRecreate(int state)
{
if (state == Qt::Checked && (warnFileSystemChange() || KMessageBox::warningContinueCancel(this,
xi18nc("@info", "<para><warning>You are about to lose all data on partition <filename>%1</filename>.</warning></para>"
"<para>Recreating a file system will erase all its contents. If you continue now and apply the resulting operation in the main window, all data on <filesystem>%1</filesystem> will unrecoverably be lost.</para>", partition().deviceNode()),
xi18nc("@title:window", "Really Recreate File System on <filename>%1</filename>?", partition().deviceNode()),
KGuiItem(xi18nc("@action:button", "Recreate the File System"), QStringLiteral("arrow-right")),
KGuiItem(xi18nc("@action:button", "Do Not Recreate the File System"), QStringLiteral("dialog-cancel")), QStringLiteral("reallyRecreateFileSystem")) == KMessageBox::Continue)) {
setDirty();
setWarnFileSystemChange();
setForceRecreate(true);
dialogWidget().fileSystem().setCurrentIndex(dialogWidget().fileSystem().findText(partition().fileSystem().name()));
dialogWidget().fileSystem().setEnabled(false);
updateHideAndShow();
updatePartitionFileSystem();
} else {
setForceRecreate(false);
dialogWidget().checkRecreate().setCheckState(Qt::Unchecked);
dialogWidget().fileSystem().setEnabled(true);
updateHideAndShow();
}
}