Compare commits

...

4 Commits

Author SHA1 Message Date
demmm
cbeeb06e59 Merge pull request #1960 from AIIX/feature/port/partition_qml_module/v1
Initial upload of WIP partitionq module
2022-05-26 21:11:55 +02:00
Aditya Mehra
dc7f91797c fix spdx headers for files 2022-05-26 17:54:42 +09:30
Aditya Mehra
abaf7630ad Add flat partition list module for qml, fix review stuff 2022-05-26 17:47:21 +09:30
Aditya Mehra
a84783d5e2 Initial upload of WIP partitionq module 2022-05-19 17:48:32 +09:30
14 changed files with 2086 additions and 0 deletions

View File

@@ -335,6 +335,8 @@ PartitionCoreModule::doInit()
{
scanForEfiSystemPartitions();
}
emit initCompleted();
}
PartitionCoreModule::~PartitionCoreModule()

View File

@@ -51,6 +51,9 @@ class QStandardItemModel;
class PartitionCoreModule : public QObject
{
Q_OBJECT
Q_PROPERTY( QAbstractListModel* deviceModel READ deviceModel CONSTANT FINAL )
Q_PROPERTY( QStandardItemModel* bootLoaderModel READ bootLoaderModel CONSTANT FINAL )
public:
/**
* This helper class calls refresh() on the module
@@ -239,6 +242,7 @@ Q_SIGNALS:
void isDirtyChanged( bool value );
void reverted();
void deviceReverted( Device* device );
void initCompleted();
private:
void refreshAfterModelChange();

View File

@@ -0,0 +1,109 @@
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
# SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
# SPDX-License-Identifier: BSD-2-Clause
#
if( NOT WITH_QML )
calamares_skip_module( "partitionq (QML is not supported in this build)" )
return()
endif()
# When debugging the timezone widget, add this debugging definition
# to have a debugging-friendly timezone widget, debug logging,
# and no intrusive timezone-setting while clicking around.
option( DEBUG_PARTITION_UNSAFE "Allow unsafe partitioning choices." OFF )
option( DEBUG_PARTITION_BAIL_OUT "Unsafe partitioning will error out on exec." ON )
option( DEBUG_PARTITION_SKIP "Don't actually do any partitioning." OFF)
include_directories( ${CMAKE_SOURCE_DIR} ) # for KPMCoreHelper
# This is very chatty, useful mostly if you don't know what KPMCore offers.
option( DEBUG_FILESYSTEMS "Log all available Filesystems from KPMCore." OFF )
set( _partition_defs )
if( DEBUG_PARTITION_UNSAFE )
if( DEBUG_PARTITION_BAIL_OUT )
list( APPEND _partition_defs DEBUG_PARTITION_BAIL_OUT )
endif()
list( APPEND _partition_defs DEBUG_PARTITION_UNSAFE )
endif()
if ( DEBUG_FILESYSTEMS )
list( APPEND _partition_defs DEBUG_FILESYSTEMS )
endif()
if( DEBUG_PARTITION_SKIP )
list( APPEND _partition_defs DEBUG_PARTITION_SKIP )
endif()
find_package(ECM ${ECM_VERSION} REQUIRED NO_MODULE)
include( KPMcoreHelper )
find_package( KF5Config CONFIG )
find_package( KF5I18n CONFIG )
if ( KPMcore_FOUND AND Qt5DBus_FOUND AND KF5CoreAddons_FOUND AND KF5Config_FOUND )
list( APPEND _partition_defs ${KPMcore_API_DEFINITIONS} )
include_directories( ${KPMCORE_INCLUDE_DIR} )
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
# Because we're sharing sources with the regular partition module
set( _partition ${CMAKE_CURRENT_SOURCE_DIR}/../partition )
# set( _partitionCore ${CMAKE_CURRENT_SOURCE_DIR}/../partition/core )
# set( _partitionJobs ${CMAKE_CURRENT_SOURCE_DIR}/../partition/jobs )
include_directories( ${_partition} )
calamares_add_plugin( partitionq
TYPE viewmodule
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
PartitionQmlViewStep.cpp
ChoicePageQml.cpp
FlatPartitionModel.cpp
${_partition}/Config.cpp
${_partition}/core/PartUtils.cpp
${_partition}/core/DeviceModel.cpp
${_partition}/core/PartitionInfo.cpp
${_partition}/core/PartitionCoreModule.cpp
${_partition}/core/BootLoaderModel.cpp
${_partition}/core/ColorUtils.cpp
${_partition}/core/DeviceList.cpp
${_partition}/core/KPMHelpers.cpp
${_partition}/core/PartitionActions.cpp
${_partition}/core/PartitionLayout.cpp
${_partition}/core/PartitionModel.cpp
${_partition}/jobs/AutoMountManagementJob.cpp
${_partition}/jobs/ChangeFilesystemLabelJob.cpp
${_partition}/jobs/ClearMountsJob.cpp
${_partition}/jobs/ClearTempMountsJob.cpp
${_partition}/jobs/CreatePartitionJob.cpp
${_partition}/jobs/CreatePartitionTableJob.cpp
${_partition}/jobs/CreateVolumeGroupJob.cpp
${_partition}/jobs/DeactivateVolumeGroupJob.cpp
${_partition}/jobs/DeletePartitionJob.cpp
${_partition}/jobs/FillGlobalStorageJob.cpp
${_partition}/jobs/FormatPartitionJob.cpp
${_partition}/jobs/PartitionJob.cpp
${_partition}/jobs/RemoveVolumeGroupJob.cpp
${_partition}/jobs/ResizePartitionJob.cpp
${_partition}/jobs/ResizeVolumeGroupJob.cpp
${_partition}/jobs/SetPartitionFlagsJob.cpp
RESOURCES
partitionq.qrc
LINK_PRIVATE_LIBRARIES
kpmcore
KF5::CoreAddons
COMPILE_DEFINITIONS ${_partition_defs}
SHARED_LIB
)
else()
if ( NOT KPMcore_FOUND )
calamares_skip_module( "partition (missing suitable KPMcore)" )
else()
calamares_skip_module( "partition (missing dependencies for KPMcore)" )
endif()
endif()

View File

@@ -0,0 +1,987 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2014-2017 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "ChoicePageQml.h"
#include "Config.h"
#include "FlatPartitionModel.h"
#include "core/BootLoaderModel.h"
#include "core/DeviceModel.h"
#include "core/KPMHelpers.h"
#include "core/OsproberEntry.h"
#include "core/PartUtils.h"
#include "core/PartitionActions.h"
#include "core/PartitionCoreModule.h"
#include "core/PartitionInfo.h"
#include "core/PartitionModel.h"
#include "Branding.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "partition/PartitionIterator.h"
#include "partition/PartitionQuery.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include "utils/Units.h"
#include <kpmcore/core/device.h>
#ifdef WITH_KPMCORE4API
#include <kpmcore/core/softwareraid.h>
#endif
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrent>
using CalamaresUtils::Partition::findPartitionByPath;
using CalamaresUtils::Partition::isPartitionFreeSpace;
using CalamaresUtils::Partition::PartitionIterator;
using InstallChoice = Config::InstallChoice;
using SwapChoice = Config::SwapChoice;
/**
* @brief ChoicePage::ChoicePage is the default constructor. Called on startup as part of
* the module loading code path.
* @param parent the QObject parent.
*/
ChoicePageQml::ChoicePageQml( Config* config, QObject* parent )
: QObject( parent )
, m_config( config )
, m_nextEnabled( false )
, m_core( nullptr )
, m_isEfi( false )
, m_enableEncryptionWidget( true )
, m_partitionModel( new PartitionModel( this ) )
, m_partitionListModel( nullptr )
{
auto gs = Calamares::JobQueue::instance()->globalStorage();
m_enableEncryptionWidget = gs->value( "enableLuksAutomatedPartitioning" ).toBool();
gs->insert( "reuseHome", false );
}
void
ChoicePageQml::init( PartitionCoreModule* core )
{
m_core = core;
m_isEfi = PartUtils::isEfiSystem();
ChoicePageQml::applyDeviceChoice();
}
void
ChoicePageQml::setDeviceIndex( int index )
{
m_deviceIndex = index;
ChoicePageQml::applyDeviceChoice();
}
/**
* @brief ChoicePage::selectedDevice queries the device picker (which may be a combo or
* a list view) to get a pointer to the currently selected Device.
* @return a Device pointer, valid in the current state of the PCM, or nullptr if
* something goes wrong.
*/
Device*
ChoicePageQml::selectedDevice()
{
Device* currentDevice = nullptr;
currentDevice
= m_core->deviceModel()->deviceForIndex( m_core->deviceModel()->index( m_deviceIndex ) );
return currentDevice;
}
void
ChoicePageQml::setInstallChoice(int choice){
if(choice == InstallChoiceType::EraseChoice){
m_config->setInstallChoice( InstallChoice::Erase );
} else if(choice == InstallChoiceType::ReplaceChoice){
m_config->setInstallChoice( InstallChoice::Replace );
} else if(choice == InstallChoiceType::AlongSideChoice){
m_config->setInstallChoice( InstallChoice::Alongside );
} else if(choice == InstallChoiceType::ManualChoice){
m_config->setInstallChoice( InstallChoice::Manual );
}
Device* currd = selectedDevice();
if ( currd )
{
applyActionChoice( m_config->installChoice() );
}
}
void
ChoicePageQml::setPartitionModelForDevice()
{
Device* currd = selectedDevice();
if ( !currd )
{
return;
}
OsproberEntryList osproberEntriesForCurrentDevice = getOsproberEntriesForDevice( currd );
m_partitionModel->init(currd, osproberEntriesForCurrentDevice);
}
PartitionModel*
ChoicePageQml::partitionModel() const
{
return m_partitionModel;
}
PartitionListModel*
ChoicePageQml::partitionListModel() const
{
return m_partitionListModel;
}
QVariantList
ChoicePageQml::getEraseSwapChoices() const
{
auto choices = m_config->swapChoices();
QVariantList qvChoices;
QVariantMap qvChoice;
// Check which choices are supported by the current device
for ( auto c : choices )
{
if ( c == SwapChoice::NoSwap )
{
qvChoice["label"] = tr( "No swap" );
qvChoice["value"] = c;
qvChoices.append( qvChoice );
}
if ( c == SwapChoice::ReuseSwap )
{
qvChoice["label"] = tr( "Reuse swap" );
qvChoice["value"] = c;
qvChoices.append( qvChoice );
}
if ( c == SwapChoice::SmallSwap )
{
qvChoice["label"] = tr( "Swap (no Hibernate)" );
qvChoice["value"] = c;
qvChoices.append( qvChoice );
}
if ( c == SwapChoice::FullSwap )
{
qvChoice["label"] = tr( "Swap (with Hibernate)" );
qvChoice["value"] = c;
qvChoices.append( qvChoice );
}
if ( c == SwapChoice::SwapFile )
{
qvChoice["label"] = tr( "Swap to file" );
qvChoice["value"] = c;
qvChoices.append( qvChoice );
}
}
// If no choices are supported, only offer the NoSwap and full swap choices
// As done in the partition config
if ( qvChoices.isEmpty() )
{
qvChoice["label"] = tr( "No swap" );
qvChoice["value"] = SwapChoice::NoSwap;
qvChoices.append( qvChoice );
qvChoice["label"] = tr( "Swap (with Hibernate)" );
qvChoice["value"] = SwapChoice::FullSwap;
qvChoices.append( qvChoice );
}
return qvChoices;
}
int
ChoicePageQml::getInitialSwapChoice() const
{
return m_config->initialSwapChoice();
}
void
ChoicePageQml::setSwapChoice(int selectedSwapChoice)
{
if ( selectedSwapChoice == SwapChoice::NoSwap ) {
m_config->setSwapChoice( SwapChoice::NoSwap );
} else if ( selectedSwapChoice == SwapChoice::ReuseSwap ) {
m_config->setSwapChoice( SwapChoice::ReuseSwap );
} else if ( selectedSwapChoice == SwapChoice::SmallSwap ) {
m_config->setSwapChoice( SwapChoice::SmallSwap );
} else if ( selectedSwapChoice == SwapChoice::FullSwap ) {
m_config->setSwapChoice( SwapChoice::FullSwap );
} else if ( selectedSwapChoice == SwapChoice::SwapFile ) {
m_config->setSwapChoice( SwapChoice::SwapFile );
}
onActionChanged();
}
bool
ChoicePageQml::encryptWidgetEnabled() const
{
return m_enableEncryptionWidget;
}
void
ChoicePageQml::setEncryptionSelected(bool enabled)
{
m_encryptionSelected = enabled;
if ( m_config->installChoice() == InstallChoice::Erase )
{
if (m_encryptionSelected)
{
applyActionChoice( m_config->installChoice() );
}
}
updateNextEnabled();
}
void
ChoicePageQml::setEncryptionPhrase(const QString& phrase)
{
m_encryptPassphrase = phrase;
}
/**
* @brief ChoicePage::applyDeviceChoice handler for the selected event of the device
* picker. Calls ChoicePage::selectedDevice() to get the current Device*, then
* updates the preview widget for the on-disk state (calls ChoicePage::
* updateDeviceStatePreview()) and finally sets up the available actions and their
* text by calling ChoicePage::setupActions().
*/
void
ChoicePageQml::applyDeviceChoice()
{
if ( !selectedDevice() )
{
return;
}
if ( m_core->isDirty() )
{
emit scanningDialogShow();
QFuture<void> future =
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertAllDevices();
} );
// wait for the future to finish
future.waitForFinished();
emit scanningDialogHide();
continueApplyDeviceChoice();
}
else
{
continueApplyDeviceChoice();
}
}
void
ChoicePageQml::continueApplyDeviceChoice()
{
Device* currd = selectedDevice();
// The device should only be nullptr immediately after a PCM reset.
// applyDeviceChoice() will be called again momentarily as soon as we handle the
// PartitionCoreModule::reverted signal.
if ( !currd )
{
return;
}
// Preview setup done. Now we show/hide choices as needed.
setupActions();
cDebug() << "Previous device" << m_lastSelectedDeviceIndex << "new device" << m_deviceIndex;
if ( m_lastSelectedDeviceIndex != m_deviceIndex )
{
m_lastSelectedDeviceIndex = m_deviceIndex;
m_lastSelectedActionIndex = -1;
m_config->setInstallChoice( m_config->initialInstallChoice() );
}
Q_EMIT actionChosen();
Q_EMIT deviceChosen();
}
void ChoicePageQml::setSelectedPartitionForAction(QModelIndex index)
{
// map the index from partition list to partition model
QModelIndex mappedIndex = m_partitionListModel->mapToSource(index);
Partition* partition = m_partitionModel->partitionForIndex(mappedIndex);
}
void
ChoicePageQml::onActionChanged()
{
Device* currd = selectedDevice();
if ( currd )
{
applyActionChoice( m_config->installChoice() );
}
}
void
ChoicePageQml::applyActionChoice( InstallChoice choice )
{
cDebug() << "Prev" << m_lastSelectedActionIndex << "InstallChoice" << choice
<< Config::installChoiceNames().find( choice );
switch ( choice )
{
case InstallChoice::Erase:
{
auto gs = Calamares::JobQueue::instance()->globalStorage();
PartitionActions::Choices::AutoPartitionOptions options { gs->value( "defaultPartitionTableType" ).toString(),
m_config->eraseFsType(),
m_encryptPassphrase,
gs->value( "efiSystemPartition" ).toString(),
CalamaresUtils::GiBtoBytes(
gs->value( "requiredStorageGiB" ).toDouble() ),
m_config->swapChoice() };
if ( m_core->isDirty() )
{
emit scanningDialogShow();
QFuture<void> future = QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} );
// wait for the future to finish
future.waitForFinished();
updateNextEnabled();
PartitionActions::doAutopartition( m_core, selectedDevice(), options );
Q_EMIT deviceChosen();
emit scanningDialogHide();
}
else
{
PartitionActions::doAutopartition( m_core, selectedDevice(), options );
Q_EMIT deviceChosen();
}
}
break;
case InstallChoice::Replace:
if ( m_core->isDirty() )
{
emit scanningDialogShow();
QFuture<void> future =
QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} );
// wait for the future to finish
future.waitForFinished();
emit scanningDialogHide();
}
break;
case InstallChoice::Alongside:
if ( m_core->isDirty() )
{
emit scanningDialogShow();
QFuture<void> future = QtConcurrent::run(
[ = ]
{
QMutexLocker locker( &m_coreMutex );
m_core->revertDevice( selectedDevice() );
} );
// wait for the future to finish
future.waitForFinished();
updateNextEnabled();
emit scanningDialogHide();
}
break;
case InstallChoice::NoChoice:
case InstallChoice::Manual:
break;
}
updateNextEnabled();
}
void
ChoicePageQml::onLeave()
{
}
bool
ChoicePageQml::eraseButtonEnabled()
{
return m_eraseButtonEnabled;
}
void
ChoicePageQml::setEraseButtonEnabled( bool enabled )
{
m_eraseButtonEnabled = enabled;
emit eraseButtonEnabledChanged();
}
bool
ChoicePageQml::alongSideButtonEnabled()
{
return m_alongSideButtonEnabled;
}
void
ChoicePageQml::setAlongSideButtonEnabled( bool enabled )
{
m_alongSideButtonEnabled = enabled;
emit alongSideButtonEnabledChanged();
}
bool
ChoicePageQml::replaceButtonEnabled()
{
return m_replaceButtonEnabled;
}
void
ChoicePageQml::setReplaceButtonEnabled( bool enabled )
{
m_replaceButtonEnabled = enabled;
emit replaceButtonEnabledChanged();
}
bool
ChoicePageQml::manualButtonEnabled()
{
return m_somethingElseButtonEnabled;
}
void
ChoicePageQml::setManualButtonEnabled( bool enabled )
{
m_somethingElseButtonEnabled = enabled;
emit manualButtonEnabledChanged();
}
QString
ChoicePageQml::messageLabel() const
{
return m_messageLabel;
}
void
ChoicePageQml::setMessageLabel( QString messageLabel )
{
m_messageLabel = messageLabel;
emit messageLabelChanged();
}
QString
ChoicePageQml::eraseButtonLabel() const
{
return m_eraseButtonLabel;
}
void
ChoicePageQml::setEraseButtonLabel( QString eraseButtonLabel )
{
m_eraseButtonLabel = eraseButtonLabel;
emit eraseButtonLabelChanged();
}
QString
ChoicePageQml::alongSideButtonLabel() const
{
return m_alongSideButtonLabel;
}
void
ChoicePageQml::setAlongSideButtonLabel( QString alongSideButtonLabel )
{
m_alongSideButtonLabel = alongSideButtonLabel;
emit alongSideButtonLabelChanged();
}
QString
ChoicePageQml::replaceButtonLabel() const
{
return m_replaceButtonLabel;
}
void
ChoicePageQml::setReplaceButtonLabel( QString replaceButtonLabel )
{
m_replaceButtonLabel = replaceButtonLabel;
emit replaceButtonLabelChanged();
}
QString
ChoicePageQml::somethingElseButtonLabel() const
{
return m_somethingElseButtonLabel;
}
void
ChoicePageQml::setSomethingElseButtonLabel( QString somethingElseButtonLabel )
{
m_somethingElseButtonLabel = somethingElseButtonLabel;
emit somethingElseButtonLabelChanged();
}
void
ChoicePageQml::setupEfiSystemPartitionSelector()
{
Q_ASSERT( m_isEfi );
// Only the already existing ones:
QList< Partition* > efiSystemPartitions = m_core->efiSystemPartitions();
if ( efiSystemPartitions.count() == 0 ) //should never happen
{
emit setEFIMessageLabel( tr( "An EFI system partition cannot be found anywhere "
"on this system. Please go back and use manual "
"partitioning to set up %1." )
.arg( Calamares::Branding::instance()->shortProductName() ) );
updateNextEnabled();
}
else if ( efiSystemPartitions.count() == 1 ) //probably most usual situation
{
emit setEFIMessageLabel( tr( "The EFI system partition at %1 will be used for "
"starting %2." )
.arg( efiSystemPartitions.first()->partitionPath() )
.arg( Calamares::Branding::instance()->shortProductName() ) );
}
else
{
//m_efiComboBox->show();
emit setEFIMessageLabel( tr( "EFI system partition:" ) );
for ( int i = 0; i < efiSystemPartitions.count(); ++i )
{
Partition* efiPartition = efiSystemPartitions.at( i );
//m_efiComboBox->addItem( efiPartition->partitionPath(), i );
// We pick an ESP on the currently selected device, if possible
if ( efiPartition->devicePath() == selectedDevice()->deviceNode() && efiPartition->number() == 1 )
{
qDebug() << "ToDo: set it in qml";
//m_efiComboBox->setCurrentIndex( i );
}
}
}
}
static inline QDebug&
operator<<( QDebug& s, PartitionIterator& it )
{
s << ( ( *it ) ? ( *it )->deviceNode() : QString( "<null device>" ) );
return s;
}
QString
describePartitionTypes( const QStringList& types )
{
if ( types.empty() )
{
return QCoreApplication::translate(
ChoicePageQml::staticMetaObject.className(), "any", "any partition-table type" );
}
if ( types.size() == 1 )
{
return types.first();
}
if ( types.size() == 2 )
{
return QCoreApplication::translate(
ChoicePageQml::staticMetaObject.className(), "%1 or %2", "partition-table types" )
.arg( types.at( 0 ), types.at( 1 ) );
}
// More than two, rather unlikely
return types.join( ", " );
}
QString
ChoicePageQml::getCurrentDevicePartitionType() const
{
return m_currentDevicePartitionTypeName;
}
/**
* @brief ChoicePage::setupActions happens every time a new Device* is selected in the
* device picker. Sets up the text and visibility of the partitioning actions based
* on the currently selected Device*, bootloader and os-prober output.
*/
void
ChoicePageQml::setupActions()
{
Logger::Once o;
m_partitionListModel = new PartitionListModel(m_partitionModel, this);
emit partitionListModelInitialized();
Device* currentDevice = selectedDevice();
OsproberEntryList osproberEntriesForCurrentDevice = getOsproberEntriesForDevice( currentDevice );
cDebug() << o << "Setting up actions for" << currentDevice->deviceNode() << "with"
<< osproberEntriesForCurrentDevice.count() << "entries.";
if ( currentDevice->partitionTable() )
{
m_currentDevicePartitionType = currentDevice->partitionTable()->type();
m_currentDevicePartitionTypeName = currentDevice->partitionTable()->typeName();
}
else
{
m_currentDevicePartitionType = PartitionTable::unknownTableType;
m_currentDevicePartitionTypeName = currentDevice->partitionTable()->typeName();
}
if ( m_config->allowManualPartitioning() )
{
setManualButtonEnabled(true);
}
else
{
setManualButtonEnabled(false);
}
bool atLeastOneCanBeResized = false;
bool atLeastOneCanBeReplaced = false;
bool atLeastOneIsMounted = false; // Suppress 'erase' if so
bool isInactiveRAID = false;
bool matchTableType = false;
#ifdef WITH_KPMCORE4API
if ( currentDevice->type() == Device::Type::SoftwareRAID_Device
&& static_cast< SoftwareRAID* >( currentDevice )->status() == SoftwareRAID::Status::Inactive )
{
cDebug() << Logger::SubEntry << "part of an inactive RAID device";
isInactiveRAID = true;
}
#endif
PartitionTable::TableType tableType = PartitionTable::unknownTableType;
if ( currentDevice->partitionTable() )
{
tableType = currentDevice->partitionTable()->type();
matchTableType = m_config->acceptPartitionTableType( tableType );
}
for ( auto it = PartitionIterator::begin( currentDevice ); it != PartitionIterator::end( currentDevice ); ++it )
{
if ( PartUtils::canBeResized( *it, o ) )
{
cDebug() << Logger::SubEntry << "contains resizable" << it;
atLeastOneCanBeResized = true;
}
if ( PartUtils::canBeReplaced( *it, o ) )
{
cDebug() << Logger::SubEntry << "contains replaceable" << it;
atLeastOneCanBeReplaced = true;
}
if ( ( *it )->isMounted() )
{
atLeastOneIsMounted = true;
}
}
if ( osproberEntriesForCurrentDevice.count() == 0 )
{
CALAMARES_RETRANSLATE(
cDebug() << "Setting texts for 0 osprober entries";
setMessageLabel(tr( "This storage device does not seem to have an operating system on it. "
"What would you like to do?<br/>"
"You will be able to review and confirm your choices "
"before any change is made to the storage device." ));
setEraseButtonLabel(tr( "<strong>Erase disk</strong><br/>"
"This will <font color=\"red\">delete</font> all data "
"currently present on the selected storage device." ));
setAlongSideButtonLabel(tr( "<strong>Install alongside</strong><br/>"
"The installer will shrink a partition to make room for %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
setReplaceButtonLabel(tr( "<strong>Replace a partition</strong><br/>"
"Replaces a partition with %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
);
emit labelsUpdated();
setReplaceButtonEnabled(false);
setAlongSideButtonEnabled(false);
}
else if ( osproberEntriesForCurrentDevice.count() == 1 )
{
QString osName = osproberEntriesForCurrentDevice.first().prettyName;
if ( !osName.isEmpty() )
{
CALAMARES_RETRANSLATE (
cDebug() << "Setting texts for 1 non-empty osprober entry";
setMessageLabel(tr( "This storage device has %1 on it. "
"What would you like to do?<br/>"
"You will be able to review and confirm your choices "
"before any change is made to the storage device." )
.arg( osName ));
setAlongSideButtonLabel(tr( "<strong>Install alongside</strong><br/>"
"The installer will shrink a partition to make room for %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
setEraseButtonLabel(tr( "<strong>Erase disk</strong><br/>"
"This will <font color=\"red\">delete</font> all data "
"currently present on the selected storage device." ));
setReplaceButtonLabel(tr( "<strong>Replace a partition</strong><br/>"
"Replaces a partition with %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
);
emit labelsUpdated();
}
else
{
CALAMARES_RETRANSLATE(
cDebug() << "Setting texts for 1 empty osprober entry";
setMessageLabel(tr( "This storage device already has an operating system on it. "
"What would you like to do?<br/>"
"You will be able to review and confirm your choices "
"before any change is made to the storage device." ));
setAlongSideButtonLabel(tr( "<strong>Install alongside</strong><br/>"
"The installer will shrink a partition to make room for %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
setEraseButtonLabel(tr( "<strong>Erase disk</strong><br/>"
"This will <font color=\"red\">delete</font> all data "
"currently present on the selected storage device." ));
setReplaceButtonLabel(tr( "<strong>Replace a partition</strong><br/>"
"Replaces a partition with %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
);
emit labelsUpdated();
}
}
else
{
// osproberEntriesForCurrentDevice has at least 2 items.
CALAMARES_RETRANSLATE(
cDebug() << "Setting texts for >= 2 osprober entries";
setMessageLabel(tr( "This storage device has multiple operating systems on it. "
"What would you like to do?<br/>"
"You will be able to review and confirm your choices "
"before any change is made to the storage device." ));
setAlongSideButtonLabel(tr( "<strong>Install alongside</strong><br/>"
"The installer will shrink a partition to make room for %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
setEraseButtonLabel(tr( "<strong>Erase disk</strong><br/>"
"This will <font color=\"red\">delete</font> all data "
"currently present on the selected storage device." ));
setReplaceButtonLabel(tr( "<strong>Replace a partition</strong><br/>"
"Replaces a partition with %1." )
.arg( Calamares::Branding::instance()->shortVersionedName() ));
);
emit labelsUpdated();
}
#ifdef DEBUG_PARTITION_UNSAFE
#ifdef DEBUG_PARTITION_BAIL_OUT
// If things can't be broken, allow all the buttons
atLeastOneCanBeReplaced = true;
atLeastOneCanBeResized = true;
atLeastOneIsMounted = false;
isInactiveRAID = false;
#endif
#endif
if ( atLeastOneCanBeReplaced )
{
setReplaceButtonEnabled(true);
}
else
{
cDebug() << "No partitions available for replace-action.";
setReplaceButtonEnabled(false);
}
if ( atLeastOneCanBeResized )
{
setAlongSideButtonEnabled(true);
}
else
{
cDebug() << "No partitions available for resize-action.";
setAlongSideButtonEnabled(false);
}
if ( !atLeastOneIsMounted && !isInactiveRAID )
{
setEraseButtonEnabled(true);
}
else
{
cDebug() << "No partitions ("
<< "any-mounted?" << atLeastOneIsMounted << "is-raid?" << isInactiveRAID << ") for erase-action.";
setEraseButtonEnabled(false);
}
bool isEfi = PartUtils::isEfiSystem();
bool efiSystemPartitionFound = !m_core->efiSystemPartitions().isEmpty();
if ( isEfi && !efiSystemPartitionFound )
{
cWarning() << "System is EFI but there's no EFI system partition, "
"DISABLING alongside and replace features.";
setAlongSideButtonEnabled(false);
setReplaceButtonEnabled(false);
}
if ( tableType != PartitionTable::unknownTableType && !matchTableType )
{
setMessageLabel(tr( "This storage device already has an operating system on it, "
"but the partition table <strong>%1</strong> is different from the "
"needed <strong>%2</strong>.<br/>" )
.arg( PartitionTable::tableTypeToName( tableType ) )
.arg( describePartitionTypes( m_config->partitionTableTypes() ) ) );
cWarning() << "Partition table" << PartitionTable::tableTypeToName( tableType )
<< "does not match the requirement " << m_config->partitionTableTypes().join( ',' )
<< ", ENABLING erase feature and DISABLING alongside, replace and manual features.";
setEraseButtonEnabled(true);
setAlongSideButtonEnabled(false);
setReplaceButtonEnabled(false);
setManualButtonEnabled(false);
cDebug() << "Replace button suppressed because partition table type mismatch.";
}
if ( !m_somethingElseButtonEnabled && !m_alongSideButtonEnabled && !m_replaceButtonEnabled
&& !m_eraseButtonEnabled)
{
if ( atLeastOneIsMounted )
{
setMessageLabel( tr( "This storage device has one of its partitions <strong>mounted</strong>." ) );
}
else
{
setMessageLabel(
tr( "This storage device is a part of an <strong>inactive RAID</strong> device." ) );
}
cWarning() << "No buttons available"
<< "replaced?" << atLeastOneCanBeReplaced << "resized?" << atLeastOneCanBeResized
<< "erased? (not-mounted and not-raid)" << !atLeastOneIsMounted << "and" << !isInactiveRAID;
}
if ( m_somethingElseButtonEnabled )
{
setSomethingElseButtonLabel(tr( "<strong>Manual partitioning</strong><br/>"
"You can create or resize partitions yourself." ));
}
emit labelsUpdated();
// Set Partition Model Here
ChoicePageQml::setPartitionModelForDevice();
}
OsproberEntryList
ChoicePageQml::getOsproberEntriesForDevice( Device* device ) const
{
OsproberEntryList eList;
for ( const OsproberEntry& entry : m_core->osproberEntries() )
{
if ( entry.path.startsWith( device->deviceNode() ) )
{
eList.append( entry );
}
}
return eList;
}
bool
ChoicePageQml::isNextEnabled() const
{
return m_nextEnabled;
}
bool
ChoicePageQml::calculateNextEnabled() const
{
bool enabled = false;
switch ( m_config->installChoice() )
{
case InstallChoice::NoChoice:
cDebug() << "No partitioning choice";
return false;
case InstallChoice::Replace:
case InstallChoice::Alongside:
enabled = true;
break;
case InstallChoice::Erase:
case InstallChoice::Manual:
enabled = true;
}
if ( !enabled )
{
cDebug() << "No valid choice made";
return false;
}
if ( m_isEfi
&& ( m_config->installChoice() == InstallChoice::Alongside
|| m_config->installChoice() == InstallChoice::Replace ) )
{
if ( m_core->efiSystemPartitions().count() == 0 )
{
cDebug() << "No EFI partition for alongside or replace";
return false;
}
}
if ( m_config->installChoice() == InstallChoice::Manual)
{
return true;
}
return true;
}
void
ChoicePageQml::updateNextEnabled()
{
bool enabled = calculateNextEnabled();
if ( enabled != m_nextEnabled )
{
m_nextEnabled = enabled;
Q_EMIT nextStatusChanged( enabled );
}
}
int
ChoicePageQml::lastSelectedDeviceIndex()
{
return m_lastSelectedDeviceIndex;
}
void
ChoicePageQml::setLastSelectedDeviceIndex( int index )
{
m_lastSelectedDeviceIndex = index;
}

View File

@@ -0,0 +1,214 @@
/* === This file is part of Calamares - <https://calamares.io> ===
* SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2014-2016 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2019 Collabora Ltd
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef CHOICEPAGEQML_H
#define CHOICEPAGEQML_H
#include <QMutex>
#include <QPointer>
#include <QSet>
#include "Config.h"
#include "core/OsproberEntry.h"
#include "FlatPartitionModel.h"
namespace Calamares
{
}
class Config;
class PartitionCoreModule;
class PartitionSplitterWidget;
class PartitionModel;
class Device;
using SwapChoiceSet = Config::SwapChoiceSet;
/**
* @brief The ChoicePage class is the first page of the partitioning interface.
* It offers a choice between partitioning operations and initiates all automated
* partitioning modes. For manual partitioning, see PartitionPage.
*/
class ChoicePageQml : public QObject
{
Q_OBJECT
Q_ENUMS(InstallChoiceType)
Q_PROPERTY( bool eraseButtonEnabled READ eraseButtonEnabled WRITE setEraseButtonEnabled NOTIFY eraseButtonEnabledChanged )
Q_PROPERTY( bool alongSideButtonEnabled READ alongSideButtonEnabled WRITE setAlongSideButtonEnabled NOTIFY alongSideButtonEnabledChanged )
Q_PROPERTY( bool replaceButtonEnabled READ replaceButtonEnabled WRITE setReplaceButtonEnabled NOTIFY replaceButtonEnabledChanged )
Q_PROPERTY( bool manualButtonEnabled READ manualButtonEnabled WRITE setManualButtonEnabled NOTIFY manualButtonEnabledChanged )
Q_PROPERTY( QString messageLabel READ messageLabel WRITE setMessageLabel NOTIFY messageLabelChanged )
Q_PROPERTY( QString eraseButtonLabel READ eraseButtonLabel WRITE setEraseButtonLabel NOTIFY eraseButtonLabelChanged )
Q_PROPERTY( QString alongSideButtonLabel READ alongSideButtonLabel WRITE setAlongSideButtonLabel NOTIFY alongSideButtonLabelChanged )
Q_PROPERTY( QString replaceButtonLabel READ replaceButtonLabel WRITE setReplaceButtonLabel NOTIFY replaceButtonLabelChanged )
Q_PROPERTY( QString somethingElseButtonLabel READ somethingElseButtonLabel WRITE setSomethingElseButtonLabel NOTIFY somethingElseButtonLabelChanged )
Q_PROPERTY( QAbstractItemModel* partitionModel READ partitionModel CONSTANT FINAL )
Q_PROPERTY( QAbstractListModel* partitionListModel READ partitionListModel CONSTANT FINAL )
public:
explicit ChoicePageQml( Config* config, QObject* parent = nullptr );
enum InstallChoiceType
{
EraseChoice,
AlongSideChoice,
ReplaceChoice,
ManualChoice
};
Q_ENUMS(InstallChoiceType)
/**
* @brief init runs when the PartitionViewStep and the PartitionCoreModule are
* ready. Sets up the rest of the UI based on os-prober output.
* @param core the PartitionCoreModule pointer.
*/
void init( PartitionCoreModule* core );
/**
* @brief isNextEnabled answers whether the current state of the page is such
* that progressing to the next page should be allowed.
* @return true if next is allowed, otherwise false.
*/
bool isNextEnabled() const;
/**
* @brief onLeave runs when control passes from this page to another one.
*/
void onLeave();
/**
* @brief applyActionChoice reacts to a choice of partitioning mode.
* @param choice the partitioning action choice.
*/
void applyActionChoice( Config::InstallChoice choice );
int lastSelectedDeviceIndex();
void setLastSelectedDeviceIndex( int index );
void onActionChanged();
bool eraseButtonEnabled();
bool alongSideButtonEnabled();
bool replaceButtonEnabled();
bool manualButtonEnabled();
void setEraseButtonEnabled( bool eraseButtonEnabled );
void setAlongSideButtonEnabled( bool alongSideButtonEnabled );
void setReplaceButtonEnabled( bool replaceButtonEnabled );
void setManualButtonEnabled( bool manualButtonEnabled );
QString messageLabel() const;
QString eraseButtonLabel() const;
QString alongSideButtonLabel() const;
QString replaceButtonLabel() const;
QString somethingElseButtonLabel() const;
void setMessageLabel( QString messageLabel );
void setEraseButtonLabel( QString eraseButtonLabel );
void setAlongSideButtonLabel( QString alongSideButtonLabel );
void setReplaceButtonLabel( QString replaceButtonLabel );
void setSomethingElseButtonLabel( QString somethingElseButtonLabel );
PartitionModel* partitionModel() const;
PartitionListModel* partitionListModel() const;
void setPartitionModelForDevice();
public Q_SLOTS:
void setDeviceIndex(int index);
void setupActions();
QString getCurrentDevicePartitionType() const;
void setInstallChoice(int installChoice);
QVariantList getEraseSwapChoices() const;
int getInitialSwapChoice() const;
void setSwapChoice(int selectedSwapChoice);
bool encryptWidgetEnabled() const;
void setEncryptionSelected(bool enabled);
void setEncryptionPhrase(const QString& phrase);
void setSelectedPartitionForAction(QModelIndex index);
Q_SIGNALS:
void partitionListModelInitialized();
void labelsUpdated();
void setEFIMessageLabel( const QString& message );
void scanningDialogShow();
void scanningDialogHide();
void eraseButtonEnabledChanged();
void alongSideButtonEnabledChanged();
void replaceButtonEnabledChanged();
void manualButtonEnabledChanged();
void messageLabelChanged();
void eraseButtonLabelChanged();
void alongSideButtonLabelChanged();
void replaceButtonLabelChanged();
void somethingElseButtonLabelChanged();
signals:
void nextStatusChanged( bool );
void actionChosen();
void deviceChosen();
private:
bool calculateNextEnabled() const;
void updateNextEnabled();
Device* selectedDevice();
void applyDeviceChoice(); // Start scanning new device
void continueApplyDeviceChoice(); // .. called after scan
OsproberEntryList getOsproberEntriesForDevice( Device* device ) const;
void setupEfiSystemPartitionSelector();
Config* m_config;
bool m_nextEnabled;
PartitionCoreModule* m_core;
PartitionModel* m_partitionModel;
PartitionListModel* m_partitionListModel;
QMutex m_previewsMutex;
bool m_isEfi;
int m_lastSelectedDeviceIndex = -1;
int m_lastSelectedActionIndex = -1;
bool m_enableEncryptionWidget;
bool m_encryptionSelected;
bool m_alongSideButtonEnabled;
bool m_eraseButtonEnabled;
bool m_replaceButtonEnabled;
bool m_somethingElseButtonEnabled;
int m_selectedDeviceIndex = -1;
int m_deviceIndex = -1;
QString m_messageLabel;
QString m_eraseButtonLabel;
QString m_alongSideButtonLabel;
QString m_replaceButtonLabel;
QString m_somethingElseButtonLabel;
QPointer< PartitionSplitterWidget > m_afterPartitionSplitterWidget;
QString m_currentDevicePartitionType;
QString m_currentDevicePartitionTypeName;
QString m_encryptPassphrase;
QMutex m_coreMutex;
};
#endif // CHOICEPAGEQML_H

View File

@@ -0,0 +1,310 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
import io.calamares.core 1.0
import io.calamares.ui 1.0
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls 1.4 as QQC1
import QtQuick.Window 2.14
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.7 as Kirigami
Item {
width: partitionPage.width
height: partitionPage.height
function listProperty(item)
{
for (var p in item)
console.log(p + ": " + item[p]);
}
Connections {
target: choicePage
onPartitionListModelInitialized: {
// Need to wait for partition model to initialize
// followed by partitionListModel
// Requires further refactor to have partitionListModel ready
// when choicePage is loaded instead of waiting for choicePage
// to load partitionModel first.
partitionView.model = choicePage.partitionListModel
}
}
RowLayout {
id: headerRowLayout
anchors.top: parent.top
width: parent.width
height: Kirigami.Units.gridUnits * 2
Item {
id: partitionTypeBox
Layout.preferredWidth: Kirigami.Units.gridUnit * 5
Layout.fillHeight: true
Item {
id: partitionTypeIcon
anchors.left: parent.left
width: parent.width * 0.35
height: parent.height
Image {
anchors.centerIn: parent
width: Kirigami.Units.iconSizes.large
height: width
source: "images/partition-table.svg"
}
}
Label {
id: partitionTypeLabel
anchors.left: partitionTypeIcon.right
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
}
}
Kirigami.Separator {
Layout.fillHeight: true
Layout.preferredWidth: 1
}
Label {
text: "Select Storage Device:"
font.bold: true
Layout.fillHeight: true
verticalAlignment: Qt.AlignVCenter
horizontalAlignment: Qt.AlignLeft
}
ComboBox {
id: deviceComboBox
Layout.fillWidth: true
Layout.fillHeight: true
model: core.deviceModel
textRole: "display"
currentIndex: core.currentDeviceIndex
onCurrentIndexChanged: {
core.currentDeviceIndex = currentIndex
choicePage.setDeviceIndex(currentIndex)
}
Component.onCompleted: {
choicePage.setupActions()
}
}
Item {
Layout.preferredWidth: Kirigami.Units.gridUnit * 5
Layout.fillHeight: true
}
}
Kirigami.Separator {
id: headerBoxLine
anchors.top: headerRowLayout.bottom
width: parent.width
height: 1
}
Item {
anchors.top: headerBoxLine.bottom
anchors.bottom: bottomSelectionBoxLine.top
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: Kirigami.Units.smallSpacing
ButtonGroup {
id: buttonGroup
}
RadioButton {
id: eraseBtn
objectName: "eraseBtn"
Layout.fillWidth: true
Layout.fillHeight: true
visible: choicePage.eraseButtonEnabled
enabled: choicePage.eraseButtonEnabled
ButtonGroup.group: buttonGroup
text: choicePage.eraseButtonLabel;
onClicked: {
choicePage.setInstallChoice(0)
}
}
RadioButton {
id: alongSideBtn
Layout.fillWidth: true
Layout.fillHeight: true
visible: choicePage.alongSideButtonEnabled
enabled: choicePage.alongSideButtonEnabled
ButtonGroup.group: buttonGroup
text: choicePage.alongSideButtonLabel;
onClicked: {
choicePage.setInstallChoice(1)
}
}
RadioButton {
id: replaceBtn
Layout.fillWidth: true
Layout.fillHeight: true
visible: choicePage.replaceButtonEnabled
enabled: choicePage.replaceButtonEnabled
ButtonGroup.group: buttonGroup
text: choicePage.replaceButtonLabel;
onClicked: {
choicePage.setInstallChoice(2)
}
}
RadioButton {
id: manualBtn
Layout.fillWidth: true
Layout.fillHeight: true
visible: choicePage.manualButtonEnabled
enabled: choicePage.manualButtonEnabled
ButtonGroup.group: buttonGroup
text: choicePage.somethingElseButtonLabel;
onClicked: {
choicePage.setInstallChoice(3)
}
}
RowLayout {
id: partitionViewLayout
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnits * 3
spacing: Kirigami.Units.smallSpacing
Repeater {
id: partitionView
delegate: Item {
id: partitionItem
Layout.preferredWidth: Kirigami.Units.gridUnit * 4
Layout.preferredHeight: Kirigami.Units.gridUnit * 3
ColumnLayout{
anchors.fill: parent
spacing: Kirigami.Units.smallSpacing
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 1.5
color: model.decoration
Label {
anchors.centerIn: parent
text: model.display
fontSizeMode: Text.Fit
font.pixelSize: parent.height * 0.25
minimumPixelSize: 8
color: Kirigami.Theme.textColor
}
MouseArea {
anchors.fill: parent
onClicked: {
listProperty(model)
}
}
}
Label {
Layout.fillWidth: true
Layout.fillHeight: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
font.pixelSize: parent.height * 0.25
minimumPixelSize: 8
color: Kirigami.Theme.textColor
text: model.toolTip.replace(" ", "\n")
}
}
}
onCountChanged: {
partitionView.forceLayout()
}
}
}
// ToDo: Partitioning Tree View Before and After View
}
}
Kirigami.Separator {
id: bottomSelectionBoxLine
anchors.bottom: selectionBoxes.top
width: parent.width
height: 1
visible: selectionBoxes.visible
}
Item {
id: selectionBoxes
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: parent.height * 0.2
visible: eraseBtn.checked || replaceBtn.checked || alongSideBtn.checked
ColumnLayout {
id: selectionBoxesLayout
width: parent.width
height: parent.height
ComboBox {
id: eraseSwapComboBox
Layout.fillWidth: true
Layout.fillHeight: true
model: choicePage.getEraseSwapChoices()
textRole: "label"
currentIndex: choicePage.getInitialSwapChoice()
onCurrentIndexChanged: {
choicePage.setSwapChoice(currentIndex)
}
visible: eraseBtn.checked
}
RadioButton {
id: encryptPartitionButton
Layout.fillWidth: true
Layout.fillHeight: true
text: "Encrypt Partition"
onClicked: {
if (encryptPartitionButton.checked) {
choicePage.setEncryptionSelected(true)
}
}
}
TextField {
id: encryptionPasswordField
Layout.fillWidth: true
Layout.fillHeight: true
visible: encryptPartitionButton.checked
onTextChanged: {
choicePage.setEncryptionPhrase(text)
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "FlatPartitionModel.h"
#include "core/PartitionModel.h"
PartitionListModel::PartitionListModel( PartitionModel* model, QObject* parent )
: QAbstractListModel( parent )
, m_model( model )
{
connect( m_model, &PartitionModel::rowsInserted, this, &PartitionListModel::rowsInserted );
connect( m_model, &PartitionModel::rowsRemoved, this, &PartitionListModel::rowsRemoved );
connect( m_model, &PartitionModel::dataChanged, this, &PartitionListModel::dataChanged );
}
int PartitionListModel::rowCount( const QModelIndex& parent ) const
{
return m_model->rowCount( parent );
}
QHash< int, QByteArray > PartitionListModel::roleNames() const
{
QHash< int, QByteArray > roles = m_model->roleNames();
roles.insert( Qt::UserRole + 1, "size" );
roles.insert( Qt::UserRole + 2, "partitionData" );
return roles;
}
QVariant PartitionListModel::data( const QModelIndex& index, int role ) const
{
// Convert the treeview index to a flat index
QModelIndex flatIndex = m_model->index( index.row(), 0, index.parent() );
QVariant data = m_model->data( flatIndex, role );
if ( role == PartitionModel::SizeRole )
{
data = m_model->data( flatIndex, PartitionModel::SizeRole );
}
if ( role == Qt::UserRole + 2 )
{
QVariantMap partitionData;
partitionData.insert( "IsFreeSpaceRole", m_model->data( flatIndex, PartitionModel::IsFreeSpaceRole ) );
partitionData.insert( "IsPartitionNewRole", m_model->data( flatIndex, PartitionModel::IsPartitionNewRole ) );
partitionData.insert( "FileSystemLabelRole", m_model->data( flatIndex, PartitionModel::FileSystemLabelRole ) );
partitionData.insert( "FileSystemTypeRole", m_model->data( flatIndex, PartitionModel::FileSystemTypeRole ) );
partitionData.insert( "PartitionPathRole", m_model->data( flatIndex, PartitionModel::PartitionPathRole ) );
partitionData.insert( "PartitionPtrRole", m_model->data( flatIndex, PartitionModel::PartitionPtrRole ) );
partitionData.insert( "OsproberNameRole", m_model->data( flatIndex, PartitionModel::OsproberNameRole ) );
partitionData.insert( "OsproberPathRole", m_model->data( flatIndex, PartitionModel::OsproberPathRole ) );
partitionData.insert( "OsproberCanBeResizedRole", m_model->data( flatIndex, PartitionModel::OsproberCanBeResizedRole ) );
partitionData.insert( "OsproberRawLineRole", m_model->data( flatIndex, PartitionModel::OsproberRawLineRole ) );
partitionData.insert( "OsproberHomePartitionPathRole", m_model->data( flatIndex, PartitionModel::OsproberHomePartitionPathRole ) );
data.setValue( partitionData );
}
return data;
}
QVariantMap PartitionListModel::get( int row ) const
{
return data( index( row, 0 ), Qt::DisplayRole ).toList().first().toMap();
}
QModelIndex PartitionListModel::mapToSource( const QModelIndex& index ) const
{
return m_model->index( index.row(), 0, index.parent() );
}

View File

@@ -0,0 +1,39 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef FLATPARTITIONMODEL_H
#define FLATPARTITIONMODEL_H
#include <QAbstractListModel>
#include <QObject>
#include <QStringList>
class PartitionModel;
// Flatten the PartionModel.
// This is used by the QML frontend to display the partition table.
class PartitionListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit PartitionListModel( PartitionModel* model, QObject* parent = nullptr );
int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
QVariant data( const QModelIndex& index, int role = Qt::DisplayRole ) const override;
QHash< int, QByteArray > roleNames() const override;
Q_INVOKABLE QVariantMap get( int row ) const;
QModelIndex mapToSource( const QModelIndex& index ) const;
private:
PartitionModel* m_model;
};
#endif // FLATPARTITIONMODEL_H

View File

@@ -0,0 +1,120 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <Aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "PartitionQmlViewStep.h"
#include "Config.h"
#include "ChoicePageQml.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "core/PartUtils.h"
#include "core/PartitionCoreModule.h"
#include <QtConcurrent/QtConcurrent>
CALAMARES_PLUGIN_FACTORY_DEFINITION( PartitionQmlViewStepFactory, registerPlugin< PartitionQmlViewStep >(); )
PartitionQmlViewStep::PartitionQmlViewStep( QObject* parent )
: Calamares::QmlViewStep( parent )
, m_config( new Config( this ) )
, m_core( nullptr )
, m_choicePage( nullptr )
{
m_choicePage = new ChoicePageQml(m_config, this);
m_core = new PartitionCoreModule( this ); // Unusable before init is complete!
Calamares::QmlViewStep::setContextProperty( "core", m_core );
Calamares::QmlViewStep::setContextProperty( "choicePage", m_choicePage );
emit nextStatusChanged( true );
}
QString
PartitionQmlViewStep::prettyName() const
{
return tr( "Partition" );
}
bool
PartitionQmlViewStep::isNextEnabled() const
{
return true;
}
bool
PartitionQmlViewStep::isBackEnabled() const
{
return true;
}
bool
PartitionQmlViewStep::isAtBeginning() const
{
return true;
}
bool
PartitionQmlViewStep::isAtEnd() const
{
return true;
}
Calamares::JobList
PartitionQmlViewStep::jobs() const
{
return m_core->jobs( m_config );
}
void
PartitionQmlViewStep::onActivate()
{
m_choicePage->init( m_core );
}
void
PartitionQmlViewStep::onLeave()
{
}
QObject*
PartitionQmlViewStep::getConfig()
{
return m_config;
}
QObject* PartitionQmlViewStep::getCore()
{
return m_core;
}
void
PartitionQmlViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{
m_config->setConfigurationMap( configurationMap );
Calamares::QmlViewStep::setConfigurationMap( configurationMap );
m_future = new QFutureWatcher< void >();
connect( m_future,
&QFutureWatcher< void >::finished,
this,
[ this ]
{
this->m_future->deleteLater();
this->m_future = nullptr;
} );
QFuture< void > future = QtConcurrent::run( this, &PartitionQmlViewStep::initPartitionCoreModule );
m_future->setFuture( future );
}
void
PartitionQmlViewStep::initPartitionCoreModule()
{
Q_ASSERT( m_core );
m_core->init();
}

View File

@@ -0,0 +1,60 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <Aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef PARTITIONQMLVIEWSTEP_H
#define PARTITIONQMLVIEWSTEP_H
#include "Config.h"
#include "DllMacro.h"
#include "utils/PluginFactory.h"
#include "viewpages/QmlViewStep.h"
#include "core/PartitionCoreModule.h"
#include "ChoicePageQml.h"
#include <QObject>
template < typename T >
class QFutureWatcher;
class PLUGINDLLEXPORT PartitionQmlViewStep : public Calamares::QmlViewStep
{
Q_OBJECT
public:
explicit PartitionQmlViewStep( QObject* parent = nullptr );
QString prettyName() const override;
bool isNextEnabled() const override;
bool isBackEnabled() const override;
bool isAtBeginning() const override;
bool isAtEnd() const override;
Calamares::JobList jobs() const override;
void onActivate() override;
void onLeave() override;
void setConfigurationMap( const QVariantMap& configurationMap ) override;
QObject* getConfig() override;
QObject* getCore();
private:
void initPartitionCoreModule();
Config* m_config;
PartitionCoreModule* m_core;
QFutureWatcher< void >* m_future;
ChoicePageQml* m_choicePage;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( PartitionQmlViewStepFactory )
#endif // PARTITIONQMLVIEWSTEP_H

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="22"
height="22"
id="svg3813"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="kr_diskusage.svg">
<defs
id="defs3815" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="32"
inkscape:cx="14.427263"
inkscape:cy="11.039521"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-x="-4"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="false">
<inkscape:grid
type="xygrid"
id="grid4109" />
<sodipodi:guide
position="2.0000072,19.999993"
orientation="18,0"
id="guide4115" />
<sodipodi:guide
position="2.0000072,1.9999929"
orientation="0,18"
id="guide4117" />
<sodipodi:guide
position="20.000007,1.9999929"
orientation="-18,0"
id="guide4119" />
<sodipodi:guide
position="20.000007,19.999993"
orientation="0,-18"
id="guide4121" />
<sodipodi:guide
position="3.0000072,18.999993"
orientation="16,0"
id="guide4123" />
<sodipodi:guide
position="3.0000072,2.9999929"
orientation="0,16"
id="guide4125" />
<sodipodi:guide
position="19.000007,2.9999929"
orientation="-16,0"
id="guide4127" />
<sodipodi:guide
position="19.000007,18.999993"
orientation="0,-16"
id="guide4129" />
</sodipodi:namedview>
<metadata
id="metadata3818">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-378.85714,-540.07647)">
<path
inkscape:connector-curvature="0"
style="color:#000000;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 389.85714,543.07647 c -4.432,0 -8,3.568 -8,8 0,4.432 3.568,8 8,8 4.432,0 8,-3.568 8,-8 0,-4.432 -3.568,-8 -8,-8 z m 0,1 c 3.878,0 7,3.122 7,7 0,3.878 -3.122,7 -7,7 -3.878,0 -7,-3.122 -7,-7 0,-3.878 3.122,-7 7,-7 z"
id="rect4185" />
<path
inkscape:connector-curvature="0"
style="color:#000000;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 389.85714,547.07647 c -2.216,0 -4,1.784 -4,4 0,2.216 1.784,4 4,4 2.216,0 4,-1.784 4,-4 0,-2.216 -1.784,-4 -4,-4 z m 0,1 c 0.46399,0 0.89764,0.11233 1.28906,0.29883 0.18113,-0.1837 0.43139,-0.29883 0.71094,-0.29883 0.554,0 1,0.446 1,1 0,0.27955 -0.11513,0.52981 -0.29883,0.71094 0.18651,0.39142 0.29883,0.82507 0.29883,1.28906 0,1.662 -1.338,3 -3,3 -1.662,0 -3,-1.338 -3,-3 0,-1.662 1.338,-3 3,-3 z"
id="rect4225" />
<path
inkscape:connector-curvature="0"
style="color:#000000;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 391.85714,547.07647 c -1.108,0 -2,0.892 -2,2 0,1.108 0.892,2 2,2 1.108,0 2,-0.892 2,-2 0,-1.108 -0.892,-2 -2,-2 z m 0,1 c 0.554,0 1,0.446 1,1 0,0.554 -0.446,1 -1,1 -0.554,0 -1,-0.446 -1,-1 0,-0.554 0.446,-1 1,-1 z"
id="rect4230" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -0,0 +1,4 @@
# SPDX-FileCopyrightText: no
# SPDX-License-Identifier: CC0-1.0
# ToDo: use for qml module specific settings

View File

@@ -0,0 +1,46 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Aditya Mehra <aix.m@outlook.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
import io.calamares.core 1.0
import io.calamares.ui 1.0
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.14
import QtQuick.Layouts 1.3
import org.kde.kirigami 2.7 as Kirigami
Page {
id: partitionPage
Connections {
target: core
onInitCompleted: {
console.log("Partition Core Module Init Completed")
busyIndicator.running = false
busyIndicator.visible = false
partitionViewLoader.source = "qrc:/ChoosePage.qml"
}
}
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
running: true
visible: true
width: parent.width / 2
height: parent.height / 2
}
Loader {
id: partitionViewLoader
anchors.fill: parent
}
}

View File

@@ -0,0 +1,7 @@
<RCC>
<qresource>
<file>partitionq.qml</file>
<file>ChoosePage.qml</file>
<file>images/partition-table.svg</file>
</qresource>
</RCC>