| 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 | |