/* SPDX-FileCopyrightText: 2008-2010 Volker Lanz SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas SPDX-FileCopyrightText: 2014 Yuri Chornoivan SPDX-FileCopyrightText: 2018 Abhijeet Sharma SPDX-License-Identifier: GPL-3.0-or-later */ #include "gui/partpropsdialog.h" #include "gui/partpropswidget.h" #include #include #include #include #include #include "util/guihelpers.h" #include #include #include #include #include #include #include #include #include #include #include #include /** 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: %1", partition().deviceNode())); setupDialog(); setupConnections(); KConfigGroup kcg(KSharedConfig::openConfig(), "partPropsDialog"); restoreGeometry(kcg.readEntry("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(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 %1", 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(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(&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", "You are about to lose all data on partition %1." "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 %1 will unrecoverably be lost.", partition().deviceNode()), xi18nc("@title:window", "Really Recreate %1 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(&QComboBox::currentIndexChanged), this, &PartPropsDialog::onFilesystemChanged); } } void PartPropsDialog::onRecreate(int state) { if (state == Qt::Checked && (warnFileSystemChange() || KMessageBox::warningContinueCancel(this, xi18nc("@info", "You are about to lose all data on partition %1." "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 %1 will unrecoverably be lost.", partition().deviceNode()), xi18nc("@title:window", "Really Recreate File System on %1?", 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(); } }