diff options
Diffstat (limited to 'ui/qt5/pkgmodel.cc')
-rw-r--r-- | ui/qt5/pkgmodel.cc | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/ui/qt5/pkgmodel.cc b/ui/qt5/pkgmodel.cc new file mode 100644 index 0000000..4cd9170 --- /dev/null +++ b/ui/qt5/pkgmodel.cc @@ -0,0 +1,296 @@ +/* + * pkgmodel.cc - Implementation of the custom package selection model classes + * horizon-qt5, the Qt 5 user interface for + * Project Horizon + * + * Copyright (c) 2019 Adélie Linux and contributors. All rights reserved. + * This code is licensed under the AGPL 3.0 license, as noted in the + * LICENSE-code file in the root directory of this repository. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +#include "pkgmodel.hh" +#include <QFile> +#include <QIcon> +#include <QMessageBox> + +PkgItem::PkgItem(PkgItem *parent, QString friendly, QString internal, + QString description, QString icon) : _parent(parent), + _friendly(friendly), + _internal(internal), + _desc(description) { + if(icon.isEmpty()) { + _icon = internal; + } else { + _icon = icon; + } +} + +PkgItem::~PkgItem() { + qDeleteAll(_children); +} + +void PkgItem::addChild(PkgItem *pkg) { + _children.append(pkg); +} + +PkgItem *PkgItem::child(int row) { + if(row < 0 || row > _children.size()) { + return nullptr; + } + + return _children.at(row); +} + +int PkgItem::childCount() const { + return _children.size(); +} + +QVariant PkgItem::data(int column, int role) { + if(column != 0) { + return QVariant(); + } + + switch(role) { + case Qt::DisplayRole: + return QVariant(_friendly); + case Qt::DecorationRole: + return QVariant(QIcon::fromTheme(_icon)); + case Qt::StatusTipRole: + case Qt::WhatsThisRole: + return QVariant(_desc); + case Qt::UserRole: + return QVariant(_internal); + } + + return QVariant(); +} + +QStringList PkgItem::internalNames() const { + QStringList list; + + if(!_internal.isEmpty()) { + list.append(_internal); + } + + for(auto &child : _children) { + list.append(child->internalNames()); + } + + return list; +} + +PkgItem *PkgItem::parent() { + return _parent; +} + +int PkgItem::row() const { + if(_parent == nullptr) { + return 0; + } + + return _parent->_children.indexOf(const_cast<PkgItem *>(this)); +} + + +PkgItemModel::PkgItemModel(QStringList *pkgList, QObject *parent) + : QAbstractItemModel(parent), _packages(pkgList) { + _root = new PkgItem(nullptr, "Root Item", "root", "The root of the tree. Never seen."); + + loadPackages(); +} + +PkgItemModel::~PkgItemModel() { + delete _root; +} + +int PkgItemModel::columnCount(const QModelIndex &) const { + return 1; +} + +QVariant PkgItemModel::data(const QModelIndex &index, int role) const { + if(!index.isValid()) { + return QVariant(); + } + + PkgItem *item = static_cast<PkgItem *>(index.internalPointer()); + if(role == Qt::CheckStateRole && index.column() == 0) { + QStringList pkgs = item->internalNames(); + if(_packages && !pkgs.isEmpty()) { + bool all = true, any = false; + for(auto &pkg : pkgs) { + if(_packages->contains(pkg)) { + any = true; + } else { + all = false; + } + } + + if(all) return Qt::Checked; + if(any) return Qt::PartiallyChecked; + } + return Qt::Unchecked; + } + + return item->data(index.column(), role); +} + +Qt::ItemFlags PkgItemModel::flags(const QModelIndex &index) const { + if(!index.isValid()) { + return Qt::NoItemFlags; + } + + Qt::ItemFlags flags = Qt::ItemIsEnabled; + if(index.column() == 0) { + flags |= Qt::ItemIsUserCheckable | Qt::ItemIsSelectable | Qt::ItemIsAutoTristate; + } + + return flags; +} + +QVariant PkgItemModel::headerData(int, Qt::Orientation, int) const { + return QVariant(); +} + +QModelIndex PkgItemModel::index(int row, int column, + const QModelIndex &parent) const { + if(!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + PkgItem *p; + + if(parent.isValid()) { + p = static_cast<PkgItem *>(parent.internalPointer()); + } else { + p = _root; + } + + PkgItem *item = p->child(row); + if(item) { + return createIndex(row, column, item); + } else { + return QModelIndex(); + } +} + +QModelIndex PkgItemModel::parent(const QModelIndex &child) const { + if(!child.isValid()) { + return QModelIndex(); + } + + PkgItem *item = static_cast<PkgItem *>(child.internalPointer()); + PkgItem *parent = item->parent(); + + if(parent == _root) { + return QModelIndex(); + } else { + return createIndex(parent->row(), 0, parent); + } +} + +int PkgItemModel::rowCount(const QModelIndex &parent) const { + if(parent.column() > 0) { + return 0; + } + + if(parent.isValid()) { + PkgItem *item = static_cast<PkgItem *>(parent.internalPointer()); + return item->childCount(); + } else { + return _root->childCount(); + } +} + +bool PkgItemModel::setData(const QModelIndex &index, const QVariant &value, + int role) { + if(!index.isValid() || role != Qt::CheckStateRole) { + return false; + } + + PkgItem *item = static_cast<PkgItem *>(index.internalPointer()); + if(item) { + QStringList pkgs = item->internalNames(); + if(value.toBool()) { + _packages->append(pkgs); + } else { + for(auto &pkg : pkgs) { + _packages->removeAll(pkg); + } + } + emit dataChanged(index, index, {Qt::CheckStateRole}); + if(item->childCount() > 0) { + emit dataChanged(createIndex(0, 0, item), + createIndex(item->childCount(), 0, item), + {Qt::CheckStateRole}); + } + + PkgItem *parent = item; + while(parent->parent() != _root) { + parent = parent->parent(); + QModelIndex pidx = createIndex(parent->row(), 0, parent); + emit dataChanged(pidx, pidx, {Qt::CheckStateRole}); + } + } + + return true; +} + +void PkgItemModel::setPackageList(QStringList *pkgList) { + _packages = pkgList; +} + +void PkgItemModel::loadPackages() { + QFile myList(":/packages.txt"); + if(!myList.open(QFile::ReadOnly)) { + QMessageBox::critical(nullptr, tr("Could Not Load Software List"), + tr("An issue occurred loading the available software list.")); + return; + } + + QByteArray raw_pkgs = myList.readAll(); + QList<QByteArray> pkgs = raw_pkgs.split('\n'); + int indent, lastIndent = 0; + PkgItem *temp = new PkgItem(_root, "Placeholder", "Placeholder", "Used for loading only"); + PkgItem *last = temp; + + for(auto &pkg : pkgs) { + indent = 0; + + if(pkg.isEmpty()) continue; + + while(pkg.size() > 1 && pkg.at(0) == ' ') { + indent++; + pkg.remove(0, 1); + } + + /* minimum valid size: A\tb\tC */ + if(pkg.size() < 6) continue; + + PkgItem *myParent; + + if(indent == lastIndent) { + /* We're siblings of the last item. */ + myParent = last->parent(); + } else if(indent < lastIndent) { + /* Traverse up until we're equal */ + while(lastIndent-- > indent) { + last = last->parent(); + } + myParent = last->parent(); + } else /*(indent > lastIndent)*/ { + /* We're children of the last item. */ + myParent = last; + } + + QList<QByteArray> fields = pkg.split('\t'); + /* at least friendly, internal, desc required */ + if(fields.size() < 3) continue; + last = new PkgItem(myParent, fields[0], fields[1], fields[2], + (fields.size() == 4 ? fields[3] : fields[1])); + myParent->addChild(last); + lastIndent = indent; + } + delete temp; +} |