File indexing completed on 2024-03-24 04:02:26

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 
0253 /**
0254  * A QTreeWidgetItem that can handle QActions.
0255  *
0256  * It provides undo, commit functionality for changes made. Changes are effective immediately. You
0257  * have to commit them or they will be undone when deleting the item.
0258  *
0259  * @internal
0260  */
0261 class KShortcutsEditorItem : public QTreeWidgetItem
0262 {
0263 public:
0264     KShortcutsEditorItem(QTreeWidgetItem *parent, QAction *action);
0265 
0266     /**
0267      * Destructor
0268      *
0269      * Will undo pending changes. If you don't want that. Call commitChanges before
0270      */
0271     ~KShortcutsEditorItem() override;
0272 
0273     //! Undo the changes since the last commit.
0274     void undo();
0275 
0276     //! Commit the changes.
0277     void commit();
0278 
0279     QVariant data(int column, int role = Qt::DisplayRole) const override;
0280     bool operator<(const QTreeWidgetItem &other) const override;
0281 
0282     QKeySequence keySequence(uint column) const;
0283     void setKeySequence(uint column, const QKeySequence &seq);
0284 #if 0
0285     void setShapeGesture(const KShapeGesture &gst);
0286     void setRockerGesture(const KRockerGesture &gst);
0287 #endif
0288 
0289     bool isModified(uint column) const;
0290     bool isModified() const;
0291 
0292     void setNameBold(bool flag)
0293     {
0294         m_isNameBold = flag;
0295     }
0296 
0297 private:
0298     friend class KShortcutsEditorPrivate;
0299 
0300     //! Recheck modified status - could have changed back to initial value
0301     void updateModified();
0302 
0303     //! The action this item is responsible for
0304     QAction *m_action;
0305 
0306     //! Should the Name column be painted in bold?
0307     bool m_isNameBold;
0308 
0309     //@{
0310     //! The original shortcuts before user changes. 0 means no change.
0311     QList<QKeySequence> *m_oldLocalShortcut = nullptr;
0312     QList<QKeySequence> *m_oldGlobalShortcut = nullptr;
0313 #if 0
0314     KShapeGesture *m_oldShapeGesture;
0315     KRockerGesture *m_oldRockerGesture;
0316 #endif
0317     //@}
0318 
0319     //! The localized action name
0320     QString m_actionNameInTable;
0321 
0322     //! The action id. Needed for exporting and importing
0323     QString m_id;
0324 
0325     //! The collator, for sorting
0326     QCollator m_collator;
0327 };
0328 
0329 // NEEDED FOR KShortcutsEditorPrivate
0330 #include "ui_kshortcutsdialog.h"
0331 
0332 // Hack to make two protected methods public.
0333 // Used by both KShortcutsEditorPrivate and KShortcutsEditorDelegate
0334 class QTreeWidgetHack : public QTreeWidget
0335 {
0336 public:
0337     QTreeWidgetItem *itemFromIndex(const QModelIndex &index) const
0338     {
0339         return QTreeWidget::itemFromIndex(index);
0340     }
0341     QModelIndex indexFromItem(QTreeWidgetItem *item, int column) const
0342     {
0343         return QTreeWidget::indexFromItem(item, column);
0344     }
0345 };
0346 
0347 /**
0348  * This class should belong into kshortcutseditor.cpp. But kshortcutseditordelegate uses a static
0349  * function of this class. So for now it's here. But i will remove it later.
0350  *
0351  * @internal
0352  */
0353 class KShortcutsEditorPrivate
0354 {
0355 public:
0356     explicit KShortcutsEditorPrivate(KShortcutsEditor *qq);
0357 
0358     void initGUI(KShortcutsEditor::ActionTypes actionTypes, KShortcutsEditor::LetterShortcuts allowLetterShortcuts);
0359     void appendToView(uint nList, const QString &title = QString());
0360     // used in appendToView
0361     QTreeWidgetItem *findOrMakeItem(QTreeWidgetItem *parent, const QString &name);
0362 
0363     static KShortcutsEditorItem *itemFromIndex(QTreeWidget *const w, const QModelIndex &index);
0364 
0365     // Set all shortcuts to their default values (bindings).
0366     void allDefault();
0367 
0368     // Import shortcuts from file
0369     void importConfiguration(KConfigBase *config);
0370 
0371 #if 0
0372     //helper functions for conflict resolution
0373     bool stealShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &gest);
0374     bool stealRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &gest);
0375 #endif
0376 
0377     // conflict resolution functions
0378     void changeKeyShortcut(KShortcutsEditorItem *item, uint column, const QKeySequence &capture);
0379 #if 0
0380     void changeShapeGesture(KShortcutsEditorItem *item, const KShapeGesture &capture);
0381     void changeRockerGesture(KShortcutsEditorItem *item, const KRockerGesture &capture);
0382 #endif
0383 
0384     // private slots
0385     // this invokes the appropriate conflict resolution function
0386     void capturedShortcut(const QVariant &, const QModelIndex &);
0387 
0388     //! Represents the three hierarchies the dialog handles.
0389     struct HierarchyInfo {
0390         QTreeWidgetItem *root = nullptr;
0391         QTreeWidgetItem *program = nullptr;
0392         QTreeWidgetItem *action = nullptr;
0393     };
0394 
0395     /**
0396      * Add @p action at @p level. Checks for QActions and unnamed actions
0397      * before adding.
0398      *
0399      * @return @c true if the action was really added, @c false if not
0400      */
0401     bool addAction(QAction *action, QTreeWidgetItem *parent);
0402 
0403     void printShortcuts() const;
0404 
0405     void setActionTypes(KShortcutsEditor::ActionTypes actionTypes);
0406 
0407     void setGlobalColumnsHidden(bool hide);
0408     void setLocalColumnsHidden(bool hide);
0409 
0410     // members
0411     QList<KActionCollection *> actionCollections;
0412     KShortcutsEditor *q;
0413 
0414     Ui::KShortcutsDialog ui;
0415 
0416     KShortcutsEditor::ActionTypes actionTypes;
0417     KShortcutsEditorDelegate *delegate;
0418 
0419     // Tracks if there are any local shortcuts in any of the action collections shown in the dialog
0420     bool m_hasAnyLocalShortcuts = false;
0421     // Tracks if there are any Global shortcuts in any of the action collections shown in the dialog
0422     bool m_hasAnyGlobalShortcuts = false;
0423 };
0424 
0425 Q_DECLARE_METATYPE(KShortcutsEditorItem *)
0426 
0427 #endif /* KSHORTCUTSDIALOG_P_H */