summaryrefslogblamecommitdiff
path: root/ui/qt5/pkgmodel.cc
blob: c6736bc6f7d5090ea2a0a844a46188688bb6224d (plain) (tree)




























































































































































































































































                                                                                            
                       



                                                                                              
                       




































                                                                     
/*
 * 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 lastIndent = 0;
    PkgItem *temp = new PkgItem(_root, "Placeholder", "Placeholder", "Used for loading only");
    PkgItem *last = temp;

    for(auto &pkg : pkgs) {
        int 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;
}