1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1998 Mark Donohoe <donohoe@kde.org>
4 SPDX-FileCopyrightText: 1997 Nicolas Hadacek <hadacek@kde.org>
5 SPDX-FileCopyrightText: 1998 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2001 Ellis Whitehead <ellis@kde.org>
7 SPDX-FileCopyrightText: 2006 Hamish Rodda <rodda@kde.org>
8 SPDX-FileCopyrightText: 2007 Roberto Raggi <roberto@kdevelop.org>
9 SPDX-FileCopyrightText: 2007 Andreas Hartmetz <ahartmetz@gmail.com>
10 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
11
12 SPDX-License-Identifier: LGPL-2.0-or-later
13*/
14#include "config-xmlgui.h"
15
16#include "debug.h"
17#include "kshortcutsdialog_p.h"
18
19#include <QAction>
20#include <QTreeWidgetItem>
21
22#if HAVE_GLOBALACCEL
23#include <KGlobalAccel>
24#endif
25
26KShortcutsEditorItem::KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action)
27 : QTreeWidgetItem(parent, ActionItem)
28 , m_action(action)
29 , m_isNameBold(false)
30 , m_oldLocalShortcut(nullptr)
31 , m_oldGlobalShortcut(nullptr)
32{
33 // Filtering message requested by translators (scripting).
34 m_id = m_action->objectName();
35 m_actionNameInTable = i18nc("@item:intable Action name in shortcuts configuration", "%1", KLocalizedString::removeAcceleratorMarker(m_action->text()));
36 if (m_actionNameInTable.isEmpty()) {
37 qCWarning(DEBUG_KXMLGUI) << "Action without text!" << m_action->objectName();
38 m_actionNameInTable = m_id;
39 }
40
41 m_collator.setNumericMode(true);
42 m_collator.setCaseSensitivity(Qt::CaseSensitive);
43}
44
45KShortcutsEditorItem::~KShortcutsEditorItem()
46{
47 delete m_oldLocalShortcut;
48 delete m_oldGlobalShortcut;
49}
50
51bool KShortcutsEditorItem::isModified() const
52{
53 return m_oldLocalShortcut || m_oldGlobalShortcut;
54}
55
56QVariant KShortcutsEditorItem::data(int column, int role) const
57{
58 switch (role) {
59 case Qt::DisplayRole:
60 switch (column) {
61 case Name:
62 return m_actionNameInTable;
63 case Id:
64 return m_id;
65 case LocalPrimary:
66 case LocalAlternate:
67 case GlobalPrimary:
68 case GlobalAlternate:
69 return keySequence(column);
70 default:
71 break;
72 }
73 break;
74 case Qt::DecorationRole:
75 if (column == Name) {
76 return m_action->icon();
77 } else {
78 return QIcon();
79 }
80 case Qt::WhatsThisRole:
81 return m_action->whatsThis();
82 case Qt::ToolTipRole:
83 // There is no such thing as a QAction::description(). So we have
84 // nothing to display here.
85 return QVariant();
86 case Qt::FontRole:
87 if (column == Name && m_isNameBold) {
88 QFont modifiedFont = treeWidget()->font();
89 modifiedFont.setBold(true);
90 return modifiedFont;
91 }
92 break;
93 case KExtendableItemDelegate::ShowExtensionIndicatorRole:
94 switch (column) {
95 case Name:
96 return false;
97 case LocalPrimary:
98 case LocalAlternate:
99 return !m_action->property(name: "isShortcutConfigurable").isValid() //
100 || m_action->property(name: "isShortcutConfigurable").toBool();
101#if HAVE_GLOBALACCEL
102 case GlobalPrimary:
103 case GlobalAlternate:
104 if (!KGlobalAccel::self()->hasShortcut(action: m_action)) {
105 return false;
106 }
107 return true;
108#endif
109 default:
110 return false;
111 }
112 // the following are custom roles, defined in this source file only
113 case ShortcutRole:
114 switch (column) {
115 case LocalPrimary:
116 case LocalAlternate:
117 case GlobalPrimary:
118 case GlobalAlternate:
119 return keySequence(column);
120 default:
121 // Column not valid for this role
122 Q_ASSERT(false);
123 return QVariant();
124 }
125
126 case DefaultShortcutRole: {
127 QList<QKeySequence> defaultShortcuts = m_action->property(name: "defaultShortcuts").value<QList<QKeySequence>>();
128#if HAVE_GLOBALACCEL
129 QList<QKeySequence> defaultGlobalShortcuts = KGlobalAccel::self()->defaultShortcut(action: m_action);
130#endif
131
132 switch (column) {
133 case LocalPrimary:
134 return primarySequence(sequences: defaultShortcuts);
135 case LocalAlternate:
136 return alternateSequence(sequences: defaultShortcuts);
137#if HAVE_GLOBALACCEL
138 case GlobalPrimary:
139 return primarySequence(sequences: defaultGlobalShortcuts);
140 case GlobalAlternate:
141 return alternateSequence(sequences: defaultGlobalShortcuts);
142#endif
143 default:
144 // Column not valid for this role
145 Q_ASSERT(false);
146 return QVariant();
147 }
148 }
149 case ObjectRole:
150 return QVariant::fromValue(value: static_cast<QObject *>(m_action));
151
152 default:
153 break;
154 }
155
156 return QVariant();
157}
158
159bool KShortcutsEditorItem::operator<(const QTreeWidgetItem &other) const
160{
161 const int column = treeWidget() ? treeWidget()->sortColumn() : 0;
162 return m_collator.compare(s1: text(column), s2: other.text(column)) < 0;
163}
164
165QKeySequence KShortcutsEditorItem::keySequence(uint column) const
166{
167 auto shortcuts = [this]() {
168 return m_action->shortcuts();
169 };
170
171#if HAVE_GLOBALACCEL
172 auto globalShortcuts = [this]() {
173 return KGlobalAccel::self()->shortcut(action: m_action);
174 };
175#endif
176
177 switch (column) {
178 case LocalPrimary:
179 return primarySequence(sequences: shortcuts());
180 case LocalAlternate:
181 return alternateSequence(sequences: shortcuts());
182#if HAVE_GLOBALACCEL
183 case GlobalPrimary:
184 return primarySequence(sequences: globalShortcuts());
185 case GlobalAlternate:
186 return alternateSequence(sequences: globalShortcuts());
187#endif
188 default:
189 return QKeySequence();
190 }
191}
192
193void KShortcutsEditorItem::setKeySequence(uint column, const QKeySequence &seq)
194{
195 QList<QKeySequence> ks;
196#if HAVE_GLOBALACCEL
197 if (column == GlobalPrimary || column == GlobalAlternate) {
198 ks = KGlobalAccel::self()->shortcut(action: m_action);
199 if (!m_oldGlobalShortcut) {
200 m_oldGlobalShortcut = new QList<QKeySequence>(ks);
201 }
202 } else
203#endif
204 {
205 ks = m_action->shortcuts();
206 if (!m_oldLocalShortcut) {
207 m_oldLocalShortcut = new QList<QKeySequence>(ks);
208 }
209 }
210
211 if (column == LocalAlternate || column == GlobalAlternate) {
212 if (ks.isEmpty()) {
213 ks << QKeySequence();
214 }
215
216 if (ks.size() <= 1) {
217 ks << seq;
218 } else {
219 ks[1] = seq;
220 }
221 } else {
222 if (ks.isEmpty()) {
223 ks << seq;
224 } else {
225 ks[0] = seq;
226 }
227 }
228
229 // avoid also setting the default shortcut - what we are setting here is custom by definition
230#if HAVE_GLOBALACCEL
231 if (column == GlobalPrimary || column == GlobalAlternate) {
232 KGlobalAccel::self()->setShortcut(action: m_action, shortcut: ks, loadFlag: KGlobalAccel::NoAutoloading);
233
234 } else
235#endif
236 {
237 m_action->setShortcuts(ks);
238 }
239
240 updateModified();
241}
242
243// our definition of modified is "modified since the chooser was shown".
244void KShortcutsEditorItem::updateModified()
245{
246 if (m_oldLocalShortcut && *m_oldLocalShortcut == m_action->shortcuts()) {
247 delete m_oldLocalShortcut;
248 m_oldLocalShortcut = nullptr;
249 }
250#if HAVE_GLOBALACCEL
251 if (m_oldGlobalShortcut && *m_oldGlobalShortcut == KGlobalAccel::self()->shortcut(action: m_action)) {
252 delete m_oldGlobalShortcut;
253 m_oldGlobalShortcut = nullptr;
254 }
255#endif
256}
257
258bool KShortcutsEditorItem::isModified(uint column) const
259{
260 switch (column) {
261 case Name:
262 return false;
263 case LocalPrimary:
264 case LocalAlternate:
265 if (!m_oldLocalShortcut) {
266 return false;
267 }
268 if (column == LocalPrimary) {
269 return primarySequence(sequences: *m_oldLocalShortcut) != primarySequence(sequences: m_action->shortcuts());
270 } else {
271 return alternateSequence(sequences: *m_oldLocalShortcut) != alternateSequence(sequences: m_action->shortcuts());
272 }
273#if HAVE_GLOBALACCEL
274 case GlobalPrimary:
275 case GlobalAlternate:
276 if (!m_oldGlobalShortcut) {
277 return false;
278 }
279 if (column == GlobalPrimary) {
280 return primarySequence(sequences: *m_oldGlobalShortcut) != primarySequence(sequences: KGlobalAccel::self()->shortcut(action: m_action));
281 } else {
282 return alternateSequence(sequences: *m_oldGlobalShortcut) != alternateSequence(sequences: KGlobalAccel::self()->shortcut(action: m_action));
283 }
284#endif
285 default:
286 return false;
287 }
288}
289
290void KShortcutsEditorItem::undo()
291{
292 if (m_oldLocalShortcut) {
293 // We only ever reset the active Shortcut
294 m_action->setShortcuts(*m_oldLocalShortcut);
295 }
296
297#if HAVE_GLOBALACCEL
298 if (m_oldGlobalShortcut) {
299 KGlobalAccel::self()->setShortcut(action: m_action, shortcut: *m_oldGlobalShortcut, loadFlag: KGlobalAccel::NoAutoloading);
300 }
301#endif
302
303 updateModified();
304}
305
306void KShortcutsEditorItem::commit()
307{
308 delete m_oldLocalShortcut;
309 m_oldLocalShortcut = nullptr;
310 delete m_oldGlobalShortcut;
311 m_oldGlobalShortcut = nullptr;
312}
313

source code of kxmlgui/src/kshortcutseditoritem.cpp