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:
QActionGroupPrivate()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 
_q_actionChanged()71 void QActionGroupPrivate::_q_actionChanged()
72 {
73     Q_Q(QActionGroup);
74     QAction *action = qobject_cast<QAction*>(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 
_q_actionTriggered()89 void QActionGroupPrivate::_q_actionTriggered()
90 {
91     Q_Q(QActionGroup);
92     QAction *action = qobject_cast<QAction*>(q->sender());
93     Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionTriggered", "internal error");
94     emit q->triggered(action);
95 }
96 
_q_actionHovered()97 void QActionGroupPrivate::_q_actionHovered()
98 {
99     Q_Q(QActionGroup);
100     QAction *action = qobject_cast<QAction*>(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 */
QActionGroup(QObject * parent)185 QActionGroup::QActionGroup(QObject* parent) : QObject(*new QActionGroupPrivate, parent)
186 {
187 }
188 
189 /*!
190     Destroys the action group.
191 */
~QActionGroup()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 */
addAction(QAction * a)206 QAction *QActionGroup::addAction(QAction* a)
207 {
208     Q_D(QActionGroup);
209     if(!d->actions.contains(a)) {
210         d->actions.append(a);
211         QObject::connect(a, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
212         QObject::connect(a, SIGNAL(changed()), this, SLOT(_q_actionChanged()));
213         QObject::connect(a, SIGNAL(hovered()), 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 */
addAction(const QString & text)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 */
addAction(const QIcon & icon,const QString & text)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 */
removeAction(QAction * action)269 void QActionGroup::removeAction(QAction *action)
270 {
271     Q_D(QActionGroup);
272     if (d->actions.removeAll(action)) {
273         if (action == d->current)
274             d->current = nullptr;
275         QObject::disconnect(action, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
276         QObject::disconnect(action, SIGNAL(changed()), this, SLOT(_q_actionChanged()));
277         QObject::disconnect(action, SIGNAL(hovered()), 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 */
actions() const285 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 */
setExclusive(bool b)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 */
isExclusive() const313 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 */
setExclusionPolicy(QActionGroup::ExclusionPolicy policy)333 void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy)
334 {
335     Q_D(QActionGroup);
336     d->exclusionPolicy = policy;
337 }
338 
exclusionPolicy() const339 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 */
setEnabled(bool b)362 void QActionGroup::setEnabled(bool b)
363 {
364     Q_D(QActionGroup);
365     d->enabled = b;
366     for (auto action : qAsConst(d->actions)) {
367         if (!action->d_func()->forceDisabled) {
368             action->setEnabled(b);
369             action->d_func()->forceDisabled = false;
370         }
371     }
372 }
373 
isEnabled() const374 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 */
checkedAction() const384 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 */
setVisible(bool b)399 void QActionGroup::setVisible(bool b)
400 {
401     Q_D(QActionGroup);
402     d->visible = b;
403     for (auto action : qAsConst(d->actions)) {
404         if (!action->d_func()->forceInvisible) {
405             action->setVisible(b);
406             action->d_func()->forceInvisible = false;
407         }
408     }
409 }
410 
isVisible() const411 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