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 }