1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2006, 2007 Andreas Hartmetz <ahartmetz@gmail.com> |
4 | SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz> |
5 | SPDX-FileCopyrightText: 2008 Alexander Dymo <adymo@kdevelop.org> |
6 | |
7 | SPDX-License-Identifier: LGPL-2.0-or-later |
8 | */ |
9 | |
10 | #ifndef KSHORTCUTSDIALOG_P_H |
11 | #define KSHORTCUTSDIALOG_P_H |
12 | |
13 | #include "kkeysequencewidget.h" |
14 | #include "kshortcutseditor.h" |
15 | |
16 | #include <KExtendableItemDelegate> |
17 | |
18 | #include <QCollator> |
19 | #include <QGroupBox> |
20 | #include <QKeySequence> |
21 | #include <QList> |
22 | #include <QMetaType> |
23 | #include <QModelIndex> |
24 | #include <QTreeWidget> |
25 | |
26 | class QLabel; |
27 | class QTreeWidgetItem; |
28 | class QRadioButton; |
29 | class QAction; |
30 | class KActionCollection; |
31 | class QPushButton; |
32 | class QComboBox; |
33 | class KShortcutsDialog; |
34 | |
35 | enum ColumnDesignation { |
36 | Name = 0, |
37 | LocalPrimary, |
38 | LocalAlternate, |
39 | GlobalPrimary, |
40 | GlobalAlternate, |
41 | RockerGesture, |
42 | ShapeGesture, |
43 | Id, |
44 | }; |
45 | |
46 | enum MyRoles { |
47 | ShortcutRole = Qt::UserRole, |
48 | DefaultShortcutRole, |
49 | ObjectRole, |
50 | }; |
51 | |
52 | /** |
53 | * Type used for QTreeWidgetItems |
54 | * |
55 | * @internal |
56 | */ |
57 | enum ItemTypes { |
58 | NonActionItem = 0, |
59 | ActionItem = 1, |
60 | }; |
61 | |
62 | QKeySequence primarySequence(const QList<QKeySequence> &sequences); |
63 | QKeySequence alternateSequence(const QList<QKeySequence> &sequences); |
64 | |
65 | /** |
66 | * Mixes the KShortcutWidget into the treeview used by KShortcutsEditor. When selecting an shortcut |
67 | * it changes the display from "CTRL-W" to the Widget. |
68 | * |
69 | * @bug That delegate uses KExtendableItemDelegate. That means a cell can be expanded. When selected |
70 | * a cell is replaced by a KShortcutsEditor. When painting the widget KExtendableItemDelegate |
71 | * reparents the widget to the viewport of the itemview it belongs to. The widget is destroyed when |
72 | * the user selects another shortcut or explicitly issues a contractItem event. But when the user |
73 | * clears the model the delegate misses that event and doesn't delete the KShortcutseditor. And |
74 | * remains as a visible artefact in your treeview. Additionally when closing your application you get |
75 | * an assertion failure from KExtendableItemDelegate. |
76 | * |
77 | * @internal |
78 | */ |
79 | class KShortcutsEditorDelegate : public KExtendableItemDelegate |
80 | { |
81 | Q_OBJECT |
82 | public: |
83 | KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts); |
84 | // reimplemented to have some extra height |
85 | QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; |
86 | |
87 | /** |
88 | * Set a list of action collections to check against for conflicting |
89 | * shortcuts. |
90 | * |
91 | * @see KKeySequenceWidget::setCheckActionCollections |
92 | */ |
93 | void setCheckActionCollections(const QList<KActionCollection *> &checkActionCollections); |
94 | |
95 | Q_SIGNALS: |
96 | void shortcutChanged(const QVariant &, const QModelIndex &); |
97 | public Q_SLOTS: |
98 | void hiddenBySearchLine(QTreeWidgetItem *, bool); |
99 | |
100 | protected: |
101 | bool eventFilter(QObject *, QEvent *) override; |
102 | |
103 | private: |
104 | mutable QPersistentModelIndex m_editingIndex; |
105 | const bool m_allowLetterShortcuts; |
106 | QWidget *m_editor = nullptr; |
107 | |
108 | //! List of actionCollections to check for conflicts. |
109 | QList<KActionCollection *> m_checkActionCollections; |
110 | |
111 | private Q_SLOTS: |
112 | void itemActivated(const QModelIndex &index); |
113 | |
114 | /** |
115 | * When the user collapses a hole subtree of shortcuts then remove eventually |
116 | * extended items. Else we get that artefact bug. See above. |
117 | */ |
118 | void itemCollapsed(const QModelIndex &index); |
119 | |
120 | /** |
121 | * If the user allowed stealing a shortcut we want to be able to undo |
122 | * that. |
123 | */ |
124 | void stealShortcut(const QKeySequence &seq, QAction *action); |
125 | |
126 | void keySequenceChanged(const QKeySequence &); |
127 | |
128 | #if 0 |
129 | void shapeGestureChanged(const KShapeGesture &); |
130 | void rockerGestureChanged(const KRockerGesture &); |
131 | #endif |
132 | }; |
133 | |
134 | /** |
135 | * That widget draws the decoration for KShortCutWidget. That widget is currently the only user. |
136 | * |
137 | * @internal |
138 | */ |
139 | class TabConnectedWidget : public QWidget |
140 | { |
141 | Q_OBJECT |
142 | public: |
143 | explicit TabConnectedWidget(QWidget *parent) |
144 | : QWidget(parent) |
145 | { |
146 | } |
147 | |
148 | protected: |
149 | void paintEvent(QPaintEvent *pe) override; |
150 | }; |
151 | |
152 | /** |
153 | * Edit a shortcut. Let you select between using the default shortcut and configuring your own. |
154 | * |
155 | * @internal |
156 | */ |
157 | class ShortcutEditWidget : public TabConnectedWidget |
158 | { |
159 | Q_OBJECT |
160 | public: |
161 | ShortcutEditWidget(QWidget *viewport, const QKeySequence &defaultSeq, const QKeySequence &activeSeq, bool allowLetterShortcuts); |
162 | |
163 | //! @see KKeySequenceWidget::setCheckActionCollections() |
164 | void setCheckActionCollections(const QList<KActionCollection *> &checkActionCollections); |
165 | |
166 | //@{ |
167 | //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() |
168 | KKeySequenceWidget::ShortcutTypes checkForConflictsAgainst() const; |
169 | void setCheckForConflictsAgainst(KKeySequenceWidget::ShortcutTypes); |
170 | //@} |
171 | |
172 | //@{ |
173 | //! @see KKeySequenceWidget::checkAgainstStandardShortcuts() |
174 | bool multiKeyShortcutsAllowed() const; |
175 | void setMultiKeyShortcutsAllowed(bool); |
176 | //@} |
177 | |
178 | //! @see KKeySequenceWidget::setComponentName |
179 | void setComponentName(const QString &componentName); |
180 | |
181 | void setAction(QObject *action); |
182 | |
183 | public Q_SLOTS: |
184 | |
185 | //! Set the displayed sequences |
186 | void setKeySequence(const QKeySequence &activeSeq); |
187 | |
188 | Q_SIGNALS: |
189 | |
190 | //! Emitted when the key sequence is changed. |
191 | void keySequenceChanged(const QKeySequence &); |
192 | |
193 | //! @see KKeySequenceWidget::stealShortcut() |
194 | void stealShortcut(const QKeySequence &seq, QAction *action); |
195 | |
196 | private Q_SLOTS: |
197 | |
198 | void defaultToggled(bool); |
199 | void setCustom(const QKeySequence &); |
200 | |
201 | private: |
202 | QLabel *m_defaultLabel; |
203 | QKeySequence m_defaultKeySequence; |
204 | QRadioButton *m_defaultRadio; |
205 | QRadioButton *m_customRadio; |
206 | KKeySequenceWidget *m_customEditor; |
207 | bool m_isUpdating; |
208 | QObject *m_action; |
209 | const QString m_noneText; // Translated "None" text for labels |
210 | }; |
211 | |
212 | #if 0 |
213 | Q_DECLARE_METATYPE(KShapeGesture) |
214 | Q_DECLARE_METATYPE(KRockerGesture) |
215 | #endif |
216 | |
217 | class KShortcutSchemesEditor : public QGroupBox |
218 | { |
219 | Q_OBJECT |
220 | public: |
221 | explicit KShortcutSchemesEditor(KShortcutsDialog *parent); |
222 | |
223 | /** @return the currently selected scheme in the editor (may differ from current app's scheme.*/ |
224 | QString currentScheme(); |
225 | void refreshSchemes(); |
226 | void (QAction *action); |
227 | |
228 | private Q_SLOTS: |
229 | void newScheme(); |
230 | void deleteScheme(); |
231 | void exportShortcutsScheme(); |
232 | void importShortcutsScheme(); |
233 | void saveAsDefaultsForScheme(); |
234 | |
235 | Q_SIGNALS: |
236 | void shortcutsSchemeChanged(const QString &); |
237 | |
238 | protected: |
239 | void updateDeleteButton(); |
240 | |
241 | private: |
242 | QPushButton *m_newScheme; |
243 | QPushButton *m_deleteScheme; |
244 | QPushButton *m_exportScheme; |
245 | QComboBox *m_schemesList; |
246 | QMenu *; |
247 | |
248 | KShortcutsDialog *m_dialog; |
249 | }; |
250 | |
251 | class QAction; |
252 | |
253 | /** |
254 | * A QTreeWidgetItem that can handle QActions. |
255 | * |
256 | * It provides undo, commit functionality for changes made. Changes are effective immediately. You |
257 | * have to commit them or they will be undone when deleting the item. |
258 | * |
259 | * @internal |
260 | */ |
261 | class KShortcutsEditorItem : public QTreeWidgetItem |
262 | { |
263 | public: |
264 | KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action); |
265 | |
266 | /** |
267 | * Destructor |
268 | * |
269 | * Will undo pending changes. If you don't want that. Call commitChanges before |
270 | */ |
271 | ~KShortcutsEditorItem() override; |
272 | |
273 | //! Undo the changes since the last commit. |
274 | void undo(); |
275 | |
276 | //! Commit the changes. |
277 | void commit(); |
278 | |
279 | QVariant data(int column, int role = Qt::DisplayRole) const override; |
280 | bool operator<(const QTreeWidgetItem &other) const override; |
281 | |
282 | QKeySequence keySequence(uint column) const; |
283 | void setKeySequence(uint column, const QKeySequence &seq); |
284 | #if 0 |
285 | void setShapeGesture(const KShapeGesture &gst); |
286 | void setRockerGesture(const KRockerGesture &gst); |
287 | #endif |
288 | |
289 | bool isModified(uint column) const; |
290 | bool isModified() const; |
291 | |
292 | void setNameBold(bool flag) |
293 | { |
294 | m_isNameBold = flag; |
295 | } |
296 | |
297 | private: |
298 | friend class KShortcutsEditorPrivate; |
299 | |
300 | //! Recheck modified status - could have changed back to initial value |
301 | void updateModified(); |
302 | |
303 | //! The action this item is responsible for |
304 | QAction *m_action; |
305 | |
306 | //! Should the Name column be painted in bold? |
307 | bool m_isNameBold; |
308 | |
309 | //@{ |
310 | //! The original shortcuts before user changes. 0 means no change. |
311 | QList<QKeySequence> *m_oldLocalShortcut = nullptr; |
312 | QList<QKeySequence> *m_oldGlobalShortcut = nullptr; |
313 | #if 0 |
314 | KShapeGesture *m_oldShapeGesture; |
315 | KRockerGesture *m_oldRockerGesture; |
316 | #endif |
317 | //@} |
318 | |
319 | //! The localized action name |
320 | QString m_actionNameInTable; |
321 | |
322 | //! The action id. Needed for exporting and importing |
323 | QString m_id; |
324 | |
325 | //! The collator, for sorting |
326 | QCollator m_collator; |
327 | }; |
328 | |
329 | // NEEDED FOR KShortcutsEditorPrivate |
330 | #include "ui_kshortcutsdialog.h" |
331 | |
332 | // Hack to make two protected methods public. |
333 | // Used by both KShortcutsEditorPrivate and KShortcutsEditorDelegate |
334 | class QTreeWidgetHack : public QTreeWidget |
335 | { |
336 | public: |
337 | QTreeWidgetItem *itemFromIndex(const QModelIndex &index) const |
338 | { |
339 | return QTreeWidget::itemFromIndex(index); |
340 | } |
341 | QModelIndex indexFromItem(QTreeWidgetItem *item, int column) const |
342 | { |
343 | return QTreeWidget::indexFromItem(item, column); |
344 | } |
345 | }; |
346 | |
347 | /** |
348 | * This class should belong into kshortcutseditor.cpp. But kshortcutseditordelegate uses a static |
349 | * function of this class. So for now it's here. But i will remove it later. |
350 | * |
351 | * @internal |
352 | */ |
353 | class KShortcutsEditorPrivate |
354 | { |
355 | public: |
356 | explicit KShortcutsEditorPrivate(KShortcutsEditor *qq); |
357 | |
358 | void initGUI(KShortcutsEditor::ActionTypes actionTypes, KShortcutsEditor::LetterShortcuts allowLetterShortcuts); |
359 | void appendToView(uint nList, const QString &title = QString()); |
360 | // used in appendToView |
361 | QTreeWidgetItem *findOrMakeItem(QTreeWidgetItem *parent, const QString &name); |
362 | |
363 | static KShortcutsEditorItem *itemFromIndex(QTreeWidget *const w, const QModelIndex &index); |
364 | |
365 | // Set all shortcuts to their default values (bindings). |
366 | void allDefault(); |
367 | |
368 | // Import shortcuts from file |
369 | void importConfiguration(KConfigBase *config); |
370 | |
371 | #if 0 |
372 | //helper functions for conflict resolution |
373 | bool stealShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &gest); |
374 | bool stealRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &gest); |
375 | #endif |
376 | |
377 | // conflict resolution functions |
378 | void changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture); |
379 | #if 0 |
380 | void changeShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &capture); |
381 | void changeRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &capture); |
382 | #endif |
383 | |
384 | // private slots |
385 | // this invokes the appropriate conflict resolution function |
386 | void capturedShortcut(const QVariant &, const QModelIndex &); |
387 | |
388 | //! Represents the three hierarchies the dialog handles. |
389 | struct HierarchyInfo { |
390 | QTreeWidgetItem *root = nullptr; |
391 | QTreeWidgetItem *program = nullptr; |
392 | QTreeWidgetItem *action = nullptr; |
393 | }; |
394 | |
395 | /** |
396 | * Add @p action at @p level. Checks for QActions and unnamed actions |
397 | * before adding. |
398 | * |
399 | * @return @c true if the action was really added, @c false if not |
400 | */ |
401 | bool addAction(QAction *action, QTreeWidgetItem *parent); |
402 | |
403 | void printShortcuts() const; |
404 | |
405 | void setActionTypes(KShortcutsEditor::ActionTypes actionTypes); |
406 | |
407 | void setGlobalColumnsHidden(bool hide); |
408 | void setLocalColumnsHidden(bool hide); |
409 | |
410 | // members |
411 | QList<KActionCollection *> actionCollections; |
412 | KShortcutsEditor *q; |
413 | |
414 | Ui::KShortcutsDialog ui; |
415 | |
416 | KShortcutsEditor::ActionTypes actionTypes; |
417 | KShortcutsEditorDelegate *delegate; |
418 | |
419 | // Tracks if there are any local shortcuts in any of the action collections shown in the dialog |
420 | bool m_hasAnyLocalShortcuts = false; |
421 | // Tracks if there are any Global shortcuts in any of the action collections shown in the dialog |
422 | bool m_hasAnyGlobalShortcuts = false; |
423 | }; |
424 | |
425 | Q_DECLARE_METATYPE(KShortcutsEditorItem *) |
426 | |
427 | #endif /* KSHORTCUTSDIALOG_P_H */ |
428 | |