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