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