File indexing completed on 2024-04-28 05:46:35
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Volker Lanz <vl@fidra.de> 0003 SPDX-FileCopyrightText: 2014-2020 Andrius Štikonas <andrius@stikonas.eu> 0004 SPDX-FileCopyrightText: 2014 Yuri Chornoivan <yurchor@ukr.net> 0005 SPDX-FileCopyrightText: 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-3.0-or-later 0008 */ 0009 0010 #include "gui/partpropsdialog.h" 0011 #include "gui/partpropswidget.h" 0012 0013 #include <core/partition.h> 0014 #include <core/device.h> 0015 0016 #include <fs/filesystemfactory.h> 0017 0018 #include <util/capacity.h> 0019 #include <util/helpers.h> 0020 #include "util/guihelpers.h" 0021 0022 #include <utility> 0023 0024 #include <QComboBox> 0025 #include <QFontDatabase> 0026 #include <QtGlobal> 0027 #include <QLineEdit> 0028 #include <QLocale> 0029 #include <QPalette> 0030 #include <QPushButton> 0031 0032 #include <KConfigGroup> 0033 #include <KLocalizedString> 0034 #include <KMessageBox> 0035 #include <KSharedConfig> 0036 0037 /** Creates a new PartPropsDialog 0038 @param parent pointer to the parent widget 0039 @param d the Device the Partition is on 0040 @param p the Partition to show properties for 0041 */ 0042 PartPropsDialog::PartPropsDialog(QWidget* parent, Device& d, Partition& p) : 0043 QDialog(parent), 0044 m_Device(d), 0045 m_Partition(p), 0046 m_WarnFileSystemChange(false), 0047 m_DialogWidget(new PartPropsWidget(this)), 0048 m_ReadOnly(partition().isMounted() || partition().state() == Partition::State::Copy || partition().state() == Partition::State::Restore || d.partitionTable()->isReadOnly()), 0049 m_ForceRecreate(false) 0050 { 0051 mainLayout = new QVBoxLayout(this); 0052 setLayout(mainLayout); 0053 mainLayout->addWidget(&dialogWidget()); 0054 0055 setWindowTitle(xi18nc("@title:window", "Partition properties: <filename>%1</filename>", partition().deviceNode())); 0056 0057 setupDialog(); 0058 setupConnections(); 0059 0060 KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("partPropsDialog")); 0061 restoreGeometry(kcg.readEntry<QByteArray>("Geometry", QByteArray())); 0062 } 0063 0064 /** Destroys a PartPropsDialog */ 0065 PartPropsDialog::~PartPropsDialog() 0066 { 0067 KConfigGroup kcg(KSharedConfig::openConfig(), QStringLiteral("partPropsDialog")); 0068 kcg.writeEntry("Geometry", saveGeometry()); 0069 } 0070 0071 /** @return the new label */ 0072 QString PartPropsDialog::newLabel() const 0073 { 0074 return dialogWidget().label().text(); 0075 } 0076 0077 /** @return the new Partition flags */ 0078 PartitionTable::Flags PartPropsDialog::newFlags() const 0079 { 0080 PartitionTable::Flags flags; 0081 0082 for (int i = 0; i < dialogWidget().listFlags().count(); i++) 0083 if (dialogWidget().listFlags().item(i)->checkState() == Qt::Checked) 0084 flags |= static_cast<PartitionTable::Flag>(dialogWidget().listFlags().item(i)->data(Qt::UserRole).toInt()); 0085 0086 return flags; 0087 } 0088 0089 /** @return the new FileSystem type */ 0090 FileSystem::Type PartPropsDialog::newFileSystemType() const 0091 { 0092 return FileSystem::typeForName(dialogWidget().fileSystem().currentText()); 0093 } 0094 0095 void PartPropsDialog::setupDialog() 0096 { 0097 dialogButtonBox = new QDialogButtonBox; 0098 okButton = dialogButtonBox->addButton(QDialogButtonBox::Ok); 0099 cancelButton = dialogButtonBox->addButton(QDialogButtonBox::Cancel); 0100 mainLayout->addWidget(dialogButtonBox); 0101 okButton->setEnabled(false); 0102 cancelButton->setFocus(); 0103 cancelButton->setDefault(true); 0104 connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &PartPropsDialog::accept); 0105 connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &PartPropsDialog::reject); 0106 0107 dialogWidget().partWidget().init(&partition()); 0108 0109 const QString mp = partition().mountPoint().isEmpty() 0110 ? xi18nc("@item mountpoint", "(none found)") 0111 : partition().mountPoint(); 0112 dialogWidget().mountPoint().setText(mp); 0113 0114 const QString devPath = partition().deviceNode().isEmpty() || partition().deviceNode() == QStringLiteral("unallocated") 0115 ? xi18nc("@item device path", "(not found)") 0116 : partition().deviceNode(); 0117 dialogWidget().devicePath().setText(devPath); 0118 0119 dialogWidget().role().setText(partition().roles().toString()); 0120 0121 QString statusText = xi18nc("@label partition state", "idle"); 0122 if (partition().isMounted()) { 0123 if (partition().roles().has(PartitionRole::Extended)) 0124 statusText = xi18nc("@label partition state", "At least one logical partition is mounted."); 0125 else if (!partition().mountPoint().isEmpty()) 0126 statusText = xi18nc("@label partition state", "mounted on <filename>%1</filename>", mp); 0127 else 0128 statusText = xi18nc("@label partition state", "mounted"); 0129 } 0130 0131 dialogWidget().status().setText(statusText); 0132 dialogWidget().uuid().setText(partition().fileSystem().uuid().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().fileSystem().uuid()); 0133 0134 if(device().partitionTable()->type() == PartitionTable::gpt){ 0135 QString PartitionLabel = partition().label().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().label(); 0136 QString PartitionUUID = partition().uuid().isEmpty() ? xi18nc("@item uuid", "(none)") : partition().uuid(); 0137 0138 dialogWidget().partitionLabel().setText(PartitionLabel); 0139 dialogWidget().partitionUuid().setText(PartitionUUID); 0140 } 0141 else{ 0142 dialogWidget().partitionLabel().hide(); 0143 dialogWidget().partitionTextLabel().hide(); 0144 dialogWidget().partitionUuid().hide(); 0145 dialogWidget().partitionTextUuid().hide(); 0146 } 0147 0148 setupFileSystemComboBox(); 0149 0150 // don't do this before the file system combo box has been set up! 0151 dialogWidget().label().setText(newLabel().isEmpty() ? partition().fileSystem().label() : newLabel()); 0152 dialogWidget().capacity().setText(Capacity::formatByteSize(partition().capacity())); 0153 0154 if (Capacity(partition(), Capacity::Type::Available).isValid()) { 0155 const qint64 availPercent = (partition().fileSystem().length() - partition().fileSystem().sectorsUsed()) * 100 / partition().fileSystem().length(); 0156 0157 const QString availString = QStringLiteral("%1% - %2") 0158 .arg(availPercent) 0159 .arg(Capacity::formatByteSize(partition().available())); 0160 const QString usedString = QStringLiteral("%1% - %2") 0161 .arg(100 - availPercent) 0162 .arg(Capacity::formatByteSize(partition().used())); 0163 0164 dialogWidget().available().setText(availString); 0165 dialogWidget().used().setText(usedString); 0166 } else { 0167 dialogWidget().available().setText(Capacity::invalidString()); 0168 dialogWidget().used().setText(Capacity::invalidString()); 0169 } 0170 0171 dialogWidget().firstSector().setText(QLocale().toString(partition().firstSector())); 0172 dialogWidget().lastSector().setText(QLocale().toString(partition().lastSector())); 0173 dialogWidget().numSectors().setText(QLocale().toString(partition().length())); 0174 0175 setupFlagsList(); 0176 0177 updateHideAndShow(); 0178 0179 setMinimumSize(dialogWidget().size()); 0180 resize(dialogWidget().size()); 0181 } 0182 0183 void PartPropsDialog::setupFlagsList() 0184 { 0185 int f = 1; 0186 QString s; 0187 while (!(s = PartitionTable::flagName(static_cast<PartitionTable::Flag>(f))).isEmpty()) { 0188 if (partition().availableFlags() & f) { 0189 QListWidgetItem* item = new QListWidgetItem(s); 0190 dialogWidget().listFlags().addItem(item); 0191 item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); 0192 item->setData(Qt::UserRole, f); 0193 item->setCheckState((partition().activeFlags() & f) ? Qt::Checked : Qt::Unchecked); 0194 } 0195 0196 f <<= 1; 0197 } 0198 } 0199 0200 void PartPropsDialog::updateHideAndShow() 0201 { 0202 // create a temporary fs for some checks 0203 const FileSystem* fs = FileSystemFactory::create(newFileSystemType(), -1, -1, -1, -1, QString()); 0204 0205 if (fs == nullptr || fs->supportSetLabel() == FileSystem::cmdSupportNone) { 0206 dialogWidget().label().setReadOnly(true); 0207 dialogWidget().noSetLabel().setVisible(true); 0208 dialogWidget().noSetLabel().setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); 0209 0210 QPalette palette = dialogWidget().noSetLabel().palette(); 0211 QColor f = palette.color(QPalette::WindowText); 0212 f.setAlpha(128); 0213 palette.setColor(QPalette::WindowText, f); 0214 dialogWidget().noSetLabel().setPalette(palette); 0215 } else { 0216 dialogWidget().label().setReadOnly(isReadOnly() && !partition().fileSystem().supportSetLabelOnline()); 0217 dialogWidget().noSetLabel().setVisible(false); 0218 } 0219 0220 // when do we show the uuid? 0221 const bool showUuid = 0222 partition().state() != Partition::State::New && // not for new partitions 0223 !(fs == nullptr || fs->supportGetUUID() == FileSystem::cmdSupportNone); // not if the FS doesn't support it 0224 0225 dialogWidget().showUuid(showUuid); 0226 0227 delete fs; 0228 0229 // when do we show available and used capacity? 0230 const bool showAvailableAndUsed = 0231 partition().state() != Partition::State::New && // not for new partitions 0232 !partition().roles().has(PartitionRole::Extended) && // neither for extended 0233 !partition().roles().has(PartitionRole::Unallocated) && // or for unallocated 0234 newFileSystemType() != FileSystem::Type::Unformatted; // and not for unformatted file systems 0235 0236 dialogWidget().showAvailable(showAvailableAndUsed); 0237 dialogWidget().showUsed(showAvailableAndUsed); 0238 0239 // when do we show the file system combo box? 0240 const bool showFileSystem = 0241 !partition().roles().has(PartitionRole::Extended) && // not for extended, they have no file system 0242 !partition().roles().has(PartitionRole::Unallocated) && // and not for unallocated: no choice there 0243 // do not show file system comboBox for open luks volumes. 0244 !(partition().roles().has(PartitionRole::Luks) && partition().fileSystem().type() != FileSystem::Type::Luks); 0245 dialogWidget().showFileSystem(showFileSystem); 0246 0247 // when do we show the recreate file system check box? 0248 const bool showCheckRecreate = 0249 showFileSystem && // only if we also show the file system 0250 partition().fileSystem().supportCreate() != FileSystem::cmdSupportNone && // and support creating this file system 0251 partition().fileSystem().type() != FileSystem::Type::Unknown && // and not for unknown file systems 0252 partition().state() != Partition::State::New && // or new partitions 0253 !partition().roles().has(PartitionRole::Luks); // or encrypted filesystems 0254 0255 dialogWidget().showCheckRecreate(showCheckRecreate); 0256 0257 // when do we show the list of partition flags? 0258 const bool showListFlags = 0259 partition().state() != Partition::State::New && // not for new partitions 0260 !partition().roles().has(PartitionRole::Unallocated); // and not for unallocated space 0261 0262 dialogWidget().showListFlags(showListFlags); 0263 0264 dialogWidget().checkRecreate().setEnabled(!isReadOnly()); 0265 dialogWidget().listFlags().setEnabled(!isReadOnly()); 0266 dialogWidget().fileSystem().setEnabled(!isReadOnly() && !forceRecreate()); 0267 } 0268 0269 void PartPropsDialog::setupConnections() 0270 { 0271 connect(&dialogWidget().label(), &QLineEdit::textEdited, [this] (const QString &) {setDirty();}); 0272 connect(&dialogWidget().fileSystem(), &QComboBox::currentIndexChanged, this, &PartPropsDialog::onFilesystemChanged); 0273 connect(&dialogWidget().checkRecreate(), &QCheckBox::stateChanged, this, &PartPropsDialog::onRecreate); 0274 0275 // We want to enable the OK-button whenever the user checks or unchecks a flag in the flag list. 0276 // But it seems Qt doesn't offer a foolproof way to detect if this has happened: The currentRow/ItemChanged 0277 // signal only means the _current_ item has changed, but not necessarily that it was checked/unchecked. And 0278 // itemClicked alone isn't enough either. We choose to rather enable the OK-button too often than too 0279 // seldom. 0280 connect(&dialogWidget().listFlags(), &QListWidget::itemClicked, this, &PartPropsDialog::setDirty); 0281 connect(&dialogWidget().listFlags(), &QListWidget::currentRowChanged, [this] (int) {setDirty();}); 0282 } 0283 0284 void PartPropsDialog::setDirty(void*) 0285 { 0286 okButton->setEnabled(true); 0287 okButton->setDefault(true); 0288 } 0289 0290 void PartPropsDialog::setupFileSystemComboBox() 0291 { 0292 dialogWidget().fileSystem().clear(); 0293 QString selected; 0294 QStringList fsNames; 0295 0296 for(const auto &fs : FileSystemFactory::map()) 0297 { 0298 // If the partition isn't encrypted, skip the luks FS 0299 if (fs->type() == FileSystem::Type::Luks && partition().fileSystem().type() != FileSystem::Type::Luks) 0300 continue; 0301 if (fs->type() == FileSystem::Type::Luks2 && partition().fileSystem().type() != FileSystem::Type::Luks2) 0302 continue; 0303 if (partition().fileSystem().type() == fs->type() || (fs->supportCreate() != FileSystem::cmdSupportNone && 0304 partition().capacity() >= fs->minCapacity() && partition().capacity() <= fs->maxCapacity())) { 0305 QString name = fs->name(); 0306 0307 if (partition().fileSystem().type() == fs->type()) 0308 selected = name; 0309 0310 // If the partition isn't extended, skip the extended FS 0311 if (fs->type() == FileSystem::Type::Extended && !partition().roles().has(PartitionRole::Extended)) 0312 continue; 0313 0314 // The user cannot change the filesystem back to "unformatted" once a filesystem has been created. 0315 if (fs->type() == FileSystem::Type::Unformatted) { 0316 // .. but if the file system is unknown to us, show the unformatted option as the currently selected one 0317 if (partition().fileSystem().type() == FileSystem::Type::Unknown) { 0318 name = FileSystem::nameForType(FileSystem::Type::Unformatted); 0319 selected = name; 0320 } else if (partition().fileSystem().type() != FileSystem::Type::Unformatted && partition().state() != Partition::State::New) 0321 continue; 0322 } 0323 0324 fsNames.append(name); 0325 } 0326 } 0327 0328 std::sort(fsNames.begin(), fsNames.end(), caseInsensitiveLessThan); 0329 0330 for (const auto &fsName : std::as_const(fsNames)) 0331 dialogWidget().fileSystem().addItem(createFileSystemColor(FileSystem::typeForName(fsName), 8), fsName); 0332 0333 dialogWidget().fileSystem().setCurrentIndex(dialogWidget().fileSystem().findText(selected)); 0334 0335 const FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(dialogWidget().fileSystem().currentText()), -1, -1, -1, -1, QString()); 0336 dialogWidget().m_EditLabel->setMaxLength(fs->maxLabelLength()); 0337 dialogWidget().m_EditLabel->setValidator(fs->labelValidator(dialogWidget().m_EditLabel)); 0338 } 0339 0340 void PartPropsDialog::updatePartitionFileSystem() 0341 { 0342 FileSystem* fs = FileSystemFactory::create(newFileSystemType(), partition().firstSector(), partition().lastSector(), partition().sectorSize()); 0343 partition().deleteFileSystem(); 0344 partition().setFileSystem(fs); 0345 dialogWidget().partWidget().update(); 0346 } 0347 0348 void PartPropsDialog::onFilesystemChanged(int) 0349 { 0350 if (partition().state() == Partition::State::New || warnFileSystemChange() || KMessageBox::warningContinueCancel(this, 0351 xi18nc("@info", "<para><warning>You are about to lose all data on partition <filename>%1</filename>.</warning></para>" 0352 "<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()), 0353 xi18nc("@title:window", "Really Recreate <filename>%1</filename> with File System %2?", partition().deviceNode(), dialogWidget().fileSystem().currentText()), 0354 KGuiItem(xi18nc("@action:button", "Change the File System"), QStringLiteral("arrow-right")), 0355 KGuiItem(xi18nc("@action:button", "Do Not Change the File System"), QStringLiteral("dialog-cancel")), QStringLiteral("reallyChangeFileSystem")) == KMessageBox::Continue) { 0356 setDirty(); 0357 updateHideAndShow(); 0358 setWarnFileSystemChange(); 0359 updatePartitionFileSystem(); 0360 0361 const FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(dialogWidget().fileSystem().currentText()), -1, -1, -1, -1, QString()); 0362 dialogWidget().m_EditLabel->setMaxLength(fs->maxLabelLength()); 0363 dialogWidget().m_EditLabel->setValidator(fs->labelValidator(dialogWidget().m_EditLabel)); 0364 } else { 0365 dialogWidget().fileSystem().disconnect(this); 0366 setupFileSystemComboBox(); 0367 connect(&dialogWidget().fileSystem(), &QComboBox::currentIndexChanged, this, &PartPropsDialog::onFilesystemChanged); 0368 } 0369 } 0370 0371 void PartPropsDialog::onRecreate(int state) 0372 { 0373 if (state == Qt::Checked && (warnFileSystemChange() || KMessageBox::warningContinueCancel(this, 0374 xi18nc("@info", "<para><warning>You are about to lose all data on partition <filename>%1</filename>.</warning></para>" 0375 "<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()), 0376 xi18nc("@title:window", "Really Recreate File System on <filename>%1</filename>?", partition().deviceNode()), 0377 KGuiItem(xi18nc("@action:button", "Recreate the File System"), QStringLiteral("arrow-right")), 0378 KGuiItem(xi18nc("@action:button", "Do Not Recreate the File System"), QStringLiteral("dialog-cancel")), QStringLiteral("reallyRecreateFileSystem")) == KMessageBox::Continue)) { 0379 setDirty(); 0380 setWarnFileSystemChange(); 0381 setForceRecreate(true); 0382 dialogWidget().fileSystem().setCurrentIndex(dialogWidget().fileSystem().findText(partition().fileSystem().name())); 0383 dialogWidget().fileSystem().setEnabled(false); 0384 updateHideAndShow(); 0385 updatePartitionFileSystem(); 0386 } else { 0387 setForceRecreate(false); 0388 dialogWidget().checkRecreate().setCheckState(Qt::Unchecked); 0389 dialogWidget().fileSystem().setEnabled(true); 0390 updateHideAndShow(); 0391 } 0392 }