File indexing completed on 2024-11-24 04:50:43

0001 // SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include "actionsmodel.h"
0005 
0006 #include <KLocalizedString>
0007 
0008 #include <QAction>
0009 #include <QMenu>
0010 
0011 #include <unordered_set>
0012 
0013 KalCommandBarModel::KalCommandBarModel(QObject *parent)
0014     : QAbstractTableModel(parent)
0015 {
0016 }
0017 
0018 void fillRows(QList<KalCommandBarModel::Item> &rows, const QString &title, const QList<QAction *> &actions, std::unordered_set<QAction *> &uniqueActions)
0019 {
0020     for (const auto &action : actions) {
0021         // We don't want disabled actions
0022         if (!action->isEnabled()) {
0023             continue;
0024         }
0025 
0026         // Is this action actually a menu?
0027         if (auto menu = action->menu()) {
0028             auto menuActionList = menu->actions();
0029 
0030             // Empty? => Maybe the menu loads action on aboutToShow()?
0031             if (menuActionList.isEmpty()) {
0032                 Q_EMIT menu->aboutToShow();
0033                 menuActionList = menu->actions();
0034             }
0035 
0036             const QString menuTitle = menu->title();
0037             fillRows(rows, menuTitle, menuActionList, uniqueActions);
0038             continue;
0039         }
0040 
0041         if (uniqueActions.insert(action).second) {
0042             rows.push_back(KalCommandBarModel::Item{title, action, -1});
0043         }
0044     }
0045 }
0046 
0047 void KalCommandBarModel::refresh(const QList<ActionGroup> &actionGroups)
0048 {
0049     int totalActions = std::accumulate(actionGroups.begin(), actionGroups.end(), 0, [](int a, const ActionGroup &ag) {
0050         return a + ag.actions.count();
0051     });
0052 
0053     QList<Item> temp_rows;
0054     std::unordered_set<QAction *> uniqueActions;
0055     temp_rows.reserve(totalActions);
0056     int actionGroupIdx = 0;
0057     for (const auto &ag : actionGroups) {
0058         const auto &agActions = ag.actions;
0059         fillRows(temp_rows, ag.name, agActions, uniqueActions);
0060 
0061         actionGroupIdx++;
0062     }
0063 
0064     /**
0065      * For each action in last triggered actions,
0066      *  - Find it in the actions
0067      *  - Use the score variable to set its score
0068      *
0069      * Items in m_lastTriggered are stored in descending order
0070      * by their usage i.e., the first item in the vector is the most
0071      * recently invoked action.
0072      *
0073      * Here we traverse them in reverse order, i.e., from least recent to
0074      * most recent and then assign a score to them in a way that most recent
0075      * ends up having the highest score. Thus when proxy model does the sorting
0076      * later, most recent item will end up on the top
0077      */
0078     int score = 0;
0079     std::for_each(m_lastTriggered.crbegin(), m_lastTriggered.crend(), [&score, &temp_rows](const QString &act) {
0080         auto it = std::find_if(temp_rows.begin(), temp_rows.end(), [act](const KalCommandBarModel::Item &i) {
0081             return i.action->text() == act;
0082         });
0083         if (it != temp_rows.end()) {
0084             it->score = score++;
0085         }
0086     });
0087 
0088     beginResetModel();
0089     m_rows = std::move(temp_rows);
0090     endResetModel();
0091 }
0092 
0093 QVariant KalCommandBarModel::data(const QModelIndex &index, int role) const
0094 {
0095     if (!index.isValid()) {
0096         return {};
0097     }
0098 
0099     const auto &entry = m_rows[index.row()];
0100     const int col = index.column();
0101 
0102     switch (role) {
0103     case Qt::DisplayRole:
0104     case DisplayNameRole:
0105         if (col == 0) {
0106             QString groupName = KLocalizedString::removeAcceleratorMarker(entry.groupName);
0107             QString actionText = KLocalizedString::removeAcceleratorMarker(entry.action->text());
0108             return QString(groupName + QStringLiteral(": ") + actionText);
0109         } else {
0110             return entry.action->shortcut().toString();
0111         }
0112     case ShortcutRole:
0113         return entry.action->shortcut().toString();
0114     case Qt::DecorationRole:
0115         if (col == 0) {
0116             return entry.action->icon().name();
0117         }
0118         break;
0119     case Qt::TextAlignmentRole:
0120         if (col == 0) {
0121             return Qt::AlignLeft;
0122         } else {
0123             return Qt::AlignRight;
0124         }
0125     case Qt::UserRole: {
0126         return QVariant::fromValue(entry.action);
0127     }
0128     case Role::Score:
0129         return entry.score;
0130     }
0131 
0132     return {};
0133 }
0134 
0135 void KalCommandBarModel::actionTriggered(const QString &name)
0136 {
0137     if (m_lastTriggered.size() == 6) {
0138         m_lastTriggered.pop_back();
0139     }
0140     m_lastTriggered.push_front(name);
0141 }
0142 
0143 QStringList KalCommandBarModel::lastUsedActions() const
0144 {
0145     return m_lastTriggered;
0146 }
0147 
0148 void KalCommandBarModel::setLastUsedActions(const QStringList &actionNames)
0149 {
0150     m_lastTriggered = actionNames;
0151 
0152     while (m_lastTriggered.size() > 6) {
0153         m_lastTriggered.pop_back();
0154     }
0155 }
0156 
0157 QHash<int, QByteArray> KalCommandBarModel::roleNames() const
0158 {
0159     auto roles = QAbstractTableModel::roleNames();
0160     roles[Qt::UserRole] = QByteArrayLiteral("qaction");
0161     roles[Score] = QByteArrayLiteral("score");
0162     roles[ShortcutRole] = QByteArrayLiteral("shortcut");
0163     roles[DisplayNameRole] = QByteArrayLiteral("displayName");
0164     return roles;
0165 }
0166 
0167 #include "moc_actionsmodel.cpp"