File indexing completed on 2024-06-23 04:42:34

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(QVector<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 QVector<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     QVector<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         if (col == 0) {
0105             QString groupName = KLocalizedString::removeAcceleratorMarker(entry.groupName);
0106             QString actionText = KLocalizedString::removeAcceleratorMarker(entry.action->text());
0107             return QString(groupName + QStringLiteral(": ") + actionText);
0108         } else {
0109             return entry.action->shortcut().toString();
0110         }
0111     case ShortcutRole:
0112         return entry.action->shortcut().toString();
0113     case Qt::DecorationRole:
0114         if (col == 0) {
0115             return entry.action->icon().name();
0116         }
0117         break;
0118     case Qt::TextAlignmentRole:
0119         if (col == 0) {
0120             return Qt::AlignLeft;
0121         } else {
0122             return Qt::AlignRight;
0123         }
0124     case Qt::UserRole: {
0125         return QVariant::fromValue(entry.action);
0126     }
0127     case Role::Score:
0128         return entry.score;
0129     }
0130 
0131     return {};
0132 }
0133 
0134 void KalCommandBarModel::actionTriggered(const QString &name)
0135 {
0136     if (m_lastTriggered.size() == 6) {
0137         m_lastTriggered.pop_back();
0138     }
0139     m_lastTriggered.push_front(name);
0140 }
0141 
0142 QStringList KalCommandBarModel::lastUsedActions() const
0143 {
0144     return m_lastTriggered;
0145 }
0146 
0147 void KalCommandBarModel::setLastUsedActions(const QStringList &actionNames)
0148 {
0149     m_lastTriggered = actionNames;
0150 
0151     while (m_lastTriggered.size() > 6) {
0152         m_lastTriggered.pop_back();
0153     }
0154 }
0155 
0156 QHash<int, QByteArray> KalCommandBarModel::roleNames() const
0157 {
0158     auto roles = QAbstractTableModel::roleNames();
0159     roles[Qt::UserRole] = QByteArrayLiteral("action");
0160     roles[Score] = QByteArrayLiteral("score");
0161     roles[ShortcutRole] = QByteArrayLiteral("shortcut");
0162     return roles;
0163 }