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 }