diff --git a/TODO b/TODO index 4da0bf8..022a0dc 100644 --- a/TODO +++ b/TODO @@ -19,5 +19,20 @@ Random plans and ideas for 1.1 and beyond: * save msdos mbr? +* don't call updatePartitions() or anything after pushing an operation to the + stack: MainWindow::on_m_OperationStack_operationsChanged() will be called and + will deal with all that. warning: this might cause problems, it calls + PartitionManagerWidget::updatePartitions() which currently clears the + selection -- maybe it shouldn't... + +* let the user specify extern command locations and options in the settings + +* move device scanning to backend plugin (call it from the device scanner + thread though) + Bugs: + +* don't show available and used values for extended partitions + +* solid and its object-created-with-parent-in-wrong-thread problem diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index 56554d4..ec79b56 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -40,6 +40,7 @@ #include "ops/checkoperation.h" #include "fs/filesystem.h" +#include "fs/filesystemfactory.h" #include #include @@ -225,10 +226,18 @@ void MainWindow::setupActions() exportPartitionTable->setEnabled(false); exportPartitionTable->setText(i18nc("@action:inmenu", "Export Partition Table")); exportPartitionTable->setToolTip(i18nc("@info:tooltip", "Export a partition table")); - exportPartitionTable->setStatusTip(i18nc("@info:status", "Exports the device's partition table in sfdisk-compatible format to a text file.")); + exportPartitionTable->setStatusTip(i18nc("@info:status", "Export the device's partition table to a text file.")); exportPartitionTable->setIcon(BarIcon("document-export")); + KAction* importPartitionTable = actionCollection()->addAction("importPartitionTable", this, SLOT(onImportPartitionTable())); + importPartitionTable->setEnabled(false); + importPartitionTable->setText(i18nc("@action:inmenu", "Import Partition Table")); + importPartitionTable->setToolTip(i18nc("@info:tooltip", "Import a partition table")); + importPartitionTable->setStatusTip(i18nc("@info:status", "Import a partition table from a text file.")); + importPartitionTable->setIcon(BarIcon("document-import")); + KAction* propertiesDevice = actionCollection()->addAction("propertiesDevice", this, SLOT(onPropertiesDevice())); + propertiesDevice->setEnabled(false); propertiesDevice->setText(i18nc("@action:inmenu", "Properties")); propertiesDevice->setToolTip(i18nc("@info:tooltip", "Show device properties dialog")); propertiesDevice->setStatusTip(i18nc("@info:status", "View and modify device properties")); @@ -371,6 +380,8 @@ void MainWindow::enableActions() { actionCollection()->action("createNewPartitionTable")->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice())); actionCollection()->action("exportPartitionTable")->setEnabled(pmWidget().selectedDevice() && pmWidget().selectedDevice()->partitionTable() && numPendingOperations() == 0); + actionCollection()->action("importPartitionTable")->setEnabled(CreatePartitionTableOperation::canCreate(pmWidget().selectedDevice())); + actionCollection()->action("propertiesDevice")->setEnabled(pmWidget().selectedDevice() != NULL); actionCollection()->action("undoOperation")->setEnabled(numPendingOperations() > 0); actionCollection()->action("clearAllOperations")->setEnabled(numPendingOperations() > 0); @@ -411,6 +422,11 @@ void MainWindow::on_m_ApplyProgressDialog_finished() void MainWindow::on_m_OperationStack_operationsChanged() { listOperations().updateOperations(operationStack().operations()); + pmWidget().updatePartitions(); + enableActions(); + + // this will make sure that the info pane gets updated + on_m_PartitionManagerWidget_selectedPartitionChanged(pmWidget().selectedPartition()); if (!isKPart()) statusText().setText(i18ncp("@info:status", "One pending operation", "%1 pending operations", numPendingOperations())); @@ -508,7 +524,7 @@ void MainWindow::onShowMenuBar() else { const QString accel = menuBarAction->shortcut().toString(); - KMessageBox::information(this, i18nc("@info", "This will hide the menu bar completely. You can show it again by typing %1.", accel), i18nc("@window:title", "Hide Menu Bar"), "hideMenuBarWarning"); + KMessageBox::information(this, i18nc("@info", "This will hide the menu bar completely. You can show it again by typing %1.", accel), i18nc("@title:window", "Hide Menu Bar"), "hideMenuBarWarning"); menuBar()->hide(); } @@ -655,15 +671,166 @@ void MainWindow::onCreateNewPartitionTable() QPointer dlg = new CreatePartitionTableDialog(this, *pmWidget().selectedDevice()); if (dlg->exec() == KDialog::Accepted) - { operationStack().push(new CreatePartitionTableOperation(*pmWidget().selectedDevice(), dlg->type())); - pmWidget().updatePartitions(); - enableActions(); - infoPane().showDevice(dockWidgetArea(&dockInformation()), *pmWidget().selectedDevice()); + delete dlg; +} + +void MainWindow::onImportPartitionTable() +{ + Q_ASSERT(pmWidget().selectedDevice()); + + Device& device = *pmWidget().selectedDevice(); + + QString fileName = KFileDialog::getOpenFileName(KUrl("kfiledialog://importPartitionTable")); + + if (fileName.isEmpty()) + return; + + QFile file(fileName); + + if (!file.open(QFile::ReadOnly)) + { + KMessageBox::error(this, i18nc("@info", "Could not open input file %1.", fileName), i18nc("@title:window", "Error Importing Partition Table")); + return; } - delete dlg; + QByteArray line; + QRegExp rxPartition("(\\d+);(\\d+);(\\d+);(\\w+);(\\w+);(\"\\w*\");(\"[^\"]*\")"); + QRegExp rxType("type:\\s\"(.+)\""); + QRegExp rxMagic("^##|v(\\d+)|##"); + quint32 lineNo = 0; + bool haveMagic = false; + PartitionTable* ptable = NULL; + PartitionTable::TableType tableType = PartitionTable::unknownTableType; + + while (!(line = file.readLine()).isEmpty()) + { + lineNo++; + line = line.simplified(); + + if (line.isEmpty()) + continue; + + if (!haveMagic && rxMagic.indexIn(line) == -1) + { + KMessageBox::error(this, i18nc("@info", "The file %1 is not a valid partition table text file.", fileName), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + else + haveMagic = true; + + if (line.startsWith('#')) + continue; + + if (rxType.indexIn(line) != -1) + { + if (ptable != NULL) + { + KMessageBox::error(this, i18nc("@info", "Found more than one partition table type in import file (line %1).", lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + tableType = PartitionTable::nameToTableType(rxType.cap(1)); + + if (tableType == PartitionTable::unknownTableType) + { + KMessageBox::error(this, i18nc("@info", "Partition table type \"%1\" is unknown (line %2).", rxType.cap(1), lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + if (tableType != PartitionTable::msdos && tableType != PartitionTable::gpt) + { + KMessageBox::error(this, i18nc("@info", "Partition table type \"%1\" is not supported for import (line %2).", rxType.cap(1), lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + ptable = new PartitionTable(tableType, PartitionTable::defaultFirstUsable(device, tableType), PartitionTable::defaultLastUsable(device, tableType)); + operationStack().push(new CreatePartitionTableOperation(device, ptable)); + } + else if (rxPartition.indexIn(line) != -1) + { + if (ptable == NULL) + { + KMessageBox::error(this, i18nc("@info", "Found partition but no partition table type (line %1).", lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + qint32 num = rxPartition.cap(1).toInt(); + qint64 firstSector = rxPartition.cap(2).toLongLong(); + qint64 lastSector = rxPartition.cap(3).toLongLong(); + QString fsName = rxPartition.cap(4); + QString roleNames = rxPartition.cap(5); + QString volumeLabel = rxPartition.cap(6).replace('"', ""); + QStringList flags = rxPartition.cap(7).replace('"', "").split(','); + + if (firstSector < ptable->firstUsable() || lastSector > ptable->lastUsable()) + { + KMessageBox::error(this, i18nc("@info", "Partition %1 would be outside the device's boundaries (line %2).", num, lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + if (firstSector >= lastSector) + { + KMessageBox::error(this, i18nc("@info", "Partition %1 has end before start sector (line %2).", num, lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + PartitionNode* parent = ptable; + + Q_ASSERT(parent); + + PartitionRole role(PartitionRole::None); + + if (roleNames == "extended") + role = PartitionRole(PartitionRole::Extended); + else if (roleNames == "logical") + { + role = PartitionRole(PartitionRole::Logical); + parent = ptable->findPartitionBySector(firstSector, PartitionRole(PartitionRole::Extended)); + } + else if (roleNames == "primary") + role = PartitionRole(PartitionRole::Primary); + + if (role == PartitionRole(PartitionRole::None)) + { + KMessageBox::error(this, i18nc("@info", "Unrecognized partition role \"%1\" for partition %2 (line %3).", roleNames, num, lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + if (parent == NULL) + { + KMessageBox::error(this, i18nc("@info", "No parent partition or partition table found for partition %1 (line %2).", num, lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + if (role.has(PartitionRole::Extended) && !PartitionTable::tableTypeSupportsExtended(tableType)) + { + KMessageBox::error(this, i18nc("@info", "The partition table type \"%1\" does not support extended partitions, but one was found (line %2).", PartitionTable::tableTypeToName(tableType), lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + FileSystem* fs = FileSystemFactory::create(FileSystem::typeForName(fsName), firstSector, lastSector); + + if (fs == NULL) + { + KMessageBox::error(this, i18nc("@info", "Could not create file system \"%1\" for partition %2 (line %3).", fsName, num, lineNo), i18nc("@title:window", "Error While Importing Partition Table")); + return; + } + + if (fs->supportSetLabel() != FileSystem::cmdSupportNone && !volumeLabel.isEmpty()) + fs->setLabel(volumeLabel); + + Partition* p = new Partition(parent, device, role, fs, firstSector, lastSector, -1, PartitionTable::FlagNone, QStringList(), false, PartitionTable::FlagNone, Partition::StateNew); + + operationStack().push(new NewOperation(device, p)); + } + else + Log(Log::warning) << i18nc("@info/plain", "Could not parse line %1 from import file. Ignoring it.", lineNo); + } + + if (ptable->type() == PartitionTable::msdos && ptable->isSectorBased()) + ptable->setType(device, PartitionTable::msdos_sectorbased); } void MainWindow::onExportPartitionTable() @@ -683,13 +850,13 @@ void MainWindow::onExportPartitionTable() if (!file.open(QFile::WriteOnly | QFile::Truncate)) { - KMessageBox::error(this, i18nc("@info", "Could not create output file %1.", fileName), i18nc("@window:title", "Error Exporting Partition Table")); + KMessageBox::error(this, i18nc("@info", "Could not create output file %1.", fileName), i18nc("@title:window", "Error Exporting Partition Table")); return; } QTextStream stream(&file); - stream << "# partition table of " << pmWidget().selectedDevice()->deviceNode() << "\n"; + stream << "##|v1|## partition table of " << pmWidget().selectedDevice()->deviceNode() << "\n"; stream << "# on " << QDateTime::currentDateTime().toString() << "\n"; stream << *pmWidget().selectedDevice()->partitionTable(); } diff --git a/src/gui/mainwindow.h b/src/gui/mainwindow.h index 23204a5..59d8448 100644 --- a/src/gui/mainwindow.h +++ b/src/gui/mainwindow.h @@ -149,6 +149,7 @@ class LIBPARTITIONMANAGERPRIVATE_EXPORT MainWindow : public KXmlGuiWindow, publi void onRefreshDevices(); void onCreateNewPartitionTable(); void onExportPartitionTable(); + void onImportPartitionTable(); void onApplyAllOperations(); void onUndoOperation(); diff --git a/src/gui/partitionmanagerui.rc b/src/gui/partitionmanagerui.rc index b00c0d0..cda7325 100644 --- a/src/gui/partitionmanagerui.rc +++ b/src/gui/partitionmanagerui.rc @@ -45,7 +45,10 @@ Device + + + diff --git a/src/ops/createpartitiontableoperation.cpp b/src/ops/createpartitiontableoperation.cpp index d204fee..0ba011c 100644 --- a/src/ops/createpartitiontableoperation.cpp +++ b/src/ops/createpartitiontableoperation.cpp @@ -44,6 +44,20 @@ CreatePartitionTableOperation::CreatePartitionTableOperation(Device& d, Partitio addJob(createPartitionTableJob()); } +/** Creates a new CreatePartitionTableOperation. + @param d the Device to create the new PartitionTable on + @param ptable pointer to the new partition table object. the operation takes ownership. +*/ +CreatePartitionTableOperation::CreatePartitionTableOperation(Device& d, PartitionTable* ptable) : + Operation(), + m_TargetDevice(d), + m_OldPartitionTable(targetDevice().partitionTable()), + m_PartitionTable(ptable), + m_CreatePartitionTableJob(new CreatePartitionTableJob(targetDevice())) +{ + addJob(createPartitionTableJob()); +} + CreatePartitionTableOperation::~CreatePartitionTableOperation() { if (status() == StatusPending) diff --git a/src/ops/createpartitiontableoperation.h b/src/ops/createpartitiontableoperation.h index 44e4575..de36e8d 100644 --- a/src/ops/createpartitiontableoperation.h +++ b/src/ops/createpartitiontableoperation.h @@ -41,6 +41,7 @@ class CreatePartitionTableOperation : public Operation public: CreatePartitionTableOperation(Device& d, PartitionTable::TableType t); + CreatePartitionTableOperation(Device& d, PartitionTable* ptable); ~CreatePartitionTableOperation(); public: