1 /*
2  * Stellarium
3  * Copyright (C) 2013 Guillaume Chereau
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18  */
19 
20 #ifndef STELACTIONMGR_HPP
21 #define STELACTIONMGR_HPP
22 
23 #include "StelPropertyMgr.hpp"
24 #include <QKeySequence>
25 #include <QList>
26 
27 //! Wrapper around an argumentless QObject slot or a bool Q_PROPERTY with WRITE method,
28 //! allowing the slot to be called/property to be toggled using this action object.
29 //! The action object can be identified by a unique string, and found through StelActionMgr::findAction.
30 //! Use StelActionMgr::addAction to define a new action.
31 //! In StelModule subclasses, one can also use StelModule::addAction for convenience.
32 //!
33 //! StelAction objects are intended for user interaction. They automatically show up in the hotkey configuration dialog
34 //! (ShortcutsDialog), and can be bound to interface buttons (StelButton).
35 //!
36 //! StelAction internally uses a StelProperty, if connected to a property.
37 //! A new StelProperty with the name of the action is registered automatically in this case.
38 //! A NOTIFY signal should be provided, though not strictly necessary, it is really recommended.
39 //! @note If you want to have a globally accessible reference to arbitrary Q_PROPERTY instances (not just bool),
40 //! or don't want to expose the property to the user you could use a StelProperty directly registered through the StelPropertyMgr instead.
41 //! @see StelActionMgr, StelProperty
42 class StelAction : public QObject
43 {
44 	Q_OBJECT
45 public:
46 	//! When the StelAction is @ref checkable, this may be used to get/set the current value.
47 	//! Note that the @ref toggled signal may not be emitted on all changes of the connected property
48 	//! @warning If used on a non-checkable action, the program may crash.
49 	Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY toggled)
50 	//! If this is true, this StelAction can be toggled.
51 	//! This is the case when connected to a boolean Q_PROPERTY.
52 	//! This means the @ref checked property as well as the toggle() function may be used
53 	//! If false, the StelAction represents a simple argumentless slot call. Using @ref checked or toggle() may
54 	//! result in an error.
55 	Q_PROPERTY(bool checkable READ isCheckable)
56 
57 	//! @see checkable
58 	bool isCheckable() const {return boolProperty;}
59 	//! @see checked
isChecked() const60 	bool isChecked() const {return boolProperty ? boolProperty->getValue().toBool() : false; }
isGlobal() const61 	bool isGlobal() const {return global;}
62 	//! Defines the key-combination used to call this action
63 	void setShortcut(const QString& key);
64 	//! Defines an alternative key-combination
65 	void setAltShortcut(const QString& key);
66 	QKeySequence::SequenceMatch matches(const QKeySequence& seq) const;
67 
68 	//! The ID of this action. Must be unique.
getId() const69 	QString getId() const {return objectName();}
getGroup() const70 	QString getGroup() const {return group;}
getShortcut() const71 	const QKeySequence getShortcut() const {return keySequence;}
getAltShortcut() const72 	const QKeySequence getAltShortcut() const {return altKeySequence;}
73 	QString getText() const;
setText(const QString & value)74 	void setText(const QString& value) {text = value; emit changed();}
75 signals:
76 	//! Emitted when the boolean state of this StelAction changes.
77 	//! When the action is connected to a StelProperty,
78 	//! this is equivalent to the StelProperty::valueChanged signal.
79 	//! In the other cases, this state may not always be emitted correctly
80 	//! (i.e. when the state changes through other mechanisms than StelAction)
81 	void toggled(bool);
82 	//! Emitted after an argumentless slot has been called
83 	void triggered();
84 	//! Emitted when additional data associated with this action changed (i.e. shortcuts, text,...)
85 	void changed();
86 public slots:
87 	//! @see checked
88 	//! @warning If used on a non-checkable action, the program may crash.
89 	void setChecked(bool);
90 	//! If the action is @ref checkable, toggle() is called.
91 	//! Otherwise, the connected slot is invoked.
92 	void trigger();
93 	//! If the action is @ref checkable, this toggles the value of
94 	//! the connected boolean property.
95 	//! @warning If used on a non-checkable action, the program may crash.
96 	void toggle();
97 private slots:
98 	void propertyChanged(bool);
99 private:
100 	friend class StelActionMgr;
101 
102 	//! Constructor is used by StelActionMgr
103 	StelAction(const QString& actionId,
104 		   const QString& groupId,
105 		   const QString& text,
106 		   const QString& primaryKey="",
107 		   const QString& altKey="",
108 		   bool global=false);
109 	//! Connect the action to an object property or slot.
110 	//! @param slot A property or a slot name.  The slot can either have the signature `func()`, and in that
111 	//! case the action is made not checkable, or have the signature `func(bool)` and in that case the action
112 	//! is made checkable.  When linked to a property the action is always made checkable.
113 	void connectToObject(QObject* target, const char* slot);
114 
115 	QString group;
116 	QString text;
117 	bool global;
118 	QKeySequence keySequence;
119 	QKeySequence altKeySequence;
120 	const QKeySequence defaultKeySequence;
121 	const QKeySequence defaultAltKeySequence;
122 	QObject* target;
123 	//If the StelAction is connected to a boolean property with a NOTIFY signal, a StelProperty is used for the connection
124 	StelProperty* boolProperty;
125 	QMetaMethod slot;
126 
127 	// Currently, there is no proper way to handle shortcuts with non latin
128 	// keyboards layouts.  So for the moment, if we don't use QuickView, we
129 	// create a QAction added to the main view that will trigger the
130 	// StelAction when the shortcut is typed.
131 #ifndef USE_QUICKVIEW
132 private slots:
133 	void onChanged();
134 private:
135 	class QAction* qAction;
136 #endif
137 };
138 
139 //! Manager for StelAction instances. Allows registration of new actions, and finding an existing one by name.
140 class StelActionMgr : public QObject
141 {
142 	Q_OBJECT
143 public:
144 	StelActionMgr();
145 	~StelActionMgr();
146 	//! Create and add a new StelAction, connected to an object property or slot.
147 	//! @param id Global identifier.
148 	//! @param groupId Group identifier.
149 	//! @param text Short human-readable description in English.
150 	//! @param target The QObject the action is linked to.
151 	//! @param slot The target slot or property that the action will trigger.
152 	//!             Either a slot name of the form 'func()' and in that case the
153 	//!             action is made non checkable, a slot name of the form
154 	//!             'func(bool)' and in that case the action is made checkable,
155 	//!             or a property name and in that case the action is made
156 	//!             checkable.
157 	//! @param shortcut Default shortcut/key combination for this action
158 	//! @param altShortcut Alternative shortcut
159 	//! @param global determines QAction shortcut context (not necessary anymore?)
160 	StelAction* addAction(const QString& id, const QString& groupId, const QString& text,
161 			      QObject* target, const char* slot,
162 			      const QString& shortcut="", const QString& altShortcut="",
163 			      bool global=false);
164 
165 	//! Create and add a new StelAction, connected to an object slot.
166 	//! @param id Global identifier.
167 	//! @param groupId Group identifier.
168 	//! @param text Short human-readable description in English.
169 	//! @param context a reference object. When this is deleted, the Lambda function will not be called.
170 	//! @param lambda a void function (Lambda). This can call slots and other functions.
171 	//! @param shortcut Default shortcut/key combination for this action
172 	//! @param altShortcut Alternative shortcut
173 	//! @param global determines QAction shortcut context (not necessary anymore?)
174 	StelAction* addAction(const QString& id, const QString& groupId, const QString& text,
175 			      QObject* context,  std::function<void()> lambda,
176 			      const QString& shortcut="", const QString& altShortcut="",
177 			      bool global=false);
178 
179 	StelAction* findAction(const QString& id);
180 	StelAction* findActionFromShortcut(const QString& shortcut);
181 	bool pushKey(int key, bool global=false);
182 
183 	//! Returns a list of all current StelAction groups
184 	QStringList getGroupList() const;
185 	//! Returns all StelActions in the specified group
186 	QList<StelAction*> getActionList(const QString& group) const;
187 	//! Returns all registered StelActions
188 	QList<StelAction*> getActionList() const;
189 	QStringList getShortcutsList() const;
190 
191 	//! Save current shortcuts to file.
192 	void saveShortcuts();
193 	//! Restore the default shortcuts combinations
194 	void restoreDefaultShortcuts();
195 	void restoreDefaultShortcut(StelAction* action);
196 
197 signals:
198 	//! Emitted when any action registered with this StelActionMgr is toggled
199 	//! @param id The id of the action that was toggled
200 	//! @param value The new value of the action
201 	void actionToggled(const QString& id, bool value);
202 
203 	void shortcutsChanged();
204 
205 public slots:
206 	//! Enable/disable all actions of application.
207 	//! need for editing shortcuts without trigging any actions
208 	//! @todo find out if this is really necessary and why.
setAllActionsEnabled(bool value)209 	void setAllActionsEnabled(bool value) {actionsEnabled = value;}
210 
211 private slots:
212 	void onStelActionToggled(bool val);
213 
214 private:
215 	bool actionsEnabled;
216 	QList<int> keySequence;
217 };
218 
219 #endif // STELACTIONMGR_HPP
220