1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWidgets module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qactiongroup.h" |
41 | |
42 | #ifndef QT_NO_ACTION |
43 | |
44 | #include "qaction_p.h" |
45 | #include "qevent.h" |
46 | #include "qlist.h" |
47 | |
48 | QT_BEGIN_NAMESPACE |
49 | |
50 | class QActionGroupPrivate : public QObjectPrivate |
51 | { |
52 | Q_DECLARE_PUBLIC(QActionGroup) |
53 | public: |
54 | QActionGroupPrivate() : enabled(1), |
55 | visible(1), |
56 | exclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive) |
57 | { |
58 | } |
59 | QList<QAction *> actions; |
60 | QPointer<QAction> current; |
61 | uint enabled : 1; |
62 | uint visible : 1; |
63 | QActionGroup::ExclusionPolicy exclusionPolicy; |
64 | |
65 | private: |
66 | void _q_actionTriggered(); //private slot |
67 | void _q_actionChanged(); //private slot |
68 | void _q_actionHovered(); //private slot |
69 | }; |
70 | |
71 | void QActionGroupPrivate::_q_actionChanged() |
72 | { |
73 | Q_Q(QActionGroup); |
74 | QAction *action = qobject_cast<QAction*>(object: q->sender()); |
75 | Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionChanged" , "internal error" ); |
76 | if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) { |
77 | if (action->isChecked()) { |
78 | if (action != current) { |
79 | if(current) |
80 | current->setChecked(false); |
81 | current = action; |
82 | } |
83 | } else if (action == current) { |
84 | current = nullptr; |
85 | } |
86 | } |
87 | } |
88 | |
89 | void QActionGroupPrivate::_q_actionTriggered() |
90 | { |
91 | Q_Q(QActionGroup); |
92 | QAction *action = qobject_cast<QAction*>(object: q->sender()); |
93 | Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionTriggered" , "internal error" ); |
94 | emit q->triggered(action); |
95 | } |
96 | |
97 | void QActionGroupPrivate::_q_actionHovered() |
98 | { |
99 | Q_Q(QActionGroup); |
100 | QAction *action = qobject_cast<QAction*>(object: q->sender()); |
101 | Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionHovered" , "internal error" ); |
102 | emit q->hovered(action); |
103 | } |
104 | |
105 | /*! |
106 | \class QActionGroup |
107 | \brief The QActionGroup class groups actions together. |
108 | |
109 | \ingroup mainwindow-classes |
110 | \inmodule QtWidgets |
111 | |
112 | In some situations it is useful to group QAction objects together. |
113 | For example, if you have a \uicontrol{Left Align} action, a \uicontrol{Right |
114 | Align} action, a \uicontrol{Justify} action, and a \uicontrol{Center} action, |
115 | only one of these actions should be active at any one time. One |
116 | simple way of achieving this is to group the actions together in |
117 | an action group. |
118 | |
119 | Here's a example (from the \l{mainwindows/menus}{Menus} example): |
120 | |
121 | \snippet mainwindows/menus/mainwindow.cpp 6 |
122 | |
123 | Here we create a new action group. Since the action group is |
124 | exclusive by default, only one of the actions in the group is |
125 | checked at any one time. |
126 | |
127 | \image qactiongroup-align.png Alignment options in a QMenu |
128 | |
129 | A QActionGroup emits an triggered() signal when one of its |
130 | actions is chosen. Each action in an action group emits its |
131 | triggered() signal as usual. |
132 | |
133 | As stated above, an action group is exclusive by default; it |
134 | ensures that at most only one checkable action is active at any one time. |
135 | If you want to group checkable actions without making them |
136 | exclusive, you can turn off exclusiveness by calling |
137 | setExclusive(false). |
138 | |
139 | By default the active action of an exclusive group cannot be unchecked. |
140 | In some cases it may be useful to allow unchecking all the actions, |
141 | you can allow this by calling |
142 | setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional). |
143 | |
144 | Actions can be added to an action group using addAction(), but it |
145 | is usually more convenient to specify a group when creating |
146 | actions; this ensures that actions are automatically created with |
147 | a parent. Actions can be visually separated from each other by |
148 | adding a separator action to the group; create an action and use |
149 | QAction's \l {QAction::}{setSeparator()} function to make it |
150 | considered a separator. Action groups are added to widgets with |
151 | the QWidget::addActions() function. |
152 | |
153 | \sa QAction |
154 | */ |
155 | |
156 | /*! |
157 | \enum QActionGroup::ExclusionPolicy |
158 | |
159 | This enum specifies the different policies that can be used to |
160 | control how the group performs exclusive checking on checkable actions. |
161 | |
162 | \value None |
163 | The actions in the group can be checked independently of each other. |
164 | |
165 | \value Exclusive |
166 | Exactly one action can be checked at any one time. |
167 | This is the default policy. |
168 | |
169 | \value ExclusiveOptional |
170 | At most one action can be checked at any one time. The actions |
171 | can also be all unchecked. |
172 | |
173 | \sa exclusionPolicy |
174 | \since 5.14 |
175 | */ |
176 | |
177 | /*! |
178 | Constructs an action group for the \a parent object. |
179 | |
180 | The action group is exclusive by default. Call setExclusive(false) |
181 | to make the action group non-exclusive. To make the group exclusive |
182 | but allow unchecking the active action call instead |
183 | setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional) |
184 | */ |
185 | QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent) |
186 | { |
187 | } |
188 | |
189 | /*! |
190 | Destroys the action group. |
191 | */ |
192 | QActionGroup::~QActionGroup() |
193 | { |
194 | } |
195 | |
196 | /*! |
197 | \fn QAction *QActionGroup::addAction(QAction *action) |
198 | |
199 | Adds the \a action to this group, and returns it. |
200 | |
201 | Normally an action is added to a group by creating it with the |
202 | group as its parent, so this function is not usually used. |
203 | |
204 | \sa QAction::setActionGroup() |
205 | */ |
206 | QAction *QActionGroup::addAction(QAction* a) |
207 | { |
208 | Q_D(QActionGroup); |
209 | if(!d->actions.contains(t: a)) { |
210 | d->actions.append(t: a); |
211 | QObject::connect(sender: a, SIGNAL(triggered()), receiver: this, SLOT(_q_actionTriggered())); |
212 | QObject::connect(sender: a, SIGNAL(changed()), receiver: this, SLOT(_q_actionChanged())); |
213 | QObject::connect(sender: a, SIGNAL(hovered()), receiver: this, SLOT(_q_actionHovered())); |
214 | } |
215 | if(!a->d_func()->forceDisabled) { |
216 | a->setEnabled(d->enabled); |
217 | a->d_func()->forceDisabled = false; |
218 | } |
219 | if(!a->d_func()->forceInvisible) { |
220 | a->setVisible(d->visible); |
221 | a->d_func()->forceInvisible = false; |
222 | } |
223 | if(a->isChecked()) |
224 | d->current = a; |
225 | QActionGroup *oldGroup = a->d_func()->group; |
226 | if(oldGroup != this) { |
227 | if (oldGroup) |
228 | oldGroup->removeAction(a); |
229 | a->d_func()->group = this; |
230 | a->d_func()->sendDataChanged(); |
231 | } |
232 | return a; |
233 | } |
234 | |
235 | /*! |
236 | Creates and returns an action with \a text. The newly created |
237 | action is a child of this action group. |
238 | |
239 | Normally an action is added to a group by creating it with the |
240 | group as parent, so this function is not usually used. |
241 | |
242 | \sa QAction::setActionGroup() |
243 | */ |
244 | QAction *QActionGroup::addAction(const QString &text) |
245 | { |
246 | return new QAction(text, this); |
247 | } |
248 | |
249 | /*! |
250 | Creates and returns an action with \a text and an \a icon. The |
251 | newly created action is a child of this action group. |
252 | |
253 | Normally an action is added to a group by creating it with the |
254 | group as its parent, so this function is not usually used. |
255 | |
256 | \sa QAction::setActionGroup() |
257 | */ |
258 | QAction *QActionGroup::addAction(const QIcon &icon, const QString &text) |
259 | { |
260 | return new QAction(icon, text, this); |
261 | } |
262 | |
263 | /*! |
264 | Removes the \a action from this group. The action will have no |
265 | parent as a result. |
266 | |
267 | \sa QAction::setActionGroup() |
268 | */ |
269 | void QActionGroup::removeAction(QAction *action) |
270 | { |
271 | Q_D(QActionGroup); |
272 | if (d->actions.removeAll(t: action)) { |
273 | if (action == d->current) |
274 | d->current = nullptr; |
275 | QObject::disconnect(sender: action, SIGNAL(triggered()), receiver: this, SLOT(_q_actionTriggered())); |
276 | QObject::disconnect(sender: action, SIGNAL(changed()), receiver: this, SLOT(_q_actionChanged())); |
277 | QObject::disconnect(sender: action, SIGNAL(hovered()), receiver: this, SLOT(_q_actionHovered())); |
278 | action->d_func()->group = nullptr; |
279 | } |
280 | } |
281 | |
282 | /*! |
283 | Returns the list of this groups's actions. This may be empty. |
284 | */ |
285 | QList<QAction*> QActionGroup::actions() const |
286 | { |
287 | Q_D(const QActionGroup); |
288 | return d->actions; |
289 | } |
290 | |
291 | /*! |
292 | \brief Enable or disable the group exclusion checking |
293 | |
294 | This is a convenience method that calls |
295 | setExclusionPolicy(ExclusionPolicy::Exclusive) when \a b is true, |
296 | else setExclusionPolicy(QActionGroup::ExclusionPolicy::None). |
297 | |
298 | \sa QActionGroup::exclusionPolicy |
299 | */ |
300 | void QActionGroup::setExclusive(bool b) |
301 | { |
302 | setExclusionPolicy(b ? QActionGroup::ExclusionPolicy::Exclusive |
303 | : QActionGroup::ExclusionPolicy::None); |
304 | } |
305 | |
306 | /*! |
307 | \brief Returns true if the group is exclusive |
308 | |
309 | The group is exclusive if the ExclusionPolicy is either Exclusive |
310 | or ExclusionOptional. |
311 | |
312 | */ |
313 | bool QActionGroup::isExclusive() const |
314 | { |
315 | return exclusionPolicy() != QActionGroup::ExclusionPolicy::None; |
316 | } |
317 | |
318 | /*! |
319 | \property QActionGroup::exclusionPolicy |
320 | \brief This property holds the group exclusive checking policy |
321 | |
322 | If exclusionPolicy is set to Exclusive, only one checkable |
323 | action in the action group can ever be active at any time. If the user |
324 | chooses another checkable action in the group, the one they chose becomes |
325 | active and the one that was active becomes inactive. If exclusionPolicy is |
326 | set to ExclusionOptional the group is exclusive but the active checkable |
327 | action in the group can be unchecked leaving the group with no actions |
328 | checked. |
329 | |
330 | \sa QAction::checkable |
331 | \since 5.14 |
332 | */ |
333 | void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy) |
334 | { |
335 | Q_D(QActionGroup); |
336 | d->exclusionPolicy = policy; |
337 | } |
338 | |
339 | QActionGroup::ExclusionPolicy QActionGroup::exclusionPolicy() const |
340 | { |
341 | Q_D(const QActionGroup); |
342 | return d->exclusionPolicy; |
343 | } |
344 | |
345 | /*! |
346 | \fn void QActionGroup::setDisabled(bool b) |
347 | |
348 | This is a convenience function for the \l enabled property, that |
349 | is useful for signals--slots connections. If \a b is true the |
350 | action group is disabled; otherwise it is enabled. |
351 | */ |
352 | |
353 | /*! |
354 | \property QActionGroup::enabled |
355 | \brief whether the action group is enabled |
356 | |
357 | Each action in the group will be enabled or disabled unless it |
358 | has been explicitly disabled. |
359 | |
360 | \sa QAction::setEnabled() |
361 | */ |
362 | void QActionGroup::setEnabled(bool b) |
363 | { |
364 | Q_D(QActionGroup); |
365 | d->enabled = b; |
366 | for (auto action : qAsConst(t&: d->actions)) { |
367 | if (!action->d_func()->forceDisabled) { |
368 | action->setEnabled(b); |
369 | action->d_func()->forceDisabled = false; |
370 | } |
371 | } |
372 | } |
373 | |
374 | bool QActionGroup::isEnabled() const |
375 | { |
376 | Q_D(const QActionGroup); |
377 | return d->enabled; |
378 | } |
379 | |
380 | /*! |
381 | Returns the currently checked action in the group, or \nullptr if |
382 | none are checked. |
383 | */ |
384 | QAction *QActionGroup::checkedAction() const |
385 | { |
386 | Q_D(const QActionGroup); |
387 | return d->current; |
388 | } |
389 | |
390 | /*! |
391 | \property QActionGroup::visible |
392 | \brief whether the action group is visible |
393 | |
394 | Each action in the action group will match the visible state of |
395 | this group unless it has been explicitly hidden. |
396 | |
397 | \sa QAction::setEnabled() |
398 | */ |
399 | void QActionGroup::setVisible(bool b) |
400 | { |
401 | Q_D(QActionGroup); |
402 | d->visible = b; |
403 | for (auto action : qAsConst(t&: d->actions)) { |
404 | if (!action->d_func()->forceInvisible) { |
405 | action->setVisible(b); |
406 | action->d_func()->forceInvisible = false; |
407 | } |
408 | } |
409 | } |
410 | |
411 | bool QActionGroup::isVisible() const |
412 | { |
413 | Q_D(const QActionGroup); |
414 | return d->visible; |
415 | } |
416 | |
417 | /*! |
418 | \fn void QActionGroup::triggered(QAction *action) |
419 | |
420 | This signal is emitted when the given \a action in the action |
421 | group is activated by the user; for example, when the user clicks |
422 | a menu option, toolbar button, or presses an action's shortcut key |
423 | combination. |
424 | |
425 | Connect to this signal for command actions. |
426 | |
427 | \sa QAction::activate() |
428 | */ |
429 | |
430 | /*! |
431 | \fn void QActionGroup::hovered(QAction *action) |
432 | |
433 | This signal is emitted when the given \a action in the action |
434 | group is highlighted by the user; for example, when the user |
435 | pauses with the cursor over a menu option, toolbar button, or |
436 | presses an action's shortcut key combination. |
437 | |
438 | \sa QAction::activate() |
439 | */ |
440 | |
441 | QT_END_NAMESPACE |
442 | |
443 | #include "moc_qactiongroup.cpp" |
444 | |
445 | #endif // QT_NO_ACTION |
446 | |