/* === This file is part of Calamares - === * * SPDX-FileCopyrightText: 2017 Kyle Robbertze * SPDX-FileCopyrightText: 2017-2018 2020, Adriaan de Groot * SPDX-License-Identifier: GPL-3.0-or-later * * Calamares is Free Software: see the License-Identifier above. * */ #include "PackageModel.h" #include "utils/Logger.h" #include "utils/Variant.h" #include "utils/Yaml.h" PackageModel::PackageModel( QObject* parent ) : QAbstractItemModel( parent ) { } PackageModel::~PackageModel() { delete m_rootItem; } QModelIndex PackageModel::index( int row, int column, const QModelIndex& parent ) const { if ( !m_rootItem || !hasIndex( row, column, parent ) ) { return QModelIndex(); } PackageTreeItem* parentItem; if ( !parent.isValid() ) { parentItem = m_rootItem; } else { parentItem = static_cast< PackageTreeItem* >( parent.internalPointer() ); } PackageTreeItem* childItem = parentItem->child( row ); if ( childItem ) { return createIndex( row, column, childItem ); } else { return QModelIndex(); } } QModelIndex PackageModel::parent( const QModelIndex& index ) const { if ( !m_rootItem || !index.isValid() ) { return QModelIndex(); } PackageTreeItem* child = static_cast< PackageTreeItem* >( index.internalPointer() ); PackageTreeItem* parent = child->parentItem(); if ( parent == m_rootItem ) { return QModelIndex(); } return createIndex( parent->row(), 0, parent ); } int PackageModel::rowCount( const QModelIndex& parent ) const { if ( !m_rootItem || ( parent.column() > 0 ) ) { return 0; } PackageTreeItem* parentItem; if ( !parent.isValid() ) { parentItem = m_rootItem; } else { parentItem = static_cast< PackageTreeItem* >( parent.internalPointer() ); } return parentItem->childCount(); } int PackageModel::columnCount( const QModelIndex& ) const { return 2; } QVariant PackageModel::data( const QModelIndex& index, int role ) const { if ( !m_rootItem || !index.isValid() ) { return QVariant(); } PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() ); switch ( role ) { case Qt::CheckStateRole: return index.column() == NameColumn ? ( item->isImmutable() ? QVariant() : item->isSelected() ) : QVariant(); case Qt::DisplayRole: return item->isHidden() ? QVariant() : item->data( index.column() ); case MetaExpandRole: return item->isHidden() ? false : item->expandOnStart(); default: return QVariant(); } } bool PackageModel::setData( const QModelIndex& index, const QVariant& value, int role ) { if ( !m_rootItem ) { return false; } if ( role == Qt::CheckStateRole && index.isValid() ) { PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() ); item->setSelected( static_cast< Qt::CheckState >( value.toInt() ) ); emit dataChanged( this->index( 0, 0 ), index.sibling( index.column(), index.row() + 1 ), QVector< int >( Qt::CheckStateRole ) ); } return true; } Qt::ItemFlags PackageModel::flags( const QModelIndex& index ) const { if ( !m_rootItem || !index.isValid() ) { return Qt::ItemFlags(); } if ( index.column() == NameColumn ) { PackageTreeItem* item = static_cast< PackageTreeItem* >( index.internalPointer() ); if ( item->isImmutable() ) { return QAbstractItemModel::flags( index ); //Qt::NoItemFlags; } return Qt::ItemIsUserCheckable | QAbstractItemModel::flags( index ); } return QAbstractItemModel::flags( index ); } QVariant PackageModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { return ( section == NameColumn ) ? tr( "Name" ) : tr( "Description" ); } return QVariant(); } PackageTreeItem::List PackageModel::getPackages() const { if ( !m_rootItem ) { return PackageTreeItem::List(); } auto items = getItemPackages( m_rootItem ); for ( auto package : m_hiddenItems ) { if ( package->hiddenSelected() ) { items.append( getItemPackages( package ) ); } } return items; } PackageTreeItem::List PackageModel::getItemPackages( PackageTreeItem* item ) const { PackageTreeItem::List selectedPackages; for ( int i = 0; i < item->childCount(); i++ ) { auto* child = item->child( i ); if ( child->isSelected() == Qt::Unchecked ) { continue; } if ( child->isPackage() ) // package { selectedPackages.append( child ); } else { selectedPackages.append( getItemPackages( child ) ); } } return selectedPackages; } void PackageModel::setupModelData( const QVariantList& groupList, PackageTreeItem* parent ) { for ( const auto& group : groupList ) { QVariantMap groupMap = group.toMap(); if ( groupMap.isEmpty() ) { continue; } PackageTreeItem* item = new PackageTreeItem( groupMap, PackageTreeItem::GroupTag { parent } ); if ( groupMap.contains( "selected" ) ) { item->setSelected( CalamaresUtils::getBool( groupMap, "selected", false ) ? Qt::Checked : Qt::Unchecked ); } if ( groupMap.contains( "packages" ) ) { for ( const auto& packageName : groupMap.value( "packages" ).toList() ) { if ( packageName.type() == QVariant::String ) { item->appendChild( new PackageTreeItem( packageName.toString(), item ) ); } else { QVariantMap m = packageName.toMap(); if ( !m.isEmpty() ) { item->appendChild( new PackageTreeItem( m, PackageTreeItem::PackageTag { item } ) ); } } } if ( !item->childCount() ) { cWarning() << "*packages* under" << item->name() << "is empty."; } } if ( groupMap.contains( "subgroups" ) ) { bool haveWarned = false; const auto& subgroupValue = groupMap.value( "subgroups" ); if ( !subgroupValue.canConvert( QVariant::List ) ) { cWarning() << "*subgroups* under" << item->name() << "is not a list."; haveWarned = true; } QVariantList subgroups = groupMap.value( "subgroups" ).toList(); if ( !subgroups.isEmpty() ) { setupModelData( subgroups, item ); // The children might be checked while the parent isn't (yet). // Children are added to their parent (below) without affecting // the checked-state -- do it manually. Items with subgroups // but no children have only hidden children -- those get // handled specially. if ( item->childCount() > 0 ) { item->updateSelected(); } } else { if ( !haveWarned ) { cWarning() << "*subgroups* list under" << item->name() << "is empty."; } } } if ( item->isHidden() ) { m_hiddenItems.append( item ); if ( !item->isSelected() ) { cWarning() << "Item" << ( item->parentItem() ? item->parentItem()->name() : QString() ) << '.' << item->name() << "is hidden, but not selected."; } } else { item->setCheckable( true ); parent->appendChild( item ); } } } void PackageModel::setupModelData( const QVariantList& l ) { emit beginResetModel(); delete m_rootItem; m_rootItem = new PackageTreeItem(); setupModelData( l, m_rootItem ); emit endResetModel(); }