File indexing completed on 2024-04-14 14:30:02

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2006, 2007 Andreas Hartmetz <ahartmetz@gmail.com>
0004     SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz>
0005     SPDX-FileCopyrightText: 2008 Alexander Dymo <adymo@kdevelop.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #ifndef KSHORTCUTSDIALOG_P_H
0011 #define KSHORTCUTSDIALOG_P_H
0012 
0013 #include "kkeysequencewidget.h"
0014 #include "kshortcutseditor.h"
0015 
0016 #include <KExtendableItemDelegate>
0017 
0018 #include <QCollator>
0019 #include <QGroupBox>
0020 #include <QKeySequence>
0021 #include <QList>
0022 #include <QMetaType>
0023 #include <QModelIndex>
0024 #include <QTreeWidget>
0025 
0026 class QLabel;
0027 class QTreeWidgetItem;
0028 class QRadioButton;
0029 class QAction;
0030 class KActionCollection;
0031 class QPushButton;
0032 class QComboBox;
0033 class KShortcutsDialog;
0034 
0035 enum ColumnDesignation {
0036     Name = 0,
0037     LocalPrimary,
0038     LocalAlternate,
0039     GlobalPrimary,
0040     GlobalAlternate,
0041     RockerGesture,
0042     ShapeGesture,
0043     Id,
0044 };
0045 
0046 enum MyRoles {
0047     ShortcutRole = Qt::UserRole,
0048     DefaultShortcutRole,
0049     ObjectRole,
0050 };
0051 
0052 /**
0053  * Type used for QTreeWidgetItems
0054  *
0055  * @internal
0056  */
0057 enum ItemTypes {
0058     NonActionItem = 0,
0059     ActionItem = 1,
0060 };
0061 
0062 QKeySequence primarySequence(const QList<QKeySequence> &sequences);
0063 QKeySequence alternateSequence(const QList<QKeySequence> &sequences);
0064 
0065 /**
0066  * Mixes the KShortcutWidget into the treeview used by KShortcutsEditor. When selecting an shortcut
0067  * it changes the display from "CTRL-W" to the Widget.
0068  *
0069  * @bug That delegate uses KExtendableItemDelegate. That means a cell can be expanded. When selected
0070  * a cell is replaced by a KShortcutsEditor. When painting the widget KExtendableItemDelegate
0071  * reparents the widget to the viewport of the itemview it belongs to. The widget is destroyed when
0072  * the user selects another shortcut or explicitly issues a contractItem event. But when the user
0073  * clears the model the delegate misses that event and doesn't delete the KShortcutseditor. And
0074  * remains as a visible artefact in your treeview. Additionally when closing your application you get
0075  * an assertion failure from KExtendableItemDelegate.
0076  *
0077  * @internal
0078  */
0079 class KShortcutsEditorDelegate : public KExtendableItemDelegate
0080 {
0081     Q_OBJECT
0082 public:
0083     KShortcutsEditorDelegate(QTreeWidget *parent, bool allowLetterShortcuts);
0084     // reimplemented to have some extra height
0085     QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
0086 
0087     /**
0088      * Set a list of action collections to check against for conflicting
0089      * shortcuts.
0090      *
0091      * @see KKeySequenceWidget::setCheckActionCollections
0092      */
0093     void setCheckActionCollections(const QList<KActionCollection *> &checkActionCollections);
0094 
0095 Q_SIGNALS:
0096     void shortcutChanged(const QVariant &, const QModelIndex &);
0097 public Q_SLOTS:
0098     void hiddenBySearchLine(QTreeWidgetItem *, bool);
0099 
0100 protected:
0101     bool eventFilter(QObject *, QEvent *) override;
0102 
0103 private:
0104     mutable QPersistentModelIndex m_editingIndex;
0105     const bool m_allowLetterShortcuts;
0106     QWidget *m_editor = nullptr;
0107 
0108     //! List of actionCollections to check for conflicts.
0109     QList<KActionCollection *> m_checkActionCollections;
0110 
0111 private Q_SLOTS:
0112     void itemActivated(const QModelIndex &index);
0113 
0114     /**
0115      * When the user collapses a hole subtree of shortcuts then remove eventually
0116      * extended items. Else we get that artefact bug. See above.
0117      */
0118     void itemCollapsed(const QModelIndex &index);
0119 
0120     /**
0121      * If the user allowed stealing a shortcut we want to be able to undo
0122      * that.
0123      */
0124     void stealShortcut(const QKeySequence &seq, QAction *action);
0125 
0126     void keySequenceChanged(const QKeySequence &);
0127 
0128 #if 0
0129     void shapeGestureChanged(const KShapeGesture &);
0130     void rockerGestureChanged(const KRockerGesture &);
0131 #endif
0132 };
0133 
0134 /**
0135  * That widget draws the decoration for KShortCutWidget. That widget is currently the only user.
0136  *
0137  * @internal
0138  */
0139 class TabConnectedWidget : public QWidget
0140 {
0141     Q_OBJECT
0142 public:
0143     explicit TabConnectedWidget(QWidget *parent)
0144         : QWidget(parent)
0145     {
0146     }
0147 
0148 protected:
0149     void paintEvent(QPaintEvent *pe) override;
0150 };
0151 
0152 /**
0153  * Edit a shortcut. Let you select between using the default shortcut and configuring your own.
0154  *
0155  * @internal
0156  */
0157 class ShortcutEditWidget : public TabConnectedWidget
0158 {
0159     Q_OBJECT
0160 public:
0161     ShortcutEditWidget(QWidget *viewport, const QKeySequence &defaultSeq, const QKeySequence &activeSeq, bool allowLetterShortcuts);
0162 
0163     //! @see KKeySequenceWidget::setCheckActionCollections()
0164     void setCheckActionCollections(const QList<KActionCollection *> &checkActionCollections);
0165 
0166     //@{
0167     //! @see KKeySequenceWidget::checkAgainstStandardShortcuts()
0168     KKeySequenceWidget::ShortcutTypes checkForConflictsAgainst() const;
0169     void setCheckForConflictsAgainst(KKeySequenceWidget::ShortcutTypes);
0170     //@}
0171 
0172     //@{
0173     //! @see KKeySequenceWidget::checkAgainstStandardShortcuts()
0174     bool multiKeyShortcutsAllowed() const;
0175     void setMultiKeyShortcutsAllowed(bool);
0176     //@}
0177 
0178     //! @see KKeySequenceWidget::setComponentName
0179     void setComponentName(const QString &componentName);
0180 
0181     void setAction(QObject *action);
0182 
0183 public Q_SLOTS:
0184 
0185     //! Set the displayed sequences
0186     void setKeySequence(const QKeySequence &activeSeq);
0187 
0188 Q_SIGNALS:
0189 
0190     //! Emitted when the key sequence is changed.
0191     void keySequenceChanged(const QKeySequence &);
0192 
0193     //! @see KKeySequenceWidget::stealShortcut()
0194     void stealShortcut(const QKeySequence &seq, QAction *action);
0195 
0196 private Q_SLOTS:
0197 
0198     void defaultToggled(bool);
0199     void setCustom(const QKeySequence &);
0200 
0201 private:
0202     QLabel *m_defaultLabel;
0203     QKeySequence m_defaultKeySequence;
0204     QRadioButton *m_defaultRadio;
0205     QRadioButton *m_customRadio;
0206     KKeySequenceWidget *m_customEditor;
0207     bool m_isUpdating;
0208     QObject *m_action;
0209     const QString m_noneText; // Translated "None" text for labels
0210 };
0211 
0212 #if 0
0213 Q_DECLARE_METATYPE(KShapeGesture)
0214 Q_DECLARE_METATYPE(KRockerGesture)
0215 #endif
0216 
0217 class KShortcutSchemesEditor : public QGroupBox
0218 {
0219     Q_OBJECT
0220 public:
0221     explicit KShortcutSchemesEditor(KShortcutsDialog *parent);
0222 
0223     /** @return the currently selected scheme in the editor (may differ from current app's scheme.*/
0224     QString currentScheme();
0225     void refreshSchemes();
0226     void addMoreMenuAction(QAction *action);
0227 
0228 private Q_SLOTS:
0229     void newScheme();
0230     void deleteScheme();
0231     void exportShortcutsScheme();
0232     void importShortcutsScheme();
0233     void saveAsDefaultsForScheme();
0234 
0235 Q_SIGNALS:
0236     void shortcutsSchemeChanged(const QString &);
0237 
0238 protected:
0239     void updateDeleteButton();
0240 
0241 private:
0242     QPushButton *m_newScheme;
0243     QPushButton *m_deleteScheme;
0244     QPushButton *m_exportScheme;
0245     QComboBox *m_schemesList;
0246     QMenu *m_moreActionsMenu;
0247 
0248     KShortcutsDialog *m_dialog;
0249 };
0250 
0251 class QAction;
0252 #if 0
0253 class KShapeGesture;
0254 class KRockerGesture;
0255 #endif
0256 
0257 /**
0258  * A QTreeWidgetItem that can handle QActions.
0259  *
0260  * It provides undo, commit functionality for changes made. Changes are effective immediately. You
0261  * have to commit them or they will be undone when deleting the item.
0262  *
0263  * @internal
0264  */
0265 class KShortcutsEditorItem : public QTreeWidgetItem
0266 {
0267 public:
0268     KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action);
0269 
0270     /**
0271      * Destructor
0272      *
0273      * Will undo pending changes. If you don't want that. Call commitChanges before
0274      */
0275     ~KShortcutsEditorItem() override;
0276 
0277     //! Undo the changes since the last commit.
0278     void undo();
0279 
0280     //! Commit the changes.
0281     void commit();
0282 
0283     QVariant data(int column, int role = Qt::DisplayRole) const override;
0284     bool operator<(const QTreeWidgetItem &other) const override;
0285 
0286     QKeySequence keySequence(uint column) const;
0287     void setKeySequence(uint column, const QKeySequence &seq);
0288 #if 0
0289     void setShapeGesture(const KShapeGesture &gst);
0290     void setRockerGesture(const KRockerGesture &gst);
0291 #endif
0292 
0293     bool isModified(uint column) const;
0294     bool isModified() const;
0295 
0296     void setNameBold(bool flag)
0297     {
0298         m_isNameBold = flag;
0299     }
0300 
0301 private:
0302     friend class KShortcutsEditorPrivate;
0303 
0304     //! Recheck modified status - could have changed back to initial value
0305     void updateModified();
0306 
0307     //! The action this item is responsible for
0308     QAction *m_action;
0309 
0310     //! Should the Name column be painted in bold?
0311     bool m_isNameBold;
0312 
0313     //@{
0314     //! The original shortcuts before user changes. 0 means no change.
0315     QList<QKeySequence> *m_oldLocalShortcut = nullptr;
0316     QList<QKeySequence> *m_oldGlobalShortcut = nullptr;
0317 #if 0
0318     KShapeGesture *m_oldShapeGesture;
0319     KRockerGesture *m_oldRockerGesture;
0320 #endif
0321     //@}
0322 
0323     //! The localized action name
0324     QString m_actionNameInTable;
0325 
0326     //! The action id. Needed for exporting and importing
0327     QString m_id;
0328 
0329     //! The collator, for sorting
0330     QCollator m_collator;
0331 };
0332 
0333 // NEEDED FOR KShortcutsEditorPrivate
0334 #include "ui_kshortcutsdialog.h"
0335 
0336 // Hack to make two protected methods public.
0337 // Used by both KShortcutsEditorPrivate and KShortcutsEditorDelegate
0338 class QTreeWidgetHack : public QTreeWidget
0339 {
0340 public:
0341     QTreeWidgetItem *itemFromIndex(const QModelIndex &index) const
0342     {
0343         return QTreeWidget::itemFromIndex(index);
0344     }
0345     QModelIndex indexFromItem(QTreeWidgetItem *item, int column) const
0346     {
0347         return QTreeWidget::indexFromItem(item, column);
0348     }
0349 };
0350 
0351 /**
0352  * This class should belong into kshortcutseditor.cpp. But kshortcutseditordelegate uses a static
0353  * function of this class. So for now it's here. But i will remove it later.
0354  *
0355  * @internal
0356  */
0357 class KShortcutsEditorPrivate
0358 {
0359 public:
0360     explicit KShortcutsEditorPrivate(KShortcutsEditor *qq);
0361 
0362     void initGUI(KShortcutsEditor::ActionTypes actionTypes, KShortcutsEditor::LetterShortcuts allowLetterShortcuts);
0363     void appendToView(uint nList, const QString &title = QString());
0364     // used in appendToView
0365     QTreeWidgetItem *findOrMakeItem(QTreeWidgetItem *parent, const QString &name);
0366 
0367     static KShortcutsEditorItem *itemFromIndex(QTreeWidget *const w, const QModelIndex &index);
0368 
0369     // Set all shortcuts to their default values (bindings).
0370     void allDefault();
0371 
0372     // clear all shortcuts
0373     void clearConfiguration();
0374 
0375     // Import shortcuts from file
0376     void importConfiguration(KConfigBase *config);
0377 
0378 #if 0
0379     //helper functions for conflict resolution
0380     bool stealShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &gest);
0381     bool stealRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &gest);
0382 #endif
0383 
0384     // conflict resolution functions
0385     void changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture);
0386 #if 0
0387     void changeShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &capture);
0388     void changeRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &capture);
0389 #endif
0390 
0391     // private slots
0392     // this invokes the appropriate conflict resolution function
0393     void capturedShortcut(const QVariant &, const QModelIndex &);
0394 
0395     //! Represents the three hierarchies the dialog handles.
0396     struct HierarchyInfo {
0397         QTreeWidgetItem *root = nullptr;
0398         QTreeWidgetItem *program = nullptr;
0399         QTreeWidgetItem *action = nullptr;
0400     };
0401 
0402     /**
0403      * Add @p action at @p level. Checks for QActions and unnamed actions
0404      * before adding.
0405      *
0406      * @return @c true if the action was really added, @c false if not
0407      */
0408     bool addAction(QAction *action, QTreeWidgetItem *parent);
0409 
0410     void printShortcuts() const;
0411 
0412     void setActionTypes(KShortcutsEditor::ActionTypes actionTypes);
0413 
0414     void setGlobalColumnsHidden(bool hide);
0415     void setLocalColumnsHidden(bool hide);
0416 
0417     // members
0418     QList<KActionCollection *> actionCollections;
0419     KShortcutsEditor *q;
0420 
0421     Ui::KShortcutsDialog ui;
0422 
0423     KShortcutsEditor::ActionTypes actionTypes;
0424     KShortcutsEditorDelegate *delegate;
0425 
0426     // Tracks if there are any Global shortcuts in any of the action collections shown in the dialog
0427     bool m_hasAnyGlobalShortcuts = false;
0428 };
0429 
0430 Q_DECLARE_METATYPE(KShortcutsEditorItem *)
0431 
0432 #endif /* KSHORTCUTSDIALOG_P_H */