source: vendor/trolltech/current/src/widgets/qbuttongroup.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 17.9 KB
Line 
1/****************************************************************************
2** $Id: qbuttongroup.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QButtonGroup class
5**
6** Created : 950130
7**
8** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
9**
10** This file is part of the widgets module of the Qt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.QPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
22** licenses may use this file in accordance with the Qt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about Qt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for QPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38#include "qbuttongroup.h"
39#ifndef QT_NO_BUTTONGROUP
40#include "qbutton.h"
41#include "qptrlist.h"
42#include "qapplication.h"
43#include "qradiobutton.h"
44
45
46
47/*!
48 \class QButtonGroup qbuttongroup.h
49 \brief The QButtonGroup widget organizes QButton widgets in a group.
50
51 \ingroup organizers
52 \ingroup geomanagement
53 \ingroup appearance
54 \mainclass
55
56 A button group widget makes it easier to deal with groups of
57 buttons. Each button in a button group has a unique identifier.
58 The button group emits a clicked() signal with this identifier
59 when a button in the group is clicked. This makes a button group
60 particularly useful when you have several similar buttons and want
61 to connect all their clicked() signals to a single slot.
62
63 An \link setExclusive() exclusive\endlink button group switches
64 off all toggle buttons except the one that was clicked. A button
65 group is, by default, non-exclusive. Note that all radio buttons
66 that are inserted into a button group are mutually exclusive even
67 if the button group is non-exclusive. (See
68 setRadioButtonExclusive().)
69
70 There are two ways of using a button group:
71 \list
72 \i The button group is the parent widget of a number of buttons,
73 i.e. the button group is the parent argument in the button
74 constructor. The buttons are assigned identifiers 0, 1, 2, etc.,
75 in the order they are created. A QButtonGroup can display a frame
76 and a title because it inherits QGroupBox.
77 \i The button group is an invisible widget and the contained
78 buttons have some other parent widget. In this usage, each button
79 must be manually inserted, using insert(), into the button group
80 and given an identifier.
81 \endlist
82
83 A button can be removed from the group with remove(). A pointer to
84 a button with a given id can be obtained using find(). The id of a
85 button is available using id(). A button can be set \e on with
86 setButton(). The number of buttons in the group is returned by
87 count().
88
89 <img src=qbttngrp-m.png> <img src=qbttngrp-w.png>
90
91 \sa QPushButton, QCheckBox, QRadioButton
92*/
93
94/*!
95 \property QButtonGroup::exclusive
96 \brief whether the button group is exclusive
97
98 If this property is TRUE, then the buttons in the group are
99 toggled, and to untoggle a button you must click on another button
100 in the group. The default value is FALSE.
101*/
102
103/*!
104 \property QButtonGroup::radioButtonExclusive
105 \brief whether the radio buttons in the group are exclusive
106
107 If this property is TRUE (the default), the \link QRadioButton
108 radiobuttons\endlink in the group are treated exclusively.
109*/
110
111struct QButtonItem
112{
113 QButton *button;
114 int id;
115};
116
117
118class QButtonList: public QPtrList<QButtonItem>
119{
120public:
121 QButtonList() {}
122 ~QButtonList() {}
123};
124
125
126typedef QPtrListIterator<QButtonItem> QButtonListIt;
127
128
129/*!
130 Constructs a button group with no title.
131
132 The \a parent and \a name arguments are passed to the QWidget
133 constructor.
134*/
135
136QButtonGroup::QButtonGroup( QWidget *parent, const char *name )
137 : QGroupBox( parent, name )
138{
139 init();
140}
141
142/*!
143 Constructs a button group with the title \a title.
144
145 The \a parent and \a name arguments are passed to the QWidget
146 constructor.
147*/
148
149QButtonGroup::QButtonGroup( const QString &title, QWidget *parent,
150 const char *name )
151 : QGroupBox( title, parent, name )
152{
153 init();
154}
155
156/*!
157 Constructs a button group with no title. Child widgets will be
158 arranged in \a strips rows or columns (depending on \a
159 orientation).
160
161 The \a parent and \a name arguments are passed to the QWidget
162 constructor.
163*/
164
165QButtonGroup::QButtonGroup( int strips, Orientation orientation,
166 QWidget *parent, const char *name )
167 : QGroupBox( strips, orientation, parent, name )
168{
169 init();
170}
171
172/*!
173 Constructs a button group with title \a title. Child widgets will
174 be arranged in \a strips rows or columns (depending on \a
175 orientation).
176
177 The \a parent and \a name arguments are passed to the QWidget
178 constructor.
179*/
180
181QButtonGroup::QButtonGroup( int strips, Orientation orientation,
182 const QString &title, QWidget *parent,
183 const char *name )
184 : QGroupBox( strips, orientation, title, parent, name )
185{
186 init();
187}
188
189/*!
190 Initializes the button group.
191*/
192
193void QButtonGroup::init()
194{
195 buttons = new QButtonList;
196 Q_CHECK_PTR( buttons );
197 buttons->setAutoDelete( TRUE );
198 excl_grp = FALSE;
199 radio_excl = TRUE;
200}
201
202/*! \reimp */
203
204QButtonGroup::~QButtonGroup()
205{
206 QButtonList * tmp = buttons;
207 QButtonItem *bi = tmp->first();
208 buttons = 0;
209 while( bi ) {
210 bi->button->setGroup(0);
211 bi = tmp->next();
212 }
213 delete tmp;
214}
215
216bool QButtonGroup::isExclusive() const
217{
218 return excl_grp;
219}
220
221void QButtonGroup::setExclusive( bool enable )
222{
223 excl_grp = enable;
224}
225
226
227/*!
228 Inserts the \a button with the identifier \a id into the button
229 group. Returns the button identifier.
230
231 Buttons are normally inserted into a button group automatically by
232 passing the button group as the parent when the button is
233 constructed. So it is not necessary to manually insert buttons
234 that have this button group as their parent widget. An exception
235 is when you want custom identifiers instead of the default 0, 1,
236 2, etc., or if you want the buttons to have some other parent.
237
238 The button is assigned the identifier \a id or an automatically
239 generated identifier. It works as follows: If \a id >= 0, this
240 identifier is assigned. If \a id == -1 (default), the identifier
241 is equal to the number of buttons in the group. If \a id is any
242 other negative integer, for instance -2, a unique identifier
243 (negative integer \<= -2) is generated. No button has an id of -1.
244
245 \sa find(), remove(), setExclusive()
246*/
247
248int QButtonGroup::insert( QButton *button, int id )
249{
250 if ( button->group() )
251 button->group()->remove( button );
252
253 static int seq_no = -2;
254 QButtonItem *bi = new QButtonItem;
255 Q_CHECK_PTR( bi );
256
257 if ( id < -1 )
258 bi->id = seq_no--;
259 else if ( id == -1 )
260 bi->id = buttons->count();
261 else
262 bi->id = id;
263
264 bi->button = button;
265 button->setGroup(this);
266 buttons->append( bi );
267
268 connect( button, SIGNAL(pressed()) , SLOT(buttonPressed()) );
269 connect( button, SIGNAL(released()), SLOT(buttonReleased()) );
270 connect( button, SIGNAL(clicked()) , SLOT(buttonClicked()) );
271 connect( button, SIGNAL(toggled(bool)) , SLOT(buttonToggled(bool)) );
272
273 if ( button->isToggleButton() && !button->isOn() &&
274 selected() && (selected()->focusPolicy() & TabFocus) != 0 )
275 button->setFocusPolicy( (FocusPolicy)(button->focusPolicy() &
276 ~TabFocus) );
277
278 return bi->id;
279}
280
281/*!
282 Returns the number of buttons in the group.
283*/
284int QButtonGroup::count() const
285{
286 return buttons->count();
287}
288
289/*!
290 Removes the \a button from the button group.
291
292 \sa insert()
293*/
294
295void QButtonGroup::remove( QButton *button )
296{
297 if ( !buttons )
298 return;
299
300 QButtonListIt it( *buttons );
301 QButtonItem *i;
302 while ( (i=it.current()) != 0 ) {
303 ++it;
304 if ( i->button == button ) {
305 buttons->remove( i );
306 button->setGroup(0);
307 button->disconnect( this );
308 return;
309 }
310 }
311}
312
313
314/*!
315 Returns the button with the specified identifier \a id, or 0 if
316 the button was not found.
317*/
318
319QButton *QButtonGroup::find( int id ) const
320{
321 // introduce a QButtonListIt if calling anything
322 for ( QButtonItem *i=buttons->first(); i; i=buttons->next() )
323 if ( i->id == id )
324 return i->button;
325 return 0;
326}
327
328
329/*!
330 \fn void QButtonGroup::pressed( int id )
331
332 This signal is emitted when a button in the group is \link
333 QButton::pressed() pressed\endlink. The \a id argument is the
334 button's identifier.
335
336 \sa insert()
337*/
338
339/*!
340 \fn void QButtonGroup::released( int id )
341
342 This signal is emitted when a button in the group is \link
343 QButton::released() released\endlink. The \a id argument is the
344 button's identifier.
345
346 \sa insert()
347*/
348
349/*!
350 \fn void QButtonGroup::clicked( int id )
351
352 This signal is emitted when a button in the group is \link
353 QButton::clicked() clicked\endlink. The \a id argument is the
354 button's identifier.
355
356 \sa insert()
357*/
358
359
360/*!
361 \internal
362 This slot is activated when one of the buttons in the group emits the
363 QButton::pressed() signal.
364*/
365
366void QButtonGroup::buttonPressed()
367{
368 // introduce a QButtonListIt if calling anything
369 int id = -1;
370 QButton *bt = (QButton *)sender(); // object that sent the signal
371 for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() )
372 if ( bt == i->button ) { // button was clicked
373 id = i->id;
374 break;
375 }
376 if ( id != -1 )
377 emit pressed( id );
378}
379
380/*!
381 \internal
382 This slot is activated when one of the buttons in the group emits the
383 QButton::released() signal.
384*/
385
386void QButtonGroup::buttonReleased()
387{
388 // introduce a QButtonListIt if calling anything
389 int id = -1;
390 QButton *bt = (QButton *)sender(); // object that sent the signal
391 for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() )
392 if ( bt == i->button ) { // button was clicked
393 id = i->id;
394 break;
395 }
396 if ( id != -1 )
397 emit released( id );
398}
399
400/*!
401 \internal
402 This slot is activated when one of the buttons in the group emits the
403 QButton::clicked() signal.
404*/
405
406void QButtonGroup::buttonClicked()
407{
408 // introduce a QButtonListIt if calling anything
409 int id = -1;
410 QButton *bt = ::qt_cast<QButton*>(sender()); // object that sent the signal
411#if defined(QT_CHECK_NULL)
412 Q_ASSERT( bt );
413#endif
414 for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() ) {
415 if ( bt == i->button ) { // button was clicked
416 id = i->id;
417 break;
418 }
419 }
420 if ( id != -1 )
421 emit clicked( id );
422}
423
424
425/*!
426 \internal
427 This slot is activated when one of the buttons in the group emits the
428 QButton::toggled() signal.
429*/
430
431void QButtonGroup::buttonToggled( bool on )
432{
433 // introduce a QButtonListIt if calling anything
434 if ( !on || !excl_grp && !radio_excl )
435 return;
436 QButton *bt = ::qt_cast<QButton*>(sender()); // object that sent the signal
437#if defined(QT_CHECK_NULL)
438 Q_ASSERT( bt );
439 Q_ASSERT( bt->isToggleButton() );
440#endif
441
442 if ( !excl_grp && !::qt_cast<QRadioButton*>(bt) )
443 return;
444 QButtonItem * i = buttons->first();
445 bool hasTabFocus = FALSE;
446
447 while( i != 0 && hasTabFocus == FALSE ) {
448 if ( ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) &&
449 (i->button->focusPolicy() & TabFocus) )
450 hasTabFocus = TRUE;
451 i = buttons->next();
452 }
453
454 i = buttons->first();
455 while( i ) {
456 if ( bt != i->button &&
457 i->button->isToggleButton() &&
458 i->button->isOn() &&
459 ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) )
460 i->button->setOn( FALSE );
461 if ( ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) &&
462 i->button->isToggleButton() &&
463 hasTabFocus )
464 i->button->setFocusPolicy( (FocusPolicy)(i->button->focusPolicy() &
465 ~TabFocus) );
466 i = buttons->next();
467 }
468
469 if ( hasTabFocus )
470 bt->setFocusPolicy( (FocusPolicy)(bt->focusPolicy() | TabFocus) );
471}
472
473
474
475void QButtonGroup::setButton( int id )
476{
477 QButton * b = find( id );
478 if ( b )
479 b->setOn( TRUE );
480}
481
482void QButtonGroup::setRadioButtonExclusive( bool on)
483{
484 radio_excl = on;
485}
486
487
488/*!
489 Moves the keyboard focus according to \a key, and if appropriate
490 checks the new focus item.
491
492 This function does nothing unless the keyboard focus points to one
493 of the button group members and \a key is one of \c Key_Up, \c
494 Key_Down, \c Key_Left and \c Key_Right.
495*/
496
497void QButtonGroup::moveFocus( int key )
498{
499 QWidget * f = qApp->focusWidget();
500
501 QButtonItem * i;
502 i = buttons->first();
503 while( i && i->button != f )
504 i = buttons->next();
505
506 if ( !i || !i->button )
507 return;
508
509 QWidget * candidate = 0;
510 int bestScore = -1;
511
512 QPoint goal( f->mapToGlobal( f->geometry().center() ) );
513
514 i = buttons->first();
515 while( i && i->button ) {
516 if ( i->button != f &&
517 i->button->isEnabled() ) {
518 QPoint p(i->button->mapToGlobal(i->button->geometry().center()));
519 int score = (p.y() - goal.y())*(p.y() - goal.y()) +
520 (p.x() - goal.x())*(p.x() - goal.x());
521 bool betterScore = score < bestScore || !candidate;
522 switch( key ) {
523 case Key_Up:
524 if ( p.y() < goal.y() && betterScore ) {
525 if ( QABS( p.x() - goal.x() ) < QABS( p.y() - goal.y() ) ) {
526 candidate = i->button;
527 bestScore = score;
528 } else if ( i->button->x() == f->x() ) {
529 candidate = i->button;
530 bestScore = score/2;
531 }
532 }
533 break;
534 case Key_Down:
535 if ( p.y() > goal.y() && betterScore ) {
536 if ( QABS( p.x() - goal.x() ) < QABS( p.y() - goal.y() ) ) {
537 candidate = i->button;
538 bestScore = score;
539 } else if ( i->button->x() == f->x() ) {
540 candidate = i->button;
541 bestScore = score/2;
542 }
543 }
544 break;
545 case Key_Left:
546 if ( p.x() < goal.x() && betterScore ) {
547 if ( QABS( p.y() - goal.y() ) < QABS( p.x() - goal.x() ) ) {
548 candidate = i->button;
549 bestScore = score;
550 } else if ( i->button->y() == f->y() ) {
551 candidate = i->button;
552 bestScore = score/2;
553 }
554 }
555 break;
556 case Key_Right:
557 if ( p.x() > goal.x() && betterScore ) {
558 if ( QABS( p.y() - goal.y() ) < QABS( p.x() - goal.x() ) ) {
559 candidate = i->button;
560 bestScore = score;
561 } else if ( i->button->y() == f->y() ) {
562 candidate = i->button;
563 bestScore = score/2;
564 }
565 }
566 break;
567 }
568 }
569 i = buttons->next();
570 }
571
572 QButton *buttoncand = ::qt_cast<QButton*>(candidate);
573 if ( buttoncand && ::qt_cast<QButton*>(f) &&
574 ((QButton*)f)->isOn() &&
575 buttoncand->isToggleButton() &&
576 ( isExclusive() || ( ::qt_cast<QRadioButton*>(f) &&
577 ::qt_cast<QRadioButton*>(candidate)))) {
578 if ( f->focusPolicy() & TabFocus ) {
579 f->setFocusPolicy( (FocusPolicy)(f->focusPolicy() & ~TabFocus) );
580 candidate->setFocusPolicy( (FocusPolicy)(candidate->focusPolicy()|
581 TabFocus) );
582 }
583 buttoncand->setOn( TRUE );
584 buttoncand->animateClick();
585 buttoncand->animateTimeout(); // ### crude l&f hack
586 }
587
588 if ( candidate ) {
589 if (key == Key_Up || key == Key_Left)
590 QFocusEvent::setReason(QFocusEvent::Backtab);
591 else
592 QFocusEvent::setReason(QFocusEvent::Tab);
593 candidate->setFocus();
594 QFocusEvent::resetReason();
595 }
596}
597
598
599/*!
600 Returns the selected toggle button if exactly one is selected;
601 otherwise returns 0.
602
603 \sa selectedId()
604*/
605
606QButton * QButtonGroup::selected() const
607{
608 if ( !buttons )
609 return 0;
610 QButtonListIt it( *buttons );
611 QButtonItem *i;
612 QButton *candidate = 0;
613
614 while ( (i = it.current()) != 0 ) {
615 ++it;
616 if ( i->button && i->button->isToggleButton() && i->button->isOn() ) {
617 if ( candidate != 0 )
618 return 0;
619 candidate = i->button;
620 }
621 }
622 return candidate;
623}
624
625/*!
626 \property QButtonGroup::selectedId
627 \brief The id of the selected toggle button.
628
629 If no toggle button is selected, id() returns -1.
630
631 If setButton() is called on an exclusive group, the button with
632 the given id will be set to on and all the others will be set to
633 off.
634
635 \sa selected()
636*/
637
638int QButtonGroup::selectedId() const
639{
640 return id( selected() );
641}
642
643
644/*!
645 Returns the id of \a button, or -1 if \a button is not a member of
646 this group.
647
648 \sa selectedId();
649*/
650
651int QButtonGroup::id( QButton * button ) const
652{
653 QButtonItem *i = buttons->first();
654 while ( i && i->button != button )
655 i = buttons->next();
656 return i ? i->id : -1;
657}
658
659
660/*!
661 \reimp
662*/
663bool QButtonGroup::event( QEvent * e )
664{
665 if ( e->type() == QEvent::ChildInserted ) {
666 QChildEvent * ce = (QChildEvent *) e;
667 if ( radio_excl && ::qt_cast<QRadioButton*>(ce->child()) ) {
668 QButton * button = (QButton *) ce->child();
669 if ( button->isToggleButton() && !button->isOn() &&
670 selected() && (selected()->focusPolicy() & TabFocus) != 0 )
671 button->setFocusPolicy( (FocusPolicy)(button->focusPolicy() &
672 ~TabFocus) );
673 }
674 }
675 return QGroupBox::event( e );
676}
677#endif
Note: See TracBrowser for help on using the repository browser.