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
26class QLabel;
27class QTreeWidgetItem;
28class QRadioButton;
29class QAction;
30class KActionCollection;
31class QPushButton;
32class QComboBox;
33class KShortcutsDialog;
34
35enum ColumnDesignation {
36 Name = 0,
37 LocalPrimary,
38 LocalAlternate,
39 GlobalPrimary,
40 GlobalAlternate,
41 RockerGesture,
42 ShapeGesture,
43 Id,
44};
45
46enum MyRoles {
47 ShortcutRole = Qt::UserRole,
48 DefaultShortcutRole,
49 ObjectRole,
50};
51
52enum ItemTypes {
53 NonActionItem = 0,
54 ActionItem = 1,
55};
56
57QKeySequence primarySequence(const QList<QKeySequence> &sequences);
58QKeySequence 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 */
74class KShortcutsEditorDelegate : public KExtendableItemDelegate
75{
76 Q_OBJECT
77public:
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
90Q_SIGNALS:
91 void shortcutChanged(const QVariant &, const QModelIndex &);
92public Q_SLOTS:
93 void hiddenBySearchLine(QTreeWidgetItem *, bool);
94
95protected:
96 bool eventFilter(QObject *, QEvent *) override;
97
98private:
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
106private 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 */
134class TabConnectedWidget : public QWidget
135{
136 Q_OBJECT
137public:
138 explicit TabConnectedWidget(QWidget *parent)
139 : QWidget(parent)
140 {
141 }
142
143protected:
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 */
152class ShortcutEditWidget : public TabConnectedWidget
153{
154 Q_OBJECT
155public:
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
174public Q_SLOTS:
175
176 //! Set the displayed sequences
177 void setKeySequence(const QKeySequence &activeSeq);
178
179Q_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
187private Q_SLOTS:
188
189 void defaultToggled(bool);
190 void setCustom(const QKeySequence &);
191
192private:
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
204Q_DECLARE_METATYPE(KShapeGesture)
205Q_DECLARE_METATYPE(KRockerGesture)
206#endif
207
208class KShortcutSchemesEditor : public QGroupBox
209{
210 Q_OBJECT
211public:
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 addMoreMenuAction(QAction *action);
218
219private Q_SLOTS:
220 void newScheme();
221 void deleteScheme();
222 void exportShortcutsScheme();
223 void importShortcutsScheme();
224 void saveAsDefaultsForScheme();
225
226Q_SIGNALS:
227 void shortcutsSchemeChanged(const QString &);
228
229protected:
230 void updateDeleteButton();
231
232private:
233 QPushButton *m_newScheme;
234 QPushButton *m_deleteScheme;
235 QPushButton *m_exportScheme;
236 QComboBox *m_schemesList;
237 QMenu *m_moreActionsMenu;
238
239 KShortcutsDialog *m_dialog;
240};
241
242class 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 */
252class KShortcutsEditorItem : public QTreeWidgetItem
253{
254public:
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
288private:
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 */
327class KShortcutsEditorPrivate
328{
329public:
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
399Q_DECLARE_METATYPE(KShortcutsEditorItem *)
400
401#endif /* KSHORTCUTSDIALOG_P_H */
402

source code of kxmlgui/src/kshortcutsdialog_p.h