File indexing completed on 2024-06-23 05:20:47

0001 /*
0002    Copyright (C) 2012, 2013 by Glad Deschrijver <glad.deschrijver@gmail.com>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU General Public License as
0006    published by the Free Software Foundation; either version 2 of
0007    the License or (at your option) version 3 or any later version
0008    accepted by the membership of KDE e.V. (or its successor approved
0009    by the membership of KDE e.V.), which shall act as a proxy
0010    defined in Section 14 of version 3 of the license.
0011 
0012    This program is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0015    GNU General Public License for more details.
0016 
0017    You should have received a copy of the GNU General Public License
0018    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0019 */
0020 
0021 #ifndef SHORTCUTHANDLER_H
0022 #define SHORTCUTHANDLER_H
0023 
0024 #ifndef QT_NO_SHORTCUT
0025 
0026 #include <QHash>
0027 #include <QKeySequence>
0028 #include <QObject>
0029 #include <QPointer>
0030 #include <QStringList>
0031 
0032 class QAction;
0033 class QSettings;
0034 class QWidget;
0035 
0036 namespace Gui
0037 {
0038 
0039 class ShortcutConfigDialog;
0040 class ShortcutConfigWidget;
0041 
0042 class ActionDescription
0043 {
0044 public:
0045     explicit ActionDescription(const QString &iconName = QString(), const QString &text = QString(), const QString &defaultShortcut = QString(), const QString &parentId = QObject::tr("Main Window")) : iconName(iconName), text(text), defaultShortcut(defaultShortcut), shortcut(defaultShortcut), parentId(parentId) {}
0046     QString iconName;
0047     QString text;
0048     QString defaultShortcut;
0049     QString shortcut;
0050     QString parentId;
0051     QPointer<QAction> action;
0052 };
0053 
0054 /**
0055  * @short Class for handling the configuration of keyboard shortcuts.
0056  *
0057  * This class is used for handling the configuration of keyboard shortcuts
0058  * for actions defined with QAction.
0059  *
0060  * \b Initialization \n
0061  *
0062  * Only one ShortcutHandler instance can be created during the lifetime of
0063  * your application. Typically this is done in the constructor of your main
0064  * window as follows:
0065  *
0066  * \code
0067  * ShortcutHandler *shortcutHandler = new ShortcutHandler(parentWidget);
0068  * QSettings *settingsObject = new QSettings(parentWidget);
0069  * shortcutHandler->setSettingsObject(settingsObject);
0070  * \endcode
0071  *
0072  * where parentWidget is the widget which will serve as the parent widget
0073  * for the shortcut configuration dialog (typically this is the main window
0074  * of your application). The shortcut configuration dialog will be modal
0075  * w.r.t. parentWidget. Specifying the parent in this way also ensures that
0076  * the ShortcutHandler object itself will be deleted when parentWidget is
0077  * deleted.
0078  *
0079  * In the example above, a QSettings object is defined and passed to the
0080  * ShortcutHandler instance using setSettingsObject(QSettings*). This is
0081  * required. This allows you to save the shortcut configuration in the
0082  * same place as the other configuration settings of your program. Note that
0083  * ShortcutHandler doesn't take over responsibility of the QSettings object,
0084  * which should therefore be deleted by the caller. The QSettings object
0085  * passed to ShortcutHandler should exist during the entire lifetime of the
0086  * ShortcutHandler object, and should therefore not be deleted before the
0087  * ShortcutHandler object is deleted.
0088  *
0089  * \b Making the shortcuts of your actions configurable \n
0090  *
0091  * In order to make a shortcut configurable, the QAction which has this
0092  * shortcut must be added to the ShortcutHandler instance using
0093  * addAction(QAction*) or addAction(QAction*, const QString&). For example:
0094  *
0095  * \code
0096  * QAction *quitAction = new QAction(tr("&Quit"), this);
0097  * quitAction->setShortcut(tr("Ctrl+Q"));
0098  * quitAction->setObjectName("action_application_exit");
0099  * ShortcutHandler::instance()->addAction(quitAction);
0100  * \endcode
0101  *
0102  * This code can be used in any class in which you have included the
0103  * shortcuthandler.h header file. It doesn't matter in which way the
0104  * shortcut of the action is defined (e.g. the initial shortcut could
0105  * also have been set to QKeySequence::Open) and the shortcut handling
0106  * even works if there is no initial shortcut set. Note that if there
0107  * is a shortcut set before the action is added to the ShortcutHandler
0108  * object, then it will serve as the default shortcut in the configuration
0109  * dialog. Note that it is compulsary to set the object name of the action
0110  * with setObjectName(const QString&) before the action is added to the
0111  * ShortcutHandler object, because the object name of the action (which
0112  * should be unique throughout the whole application) is used to store the
0113  * shortcut in the settings on disk. The action can be safely removed
0114  * during the lifetime of the ShortcutHandler instance. Actions can be
0115  * added to the ShortcutHandler instance at any moment during its lifetime,
0116  * even after the configuration widget has already been shown (the new
0117  * actions will be visible in the configuration widget when it is shown
0118  * again).
0119  *
0120  * The second argument to addAction(QAction*, const QString&) sets the
0121  * label under which the action is classified in the shortcut configuration
0122  * dialog. If this label is not set, then it defaults to
0123  * QObject::tr("Main Window").
0124  *
0125  * After all QActions are initialized and the appropriate ones are added
0126  * to ShortcutHandler, the following code must be run in order to read
0127  * previous modifications to the shortcuts from disk:
0128  *
0129  * \code
0130  * ShortcutHandler::instance()->readSettings();
0131  * \endcode
0132  *
0133  * \b Making use of predefined shortcuts \n
0134  *
0135  * Instead of first creating actions and then adding them to ShortcutHandler,
0136  * you can predefine shortcuts using
0137  * defineAction(const QString&, const QString&, const QString&, const QString&, const QString&) or
0138  * defineAction(const QString&, const QString&, const QString&, const QKeySequence::StandardKey&, const QString&),
0139  * after which you create the actions for the predefined shortcuts with
0140  * createAction(const QString&, QWidget*) or
0141  * createAction(const QString&, QObject*, const char*, QWidget*).
0142  * The first argument of both createAction() functions is the name of an action as known
0143  * by ShortcutHandler. The last argument of both functions is the widget
0144  * that will be the parent of the action. The second function has two
0145  * additional arguments which define the receiver of the actions's
0146  * triggered() signal and the slot which is executed when this signal is
0147  * triggered. For example, in the constructor of the main window you can use:
0148  *
0149  * \code
0150  * ShortcutHandler::instance()->defineAction("action_application_exit", "application-exit", tr("E&xit"), QKeySequence::Quit, tr("Main Window"));
0151  * ShortcutHandler::instance()->defineAction("action_toggle_document_editable", "document-edit", tr("&Set/Unset Document Editable"), tr("Ctrl+E"), tr("Main Window"));
0152  * \endcode
0153  *
0154  * and anywhere in the program you can define:
0155  *
0156  * \code
0157  * QAction *quitAction = ShortcutHandler::instance()->createAction("action_application_exit", this, SLOT(slotQuitApplication()), this);
0158  * \endcode
0159  *
0160  * This code creates an action with the shortcut, text and icon defined for
0161  * "action_application_exit" in ShortcutHandler. If no text or icon is defined
0162  * for "action_application_exit", quitAction will be equal to 0. This action
0163  * will call the slotQuitApplication() slot defined in "this" (the second
0164  * argument) and has "this" (the last argument) as parent. If the action
0165  * should be checkable, then the following code can be used:
0166  *
0167  * \code
0168  * QAction *toggleEditableAction = ShortcutHandler::instance()->createAction("action_toggle_document_editable", this);
0169  * toggleEditableAction->setCheckable();
0170  * connect(toggleEditableAction, SIGNAL(triggered(bool)), this, SLOT(slotToggleEditable(bool)));
0171  * \endcode
0172  *
0173  * In both cases, if an action is created with createAction(), it can be
0174  * accessed later with action(const QString&). For example:
0175  *
0176  * \code
0177  * toolBar->addAction(ShortcutHandler::instance()->action("action_toggle_document_editable"));
0178  * \endcode
0179  *
0180  * After all actions are defined using defineAction(), the following code
0181  * must be run in order to read previous modifications to the shortcuts
0182  * from disk:
0183  *
0184  * \code
0185  * ShortcutHandler::instance()->readSettings();
0186  * \endcode
0187  *
0188  * \b Accessing the shortcut configuration dialog \n
0189  *
0190  * In order to make the shortcut configuration dialog accessible to the
0191  * users of your program, an action can be added to a menu or toolbar.
0192  * This action can be obtained with
0193  * ShortcutHandler::instance()->shortcutConfigAction()
0194  * anywhere in your program. For example:
0195  *
0196  * \code
0197  * QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings"));
0198  * settingsMenu->addAction(ShortcutHandler::instance()->shortcutConfigAction());
0199  * \endcode
0200  *
0201  * This code adds an item labeled "Configure Shortcuts" (or a localized
0202  * version of this) to settingsMenu, which when triggered shows the shortcut
0203  * configuration dialog. The shortcut configuration dialog is deleted when
0204  * the parent widget of the ShortcutHandler instance is deleted.
0205  *
0206  * Warning: this approach cannot be combined with the following approach.
0207  *
0208  * \b Adding the shortcut configuration widget to an existing dialog \n
0209  *
0210  * Alternatively to adding an action which opens a shortcut configuration
0211  * dialog as described above, it is possible to add a shortcut configuration
0212  * widget to an existing configuration dialog. For example, in the constructor
0213  * of a configuration dialog one can have:
0214  *
0215  * \code
0216  * QVBoxLayout *mainLayout = new QVBoxLayout;
0217  * mainLayout->addWidget(ShortcutHandler::instance()->configWidget());
0218  * setLayout(mainLayout);
0219  * \endcode
0220  *
0221  * You can use ShortcutHandler::instance()->configWidget() multiple times,
0222  * but this will always refer to the same widget. The shortcut configuration
0223  * widget is deleted when the parent widget of the ShortcutHandler is deleted
0224  * (or when its new parent is deleted if the widget is reparented).
0225  *
0226  * In order to save the modified shortcuts to disk, it is necessary to call
0227  * ShortcutHandler::instance()->accept() each time the configuration dialog
0228  * to which this widget is added is accepted. This can happen for example
0229  * in the configuration dialog's own accept() function.
0230  *
0231  * Warning: this approach cannot be combined with the previous approach:
0232  * either you add an action with shortcutConfigAction() (which shows a
0233  * standalone shortcut configuration dialog) or you add a widget with
0234  * configWidget(). Using both is not possible.
0235  *
0236  * \b Exclusivity groups \n
0237  *
0238  * By default all shortcuts managed by ShortcutHandler should be different.
0239  * ShortcutHandler does however not test initially if this condition is
0240  * satisfied. When the user attempts to set a shortcut which is already in
0241  * use, a warning is displayed and the possibility is given to unset the
0242  * shortcut for the action that had the shortcut previously. Sometimes it
0243  * is however useful to be able to use the same shortcut in different
0244  * situations, for example when the application has two dialogs which can
0245  * never be shown together, or when the application has a full screen mode
0246  * effectively hiding the default window of the application. In order to
0247  * be able to use the same shortcuts in such situations, exclusivity groups
0248  * can be defined. For example, the code
0249  *
0250  * \code
0251  * ShortcutHandler::instance()->addExclusivityGroup(QStringList() << tr("Presentation"));
0252  * ShortcutHandler::instance()->addExclusivityGroup(QStringList() << tr("LaTeX commands") << tr("Math commands"));
0253  * \endcode
0254  *
0255  * defines two exclusivity groups. The first one contains one element
0256  * ("Presentation"), the second one contains two elements ("LaTeX commands"
0257  * and "Math commands"). The elements must be labels which are passed
0258  * previously to addAction(QAction*, const QString&) as the second argument.
0259  * Actions having the label "Presentation" will then be required to
0260  * have unique shortcuts, no two actions which have either one of the
0261  * labels "LaTeX commands" and "Math commands" can have the same shortcuts.
0262  * On the other hand, actions with label "Presentation" can have the
0263  * same shortcuts as actions with label "LaTeX commands" or "Math commands".
0264  *
0265  * By default, there exists a label "Main Window" which belongs to its own
0266  * exclusivity group. This label only exists when actions are added with
0267  * addAction(QAction*, const QString&) without the second argument being
0268  * specified (the corresponding exclusivity group always exists, but is
0269  * ignored in this case). It is possible to add other labels to this
0270  * exclusivity group by specifying for example:
0271  *
0272  * \code
0273  * ShortcutHandler::instance()->addExclusivityGroup(QStringList() << tr("Main Window") << tr("Presentation"));
0274  * \endcode
0275  *
0276  * This code adds "Presentation" to the exclusivity group containing
0277  * "Main Window".
0278  *
0279  * If no exclusivity groups are defined, all labels are added to the same
0280  * exclusivity group (which is the group containing "Main Window").
0281  *
0282  * Note that adding an exclusivity group can only be done \b after \n all
0283  * necessary labels are added to the shortcut handler using
0284  * addAction(QAction*, const QString&) where the second argument is the
0285  * label which is added to the exclusivity group. If a label is added to
0286  * two or more exclusivity groups in subsequent calls of
0287  * addExclusivityGroup(), then the label will only belong to the last
0288  * defined exclusivity group.
0289  */
0290 class ShortcutHandler : public QObject
0291 {
0292     Q_OBJECT
0293 
0294 public:
0295     explicit ShortcutHandler(QWidget *parent);
0296     ~ShortcutHandler();
0297     static ShortcutHandler *instance() { return self; }
0298 
0299     void setSettingsObject(QSettings *settings);
0300     /**
0301      * Defines an action with a shortcut. All actions defined using this
0302      * function will be visible in the shortcut configuration widget.
0303      * \param actionName the name under which the action is known to the shortcut configuration system
0304      * \param iconName the name of the icon displayed when the action is added to a menu or toolbar. If iconName is an empty string, then no icon is shown.
0305      * \param text the text visible on the item in the menu or toolbar corresponding to this action
0306      * \param defaultShortcut the (localized) string describing the default shortcut which triggers this action, e.g. tr("Ctrl+E")
0307      * \param parentId the title under which this action is shown in the shortcut configuration widget. In the shortcut configuration widget, the actions are grouped according to parentId.
0308      */
0309     void defineAction(const QString &actionName, const QString &iconName, const QString &text, const QString &defaultShortcut = QString(), const QString &parentId = QObject::tr("Main Window"));
0310     /**
0311      * Defines an action with a shortcut. All actions defined using this
0312      * function will be visible in the shortcut configuration widget.
0313      * \param actionName the name under which the action is known to the shortcut configuration system
0314      * \param iconName the name of the icon displayed when the action is added to a menu or toolbar. If iconName is an empty string, then no icon is shown.
0315      * \param text the text visible on the item in the menu or toolbar corresponding to this action
0316      * \param key the QKeySequence::StandardKey value describing the default shortcut which triggers this action, e.g. QKeySequence::Quit
0317      * \param parentId the title under which this action is shown in the shortcut configuration widget. In the shortcut configuration widget, the actions are grouped according to parentId.
0318      */
0319     void defineAction(const QString &actionName, const QString &iconName, const QString &text, const QKeySequence::StandardKey &key, const QString &parentId = QObject::tr("Main Window"));
0320     QAction *createAction(const QString &actionName, QWidget *parent = 0);
0321     QAction *createAction(const QString &actionName, QObject *receiver, const char *member, QWidget *parent = 0);
0322     void addExclusivityGroup(const QStringList &group);
0323     QSettings *settingsObject();
0324     QAction *action(const QString &actionName);
0325     QAction *shortcutConfigAction();
0326     ShortcutConfigWidget *configWidget();
0327     void accept();
0328     void reject();
0329     void readSettings();
0330 
0331 private Q_SLOTS:
0332     void changeShortcuts(const QHash<QString, ActionDescription> &actionDescriptions);
0333     void openShortcutConfigDialog();
0334 
0335 private:
0336     static ShortcutHandler *self;
0337 
0338     QSettings *m_settings;
0339     QPointer<QAction> m_shortcutConfigAction;
0340     QPointer<ShortcutConfigDialog> m_shortcutConfigDialog;
0341     QPointer<ShortcutConfigWidget> m_shortcutConfigWidget;
0342 
0343     QHash<QString, ActionDescription> m_actionDescriptions;
0344     QList<QStringList> m_exclusivityGroups;
0345 };
0346 
0347 } // namespace Gui
0348 
0349 #endif // QT_NO_SHORTCUT
0350 
0351 #endif // SHORTCUTHANDLER_H