File indexing completed on 2024-12-22 04:12:35

0001 /*
0002  * This file is part of the KDE project
0003  * SPDX-FileCopyrightText: 2013 Arjen Hiemstra <ahiemstra@heimr.nl>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include "kis_action_shortcuts_model.h"
0009 
0010 #include <kis_debug.h>
0011 
0012 #include <KLocalizedString>
0013 #include <QApplication>
0014 #include <QMetaClassInfo>
0015 #include <QKeySequence>
0016 #include <QMessageBox>
0017 
0018 #include "kis_icon_utils.h"
0019 #include <QApplication>
0020 
0021 #include "input/kis_abstract_input_action.h"
0022 #include "input/kis_input_profile.h"
0023 #include "input/kis_input_profile_manager.h"
0024 #include "input/kis_shortcut_configuration.h"
0025 
0026 class KisActionShortcutsModel::Private
0027 {
0028 public:
0029     Private() : action(0), profile(0), temporaryShortcut(0) { }
0030 
0031     int shortcutModeCount(uint mode);
0032 
0033     KisAbstractInputAction *action;
0034     KisInputProfile *profile;
0035     QList<KisShortcutConfiguration *> shortcuts;
0036 
0037     KisShortcutConfiguration *temporaryShortcut;
0038 };
0039 
0040 KisActionShortcutsModel::KisActionShortcutsModel(QObject *parent)
0041     : QAbstractListModel(parent), d(new Private)
0042 {
0043     connect(KisInputProfileManager::instance(), SIGNAL(currentProfileChanged()), SLOT(currentProfileChanged()));
0044 }
0045 
0046 KisActionShortcutsModel::~KisActionShortcutsModel()
0047 {
0048 }
0049 
0050 QVariant KisActionShortcutsModel::data(const QModelIndex &index, int role) const
0051 {
0052     if (!index.isValid()) {
0053         return QVariant();
0054     }
0055 
0056     if (index.row() == d->shortcuts.count() && role == Qt::DisplayRole) {
0057         if (index.column() == 0) {
0058             return i18n("Add shortcut...");
0059         }
0060         else {
0061             return QVariant();
0062         }
0063     }
0064 
0065     if (role == Qt::DisplayRole) {
0066         switch (index.column()) {
0067         case 0:
0068             switch (d->shortcuts.at(index.row())->type()) {
0069             case KisShortcutConfiguration::KeyCombinationType:
0070                 return i18nc("Shortcut type", "Key Combination");
0071 
0072             case KisShortcutConfiguration::MouseButtonType:
0073                 return i18nc("Shortcut type", "Mouse Button");
0074 
0075             case KisShortcutConfiguration::MouseWheelType:
0076                 return i18nc("Shortcut type", "Mouse Wheel");
0077 
0078             case KisShortcutConfiguration::GestureType:
0079                 return i18nc("Shortcut type", "Gesture");
0080 
0081             default:
0082                 return i18n("Unknown Input");
0083             }
0084 
0085             break;
0086 
0087         case 1: {
0088             KisShortcutConfiguration *s = d->shortcuts.at(index.row());
0089             QString output;
0090 
0091             switch (s->type()) {
0092             case KisShortcutConfiguration::KeyCombinationType:
0093                 output = KisShortcutConfiguration::keysToText(s->keys());
0094                 break;
0095 
0096             case KisShortcutConfiguration::MouseButtonType:
0097                 output = KisShortcutConfiguration::buttonsInputToText(
0098                     s->keys(), s->buttons());
0099                 break;
0100 
0101             case KisShortcutConfiguration::MouseWheelType:
0102                 output = KisShortcutConfiguration::wheelInputToText(
0103                     s->keys(), s->wheel());
0104                 break;
0105 
0106             case KisShortcutConfiguration::GestureType:
0107                 output = KisShortcutConfiguration::gestureToText(s->gesture());
0108                 break;
0109 
0110             default:
0111                 break;
0112             }
0113 
0114             return output;
0115         }
0116 
0117         case 2:
0118             return d->action->shortcutIndexes().key(d->shortcuts.at(index.row())->mode());
0119 
0120         case 3:
0121             return KisIconUtils::loadIcon("edit-delete");
0122 
0123         default:
0124             break;
0125         }
0126     }
0127     else if (role == Qt::EditRole) {
0128         KisShortcutConfiguration *s;
0129 
0130         if (index.row() == d->shortcuts.count()) {
0131             if (!d->temporaryShortcut) {
0132                 d->temporaryShortcut = new KisShortcutConfiguration;
0133             }
0134 
0135             s = d->temporaryShortcut;
0136         }
0137         else {
0138             s = d->shortcuts.at(index.row());
0139         }
0140 
0141         switch (index.column()) {
0142         case 0:
0143             return s->type();
0144 
0145         case 1:
0146             return QVariant::fromValue(s);
0147 
0148         case 2:
0149             return s->mode();
0150 
0151         default:
0152             break;
0153         }
0154     }
0155 
0156     return QVariant();
0157 }
0158 
0159 int KisActionShortcutsModel::rowCount(const QModelIndex &parent) const
0160 {
0161     if (parent.isValid()) {
0162         return 0;
0163     }
0164 
0165     return d->shortcuts.count() + 1;
0166 }
0167 
0168 int KisActionShortcutsModel::columnCount(const QModelIndex & /*parent*/) const
0169 {
0170     return 3;
0171 }
0172 
0173 QVariant KisActionShortcutsModel::headerData(int section, Qt::Orientation orientation, int role) const
0174 {
0175     if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
0176         return QVariant();
0177     }
0178 
0179     switch (section) {
0180     case 0:
0181         return i18nc("Type of shortcut", "Type");
0182 
0183     case 1:
0184         return i18nc("Input for shortcut", "Input");
0185 
0186     case 2:
0187         return i18nc("Action to trigger with shortcut", "Action");
0188 
0189     default:
0190         break;
0191     }
0192 
0193     return QVariant();
0194 }
0195 
0196 Qt::ItemFlags KisActionShortcutsModel::flags(const QModelIndex &index) const
0197 {
0198     if (!index.isValid()) {
0199         return Qt::ItemIsEnabled;
0200     }
0201 
0202     if (index.row() == d->shortcuts.count() && index.column() != 0) {
0203         return Qt::ItemIsEnabled;
0204     }
0205 
0206     if (index.row() >= d->shortcuts.count()) {
0207         return Qt::ItemIsEnabled | Qt::ItemIsEditable;
0208     }
0209 
0210     KisShortcutConfiguration* config = d->shortcuts.at(index.row());
0211     if (index.column() == 2 && d->action->isShortcutRequired(config->mode()) && d->shortcutModeCount(config->mode()) < 2) {
0212         return Qt::ItemIsSelectable;
0213     }
0214 
0215     return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
0216 }
0217 
0218 bool KisActionShortcutsModel::canRemoveRow(int row) const
0219 {
0220     if (row >= d->shortcuts.size()) {
0221         return false;
0222     }
0223     KisShortcutConfiguration* config = d->shortcuts.at(row);
0224     return !(d->action->isShortcutRequired(config->mode()) && d->shortcutModeCount(config->mode()) < 2);
0225 }
0226 
0227 bool KisActionShortcutsModel::setData(const QModelIndex &index, const QVariant &value, int role)
0228 {
0229     if (!index.isValid() || role != Qt::EditRole) {
0230         return false;
0231     }
0232 
0233     if (index.row() == d->shortcuts.count()) {
0234         if (!d->temporaryShortcut || (index.column() == 0 && value.toUInt() == 0)) {
0235             return false;
0236         }
0237 
0238         beginInsertRows(QModelIndex(), d->shortcuts.count(), d->shortcuts.count());
0239         d->temporaryShortcut->setAction(d->action);
0240         d->profile->addShortcut(d->temporaryShortcut);
0241         d->shortcuts.append(d->temporaryShortcut);
0242         d->temporaryShortcut = 0;
0243         endInsertRows();
0244     }
0245 
0246     switch (index.column()) {
0247     case 0:
0248         d->shortcuts.at(index.row())->setType(static_cast<KisShortcutConfiguration::ShortcutType>(value.toUInt()));
0249         break;
0250 
0251     case 1: {
0252         KisShortcutConfiguration *newData = value.value<KisShortcutConfiguration *>();
0253         KisShortcutConfiguration *oldData = d->shortcuts.at(index.row());
0254 
0255         if (newData == oldData) {
0256             const QList<KisShortcutConfiguration *> conflictingShortcuts =
0257                 KisInputProfileManager::instance()->getConflictingShortcuts(
0258                     KisInputProfileManager::instance()->currentProfile());
0259 
0260             // check if this shortcut resulted in a conflict
0261             const bool isConflictingShortcut = std::find_if(conflictingShortcuts.begin(),
0262                                                             conflictingShortcuts.end(),
0263                                                             [&oldData](KisShortcutConfiguration *shortcut) {
0264                                                                 return *oldData == *shortcut;
0265                                                             })
0266                 != conflictingShortcuts.end();
0267 
0268             if (isConflictingShortcut) {
0269                 QMessageBox::warning(qApp->activeWindow(),
0270                                      "Warning",
0271                                      i18n("A conflict exists between two or more shortcuts."));
0272             }
0273             // change was done on the object
0274             emit dataChanged(index, index);
0275             return true;
0276         }
0277 
0278         oldData->setKeys(newData->keys());
0279         oldData->setButtons(newData->buttons());
0280         oldData->setWheel(newData->wheel());
0281         oldData->setGesture(newData->gesture());
0282 
0283         break;
0284     }
0285 
0286     case 2:
0287         d->shortcuts.at(index.row())->setMode(value.toUInt());
0288         break;
0289     }
0290 
0291     emit dataChanged(index, index);
0292 
0293     return true;
0294 }
0295 
0296 KisAbstractInputAction *KisActionShortcutsModel::action() const
0297 {
0298     return d->action;
0299 }
0300 
0301 void KisActionShortcutsModel::setAction(KisAbstractInputAction *action)
0302 {
0303     if (action != d->action) {
0304         if (d->action) {
0305             beginRemoveRows(QModelIndex(), 0, d->shortcuts.count() - 1);
0306             endRemoveRows();
0307         }
0308 
0309         d->action = action;
0310 
0311         if (d->action && d->profile) {
0312             d->shortcuts = d->profile->shortcutsForAction(d->action);
0313             beginInsertRows(QModelIndex(), 0, d->shortcuts.count() - 1);
0314             endInsertRows();
0315         }
0316     }
0317 }
0318 
0319 KisInputProfile *KisActionShortcutsModel::profile() const
0320 {
0321     return d->profile;
0322 }
0323 
0324 void KisActionShortcutsModel::setProfile(KisInputProfile *profile)
0325 {
0326     if (profile != d->profile) {
0327         if (d->profile) {
0328             beginRemoveRows(QModelIndex(), 0, d->shortcuts.count() - 1);
0329             endRemoveRows();
0330         }
0331 
0332         d->profile = profile;
0333 
0334         if (d->action && d->profile) {
0335             d->shortcuts = d->profile->shortcutsForAction(d->action);
0336             beginInsertRows(QModelIndex(), 0, d->shortcuts.count() - 1);
0337             endInsertRows();
0338         }
0339     }
0340 }
0341 
0342 void KisActionShortcutsModel::currentProfileChanged()
0343 {
0344     setProfile(KisInputProfileManager::instance()->currentProfile());
0345 }
0346 
0347 bool KisActionShortcutsModel::removeRows(int row, int count, const QModelIndex &parent)
0348 {
0349     if (row < 0 || row >= d->shortcuts.count() || count == 0) {
0350         return false;
0351     }
0352 
0353     beginRemoveRows(parent, row, row + count - 1);
0354 
0355     for (int i = row; i < d->shortcuts.count() && count > 0; ++i, count--) {
0356         KisShortcutConfiguration *s = d->shortcuts.at(i);
0357 
0358         d->profile->removeShortcut(s);
0359         d->shortcuts.removeOne(s);
0360         delete s;
0361     }
0362 
0363     endRemoveRows();
0364 
0365     return true;
0366 }
0367 
0368 int KisActionShortcutsModel::Private::shortcutModeCount(uint mode)
0369 {
0370     int count = 0;
0371     Q_FOREACH (KisShortcutConfiguration* s, shortcuts) {
0372         if(s->mode() == mode) {
0373             count++;
0374         }
0375     }
0376 
0377     return count;
0378 }