1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "private/qbuttongroup_p.h" |
5 | |
6 | #include "private/qabstractbutton_p.h" |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | // detect a checked button other than the current one |
11 | void QButtonGroupPrivate::detectCheckedButton() |
12 | { |
13 | QAbstractButton *previous = checkedButton; |
14 | checkedButton = nullptr; |
15 | if (exclusive) |
16 | return; |
17 | for (int i = 0; i < buttonList.size(); i++) { |
18 | if (buttonList.at(i) != previous && buttonList.at(i)->isChecked()) { |
19 | checkedButton = buttonList.at(i); |
20 | return; |
21 | } |
22 | } |
23 | } |
24 | |
25 | /*! |
26 | \class QButtonGroup |
27 | \brief The QButtonGroup class provides a container to organize groups of |
28 | button widgets. |
29 | |
30 | \ingroup organizers |
31 | \ingroup geomanagement |
32 | \inmodule QtWidgets |
33 | |
34 | QButtonGroup provides an abstract container into which button widgets can |
35 | be placed. It does not provide a visual representation of this container |
36 | (see QGroupBox for a container widget), but instead manages the states of |
37 | each of the buttons in the group. |
38 | |
39 | An \l {QButtonGroup::exclusive} {exclusive} button group switches |
40 | off all checkable (toggle) buttons except the one that has been |
41 | clicked. By default, a button group is exclusive. The buttons in a |
42 | button group are usually checkable \l{QPushButton}s, \l{QCheckBox}es |
43 | (normally for non-exclusive button groups), or \l{QRadioButton}s. |
44 | If you create an exclusive button group, you should ensure that |
45 | one of the buttons in the group is initially checked; otherwise, |
46 | the group will initially be in a state where no buttons are |
47 | checked. |
48 | |
49 | A button can be added to the group with addButton() and removed |
50 | with removeButton(). If the group is exclusive, the |
51 | currently checked button is available with checkedButton(). If a |
52 | button is clicked, the buttonClicked() signal is emitted; for a |
53 | checkable button in an exclusive group this means that the button |
54 | has been checked. The list of buttons in the group is returned by |
55 | buttons(). |
56 | |
57 | In addition, QButtonGroup can map between integers and buttons. |
58 | You can assign an integer id to a button with setId(), and |
59 | retrieve it with id(). The id of the currently checked button is |
60 | available with checkedId(), and there is a signal |
61 | idClicked() that emits the id of the button. The id \c {-1} |
62 | is reserved by QButtonGroup to mean "no such button". The purpose |
63 | of the mapping mechanism is to simplify the representation of enum |
64 | values in a user interface. |
65 | |
66 | \sa QGroupBox, QPushButton, QCheckBox, QRadioButton |
67 | */ |
68 | |
69 | /*! |
70 | Constructs a new, empty button group with the given \a parent. |
71 | |
72 | \sa addButton(), setExclusive() |
73 | */ |
74 | QButtonGroup::QButtonGroup(QObject *parent) |
75 | : QObject(*new QButtonGroupPrivate, parent) |
76 | { |
77 | } |
78 | |
79 | /*! |
80 | Destroys the button group. |
81 | */ |
82 | QButtonGroup::~QButtonGroup() |
83 | { |
84 | Q_D(QButtonGroup); |
85 | for (int i = 0; i < d->buttonList.size(); ++i) |
86 | d->buttonList.at(i)->d_func()->group = nullptr; |
87 | } |
88 | |
89 | /*! |
90 | \property QButtonGroup::exclusive |
91 | \brief whether the button group is exclusive |
92 | |
93 | If this property is \c true, then only one button in the group can be checked |
94 | at any given time. The user can click on any button to check it, and that |
95 | button will replace the existing one as the checked button in the group. |
96 | |
97 | In an exclusive group, the user cannot uncheck the currently checked button |
98 | by clicking on it; instead, another button in the group must be clicked |
99 | to set the new checked button for that group. |
100 | |
101 | By default, this property is \c true. |
102 | */ |
103 | bool QButtonGroup::exclusive() const |
104 | { |
105 | Q_D(const QButtonGroup); |
106 | return d->exclusive; |
107 | } |
108 | |
109 | void QButtonGroup::setExclusive(bool exclusive) |
110 | { |
111 | Q_D(QButtonGroup); |
112 | d->exclusive = exclusive; |
113 | } |
114 | |
115 | |
116 | |
117 | /*! |
118 | \fn void QButtonGroup::buttonClicked(QAbstractButton *button) |
119 | |
120 | This signal is emitted when the given \a button is clicked. A |
121 | button is clicked when it is first pressed and then released, when |
122 | its shortcut key is typed, or when QAbstractButton::click() |
123 | or QAbstractButton::animateClick() is programmatically called. |
124 | |
125 | |
126 | \sa checkedButton(), QAbstractButton::clicked() |
127 | */ |
128 | |
129 | /*! |
130 | \fn void QButtonGroup::idClicked(int id) |
131 | \since 5.15 |
132 | |
133 | This signal is emitted when a button with the given \a id is |
134 | clicked. |
135 | |
136 | \sa checkedButton(), QAbstractButton::clicked() |
137 | */ |
138 | |
139 | /*! |
140 | \fn void QButtonGroup::buttonPressed(QAbstractButton *button) |
141 | \since 4.2 |
142 | |
143 | This signal is emitted when the given \a button is pressed down. |
144 | |
145 | \sa QAbstractButton::pressed() |
146 | */ |
147 | |
148 | /*! |
149 | \fn void QButtonGroup::idPressed(int id) |
150 | \since 5.15 |
151 | |
152 | This signal is emitted when a button with the given \a id is |
153 | pressed down. |
154 | |
155 | \sa QAbstractButton::pressed() |
156 | */ |
157 | |
158 | /*! |
159 | \fn void QButtonGroup::buttonReleased(QAbstractButton *button) |
160 | \since 4.2 |
161 | |
162 | This signal is emitted when the given \a button is released. |
163 | |
164 | \sa QAbstractButton::released() |
165 | */ |
166 | |
167 | /*! |
168 | \fn void QButtonGroup::idReleased(int id) |
169 | \since 5.15 |
170 | |
171 | This signal is emitted when a button with the given \a id is |
172 | released. |
173 | |
174 | \sa QAbstractButton::released() |
175 | */ |
176 | |
177 | /*! |
178 | \fn void QButtonGroup::buttonToggled(QAbstractButton *button, bool checked) |
179 | \since 5.2 |
180 | |
181 | This signal is emitted when the given \a button is toggled. |
182 | \a checked is true if the button is checked, or false if the button is unchecked. |
183 | |
184 | \sa QAbstractButton::toggled() |
185 | */ |
186 | |
187 | /*! |
188 | \fn void QButtonGroup::idToggled(int id, bool checked) |
189 | \since 5.15 |
190 | |
191 | This signal is emitted when a button with the given \a id is toggled. |
192 | \a checked is true if the button is checked, or false if the button is unchecked. |
193 | |
194 | \sa QAbstractButton::toggled() |
195 | */ |
196 | |
197 | /*! |
198 | Adds the given \a button to the button group. If \a id is -1, |
199 | an id will be assigned to the button. |
200 | Automatically assigned ids are guaranteed to be negative, |
201 | starting with -2. If you are assigning your own ids, use |
202 | positive values to avoid conflicts. |
203 | |
204 | \sa removeButton(), buttons() |
205 | */ |
206 | void QButtonGroup::addButton(QAbstractButton *button, int id) |
207 | { |
208 | Q_D(QButtonGroup); |
209 | if (QButtonGroup *previous = button->d_func()->group) |
210 | previous->removeButton(button); |
211 | button->d_func()->group = this; |
212 | d->buttonList.append(t: button); |
213 | if (id == -1) { |
214 | const QHash<QAbstractButton*, int>::const_iterator it |
215 | = std::min_element(first: d->mapping.cbegin(), last: d->mapping.cend()); |
216 | if (it == d->mapping.cend()) |
217 | d->mapping[button] = -2; |
218 | else |
219 | d->mapping[button] = *it - 1; |
220 | } else { |
221 | d->mapping[button] = id; |
222 | } |
223 | |
224 | if (d->exclusive && button->isChecked()) |
225 | button->d_func()->notifyChecked(); |
226 | } |
227 | |
228 | /*! |
229 | Removes the given \a button from the button group. |
230 | |
231 | \sa addButton(), buttons() |
232 | */ |
233 | void QButtonGroup::removeButton(QAbstractButton *button) |
234 | { |
235 | Q_D(QButtonGroup); |
236 | if (d->checkedButton == button) { |
237 | d->detectCheckedButton(); |
238 | } |
239 | if (button->d_func()->group == this) { |
240 | button->d_func()->group = nullptr; |
241 | d->buttonList.removeAll(t: button); |
242 | d->mapping.remove(key: button); |
243 | } |
244 | } |
245 | |
246 | /*! |
247 | Returns the button group's list of buttons. This may be empty. |
248 | |
249 | \sa addButton(), removeButton() |
250 | */ |
251 | QList<QAbstractButton*> QButtonGroup::buttons() const |
252 | { |
253 | Q_D(const QButtonGroup); |
254 | return d->buttonList; |
255 | } |
256 | |
257 | /*! |
258 | Returns the button group's checked button, or \nullptr if no |
259 | buttons are checked. |
260 | |
261 | \sa buttonClicked() |
262 | */ |
263 | QAbstractButton *QButtonGroup::checkedButton() const |
264 | { |
265 | Q_D(const QButtonGroup); |
266 | return d->checkedButton; |
267 | } |
268 | |
269 | /*! |
270 | \since 4.1 |
271 | |
272 | Returns the button with the specified \a id, or \nullptr if no |
273 | such button exists. |
274 | */ |
275 | QAbstractButton *QButtonGroup::button(int id) const |
276 | { |
277 | Q_D(const QButtonGroup); |
278 | return d->mapping.key(value: id); |
279 | } |
280 | |
281 | /*! |
282 | \since 4.1 |
283 | |
284 | Sets the \a id for the specified \a button. Note that \a id cannot |
285 | be -1. |
286 | |
287 | \sa id() |
288 | */ |
289 | void QButtonGroup::setId(QAbstractButton *button, int id) |
290 | { |
291 | Q_D(QButtonGroup); |
292 | if (button && id != -1) |
293 | d->mapping[button] = id; |
294 | } |
295 | |
296 | /*! |
297 | \since 4.1 |
298 | |
299 | Returns the id for the specified \a button, or -1 if no such button |
300 | exists. |
301 | |
302 | |
303 | \sa setId() |
304 | */ |
305 | int QButtonGroup::id(QAbstractButton *button) const |
306 | { |
307 | Q_D(const QButtonGroup); |
308 | return d->mapping.value(key: button, defaultValue: -1); |
309 | } |
310 | |
311 | /*! |
312 | \since 4.1 |
313 | |
314 | Returns the id of the checkedButton(), or -1 if no button is checked. |
315 | |
316 | \sa setId() |
317 | */ |
318 | int QButtonGroup::checkedId() const |
319 | { |
320 | Q_D(const QButtonGroup); |
321 | return d->mapping.value(key: d->checkedButton, defaultValue: -1); |
322 | } |
323 | |
324 | QT_END_NAMESPACE |
325 | |
326 | #include "moc_qbuttongroup.cpp" |
327 | |