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