1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui 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#include "qaction.h"
43#include "qaction_p.h"
44#include "qactiongroup_p.h"
45#include "qevent.h"
46#include "qlist.h"
47
48QT_BEGIN_NAMESPACE
49
50QActionGroupPrivate::QActionGroupPrivate() :
51 enabled(1), visible(1)
52{
53}
54
55QActionGroupPrivate::~QActionGroupPrivate() = default;
56
57void QActionGroup::_q_actionChanged()
58{
59 Q_D(QActionGroup);
60 auto action = qobject_cast<QAction*>(sender());
61 Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionChanged", "internal error");
62 if (d->exclusionPolicy != QActionGroup::ExclusionPolicy::None) {
63 if (action->isChecked()) {
64 if (action != d->current) {
65 if (!d->current.isNull())
66 d->current->setChecked(false);
67 d->current = action;
68 }
69 } else if (action == d->current) {
70 d->current = nullptr;
71 }
72 }
73}
74
75void QActionGroup::_q_actionTriggered()
76{
77 auto action = qobject_cast<QAction*>(sender());
78 Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionTriggered", "internal error");
79 emit triggered(action);
80}
81
82void QActionGroup::_q_actionHovered()
83{
84 auto action = qobject_cast<QAction*>(sender());
85 Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionHovered", "internal error");
86 emit hovered(action);
87}
88
89/*!
90 \class QActionGroup
91 \brief The QActionGroup class groups actions together.
92 \since 6.0
93
94 \inmodule QtGui
95
96 QActionGroup is a base class for classes grouping
97 classes inhheriting QAction objects together.
98
99 In some situations it is useful to group QAction objects together.
100 For example, if you have a \uicontrol{Left Align} action, a \uicontrol{Right
101 Align} action, a \uicontrol{Justify} action, and a \uicontrol{Center} action,
102 only one of these actions should be active at any one time. One
103 simple way of achieving this is to group the actions together in
104 an action group, inheriting QActionGroup.
105
106 \sa QAction
107*/
108
109/*!
110 \enum QActionGroup::ExclusionPolicy
111
112 This enum specifies the different policies that can be used to
113 control how the group performs exclusive checking on checkable actions.
114
115 \value None
116 The actions in the group can be checked independently of each other.
117
118 \value Exclusive
119 Exactly one action can be checked at any one time.
120 This is the default policy.
121
122 \value ExclusiveOptional
123 At most one action can be checked at any one time. The actions
124 can also be all unchecked.
125
126 \sa exclusionPolicy
127*/
128
129/*!
130 Constructs an action group for the \a parent object.
131
132 The action group is exclusive by default. Call setExclusive(false)
133 to make the action group non-exclusive. To make the group exclusive
134 but allow unchecking the active action call instead
135 setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional)
136*/
137QActionGroup::QActionGroup(QObject* parent) :
138 QActionGroup(*new QActionGroupPrivate, parent)
139{
140}
141
142QActionGroup::QActionGroup(QActionGroupPrivate &dd, QObject *parent) :
143 QObject(dd, parent)
144{
145}
146
147/*!
148 Destroys the action group.
149*/
150QActionGroup::~QActionGroup() = default;
151
152/*!
153 \fn QAction *QActionGroup::addAction(QAction *action)
154
155 Adds the \a action to this group, and returns it.
156
157 Normally an action is added to a group by creating it with the
158 group as its parent, so this function is not usually used.
159
160 \sa QAction::setActionGroup()
161*/
162QAction *QActionGroup::addAction(QAction* a)
163{
164 Q_D(QActionGroup);
165 if (!d->actions.contains(a)) {
166 d->actions.append(a);
167 QObject::connect(a, &QAction::triggered, this, &QActionGroup::_q_actionTriggered);
168 QObject::connect(a, &QAction::changed, this, &QActionGroup::_q_actionChanged);
169 QObject::connect(a, &QAction::hovered, this, &QActionGroup::_q_actionHovered);
170 }
171 a->d_func()->setEnabled(d->enabled, true);
172 if (!a->d_func()->forceInvisible)
173 a->d_func()->setVisible(d->visible);
174 if (a->isChecked())
175 d->current = a;
176 QActionGroup *oldGroup = a->d_func()->group;
177 if (oldGroup != this) {
178 if (oldGroup)
179 oldGroup->removeAction(a);
180 a->d_func()->group = this;
181 a->d_func()->sendDataChanged();
182 }
183 return a;
184}
185
186/*!
187 Creates and returns an action with \a text. The newly created
188 action is a child of this action group.
189
190 Normally an action is added to a group by creating it with the
191 group as parent, so this function is not usually used.
192
193 \sa QAction::setActionGroup()
194*/
195QAction *QActionGroup::addAction(const QString &text)
196{
197 return new QAction(text, this);
198}
199
200/*!
201 Creates and returns an action with \a text and an \a icon. The
202 newly created action is a child of this action group.
203
204 Normally an action is added to a group by creating it with the
205 group as its parent, so this function is not usually used.
206
207 \sa QAction::setActionGroup()
208*/
209QAction *QActionGroup::addAction(const QIcon &icon, const QString &text)
210{
211 return new QAction(icon, text, this);
212}
213
214/*!
215 Removes the \a action from this group. The action will have no
216 parent as a result.
217
218 \sa QAction::setActionGroup()
219*/
220void QActionGroup::removeAction(QAction *action)
221{
222 Q_D(QActionGroup);
223 if (d->actions.removeAll(action)) {
224 if (action == d->current)
225 d->current = nullptr;
226 QObject::disconnect(action, &QAction::triggered, this, &QActionGroup::_q_actionTriggered);
227 QObject::disconnect(action, &QAction::changed, this, &QActionGroup::_q_actionChanged);
228 QObject::disconnect(action, &QAction::hovered, this, &QActionGroup::_q_actionHovered);
229 action->d_func()->group = nullptr;
230 }
231}
232
233/*!
234 Returns the list of this groups's actions. This may be empty.
235*/
236QList<QAction*> QActionGroup::actions() const
237{
238 Q_D(const QActionGroup);
239 return d->actions;
240}
241
242/*!
243 \brief Enable or disable the group exclusion checking
244
245 This is a convenience method that calls
246 setExclusionPolicy(ExclusionPolicy::Exclusive) when \a b is true,
247 else setExclusionPolicy(QActionGroup::ExclusionPolicy::None).
248
249 \sa QActionGroup::exclusionPolicy
250*/
251void QActionGroup::setExclusive(bool b)
252{
253 setExclusionPolicy(b ? QActionGroup::ExclusionPolicy::Exclusive
254 : QActionGroup::ExclusionPolicy::None);
255}
256
257/*!
258 \brief Returns true if the group is exclusive
259
260 The group is exclusive if the ExclusionPolicy is either Exclusive
261 or ExclusionOptional.
262
263*/
264bool QActionGroup::isExclusive() const
265{
266 return exclusionPolicy() != QActionGroup::ExclusionPolicy::None;
267}
268
269/*!
270 \property QActionGroup::exclusionPolicy
271 \brief This property holds the group exclusive checking policy
272
273 If exclusionPolicy is set to Exclusive, only one checkable
274 action in the action group can ever be active at any time. If the user
275 chooses another checkable action in the group, the one they chose becomes
276 active and the one that was active becomes inactive. If exclusionPolicy is
277 set to ExclusionOptional the group is exclusive but the active checkable
278 action in the group can be unchecked leaving the group with no actions
279 checked.
280
281 \sa QAction::checkable
282*/
283void QActionGroup::setExclusionPolicy(QActionGroup::ExclusionPolicy policy)
284{
285 Q_D(QActionGroup);
286 d->exclusionPolicy = policy;
287}
288
289QActionGroup::ExclusionPolicy QActionGroup::exclusionPolicy() const
290{
291 Q_D(const QActionGroup);
292 return d->exclusionPolicy;
293}
294
295/*!
296 \fn void QActionGroup::setDisabled(bool b)
297
298 This is a convenience function for the \l enabled property, that
299 is useful for signals--slots connections. If \a b is true the
300 action group is disabled; otherwise it is enabled.
301*/
302
303/*!
304 \property QActionGroup::enabled
305 \brief whether the action group is enabled
306
307 Each action in the group will be enabled or disabled unless it
308 has been explicitly disabled.
309
310 \sa QAction::setEnabled()
311*/
312void QActionGroup::setEnabled(bool b)
313{
314 Q_D(QActionGroup);
315 d->enabled = b;
316 for (auto action : qAsConst(d->actions)) {
317 action->d_func()->setEnabled(b, true);
318 }
319}
320
321bool QActionGroup::isEnabled() const
322{
323 Q_D(const QActionGroup);
324 return d->enabled;
325}
326
327/*!
328 Returns the currently checked action in the group, or \nullptr if
329 none are checked.
330*/
331QAction *QActionGroup::checkedAction() const
332{
333 Q_D(const QActionGroup);
334 return d->current.data();
335}
336
337/*!
338 \property QActionGroup::visible
339 \brief whether the action group is visible
340
341 Each action in the action group will match the visible state of
342 this group unless it has been explicitly hidden.
343
344 \sa QAction::setEnabled()
345*/
346void QActionGroup::setVisible(bool b)
347{
348 Q_D(QActionGroup);
349 d->visible = b;
350 for (auto action : qAsConst(d->actions)) {
351 if (!action->d_func()->forceInvisible)
352 action->d_func()->setVisible(b);
353 }
354}
355
356bool QActionGroup::isVisible() const
357{
358 Q_D(const QActionGroup);
359 return d->visible;
360}
361
362QT_END_NAMESPACE
363