File indexing completed on 2024-09-29 06:35:52
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org> 0004 SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org> 0005 SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org> 0006 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org> 0007 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org> 0008 SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org> 0009 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com> 0010 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz> 0011 0012 SPDX-License-Identifier: LGPL-2.0-or-later 0013 */ 0014 #include "config-xmlgui.h" 0015 0016 #include "debug.h" 0017 #include "kshortcutsdialog_p.h" 0018 0019 #include <QAction> 0020 #include <QTreeWidgetItem> 0021 0022 #if HAVE_GLOBALACCEL 0023 #include <KGlobalAccel> 0024 #endif 0025 0026 KShortcutsEditorItem::KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action) 0027 : QTreeWidgetItem(parent, ActionItem) 0028 , m_action(action) 0029 , m_isNameBold(false) 0030 , m_oldLocalShortcut(nullptr) 0031 , m_oldGlobalShortcut(nullptr) 0032 { 0033 // Filtering message requested by translators (scripting). 0034 m_id = m_action->objectName(); 0035 m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration", "%1", KLocalizedString::removeAcceleratorMarker(m_action->text())); 0036 if (m_actionNameInTable.isEmpty()) { 0037 qCWarning(DEBUG_KXMLGUI) << "Action without text!" << m_action->objectName(); 0038 m_actionNameInTable = m_id; 0039 } 0040 0041 m_collator.setNumericMode(true); 0042 m_collator.setCaseSensitivity(Qt::CaseSensitive); 0043 } 0044 0045 KShortcutsEditorItem::~KShortcutsEditorItem() 0046 { 0047 delete m_oldLocalShortcut; 0048 delete m_oldGlobalShortcut; 0049 } 0050 0051 bool KShortcutsEditorItem::isModified() const 0052 { 0053 return m_oldLocalShortcut || m_oldGlobalShortcut; 0054 } 0055 0056 QVariant KShortcutsEditorItem::data(int column, int role) const 0057 { 0058 switch (role) { 0059 case Qt::DisplayRole: 0060 switch (column) { 0061 case Name: 0062 return m_actionNameInTable; 0063 case Id: 0064 return m_id; 0065 case LocalPrimary: 0066 case LocalAlternate: 0067 case GlobalPrimary: 0068 case GlobalAlternate: 0069 return keySequence(column); 0070 default: 0071 break; 0072 } 0073 break; 0074 case Qt::DecorationRole: 0075 if (column == Name) { 0076 return m_action->icon(); 0077 } else { 0078 return QIcon(); 0079 } 0080 case Qt::WhatsThisRole: 0081 return m_action->whatsThis(); 0082 case Qt::ToolTipRole: 0083 // There is no such thing as a QAction::description(). So we have 0084 // nothing to display here. 0085 return QVariant(); 0086 case Qt::FontRole: 0087 if (column == Name && m_isNameBold) { 0088 QFont modifiedFont = treeWidget()->font(); 0089 modifiedFont.setBold(true); 0090 return modifiedFont; 0091 } 0092 break; 0093 case KExtendableItemDelegate::ShowExtensionIndicatorRole: 0094 switch (column) { 0095 case Name: 0096 return false; 0097 case LocalPrimary: 0098 case LocalAlternate: 0099 return !m_action->property("isShortcutConfigurable").isValid() // 0100 || m_action->property("isShortcutConfigurable").toBool(); 0101 #if HAVE_GLOBALACCEL 0102 case GlobalPrimary: 0103 case GlobalAlternate: 0104 if (!KGlobalAccel::self()->hasShortcut(m_action)) { 0105 return false; 0106 } 0107 return true; 0108 #endif 0109 default: 0110 return false; 0111 } 0112 // the following are custom roles, defined in this source file only 0113 case ShortcutRole: 0114 switch (column) { 0115 case LocalPrimary: 0116 case LocalAlternate: 0117 case GlobalPrimary: 0118 case GlobalAlternate: 0119 return keySequence(column); 0120 default: 0121 // Column not valid for this role 0122 Q_ASSERT(false); 0123 return QVariant(); 0124 } 0125 0126 case DefaultShortcutRole: { 0127 QList<QKeySequence> defaultShortcuts = m_action->property("defaultShortcuts").value<QList<QKeySequence>>(); 0128 #if HAVE_GLOBALACCEL 0129 QList<QKeySequence> defaultGlobalShortcuts = KGlobalAccel::self()->defaultShortcut(m_action); 0130 #endif 0131 0132 switch (column) { 0133 case LocalPrimary: 0134 return primarySequence(defaultShortcuts); 0135 case LocalAlternate: 0136 return alternateSequence(defaultShortcuts); 0137 #if HAVE_GLOBALACCEL 0138 case GlobalPrimary: 0139 return primarySequence(defaultGlobalShortcuts); 0140 case GlobalAlternate: 0141 return alternateSequence(defaultGlobalShortcuts); 0142 #endif 0143 default: 0144 // Column not valid for this role 0145 Q_ASSERT(false); 0146 return QVariant(); 0147 } 0148 } 0149 case ObjectRole: 0150 return QVariant::fromValue(static_cast<QObject *>(m_action)); 0151 0152 default: 0153 break; 0154 } 0155 0156 return QVariant(); 0157 } 0158 0159 bool KShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const 0160 { 0161 const int column = treeWidget() ? treeWidget()->sortColumn() : 0; 0162 return m_collator.compare(text(column), other.text(column)) < 0; 0163 } 0164 0165 QKeySequence KShortcutsEditorItem::keySequence(uint column) const 0166 { 0167 auto shortcuts = [this]() { 0168 return m_action->shortcuts(); 0169 }; 0170 0171 #if HAVE_GLOBALACCEL 0172 auto globalShortcuts = [this]() { 0173 return KGlobalAccel::self()->shortcut(m_action); 0174 }; 0175 #endif 0176 0177 switch (column) { 0178 case LocalPrimary: 0179 return primarySequence(shortcuts()); 0180 case LocalAlternate: 0181 return alternateSequence(shortcuts()); 0182 #if HAVE_GLOBALACCEL 0183 case GlobalPrimary: 0184 return primarySequence(globalShortcuts()); 0185 case GlobalAlternate: 0186 return alternateSequence(globalShortcuts()); 0187 #endif 0188 default: 0189 return QKeySequence(); 0190 } 0191 } 0192 0193 void KShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq) 0194 { 0195 QList<QKeySequence> ks; 0196 #if HAVE_GLOBALACCEL 0197 if (column == GlobalPrimary || column == GlobalAlternate) { 0198 ks = KGlobalAccel::self()->shortcut(m_action); 0199 if (!m_oldGlobalShortcut) { 0200 m_oldGlobalShortcut = new QList<QKeySequence>(ks); 0201 } 0202 } else 0203 #endif 0204 { 0205 ks = m_action->shortcuts(); 0206 if (!m_oldLocalShortcut) { 0207 m_oldLocalShortcut = new QList<QKeySequence>(ks); 0208 } 0209 } 0210 0211 if (column == LocalAlternate || column == GlobalAlternate) { 0212 if (ks.isEmpty()) { 0213 ks << QKeySequence(); 0214 } 0215 0216 if (ks.size() <= 1) { 0217 ks << seq; 0218 } else { 0219 ks[1] = seq; 0220 } 0221 } else { 0222 if (ks.isEmpty()) { 0223 ks << seq; 0224 } else { 0225 ks[0] = seq; 0226 } 0227 } 0228 0229 // avoid also setting the default shortcut - what we are setting here is custom by definition 0230 #if HAVE_GLOBALACCEL 0231 if (column == GlobalPrimary || column == GlobalAlternate) { 0232 KGlobalAccel::self()->setShortcut(m_action, ks, KGlobalAccel::NoAutoloading); 0233 0234 } else 0235 #endif 0236 { 0237 m_action->setShortcuts(ks); 0238 } 0239 0240 updateModified(); 0241 } 0242 0243 // our definition of modified is "modified since the chooser was shown". 0244 void KShortcutsEditorItem::updateModified() 0245 { 0246 if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) { 0247 delete m_oldLocalShortcut; 0248 m_oldLocalShortcut = nullptr; 0249 } 0250 #if HAVE_GLOBALACCEL 0251 if (m_oldGlobalShortcut && *m_oldGlobalShortcut == KGlobalAccel::self()->shortcut(m_action)) { 0252 delete m_oldGlobalShortcut; 0253 m_oldGlobalShortcut = nullptr; 0254 } 0255 #endif 0256 } 0257 0258 bool KShortcutsEditorItem::isModified(uint column) const 0259 { 0260 switch (column) { 0261 case Name: 0262 return false; 0263 case LocalPrimary: 0264 case LocalAlternate: 0265 if (!m_oldLocalShortcut) { 0266 return false; 0267 } 0268 if (column == LocalPrimary) { 0269 return primarySequence(*m_oldLocalShortcut) != primarySequence(m_action->shortcuts()); 0270 } else { 0271 return alternateSequence(*m_oldLocalShortcut) != alternateSequence(m_action->shortcuts()); 0272 } 0273 #if HAVE_GLOBALACCEL 0274 case GlobalPrimary: 0275 case GlobalAlternate: 0276 if (!m_oldGlobalShortcut) { 0277 return false; 0278 } 0279 if (column == GlobalPrimary) { 0280 return primarySequence(*m_oldGlobalShortcut) != primarySequence(KGlobalAccel::self()->shortcut(m_action)); 0281 } else { 0282 return alternateSequence(*m_oldGlobalShortcut) != alternateSequence(KGlobalAccel::self()->shortcut(m_action)); 0283 } 0284 #endif 0285 default: 0286 return false; 0287 } 0288 } 0289 0290 void KShortcutsEditorItem::undo() 0291 { 0292 if (m_oldLocalShortcut) { 0293 // We only ever reset the active Shortcut 0294 m_action->setShortcuts(*m_oldLocalShortcut); 0295 } 0296 0297 #if HAVE_GLOBALACCEL 0298 if (m_oldGlobalShortcut) { 0299 KGlobalAccel::self()->setShortcut(m_action, *m_oldGlobalShortcut, KGlobalAccel::NoAutoloading); 0300 } 0301 #endif 0302 0303 updateModified(); 0304 } 0305 0306 void KShortcutsEditorItem::commit() 0307 { 0308 delete m_oldLocalShortcut; 0309 m_oldLocalShortcut = nullptr; 0310 delete m_oldGlobalShortcut; 0311 m_oldGlobalShortcut = nullptr; 0312 }