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 "qcheckbox.h"
41 #include "qapplication.h"
42 #include "qbitmap.h"
43 #include "qicon.h"
44 #include "qstylepainter.h"
45 #include "qstyle.h"
46 #include "qstyleoption.h"
47 #include "qevent.h"
48 #ifndef QT_NO_ACCESSIBILITY
49 #include "qaccessible.h"
50 #endif
51 
52 #include "private/qabstractbutton_p.h"
53 
54 QT_BEGIN_NAMESPACE
55 
56 class QCheckBoxPrivate : public QAbstractButtonPrivate
57 {
58     Q_DECLARE_PUBLIC(QCheckBox)
59 public:
QCheckBoxPrivate()60     QCheckBoxPrivate()
61         : QAbstractButtonPrivate(QSizePolicy::CheckBox), tristate(false), noChange(false),
62           hovering(true), publishedState(Qt::Unchecked) {}
63 
64     uint tristate : 1;
65     uint noChange : 1;
66     uint hovering : 1;
67     uint publishedState : 2;
68 
69     void init();
70 };
71 
72 /*!
73     \class QCheckBox
74     \brief The QCheckBox widget provides a checkbox with a text label.
75 
76     \ingroup basicwidgets
77     \inmodule QtWidgets
78 
79     \image windows-checkbox.png
80 
81     A QCheckBox is an option button that can be switched on (checked) or off
82     (unchecked). Checkboxes are typically used to represent features in an
83     application that can be enabled or disabled without affecting others.
84     Different types of behavior can be implemented. For example, a
85     QButtonGroup can be used to group check buttons logically, allowing
86     exclusive checkboxes. However, QButtonGroup does not provide any visual
87     representation.
88 
89     The image below further illustrates the differences between exclusive and
90     non-exclusive checkboxes.
91 
92     \table
93     \row \li \inlineimage checkboxes-exclusive.png
94          \li \inlineimage checkboxes-non-exclusive.png
95     \endtable
96 
97     Whenever a checkbox is checked or cleared, it emits the signal
98     stateChanged(). Connect to this signal if you want to trigger an action
99     each time the checkbox changes state. You can use isChecked() to query
100     whether or not a checkbox is checked.
101 
102     In addition to the usual checked and unchecked states, QCheckBox optionally
103     provides a third state to indicate "no change". This is useful whenever you
104     need to give the user the option of neither checking nor unchecking a
105     checkbox. If you need this third state, enable it with setTristate(), and
106     use checkState() to query the current toggle state.
107 
108     Just like QPushButton, a checkbox displays text, and optionally a small
109     icon. The icon is set with setIcon(). The text can be set in the
110     constructor or with setText(). A shortcut key can be specified by preceding
111     the preferred character with an ampersand. For example:
112 
113     \snippet code/src_gui_widgets_qcheckbox.cpp 0
114 
115     In this example, the shortcut is \e{Alt+A}. See the \l{QShortcut#mnemonic}
116     {QShortcut} documentation for details. To display an actual ampersand,
117     use '&&'.
118 
119     Important inherited functions: text(), setText(), text(), pixmap(),
120     setPixmap(), accel(), setAccel(), isToggleButton(), setDown(), isDown(),
121     isOn(), checkState(), autoRepeat(), isExclusiveToggle(), group(),
122     setAutoRepeat(), toggle(), pressed(), released(), clicked(), toggled(),
123     checkState(), and stateChanged().
124 
125     \sa QAbstractButton, QRadioButton, {fowler}{GUI Design Handbook: Check Box}
126 */
127 
128 /*!
129     \fn void QCheckBox::stateChanged(int state)
130 
131     This signal is emitted whenever the checkbox's state changes, i.e.,
132     whenever the user checks or unchecks it.
133 
134     \a state contains the checkbox's new Qt::CheckState.
135 */
136 
137 /*!
138     \property QCheckBox::tristate
139     \brief whether the checkbox is a tri-state checkbox
140 
141     The default is false, i.e., the checkbox has only two states.
142 */
143 
init()144 void QCheckBoxPrivate::init()
145 {
146     Q_Q(QCheckBox);
147     q->setCheckable(true);
148     q->setMouseTracking(true);
149     q->setForegroundRole(QPalette::WindowText);
150     q->setAttribute(Qt::WA_MacShowFocusRect);
151     setLayoutItemMargins(QStyle::SE_CheckBoxLayoutItem);
152 }
153 
154 /*!
155     Initializes \a option with the values from this QCheckBox. This method is
156     useful for subclasses that require a QStyleOptionButton, but do not want
157     to fill in all the information themselves.
158 
159     \sa QStyleOption::initFrom()
160 */
initStyleOption(QStyleOptionButton * option) const161 void QCheckBox::initStyleOption(QStyleOptionButton *option) const
162 {
163     if (!option)
164         return;
165     Q_D(const QCheckBox);
166     option->initFrom(this);
167     if (d->down)
168         option->state |= QStyle::State_Sunken;
169     if (d->tristate && d->noChange)
170         option->state |= QStyle::State_NoChange;
171     else
172         option->state |= d->checked ? QStyle::State_On : QStyle::State_Off;
173     if (testAttribute(Qt::WA_Hover) && underMouse()) {
174         option->state.setFlag(QStyle::State_MouseOver, d->hovering);
175     }
176     option->text = d->text;
177     option->icon = d->icon;
178     option->iconSize = iconSize();
179 }
180 
181 /*!
182     Constructs a checkbox with the given \a parent, but with no text.
183 
184     \a parent is passed on to the QAbstractButton constructor.
185 */
186 
QCheckBox(QWidget * parent)187 QCheckBox::QCheckBox(QWidget *parent)
188     : QAbstractButton (*new QCheckBoxPrivate, parent)
189 {
190     Q_D(QCheckBox);
191     d->init();
192 }
193 
194 /*!
195     Constructs a checkbox with the given \a parent and \a text.
196 
197     \a parent is passed on to the QAbstractButton constructor.
198 */
199 
QCheckBox(const QString & text,QWidget * parent)200 QCheckBox::QCheckBox(const QString &text, QWidget *parent)
201     : QCheckBox(parent)
202 {
203     setText(text);
204 }
205 
206 /*!
207     Destructor.
208 */
~QCheckBox()209 QCheckBox::~QCheckBox()
210 {
211 }
212 
setTristate(bool y)213 void QCheckBox::setTristate(bool y)
214 {
215     Q_D(QCheckBox);
216     d->tristate = y;
217 }
218 
isTristate() const219 bool QCheckBox::isTristate() const
220 {
221     Q_D(const QCheckBox);
222     return d->tristate;
223 }
224 
225 
226 /*!
227     Returns the checkbox's check state. If you do not need tristate support,
228     you can also use \l QAbstractButton::isChecked(), which returns a boolean.
229 
230     \sa setCheckState(), Qt::CheckState
231 */
checkState() const232 Qt::CheckState QCheckBox::checkState() const
233 {
234     Q_D(const QCheckBox);
235     if (d->tristate &&  d->noChange)
236         return Qt::PartiallyChecked;
237     return d->checked ? Qt::Checked : Qt::Unchecked;
238 }
239 
240 /*!
241     Sets the checkbox's check state to \a state. If you do not need tristate
242     support, you can also use \l QAbstractButton::setChecked(), which takes a
243     boolean.
244 
245     \sa checkState(), Qt::CheckState
246 */
setCheckState(Qt::CheckState state)247 void QCheckBox::setCheckState(Qt::CheckState state)
248 {
249     Q_D(QCheckBox);
250 #ifndef QT_NO_ACCESSIBILITY
251     bool noChange = d->noChange;
252 #endif
253     if (state == Qt::PartiallyChecked) {
254         d->tristate = true;
255         d->noChange = true;
256     } else {
257         d->noChange = false;
258     }
259     d->blockRefresh = true;
260     setChecked(state != Qt::Unchecked);
261     d->blockRefresh = false;
262     d->refresh();
263     if ((uint)state != d->publishedState) {
264         d->publishedState = state;
265         emit stateChanged(state);
266     }
267 
268 #ifndef QT_NO_ACCESSIBILITY
269     if (noChange != d->noChange) {
270         QAccessible::State s;
271         s.checkStateMixed = true;
272         QAccessibleStateChangeEvent event(this, s);
273         QAccessible::updateAccessibility(&event);
274     }
275 #endif
276 }
277 
278 
279 /*!
280     \reimp
281 */
sizeHint() const282 QSize QCheckBox::sizeHint() const
283 {
284     Q_D(const QCheckBox);
285     if (d->sizeHint.isValid())
286         return d->sizeHint;
287     ensurePolished();
288     QFontMetrics fm = fontMetrics();
289     QStyleOptionButton opt;
290     initStyleOption(&opt);
291     QSize sz = style()->itemTextRect(fm, QRect(), Qt::TextShowMnemonic, false,
292                                      text()).size();
293     if (!opt.icon.isNull())
294         sz = QSize(sz.width() + opt.iconSize.width() + 4, qMax(sz.height(), opt.iconSize.height()));
295     d->sizeHint = (style()->sizeFromContents(QStyle::CT_CheckBox, &opt, sz, this)
296                   .expandedTo(QApplication::globalStrut()));
297     return d->sizeHint;
298 }
299 
300 
301 /*!
302     \reimp
303 */
minimumSizeHint() const304 QSize QCheckBox::minimumSizeHint() const
305 {
306     return sizeHint();
307 }
308 
309 /*!
310     \reimp
311 */
paintEvent(QPaintEvent *)312 void QCheckBox::paintEvent(QPaintEvent *)
313 {
314     QStylePainter p(this);
315     QStyleOptionButton opt;
316     initStyleOption(&opt);
317     p.drawControl(QStyle::CE_CheckBox, opt);
318 }
319 
320 /*!
321     \reimp
322 */
mouseMoveEvent(QMouseEvent * e)323 void QCheckBox::mouseMoveEvent(QMouseEvent *e)
324 {
325     Q_D(QCheckBox);
326     if (testAttribute(Qt::WA_Hover)) {
327         bool hit = false;
328         if (underMouse())
329             hit = hitButton(e->pos());
330 
331         if (hit != d->hovering) {
332             update(rect());
333             d->hovering = hit;
334         }
335     }
336 
337     QAbstractButton::mouseMoveEvent(e);
338 }
339 
340 
341 /*!
342     \reimp
343 */
hitButton(const QPoint & pos) const344 bool QCheckBox::hitButton(const QPoint &pos) const
345 {
346     QStyleOptionButton opt;
347     initStyleOption(&opt);
348     return style()->subElementRect(QStyle::SE_CheckBoxClickRect, &opt, this).contains(pos);
349 }
350 
351 /*!
352     \reimp
353 */
checkStateSet()354 void QCheckBox::checkStateSet()
355 {
356     Q_D(QCheckBox);
357     d->noChange = false;
358     Qt::CheckState state = checkState();
359     if ((uint)state != d->publishedState) {
360         d->publishedState = state;
361         emit stateChanged(state);
362     }
363 }
364 
365 /*!
366     \reimp
367 */
nextCheckState()368 void QCheckBox::nextCheckState()
369 {
370     Q_D(QCheckBox);
371     if (d->tristate)
372         setCheckState((Qt::CheckState)((checkState() + 1) % 3));
373     else {
374         QAbstractButton::nextCheckState();
375         QCheckBox::checkStateSet();
376     }
377 }
378 
379 /*!
380     \reimp
381 */
event(QEvent * e)382 bool QCheckBox::event(QEvent *e)
383 {
384     Q_D(QCheckBox);
385     if (e->type() == QEvent::StyleChange
386 #ifdef Q_OS_MAC
387             || e->type() == QEvent::MacSizeChange
388 #endif
389             )
390         d->setLayoutItemMargins(QStyle::SE_CheckBoxLayoutItem);
391     return QAbstractButton::event(e);
392 }
393 
394 
395 
396 QT_END_NAMESPACE
397 
398 #include "moc_qcheckbox.cpp"
399