File indexing completed on 2024-11-10 13:27:24

0001 /*
0002  * SPDX-FileCopyrightText: 2009 Ben Cooksley <bcooksley@kde.org>
0003  * SPDX-FileCopyrightText: 2007 Will Stephenson <wstephenson@kde.org>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "MenuModel.h"
0009 
0010 #include "MenuItem.h"
0011 #include <KCModuleInfo>
0012 #include <KCategorizedSortFilterProxyModel>
0013 #include <QIcon>
0014 
0015 class MenuModel::Private
0016 {
0017 public:
0018     Private()
0019     {
0020     }
0021 
0022     MenuItem *rootItem = nullptr;
0023     QList<MenuItem *> exceptions;
0024 };
0025 
0026 MenuModel::MenuModel(MenuItem *menuRoot, QObject *parent)
0027     : QAbstractItemModel(parent)
0028     , d(new Private())
0029 {
0030     d->rootItem = menuRoot;
0031 }
0032 
0033 MenuModel::~MenuModel()
0034 {
0035     d->exceptions.clear();
0036     delete d;
0037 }
0038 
0039 QHash<int, QByteArray> MenuModel::roleNames() const
0040 {
0041     QHash<int, QByteArray> names = QAbstractItemModel::roleNames();
0042     names[DepthRole] = "DepthRole";
0043     names[IsCategoryRole] = "IsCategoryRole";
0044     names[IsKCMRole] = "IsKCMRole";
0045     names[DefaultIndicatorRole] = "showDefaultIndicator";
0046     names[IconNameRole] = "iconName";
0047     return names;
0048 }
0049 
0050 int MenuModel::columnCount(const QModelIndex &parent) const
0051 {
0052     Q_UNUSED(parent);
0053     return 1;
0054 }
0055 
0056 int MenuModel::rowCount(const QModelIndex &parent) const
0057 {
0058     MenuItem *mi;
0059     if (parent.isValid()) {
0060         mi = static_cast<MenuItem *>(parent.internalPointer());
0061     } else {
0062         mi = d->rootItem;
0063     }
0064     return childrenList(mi).count();
0065 }
0066 
0067 QVariant MenuModel::data(const QModelIndex &index, int role) const
0068 {
0069     MenuItem *mi = nullptr;
0070     QVariant theData;
0071     if (!index.isValid()) {
0072         return QVariant();
0073     }
0074 
0075     mi = static_cast<MenuItem *>(index.internalPointer());
0076     switch (role) {
0077     case Qt::DisplayRole:
0078         theData.setValue(mi->name());
0079         break;
0080     case Qt::DecorationRole:
0081         theData = QVariant(QIcon::fromTheme(mi->iconName()));
0082         break;
0083     case KCategorizedSortFilterProxyModel::CategorySortRole:
0084         if (mi->parent()) {
0085             theData.setValue(QStringLiteral("%1%2").arg(QString::number(mi->parent()->weight()), 5, QLatin1Char('0')).arg(mi->parent()->name()));
0086         }
0087         break;
0088     case KCategorizedSortFilterProxyModel::CategoryDisplayRole: {
0089         MenuItem *candidate = mi->parent();
0090         // The model has an invisible single root item.
0091         // So to get the "root category" we don't go up all the way
0092         // To the actual root, but to the list of the first childs.
0093         // That's why we check for candidate->parent()->parent()
0094         while (candidate && candidate->parent() && candidate->parent()->parent()) {
0095             candidate = candidate->parent();
0096         }
0097         if (candidate) {
0098             // Children of this special root category don't have an user visible category
0099             if (!candidate->isSystemsettingsRootCategory()) {
0100                 theData.setValue(candidate->name());
0101             }
0102         }
0103         break;
0104     }
0105     case MenuModel::MenuItemRole:
0106         theData.setValue(mi);
0107         break;
0108     case MenuModel::UserFilterRole:
0109         theData.setValue(mi->keywords().join(QString()));
0110         break;
0111     case MenuModel::UserSortRole:
0112         //Category owners are always before everything else, regardless of weight
0113         if (mi->isCategoryOwner()) {
0114             theData.setValue(QStringLiteral("%1").arg(QString::number(mi->weight()), 5, QLatin1Char('0')));
0115         } else {
0116             theData.setValue(QStringLiteral("1%1").arg(QString::number(mi->weight()), 5, QLatin1Char('0')));
0117         }
0118         break;
0119     case MenuModel::DepthRole: {
0120         MenuItem *candidate = mi;
0121         // -1 excludes the invisible root, having main categories at depth 0
0122         int depth = -1;
0123         while (candidate && candidate->parent()) {
0124             candidate = candidate->parent();
0125             ++depth;
0126         }
0127 
0128         MenuItem *parent = mi->parent();
0129         // Items that are in a category with an owner are one level deeper,
0130         // except the owner
0131         if (parent && parent->menu() && parent->isLibrary() && !mi->isCategoryOwner()) {
0132             ++depth;
0133         }
0134         theData.setValue(depth);
0135         break;
0136     }
0137     case MenuModel::IsCategoryRole:
0138         theData.setValue(mi->menu());
0139         break;
0140     case MenuModel::IsKCMRole:
0141         theData.setValue(mi->isLibrary());
0142         break;
0143     case MenuModel::DefaultIndicatorRole:
0144         theData.setValue(mi->showDefaultIndicator());
0145         break;
0146     case MenuModel::IconNameRole:
0147         theData.setValue(mi->iconName());
0148         break;
0149     default:
0150         break;
0151     }
0152     return theData;
0153 }
0154 
0155 Qt::ItemFlags MenuModel::flags(const QModelIndex &index) const
0156 {
0157     if (!index.isValid()) {
0158         return Qt::NoItemFlags;
0159     }
0160 
0161     return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0162 }
0163 
0164 QModelIndex MenuModel::index(int row, int column, const QModelIndex &parent) const
0165 {
0166     if (!hasIndex(row, column, parent)) {
0167         return QModelIndex();
0168     }
0169 
0170     MenuItem *parentItem;
0171     if (!parent.isValid()) {
0172         parentItem = d->rootItem;
0173     } else {
0174         parentItem = static_cast<MenuItem *>(parent.internalPointer());
0175     }
0176 
0177     MenuItem *childItem = childrenList(parentItem).value(row);
0178     if (childItem) {
0179         return createIndex(row, column, childItem);
0180     } else {
0181         return QModelIndex();
0182     }
0183 }
0184 
0185 QModelIndex MenuModel::parent(const QModelIndex &index) const
0186 {
0187     MenuItem *childItem = static_cast<MenuItem *>(index.internalPointer());
0188     if (!childItem) {
0189         return QModelIndex();
0190     }
0191 
0192     MenuItem *parent = parentItem(childItem);
0193     MenuItem *grandParent = parentItem(parent);
0194 
0195     int childRow = 0;
0196     if (grandParent) {
0197         childRow = childrenList(grandParent).indexOf(parent);
0198     }
0199 
0200     if (parent == d->rootItem) {
0201         return QModelIndex();
0202     }
0203     return createIndex(childRow, 0, parent);
0204 }
0205 
0206 QList<MenuItem *> MenuModel::childrenList(MenuItem *parent) const
0207 {
0208     QList<MenuItem *> children = parent->children();
0209     foreach (MenuItem *child, children) {
0210         if (d->exceptions.contains(child)) {
0211             children.removeOne(child);
0212             children.append(child->children());
0213         }
0214     }
0215     return children;
0216 }
0217 
0218 MenuItem *MenuModel::parentItem(MenuItem *child) const
0219 {
0220     MenuItem *parent = child->parent();
0221     if (d->exceptions.contains(parent)) {
0222         parent = parentItem(parent);
0223     }
0224     return parent;
0225 }
0226 
0227 QModelIndex MenuModel::indexForItem(MenuItem *item) const
0228 {
0229     MenuItem *parent = parentItem(item);
0230 
0231     if (!parent) {
0232         return QModelIndex();
0233     }
0234 
0235     const int row = childrenList(parent).indexOf(item);
0236 
0237     if (row < 0) {
0238         return QModelIndex();
0239     }
0240 
0241     return createIndex(row, 0, item);
0242 }
0243 
0244 MenuItem *MenuModel::rootItem() const
0245 {
0246     return d->rootItem;
0247 }
0248 
0249 void MenuModel::addException(MenuItem *exception)
0250 {
0251     if (exception == d->rootItem) {
0252         return;
0253     }
0254     d->exceptions.append(exception);
0255 }
0256 
0257 void MenuModel::removeException(MenuItem *exception)
0258 {
0259     d->exceptions.removeAll(exception);
0260 }