diff --git a/src/core/partitiontable.cpp b/src/core/partitiontable.cpp index 49b0403..f272bd3 100644 --- a/src/core/partitiontable.cpp +++ b/src/core/partitiontable.cpp @@ -1,541 +1,552 @@ /************************************************************************* * Copyright (C) 2008 by Volker Lanz * * Copyright (C) 2016 by Andrius Štikonas * * Copyright (C) 2016 by Teo Mrnjavac * * * * 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 .* *************************************************************************/ /** @file */ #include "core/partitiontable.h" #include "core/partition.h" #include "core/device.h" #include "core/diskdevice.h" #include "core/lvmdevice.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include "fs/filesystemfactory.h" #include "util/globallog.h" #include #include #include /** Creates a new PartitionTable object with type MSDOS @param type name of the PartitionTable type (e.g. "msdos" or "gpt") */ PartitionTable::PartitionTable(TableType type, qint64 first_usable, qint64 last_usable) : PartitionNode(), m_Children(), m_MaxPrimaries(maxPrimariesForTableType(type)), m_Type(type), m_FirstUsable(first_usable), m_LastUsable(last_usable) { } /** Destroys a PartitionTable object, destroying all children */ PartitionTable::~PartitionTable() { clearChildren(); } /** Gets the number of free sectors before a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors before @returns the number of free sectors before the Partition */ qint64 PartitionTable::freeSectorsBefore(const Partition& p) const { const Partition* pred = predecessor(p); // due to the space required for extended boot records the // below is NOT the same as pred->length() if (pred && pred->roles().has(PartitionRole::Unallocated)) return p.firstSector() - pred->firstSector(); return 0; } /** Gets the number of free sectors after a given child Partition in this PartitionTable. @param p the Partition for which to get the free sectors after @returns the number of free sectors after the Partition */ qint64 PartitionTable::freeSectorsAfter(const Partition& p) const { const Partition* succ = successor(p); // due to the space required for extended boot records the // below is NOT the same as succ->length() if (succ && succ->roles().has(PartitionRole::Unallocated)) return succ->lastSector() - p.lastSector(); return 0; } +qint64 PartitionTable::freeSectors() const +{ + qint64 sectors = 0; + foreach(const Partition * p, children()) + if (p->roles().has(PartitionRole::Unallocated)) { + sectors += p->length(); + } + + return sectors; +} + /** @return true if the PartitionTable has an extended Partition */ bool PartitionTable::hasExtended() const { for (int i = 0; i < children().size(); i++) if (children()[i]->roles().has(PartitionRole::Extended)) return true; return false; } /** @return pointer to the PartitionTable's extended Partition or nullptr if none exists */ Partition* PartitionTable::extended() const { for (int i = 0; i < children().size(); i++) if (children()[i]->roles().has(PartitionRole::Extended)) return children()[i]; return nullptr; } /** Gets valid PartitionRoles for a Partition @param p the Partition @return valid roles for the given Partition */ PartitionRole::Roles PartitionTable::childRoles(const Partition& p) const { Q_ASSERT(p.parent()); PartitionRole::Roles r = p.parent()->isRoot() ? PartitionRole::Primary : PartitionRole::Logical; if (r == PartitionRole::Primary && hasExtended() == false && tableTypeSupportsExtended(type())) r |= PartitionRole::Extended; return r; } /** @return the number of primaries in this PartitionTable */ int PartitionTable::numPrimaries() const { int result = 0; foreach(const Partition * p, children()) if (p->roles().has(PartitionRole::Primary) || p->roles().has(PartitionRole::Extended)) result++; return result; } /** Appends a Partition to this PartitionTable @param partition pointer of the partition to append. Must not be nullptr. */ void PartitionTable::append(Partition* partition) { children().append(partition); } /** @param f the flag to get the name for @returns the flags name or an empty QString if the flag is not known */ QString PartitionTable::flagName(Flag f) { switch (f) { case PartitionTable::FlagBoot: return i18nc("@item partition flag", "boot"); case PartitionTable::FlagRoot: return i18nc("@item partition flag", "root"); case PartitionTable::FlagSwap: return i18nc("@item partition flag", "swap"); case PartitionTable::FlagHidden: return i18nc("@item partition flag", "hidden"); case PartitionTable::FlagRaid: return i18nc("@item partition flag", "raid"); case PartitionTable::FlagLvm: return i18nc("@item partition flag", "lvm"); case PartitionTable::FlagLba: return i18nc("@item partition flag", "lba"); case PartitionTable::FlagHpService: return i18nc("@item partition flag", "hpservice"); case PartitionTable::FlagPalo: return i18nc("@item partition flag", "palo"); case PartitionTable::FlagPrep: return i18nc("@item partition flag", "prep"); case PartitionTable::FlagMsftReserved: return i18nc("@item partition flag", "msft-reserved"); case PartitionTable::FlagBiosGrub: return i18nc("@item partition flag", "bios-grub"); case PartitionTable::FlagAppleTvRecovery: return i18nc("@item partition flag", "apple-tv-recovery"); case PartitionTable::FlagDiag: return i18nc("@item partition flag", "diag"); case PartitionTable::FlagLegacyBoot: return i18nc("@item partition flag", "legacy-boot"); case PartitionTable::FlagMsftData: return i18nc("@item partition flag", "msft-data"); case PartitionTable::FlagIrst: return i18nc("@item partition flag", "irst"); case PartitionTable::FlagEsp: return i18nc("@item partition flag", "esp"); default: break; } return QString(); } /** @return list of all flags */ QList PartitionTable::flagList() { QList rval; rval.append(PartitionTable::FlagBoot); rval.append(PartitionTable::FlagRoot); rval.append(PartitionTable::FlagSwap); rval.append(PartitionTable::FlagHidden); rval.append(PartitionTable::FlagRaid); rval.append(PartitionTable::FlagLvm); rval.append(PartitionTable::FlagLba); rval.append(PartitionTable::FlagHpService); rval.append(PartitionTable::FlagPalo); rval.append(PartitionTable::FlagPrep); rval.append(PartitionTable::FlagMsftReserved); rval.append(PartitionTable::FlagBiosGrub); rval.append(PartitionTable::FlagAppleTvRecovery); rval.append(PartitionTable::FlagDiag); rval.append(PartitionTable::FlagLegacyBoot); rval.append(PartitionTable::FlagMsftData); rval.append(PartitionTable::FlagIrst); rval.append(PartitionTable::FlagEsp); return rval; } /** @param flags the flags to get the names for @returns QStringList of the flags' names */ QStringList PartitionTable::flagNames(Flags flags) { QStringList rval; int f = 1; QString s; while (!(s = flagName(static_cast(f))).isEmpty()) { if (flags & f) rval.append(s); f <<= 1; } return rval; } bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end) { if (d.type() == Device::Disk_Device) { const DiskDevice& device = dynamic_cast(d); if (!parent.isRoot()) { Partition* extended = dynamic_cast(&parent); if (extended == nullptr) { qWarning() << "extended is null. start: " << start << ", end: " << end << ", device: " << device.deviceNode(); return false; } // Leave a track (cylinder aligned) or sector alignment sectors (sector based) free at the // start for a new partition's metadata start += device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); // .. and also at the end for the metadata for a partition to follow us, if we're not // at the end of the extended partition if (end < extended->lastSector()) end -= device.partitionTable()->type() == PartitionTable::msdos ? device.sectorsPerTrack() : PartitionAlignment::sectorAlignment(device); } return end - start + 1 >= PartitionAlignment::sectorAlignment(device); } else if (d.type() == Device::LVM_Device) { const LvmDevice& lvm = dynamic_cast(d); if (lvm.freePE() && start >= lvm.allocatedPE()) { start = lvm.allocatedPE(); end = lvm.totalPE() - 1; return true; } return false; } return false; } /** Creates a new unallocated Partition on the given Device. @param device the Device to create the new Partition on @param parent the parent PartitionNode for the new Partition @param start the new Partition's start sector @param end the new Partition's end sector @return pointer to the newly created Partition object or nullptr if the Partition could not be created */ Partition* createUnallocated(const Device& device, PartitionNode& parent, qint64 start, qint64 end) { PartitionRole::Roles r = PartitionRole::Unallocated; if (!parent.isRoot()) r |= PartitionRole::Logical; if (!PartitionTable::getUnallocatedRange(device, parent, start, end)) return nullptr; return new Partition(&parent, device, PartitionRole(r), FileSystemFactory::create(FileSystem::Unknown, start, end), start, end, QString()); } /** Removes all unallocated children from a PartitionNode @param p pointer to the parent to remove unallocated children from */ void PartitionTable::removeUnallocated(PartitionNode* p) { Q_ASSERT(p); qint32 i = 0; while (i < p->children().size()) { Partition* child = p->children()[i]; if (child->roles().has(PartitionRole::Unallocated)) { p->remove(child); delete child; continue; } if (child->roles().has(PartitionRole::Extended)) removeUnallocated(child); i++; } } /** @overload */ void PartitionTable::removeUnallocated() { removeUnallocated(this); } /** Inserts unallocated children for a Device's PartitionTable with the given parent. This method inserts unallocated Partitions for a parent, usually the Device this PartitionTable is on. It will also insert unallocated Partitions in any extended Partitions it finds. @warning This method assumes that no unallocated Partitions exist when it is called. @param d the Device this PartitionTable and @p p are on @param p the parent PartitionNode (may be this or an extended Partition) @param start the first sector to begin looking for free space */ void PartitionTable::insertUnallocated(const Device& d, PartitionNode* p, qint64 start) const { Q_ASSERT(p); qint64 lastEnd = start; foreach(Partition * child, p->children()) { p->insert(createUnallocated(d, *p, lastEnd, child->firstSector() - 1)); if (child->roles().has(PartitionRole::Extended)) insertUnallocated(d, child, child->firstSector()); lastEnd = child->lastSector() + 1; } // Take care of the free space between the end of the last child and the end // of the device or the extended partition. qint64 parentEnd = lastUsable(); if (!p->isRoot()) { Partition* extended = dynamic_cast(p); parentEnd = extended ? extended->lastSector() : -1; Q_ASSERT(extended); } if (parentEnd >= firstUsable()) p->insert(createUnallocated(d, *p, lastEnd, parentEnd)); } /** Updates the unallocated Partitions for this PartitionTable. @param d the Device this PartitionTable is on */ void PartitionTable::updateUnallocated(const Device& d) { removeUnallocated(); insertUnallocated(d, this, firstUsable()); } qint64 PartitionTable::defaultFirstUsable(const Device& d, TableType t) { Q_UNUSED(t) if (d.type() == Device::LVM_Device) { return 0; } const DiskDevice& diskDevice = dynamic_cast(d); return PartitionAlignment::sectorAlignment(diskDevice); } qint64 PartitionTable::defaultLastUsable(const Device& d, TableType t) { if (t == gpt) return d.totalLogical() - 1 - 32 - 1; return d.totalLogical() - 1; } static struct { const QString name; /**< name of partition table type */ quint32 maxPrimaries; /**< max numbers of primary partitions supported */ bool canHaveExtended; /**< does partition table type support extended partitions */ bool isReadOnly; /**< does KDE Partition Manager support this only in read only mode */ PartitionTable::TableType type; /**< enum type */ } tableTypes[] = { { QStringLiteral("aix"), 4, false, true, PartitionTable::aix }, { QStringLiteral("bsd"), 8, false, true, PartitionTable::bsd }, { QStringLiteral("dasd"), 1, false, true, PartitionTable::dasd }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos }, { QStringLiteral("msdos"), 4, true, false, PartitionTable::msdos_sectorbased }, { QStringLiteral("dvh"), 16, true, true, PartitionTable::dvh }, { QStringLiteral("gpt"), 128, false, false, PartitionTable::gpt }, { QStringLiteral("loop"), 1, false, true, PartitionTable::loop }, { QStringLiteral("mac"), 0xffff, false, true, PartitionTable::mac }, { QStringLiteral("pc98"), 16, false, true, PartitionTable::pc98 }, { QStringLiteral("amiga"), 128, false, true, PartitionTable::amiga }, { QStringLiteral("sun"), 8, false, true, PartitionTable::sun } }; PartitionTable::TableType PartitionTable::nameToTableType(const QString& n) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (n == tableTypes[i].name) return tableTypes[i].type; return PartitionTable::unknownTableType; } QString PartitionTable::tableTypeToName(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].name; return i18nc("@item partition table name", "unknown"); } qint64 PartitionTable::maxPrimariesForTableType(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].maxPrimaries; return 1; } bool PartitionTable::tableTypeSupportsExtended(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].canHaveExtended; return false; } bool PartitionTable::tableTypeIsReadOnly(TableType l) { for (size_t i = 0; i < sizeof(tableTypes) / sizeof(tableTypes[0]); i++) if (l == tableTypes[i].type) return tableTypes[i].isReadOnly; return false; } /** Simple heuristic to determine if the PartitionTable is sector aligned (i.e. if its Partitions begin at sectors evenly divisable by PartitionAlignment::sectorAlignment(). @return true if is sector aligned, otherwise false */ bool PartitionTable::isSectorBased(const Device& d) const { if (d.type() == Device::Disk_Device) { const DiskDevice& diskDevice = dynamic_cast(d); if (type() == PartitionTable::msdos) { // the default for empty partition tables is sector based if (numPrimaries() == 0) return true; quint32 numCylinderAligned = 0; quint32 numSectorAligned = 0; // see if we have more cylinder aligned partitions than sector // aligned ones. foreach(const Partition * p, children()) if (p->firstSector() % PartitionAlignment::sectorAlignment(diskDevice) == 0) numSectorAligned++; else if (p->firstSector() % diskDevice.cylinderSize() == 0) numCylinderAligned++; return numSectorAligned >= numCylinderAligned; } return type() == PartitionTable::msdos_sectorbased; } return false; } void PartitionTable::setType(const Device& d, TableType t) { setFirstUsableSector(defaultFirstUsable(d, t)); setLastUsableSector(defaultLastUsable(d, t)); m_Type = t; updateUnallocated(d); } static bool isPartitionLessThan(const Partition* p1, const Partition* p2) { return p1->number() < p2->number(); } QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable) { stream << "type: \"" << ptable.typeName() << "\"\n" << "align: \"" << (ptable.type() == PartitionTable::msdos ? "cylinder" : "sector") << "\"\n" << "\n# number start end type roles label flags\n"; QList partitions; foreach(const Partition * p, ptable.children()) if (!p->roles().has(PartitionRole::Unallocated)) { partitions.append(p); if (p->roles().has(PartitionRole::Extended)) foreach(const Partition * child, p->children()) if (!child->roles().has(PartitionRole::Unallocated)) partitions.append(child); } qSort(partitions.begin(), partitions.end(), isPartitionLessThan); foreach(const Partition * p, partitions) stream << *p; return stream; } diff --git a/src/core/partitiontable.h b/src/core/partitiontable.h index e391156..2d1f902 100644 --- a/src/core/partitiontable.h +++ b/src/core/partitiontable.h @@ -1,200 +1,201 @@ /************************************************************************* * Copyright (C) 2008, 2010 by Volker Lanz * * Copyright (C) 2016 by Teo Mrnjavac * * * * 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(PARTITIONTABLE__H) #define PARTITIONTABLE__H #include "util/libpartitionmanagerexport.h" #include "core/partitionnode.h" #include "core/partitionrole.h" #include #include class Device; class Partition; class CoreBackend; class QTextStream; /** The partition table (a.k.a Disk Label) PartitionTable represents a partition table (or disk label). PartitionTable has child nodes that represent Partitions. @author Volker Lanz */ class LIBKPMCORE_EXPORT PartitionTable : public PartitionNode { Q_DISABLE_COPY(PartitionTable) friend class CoreBackend; friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); public: enum TableType : qint8 { unknownTableType = -1, aix, bsd, dasd, msdos, msdos_sectorbased, dvh, gpt, loop, mac, pc98, amiga, sun, vmd /* Volume Manager Device */ }; /** Partition flags */ enum Flag : qint32 { FlagNone = 0, FlagBoot = 1, FlagRoot = 2, FlagSwap = 4, FlagHidden = 8, FlagRaid = 16, FlagLvm = 32, FlagLba = 64, FlagHpService = 128, FlagPalo = 256, FlagPrep = 512, FlagMsftReserved = 1024, FlagBiosGrub = 2048, FlagAppleTvRecovery = 4096, FlagDiag = 8192, FlagLegacyBoot = 16384, FlagMsftData = 32768, FlagIrst = 65536, FlagEsp = 131072 }; Q_DECLARE_FLAGS(Flags, Flag) public: PartitionTable(TableType type, qint64 first_usable, qint64 last_usable); ~PartitionTable(); public: PartitionNode* parent() override { return nullptr; /**< @return always nullptr for PartitionTable */ } const PartitionNode* parent() const override { return nullptr; /**< @return always nullptr for PartitionTable */ } bool isRoot() const override { return true; /**< @return always true for PartitionTable */ } bool isReadOnly() const { return tableTypeIsReadOnly(type()); /**< @return true if the PartitionTable is read only */ } Partitions& children() override { return m_Children; /**< @return the children in this PartitionTable */ } const Partitions& children() const override { return m_Children; /**< @return the children in this PartitionTable */ } void setType(const Device& d, TableType t); void append(Partition* partition) override; qint64 freeSectorsBefore(const Partition& p) const; qint64 freeSectorsAfter(const Partition& p) const; + qint64 freeSectors() const; bool hasExtended() const; Partition* extended() const; PartitionRole::Roles childRoles(const Partition& p) const; int numPrimaries() const; int maxPrimaries() const { return m_MaxPrimaries; /**< @return max number of primary partitions this PartitionTable can handle */ } PartitionTable::TableType type() const { return m_Type; /**< @return the PartitionTable's type */ } const QString typeName() const { return tableTypeToName(type()); /**< @return the name of this PartitionTable type */ } qint64 firstUsable() const { return m_FirstUsable; } qint64 lastUsable() const { return m_LastUsable; } void updateUnallocated(const Device& d); void insertUnallocated(const Device& d, PartitionNode* p, qint64 start) const; bool isSectorBased(const Device& d) const; static QList flagList(); static QString flagName(Flag f); static QStringList flagNames(Flags f); static bool getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end); static void removeUnallocated(PartitionNode* p); void removeUnallocated(); static qint64 defaultFirstUsable(const Device& d, TableType t); static qint64 defaultLastUsable(const Device& d, TableType t); static PartitionTable::TableType nameToTableType(const QString& n); static QString tableTypeToName(TableType l); static qint64 maxPrimariesForTableType(TableType l); static bool tableTypeSupportsExtended(TableType l); static bool tableTypeIsReadOnly(TableType l); protected: void setMaxPrimaries(qint32 n) { m_MaxPrimaries = n; } void setFirstUsableSector(qint64 s) { m_FirstUsable = s; } void setLastUsableSector(qint64 s) { m_LastUsable = s; } private: Partitions m_Children; qint32 m_MaxPrimaries; TableType m_Type; qint64 m_FirstUsable; qint64 m_LastUsable; }; Q_DECLARE_OPERATORS_FOR_FLAGS(PartitionTable::Flags) QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable); #endif diff --git a/src/gui/partresizerwidget.cpp b/src/gui/partresizerwidget.cpp index e76dc3d..cf9130e 100644 --- a/src/gui/partresizerwidget.cpp +++ b/src/gui/partresizerwidget.cpp @@ -1,480 +1,481 @@ /************************************************************************* * Copyright (C) 2008, 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 .* *************************************************************************/ #include "gui/partresizerwidget.h" #include "gui/partwidget.h" #include "core/partition.h" #include "core/device.h" #include "core/partitiontable.h" #include "core/partitionalignment.h" #include "fs/filesystem.h" #include #include #include #include #include #include #include #include const qint32 PartResizerWidget::m_HandleHeight = 59; /** Creates a new PartResizerWidget Initializing is mostly done in init(). @param parent pointer to the parent widget */ PartResizerWidget::PartResizerWidget(QWidget* parent) : QWidget(parent), m_Device(nullptr), m_Partition(nullptr), m_PartWidget(nullptr), m_MinimumFirstSector(0), m_MaximumFirstSector(-1), m_MinimumLastSector(-1), m_MaximumLastSector(0), m_MinimumLength(-1), m_MaximumLength(-1), m_LeftHandle(this), m_RightHandle(this), m_DraggedWidget(nullptr), m_Hotspot(0), m_MoveAllowed(true), m_ReadOnly(false), m_Align(true) { } /** Intializes the PartResizerWidget @param d the Device the Partition is on @param p the Partition to show and/or resize @param minFirst the minimum value for the first sector @param maxLast the maximum value for the last sector */ void PartResizerWidget::init(Device& d, Partition& p, qint64 minFirst, qint64 maxLast, bool read_only, bool move_allowed) { setDevice(d); setPartition(p); setMinimumFirstSector(minFirst); setMaximumLastSector(maxLast); setReadOnly(read_only); setMoveAllowed(move_allowed); setMinimumLength(qMax(partition().sectorsUsed(), partition().minimumSectors())); setMaximumLength(qMin(totalSectors(), partition().maximumSectors())); // set margins to accommodate to top/bottom button asymmetric layouts QStyleOptionButton bOpt; bOpt.initFrom(this); QRect buttonRect(style()->subElementRect(QStyle::SE_PushButtonContents, &bOpt)); int asym = (rect().bottom() - buttonRect.bottom()) - (buttonRect.top() - rect().top()); if (asym > 0) setContentsMargins(0, asym, 0, 0); else setContentsMargins(0, 0, 0, asym); if (!readOnly()) { QPixmap pixmap(handleWidth(), handleHeight()); pixmap.fill(Qt::transparent); QPainter p(&pixmap); QStyleOption opt; opt.state |= QStyle::State_Horizontal; opt.rect = pixmap.rect().adjusted(0, 2, 0, -2); style()->drawControl(QStyle::CE_Splitter, &opt, &p, this); leftHandle().setPixmap(pixmap); rightHandle().setPixmap(pixmap); leftHandle().setFixedSize(handleWidth(), handleHeight()); rightHandle().setFixedSize(handleWidth(), handleHeight()); } delete m_PartWidget; m_PartWidget = new PartWidget(this, &partition()); if (!readOnly()) { if (moveAllowed()) leftHandle().setCursor(Qt::SizeHorCursor); rightHandle().setCursor(Qt::SizeHorCursor); } if (moveAllowed()) partWidget().setCursor(Qt::SizeAllCursor); partWidget().setToolTip(QString()); updatePositions(); } qint32 PartResizerWidget::handleWidth() const { return style()->pixelMetric(QStyle::PM_SplitterWidth); } qint64 PartResizerWidget::sectorsPerPixel() const { + //FIXME: Make sure that this function never return 0 return totalSectors() / (width() - 2 * handleWidth()); } int PartResizerWidget::partWidgetStart() const { return handleWidth() + (partition().firstSector() - minimumFirstSector()) / sectorsPerPixel(); } int PartResizerWidget::partWidgetWidth() const { return partition().length() / sectorsPerPixel(); } void PartResizerWidget::updatePositions() { QMargins margins(contentsMargins()); partWidget().move(partWidgetStart() + margins.left(), margins.top()); partWidget().resize(partWidgetWidth() - margins.left() - margins.right(), height() - margins.top() - margins.bottom()); leftHandle().move(partWidgetStart() - leftHandle().width(), 0); rightHandle().move(partWidgetStart() + partWidgetWidth(), 0); partWidget().update(); } void PartResizerWidget::resizeEvent(QResizeEvent* event) { updatePositions(); QWidget::resizeEvent(event); } void PartResizerWidget::paintEvent(QPaintEvent*) { // draw sunken frame QPainter painter(this); QStyleOptionFrameV3 opt; opt.initFrom(this); opt.frameShape = QFrame::StyledPanel; opt.rect = contentsRect(); opt.lineWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, this); opt.midLineWidth = 0; opt.state |= QStyle::State_Sunken; style()->drawPrimitive(QStyle::PE_PanelLineEdit, &opt, &painter, this); } void PartResizerWidget::mousePressEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) { m_DraggedWidget = static_cast(childAt(event->pos())); if (m_DraggedWidget != nullptr) { if (partWidget().isAncestorOf(m_DraggedWidget)) m_DraggedWidget = &partWidget(); m_Hotspot = m_DraggedWidget->mapFromParent(event->pos()).x(); } } } bool PartResizerWidget::checkConstraints(qint64 first, qint64 last) const { return (maximumFirstSector() == -1 || first <= maximumFirstSector()) && (minimumFirstSector() == 0 || first >= minimumFirstSector()) && (minimumLastSector() == -1 || last >= minimumLastSector()) && (maximumLastSector() == 0 || last <= maximumLastSector()); } bool PartResizerWidget::movePartition(qint64 newFirstSector) { const qint64 originalLength = partition().length(); const bool isLengthAligned = PartitionAlignment::isLengthAligned(device(), partition()); if (maximumFirstSector(align()) > -1 && newFirstSector > maximumFirstSector(align())) newFirstSector = maximumFirstSector(align()); if (minimumFirstSector(align()) > 0 && newFirstSector < minimumFirstSector(align())) newFirstSector = minimumFirstSector(align()); if (align()) newFirstSector = PartitionAlignment::alignedFirstSector(device(), partition(), newFirstSector, minimumFirstSector(align()), maximumFirstSector(align()), -1, -1); qint64 delta = newFirstSector - partition().firstSector(); if (delta == 0) return false; qint64 newLastSector = partition().lastSector() + delta; if (minimumLastSector(align()) > -1 && newLastSector < minimumLastSector(align())) { const qint64 deltaLast = minimumLastSector(align()) - newLastSector; newFirstSector += deltaLast; newLastSector += deltaLast; } if (maximumLastSector(align()) > 0 && newLastSector > maximumLastSector(align())) { const qint64 deltaLast = newLastSector - maximumLastSector(align()); newFirstSector -= deltaLast; newLastSector -= deltaLast; } if (align()) newLastSector = PartitionAlignment::alignedLastSector(device(), partition(), newLastSector, minimumLastSector(align()), maximumLastSector(align()), -1, -1, originalLength, isLengthAligned); if (newLastSector == partition().lastSector()) return false; if (isLengthAligned && newLastSector - newFirstSector + 1 != partition().length()) { qDebug() << "length changes while trying to move partition " << partition().deviceNode() << ". new first: " << newFirstSector << ", new last: " << newLastSector << ", old length: " << partition().length() << ", new length: " << newLastSector - newFirstSector + 1; return false; } if (!checkConstraints(newFirstSector, newLastSector)) { qDebug() << "constraints not satisfied while trying to move partition " << partition().deviceNode() << ". new first: " << newFirstSector << ", new last: " << newLastSector; return false; } if (align() && !PartitionAlignment::isAligned(device(), partition(), newFirstSector, newLastSector, true)) { qDebug() << "partition " << partition().deviceNode() << " not aligned but supposed to be. new first: " << newFirstSector << " delta: " << PartitionAlignment::firstDelta(device(), partition(), newFirstSector) << ", new last: " << newLastSector << ", delta: " << PartitionAlignment::lastDelta(device(), partition(), newLastSector); return false; } if (partition().children().size() > 0 && (!checkAlignment(*partition().children().first(), partition().firstSector() - newFirstSector) || !checkAlignment(*partition().children().last(), partition().lastSector() - newLastSector))) { qDebug() << "cannot align children while trying to move partition " << partition().deviceNode(); return false; } partition().setFirstSector(newFirstSector); partition().fileSystem().setFirstSector(newFirstSector); partition().setLastSector(newLastSector); partition().fileSystem().setLastSector(newLastSector); updatePositions(); emit firstSectorChanged(partition().firstSector()); emit lastSectorChanged(partition().lastSector()); return true; } void PartResizerWidget::mouseMoveEvent(QMouseEvent* event) { int x = event->pos().x() - m_Hotspot; if (draggedWidget() == &leftHandle()) { const qint64 newFirstSector = qMax(minimumFirstSector() + x * sectorsPerPixel(), 0LL); updateFirstSector(newFirstSector); } else if (draggedWidget() == &rightHandle()) { const qint64 newLastSector = qMin(minimumFirstSector() + (x - rightHandle().width()) * sectorsPerPixel(), maximumLastSector()); updateLastSector(newLastSector); } else if (draggedWidget() == &partWidget() && moveAllowed()) { const qint64 newFirstSector = qMax(minimumFirstSector() + (x - handleWidth()) * sectorsPerPixel(), 0LL); movePartition(newFirstSector); } } void PartResizerWidget::mouseReleaseEvent(QMouseEvent* event) { if (event->button() == Qt::LeftButton) m_DraggedWidget = nullptr; } bool PartResizerWidget::updateFirstSector(qint64 newFirstSector) { if (maximumFirstSector(align()) > -1 && newFirstSector > maximumFirstSector(align())) newFirstSector = maximumFirstSector(align()); if (minimumFirstSector(align()) > 0 && newFirstSector < minimumFirstSector(align())) newFirstSector = minimumFirstSector(align()); const qint64 newLength = partition().lastSector() - newFirstSector + 1; if (newLength < minimumLength()) newFirstSector -= minimumLength() - newLength; if (newLength > maximumLength()) newFirstSector -= newLength - maximumLength(); if (align()) newFirstSector = PartitionAlignment::alignedFirstSector(device(), partition(), newFirstSector, minimumFirstSector(align()), maximumFirstSector(align()), minimumLength(), maximumLength()); if (newFirstSector != partition().firstSector() && (partition().children().size() == 0 || checkAlignment(*partition().children().first(), partition().firstSector() - newFirstSector))) { const qint64 deltaFirst = partition().firstSector() - newFirstSector; partition().setFirstSector(newFirstSector); partition().fileSystem().setFirstSector(newFirstSector); resizeLogicals(deltaFirst, 0); updatePositions(); emit firstSectorChanged(partition().firstSector()); return true; } return false; } bool PartResizerWidget::checkAlignment(const Partition& child, qint64 delta) const { // TODO: what is this exactly good for? and is it correct in non-cylinder-aligned // situations? if (!partition().roles().has(PartitionRole::Extended)) return true; if (child.roles().has(PartitionRole::Unallocated)) return true; return qAbs(delta) >= PartitionAlignment::sectorAlignment(device()); } void PartResizerWidget::resizeLogicals(qint64 deltaFirst, qint64 deltaLast, bool force) { if (deltaFirst != 0 && partition().children().size() > 0 && partition().children().first()->roles().has(PartitionRole::Unallocated)) { qint64 start = partition().children().first()->firstSector() - deltaFirst; qint64 end = partition().children().first()->lastSector() + deltaLast; if (PartitionTable::getUnallocatedRange(device(), partition(), start, end)) { partition().children().first()->setFirstSector(start); deltaFirst = 0; } } if (deltaLast != 0 && partition().children().size() > 0 && partition().children().last()->roles().has(PartitionRole::Unallocated)) { qint64 start = partition().children().last()->firstSector() - deltaFirst; qint64 end = partition().children().last()->lastSector() + deltaLast; if (PartitionTable::getUnallocatedRange(device(), partition(), start, end)) { partition().children().last()->setLastSector(end); deltaLast = 0; } } if (force || deltaFirst != 0 || deltaLast != 0) { Q_ASSERT(device().partitionTable()); device().partitionTable()->removeUnallocated(&partition()); if (partition().roles().has(PartitionRole::Extended)) device().partitionTable()->insertUnallocated(device(), &partition(), partition().firstSector()); } partWidget().updateChildren(); } bool PartResizerWidget::updateLastSector(qint64 newLastSector) { if (minimumLastSector(align()) > -1 && newLastSector < minimumLastSector(align())) newLastSector = minimumLastSector(align()); if (maximumLastSector(align()) > 0 && newLastSector > maximumLastSector(align())) newLastSector = maximumLastSector(align()); const qint64 newLength = newLastSector - partition().firstSector() + 1; if (newLength < minimumLength()) newLastSector += minimumLength() - newLength; if (newLength > maximumLength()) newLastSector -= newLength - maximumLength(); if (align()) newLastSector = PartitionAlignment::alignedLastSector(device(), partition(), newLastSector, minimumLastSector(align()), maximumLastSector(align()), minimumLength(), maximumLength()); if (newLastSector != partition().lastSector() && (partition().children().size() == 0 || checkAlignment(*partition().children().last(), partition().lastSector() - newLastSector))) { const qint64 deltaLast = newLastSector - partition().lastSector(); partition().setLastSector(newLastSector); partition().fileSystem().setLastSector(newLastSector); resizeLogicals(0, deltaLast); updatePositions(); emit lastSectorChanged(partition().lastSector()); return true; } return false; } /** Sets the minimum sectors the Partition can be long. @note This value can never be less than 0 and never be higher than totalSectors() @param s the new minimum length */ void PartResizerWidget::setMinimumLength(qint64 s) { m_MinimumLength = qBound(0LL, s, totalSectors()); } /** Sets the maximum sectors the Partition can be long. @note This value can never be less than 0 and never by higher than totalSectors() @param s the new maximum length */ void PartResizerWidget::setMaximumLength(qint64 s) { m_MaximumLength = qBound(0LL, s, totalSectors()); } /** Sets if moving the Partition is allowed. @param b true if moving is allowed */ void PartResizerWidget::setMoveAllowed(bool b) { m_MoveAllowed = b; if (m_PartWidget != nullptr) partWidget().setCursor(b ? Qt::SizeAllCursor : Qt::ArrowCursor); } qint64 PartResizerWidget::minimumFirstSector(bool aligned) const { if (!aligned || PartitionAlignment::firstDelta(device(), partition(), m_MinimumFirstSector) == 0) return m_MinimumFirstSector; return m_MinimumFirstSector - PartitionAlignment::firstDelta(device(), partition(), m_MinimumFirstSector) + PartitionAlignment::sectorAlignment(device()); } qint64 PartResizerWidget::maximumFirstSector(bool aligned) const { return (m_MaximumFirstSector != -1 && aligned) ? m_MaximumFirstSector - PartitionAlignment::firstDelta(device(), partition(), m_MaximumFirstSector) : m_MaximumFirstSector; } qint64 PartResizerWidget::minimumLastSector(bool aligned) const { if (!aligned || PartitionAlignment::lastDelta(device(), partition(), m_MinimumLastSector) == 1) return m_MinimumLastSector; return m_MinimumLastSector - PartitionAlignment::lastDelta(device(), partition(), m_MinimumLastSector) + 1 + PartitionAlignment::sectorAlignment(device()); } qint64 PartResizerWidget::maximumLastSector(bool aligned) const { return (m_MaximumLastSector != 0 && aligned) ? m_MaximumLastSector - PartitionAlignment::lastDelta(device(), partition(), m_MaximumLastSector) : m_MaximumLastSector; }