source: trunk/src/kernel/qaccel.cpp@ 127

Last change on this file since 127 was 107, checked in by dmik, 19 years ago

Widgets: Fixed: Pressed key's text (symbol) is now always uppercased if pressed with a modifier key before matching against UNICODE_ACCEL accelerators (to let them be independent of the Shift state). As a result, pressing Alt+NLS_letter wil now match the accelerator defined using the "w&ord" syntax (where Qt always uppercases the indicated letter) regardless of the Shift state.

  • Property svn:keywords set to Id
File size: 32.0 KB
Line 
1/****************************************************************************
2** $Id: qaccel.cpp 107 2006-07-29 14:22:12Z dmik $
3**
4** Implementation of QAccel class
5**
6** Created : 950419
7**
8** Copyright (C) 1992-2002 Trolltech AS. All rights reserved.
9**
10** This file is part of the kernel 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 "qaccel.h"
39
40#ifndef QT_NO_ACCEL
41
42#include "qsignal.h"
43#include "qapplication.h"
44#include "qwidget.h"
45#include "qptrlist.h"
46#include "qwhatsthis.h"
47#include "qguardedptr.h"
48#include "qstatusbar.h"
49#include "qdockwindow.h"
50#include "qsignalslotimp.h"
51/*!
52 \class QAccel qaccel.h
53 \brief The QAccel class handles keyboard accelerator and shortcut keys.
54
55 \ingroup misc
56
57 A keyboard accelerator triggers an action when a certain key
58 combination is pressed. The accelerator handles all keyboard
59 activity for all the children of one top-level widget, so it is
60 not affected by the keyboard focus.
61
62 In most cases, you will not need to use this class directly. Use
63 the QAction class to create actions with accelerators that can be
64 used in both menus and toolbars. If you're only interested in
65 menus use QMenuData::insertItem() or QMenuData::setAccel() to make
66 accelerators for operations that are also available on menus. Many
67 widgets automatically generate accelerators, such as QButton,
68 QGroupBox, QLabel (with QLabel::setBuddy()), QMenuBar and QTabBar.
69 Example:
70 \code
71 QPushButton p( "&Exit", parent ); // automatic shortcut ALT+Key_E
72 QPopupMenu *fileMenu = new fileMenu( parent );
73 fileMenu->insertItem( "Undo", parent, SLOT(undo()), CTRL+Key_Z );
74 \endcode
75
76 A QAccel contains a list of accelerator items that can be
77 manipulated using insertItem(), removeItem(), clear(), key() and
78 findKey().
79
80 Each accelerator item consists of an identifier and a \l
81 QKeySequence. A single key sequence consists of a keyboard code
82 combined with modifiers (\c SHIFT, \c CTRL, \c ALT or \c
83 UNICODE_ACCEL). For example, \c{CTRL + Key_P} could be a shortcut
84 for printing a document. The key codes are listed in \c
85 qnamespace.h. As an alternative, use \c UNICODE_ACCEL with the
86 unicode code point of the character. For example, \c{UNICODE_ACCEL
87 + 'A'} gives the same accelerator as \c Key_A.
88
89 When an accelerator key is pressed, the accelerator sends out the
90 signal activated() with a number that identifies this particular
91 accelerator item. Accelerator items can also be individually
92 connected, so that two different keys will activate two different
93 slots (see connectItem() and disconnectItem()).
94
95 The activated() signal is \e not emitted when two or more
96 accelerators match the same key. Instead, the first matching
97 accelerator sends out the activatedAmbiguously() signal. By
98 pressing the key multiple times, users can navigate between all
99 matching accelerators. Some standard controls like QPushButton and
100 QCheckBox connect the activatedAmbiguously() signal to the
101 harmless setFocus() slot, whereas activated() is connected to a
102 slot invoking the button's action. Most controls, like QLabel and
103 QTabBar, treat activated() and activatedAmbiguously() as
104 equivalent.
105
106 Use setEnabled() to enable or disable all the items in an
107 accelerator, or setItemEnabled() to enable or disable individual
108 items. An item is active only when both the QAccel and the item
109 itself are enabled.
110
111 The function setWhatsThis() specifies a help text that appears
112 when the user presses an accelerator key in What's This mode.
113
114 The accelerator will be deleted when \e parent is deleted,
115 and will consume relevant key events until then.
116
117 Please note that the accelerator
118 \code
119 accelerator->insertItem( QKeySequence("M") );
120 \endcode
121 can be triggered with both the 'M' key, and with Shift+M,
122 unless a second accelerator is defined for the Shift+M
123 combination.
124
125
126 Example:
127 \code
128 QAccel *a = new QAccel( myWindow ); // create accels for myWindow
129 a->connectItem( a->insertItem(Key_P+CTRL), // adds Ctrl+P accelerator
130 myWindow, // connected to myWindow's
131 SLOT(printDoc()) ); // printDoc() slot
132 \endcode
133
134 \sa QKeyEvent QWidget::keyPressEvent() QMenuData::setAccel()
135 QButton::setAccel() QLabel::setBuddy() QKeySequence
136 \link guibooks.html#fowler GUI Design Handbook: Keyboard Shortcuts \endlink.
137*/
138
139
140struct QAccelItem { // internal accelerator item
141 QAccelItem( const QKeySequence &k, int i )
142 { key=k; id=i; enabled=TRUE; signal=0; }
143 ~QAccelItem() { delete signal; }
144 int id;
145 QKeySequence key;
146 bool enabled;
147 QSignal *signal;
148 QString whatsthis;
149};
150
151
152typedef QPtrList<QAccelItem> QAccelList; // internal accelerator list
153
154class QAccelPrivate : public Qt {
155public:
156 QAccelPrivate( QAccel* p );
157 ~QAccelPrivate();
158 QAccelList aitems;
159 bool enabled;
160 QGuardedPtr<QWidget> watch;
161 bool ignorewhatsthis;
162 QAccel* parent;
163
164 void activate( QAccelItem* item );
165 void activateAmbiguously( QAccelItem* item );
166};
167
168class QAccelManager : public Qt {
169public:
170 static QAccelManager* self() { return self_ptr ? self_ptr : new QAccelManager; }
171 void registerAccel( QAccelPrivate* a ) { accels.append( a ); }
172 void unregisterAccel( QAccelPrivate* a ) { accels.removeRef( a ); if ( accels.isEmpty() ) delete this; }
173 bool tryAccelEvent( QWidget* w, QKeyEvent* e );
174 bool dispatchAccelEvent( QWidget* w, QKeyEvent* e );
175 bool tryComposeUnicode( QWidget* w, QKeyEvent* e );
176
177private:
178 QAccelManager():currentState(Qt::NoMatch), clash(-1) { self_ptr = this; }
179 ~QAccelManager() { self_ptr = 0; }
180
181 bool correctSubWindow( QWidget *w, QAccelPrivate* d );
182 SequenceMatch match( QKeyEvent* e, QAccelItem* item, QKeySequence& temp );
183 int translateModifiers( ButtonState state );
184
185 QPtrList<QAccelPrivate> accels;
186 static QAccelManager* self_ptr;
187 Qt::SequenceMatch currentState;
188 QKeySequence intermediate;
189 int clash;
190};
191QAccelManager* QAccelManager::self_ptr = 0;
192
193bool Q_EXPORT qt_tryAccelEvent( QWidget* w, QKeyEvent* e){
194 return QAccelManager::self()->tryAccelEvent( w, e );
195}
196
197bool Q_EXPORT qt_dispatchAccelEvent( QWidget* w, QKeyEvent* e){
198 return QAccelManager::self()->dispatchAccelEvent( w, e );
199}
200
201bool Q_EXPORT qt_tryComposeUnicode( QWidget* w, QKeyEvent* e){
202 return QAccelManager::self()->tryComposeUnicode( w, e );
203}
204
205#ifdef Q_WS_MAC
206static bool qt_accel_no_shortcuts = TRUE;
207#else
208static bool qt_accel_no_shortcuts = FALSE;
209#endif
210void Q_EXPORT qt_setAccelAutoShortcuts(bool b) { qt_accel_no_shortcuts = b; }
211
212/*
213 \internal
214 Returns TRUE if the accel is in the current subwindow, else FALSE.
215*/
216bool QAccelManager::correctSubWindow( QWidget* w, QAccelPrivate* d ) {
217#if !defined ( Q_OS_MACX )
218 if ( !d->watch || !d->watch->isVisible() || !d->watch->isEnabled() )
219#else
220 if ( !d->watch || (!d->watch->isVisible() && !d->watch->inherits( "QMenuBar" )) || !d->watch->isEnabled() )
221#endif
222 return FALSE;
223 QWidget* tlw = w->topLevelWidget();
224 QWidget* wtlw = d->watch->topLevelWidget();
225
226 /* if we live in a floating dock window, keep our parent's
227 * accelerators working */
228#ifndef QT_NO_MAINWINDOW
229 if ( tlw->isDialog() && tlw->parentWidget() && ::qt_cast<QDockWindow*>(tlw) )
230 return tlw->parentWidget()->topLevelWidget() == wtlw;
231
232 if ( wtlw != tlw )
233 return FALSE;
234#endif
235 /* if we live in a MDI subwindow, ignore the event if we are
236 not the active document window */
237 QWidget* sw = d->watch;
238 while ( sw && !sw->testWFlags( WSubWindow ) )
239 sw = sw->parentWidget( TRUE );
240 if ( sw ) { // we are in a subwindow indeed
241 QWidget* fw = w;
242 while ( fw && fw != sw )
243 fw = fw->parentWidget( TRUE );
244 if ( fw != sw ) // focus widget not in our subwindow
245 return FALSE;
246 }
247 return TRUE;
248}
249
250inline int QAccelManager::translateModifiers( ButtonState state )
251{
252 int result = 0;
253 if ( state & ShiftButton )
254 result |= SHIFT;
255 if ( state & ControlButton )
256 result |= CTRL;
257 if ( state & MetaButton )
258 result |= META;
259 if ( state & AltButton )
260 result |= ALT;
261 return result;
262}
263
264/*
265 \internal
266 Matches the current intermediate key sequence + the latest
267 keyevent, with and AccelItem. Returns Identical,
268 PartialMatch or NoMatch, and fills \a temp with the
269 resulting key sequence.
270*/
271Qt::SequenceMatch QAccelManager::match( QKeyEvent *e, QAccelItem* item, QKeySequence& temp )
272{
273 SequenceMatch result = Qt::NoMatch;
274 int index = intermediate.count();
275 temp = intermediate;
276
277 int modifier = translateModifiers( e->state() );
278
279 if ( e->key() && e->key() != Key_unknown) {
280 int key = e->key() | modifier;
281 if ( e->key() == Key_BackTab ) {
282 /*
283 In QApplication, we map shift+tab to shift+backtab.
284 This code here reverts the mapping in a way that keeps
285 backtab and shift+tab accelerators working, in that
286 order, meaning backtab has priority.*/
287 key &= ~SHIFT;
288
289 temp.setKey( key, index );
290 if ( Qt::NoMatch != (result = temp.matches( item->key )) )
291 return result;
292 if ( e->state() & ShiftButton )
293 key |= SHIFT;
294 key = Key_Tab | ( key & MODIFIER_MASK );
295 temp.setKey( key, index );
296 if ( Qt::NoMatch != (result = temp.matches( item->key )) )
297 return result;
298 } else {
299 temp.setKey( key, index );
300 if ( Qt::NoMatch != (result = temp.matches( item->key )) )
301 return result;
302 }
303
304 if ( key == Key_BackTab ) {
305 if ( e->state() & ShiftButton )
306 key |= SHIFT;
307 temp.setKey( key, index );
308 if ( Qt::NoMatch != (result = temp.matches( item->key )) )
309 return result;
310 }
311 }
312 if ( !e->text().isEmpty() ) {
313 QChar c = e->text()[0];
314 // be in accordance with accoQAccel::shortcutKey()
315 if ( modifier != 0 && c.isPrint() )
316 c = c.upper();
317 temp.setKey( (int)c.unicode() | UNICODE_ACCEL | modifier, index );
318 result = temp.matches( item->key );
319 }
320 return result;
321}
322
323bool QAccelManager::tryAccelEvent( QWidget* w, QKeyEvent* e )
324{
325 if ( Qt::NoMatch == currentState ) {
326 e->t = QEvent::AccelOverride;
327 e->ignore();
328 QApplication::sendSpontaneousEvent( w, e );
329 if ( e->isAccepted() )
330 return FALSE;
331 }
332 e->t = QEvent::Accel;
333 e->ignore();
334 QApplication::sendSpontaneousEvent( w, e );
335 return e->isAccepted();
336}
337
338bool QAccelManager::tryComposeUnicode( QWidget* w, QKeyEvent* e )
339{
340 if ( QApplication::metaComposeUnicode ) {
341 int value = e->key() - Key_0;
342 // Ignore acceloverrides so we don't trigger
343 // accels on keypad when Meta compose is on
344 if ( (e->type() == QEvent::AccelOverride) &&
345 (e->state() == Qt::Keypad + Qt::MetaButton) ) {
346 e->accept();
347 // Meta compose start/continue
348 } else if ( (e->type() == QEvent::KeyPress) &&
349 (e->state() == Qt::Keypad + Qt::MetaButton) ) {
350 if ( value >= 0 && value <= 9 ) {
351 QApplication::composedUnicode *= 10;
352 QApplication::composedUnicode += value;
353 return TRUE;
354 } else {
355 // Composing interrupted, dispatch!
356 if ( QApplication::composedUnicode ) {
357 QChar ch( QApplication::composedUnicode );
358 QString s( ch );
359 QKeyEvent kep( QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s );
360 QKeyEvent ker( QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s );
361 QApplication::sendEvent( w, &kep );
362 QApplication::sendEvent( w, &ker );
363 }
364 QApplication::composedUnicode = 0;
365 return TRUE;
366 }
367 // Meta compose end, dispatch
368 } else if ( (e->type() == QEvent::KeyRelease) &&
369 (e->key() == Key_Meta) &&
370 (QApplication::composedUnicode != 0) ) {
371 if ( (QApplication::composedUnicode > 0) &&
372 (QApplication::composedUnicode < 0xFFFE) ) {
373 QChar ch( QApplication::composedUnicode );
374 QString s( ch );
375 QKeyEvent kep( QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s );
376 QKeyEvent ker( QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s );
377 QApplication::sendEvent( w, &kep );
378 QApplication::sendEvent( w, &ker );
379 }
380 QApplication::composedUnicode = 0;
381 return TRUE;
382 }
383 }
384 return FALSE;
385}
386
387/*
388 \internal
389 Checks for possible accelerators, if no widget
390 ate the keypres, or we are in the middle of a
391 partial key sequence.
392*/
393bool QAccelManager::dispatchAccelEvent( QWidget* w, QKeyEvent* e )
394{
395#ifndef QT_NO_STATUSBAR
396 // Needs to be declared and used here because of "goto doclash"
397 QStatusBar* mainStatusBar = 0;
398#endif
399
400 // Modifiers can NOT be accelerators...
401 if ( e->key() >= Key_Shift &&
402 e->key() <= Key_Alt )
403 return FALSE;
404
405 SequenceMatch result = Qt::NoMatch;
406 QKeySequence tocheck, partial;
407 QAccelPrivate* accel = 0;
408 QAccelItem* item = 0;
409 QAccelPrivate* firstaccel = 0;
410 QAccelItem* firstitem = 0;
411 QAccelPrivate* lastaccel = 0;
412 QAccelItem* lastitem = 0;
413
414 QKeyEvent pe = *e;
415 int n = -1;
416 int hasShift = (e->state()&Qt::ShiftButton)?1:0;
417 bool identicalDisabled = FALSE;
418 bool matchFound = FALSE;
419 do {
420 accel = accels.first();
421 matchFound = FALSE;
422 while ( accel ) {
423 if ( correctSubWindow( w, accel ) ) {
424 if ( accel->enabled ) {
425 item = accel->aitems.last();
426 while( item ) {
427 if ( Qt::Identical == (result = match( &pe, item, tocheck )) ) {
428 if ( item->enabled ) {
429 if ( !firstaccel ) {
430 firstaccel = accel;
431 firstitem = item;
432 }
433 lastaccel = accel;
434 lastitem = item;
435 n++;
436 matchFound = TRUE;
437 if ( n > QMAX(clash,0) )
438 goto doclash;
439 } else {
440 identicalDisabled = TRUE;
441 }
442 }
443 if ( item->enabled && Qt::PartialMatch == result ) {
444 partial = tocheck;
445 matchFound = TRUE;
446 }
447 item = accel->aitems.prev();
448 }
449 } else {
450 item = accel->aitems.last();
451 while( item ) {
452 if ( Qt::Identical == match( &pe, item, tocheck ) )
453 identicalDisabled = TRUE;
454 item = accel->aitems.prev();
455 }
456 }
457 }
458 accel = accels.next();
459 }
460 pe = QKeyEvent( QEvent::Accel, pe.key(), pe.ascii(), pe.state()&~Qt::ShiftButton, pe.text() );
461 } while ( hasShift-- && !matchFound && !identicalDisabled );
462
463#ifndef QT_NO_STATUSBAR
464 mainStatusBar = (QStatusBar*) w->topLevelWidget()->child( 0, "QStatusBar" );
465#endif
466 if ( n < 0 ) { // no match found
467 currentState = partial.count() ? PartialMatch : NoMatch;
468#ifndef QT_NO_STATUSBAR
469 // Only display message if we are, or were, in a partial match
470 if ( mainStatusBar && (PartialMatch == currentState || intermediate.count() ) ) {
471 if ( currentState == Qt::PartialMatch ) {
472 mainStatusBar->message( (QString)partial + ", ...", 0 );
473 } else if (!identicalDisabled) {
474 QString message = QAccel::tr("%1, %2 not defined").
475 arg( (QString)intermediate ).
476 arg( QKeySequence::encodeString( e->key() | translateModifiers(e->state()) ) );
477 mainStatusBar->message( message, 2000 );
478 // Since we're a NoMatch, reset the clash count
479 clash = -1;
480 } else {
481 mainStatusBar->clear();
482 }
483 }
484#endif
485
486 bool eatKey = (PartialMatch == currentState || intermediate.count() );
487 intermediate = partial;
488 if ( eatKey )
489 e->accept();
490 return eatKey;
491 } else if ( n == 0 ) { // found exactly one match
492 clash = -1; // reset
493#ifndef QT_NO_STATUSBAR
494 if ( currentState == Qt::PartialMatch && mainStatusBar )
495 mainStatusBar->clear();
496#endif
497 currentState = Qt::NoMatch; // Free sequence keylock
498 intermediate = QKeySequence();
499 lastaccel->activate( lastitem );
500 e->accept();
501 return TRUE;
502 }
503
504 doclash: // found more than one match
505#ifndef QT_NO_STATUSBAR
506 if ( !mainStatusBar ) // if "goto doclash", we need to get statusbar again.
507 mainStatusBar = (QStatusBar*) w->topLevelWidget()->child( 0, "QStatusBar" );
508#endif
509
510 QString message = QAccel::tr( "Ambiguous \"%1\" not handled" ).arg( (QString)tocheck );
511 if ( clash >= 0 && n > clash ) { // pick next match
512 intermediate = QKeySequence();
513 currentState = Qt::NoMatch; // Free sequence keylock
514 clash++;
515#ifndef QT_NO_STATUSBAR
516 if ( mainStatusBar &&
517 !lastitem->signal &&
518 !(lastaccel->parent->receivers( "activatedAmbiguously(int)" )) )
519 mainStatusBar->message( message, 2000 );
520#endif
521 lastaccel->activateAmbiguously( lastitem );
522 } else { // start (or wrap) with the first matching
523 intermediate = QKeySequence();
524 currentState = Qt::NoMatch; // Free sequence keylock
525 clash = 0;
526#ifndef QT_NO_STATUSBAR
527 if ( mainStatusBar &&
528 !firstitem->signal &&
529 !(firstaccel->parent->receivers( "activatedAmbiguously(int)" )) )
530 mainStatusBar->message( message, 2000 );
531#endif
532 firstaccel->activateAmbiguously( firstitem );
533 }
534 e->accept();
535 return TRUE;
536}
537
538QAccelPrivate::QAccelPrivate( QAccel* p )
539 : parent( p )
540{
541 QAccelManager::self()->registerAccel( this );
542 aitems.setAutoDelete( TRUE );
543 ignorewhatsthis = FALSE;
544}
545
546QAccelPrivate::~QAccelPrivate()
547{
548 QAccelManager::self()->unregisterAccel( this );
549}
550
551static QAccelItem *find_id( QAccelList &list, int id )
552{
553 register QAccelItem *item = list.first();
554 while ( item && item->id != id )
555 item = list.next();
556 return item;
557}
558
559static QAccelItem *find_key( QAccelList &list, const QKeySequence &key )
560{
561 register QAccelItem *item = list.first();
562 while ( item && !( item->key == key ) )
563 item = list.next();
564 return item;
565}
566
567/*!
568 Constructs a QAccel object called \a name, with parent \a parent.
569 The accelerator operates on \a parent.
570*/
571
572QAccel::QAccel( QWidget *parent, const char *name )
573 : QObject( parent, name )
574{
575 d = new QAccelPrivate( this );
576 d->enabled = TRUE;
577 d->watch = parent;
578#if defined(QT_CHECK_NULL)
579 if ( !d->watch )
580 qWarning( "QAccel: An accelerator must have a parent or a watch widget" );
581#endif
582}
583
584/*!
585 Constructs a QAccel object called \a name, that operates on \a
586 watch, and is a child of \a parent.
587
588 This constructor is not needed for normal application programming.
589*/
590QAccel::QAccel( QWidget* watch, QObject *parent, const char *name )
591 : QObject( parent, name )
592{
593 d = new QAccelPrivate( this );
594 d->enabled = TRUE;
595 d->watch = watch;
596#if defined(QT_CHECK_NULL)
597 if ( !d->watch )
598 qWarning( "QAccel: An accelerator must have a parent or a watch widget" );
599#endif
600}
601
602/*!
603 Destroys the accelerator object and frees all allocated resources.
604*/
605
606QAccel::~QAccel()
607{
608 delete d;
609}
610
611
612/*!
613 \fn void QAccel::activated( int id )
614
615 This signal is emitted when an accelerator key is pressed. \a id
616 is a number that identifies this particular accelerator item.
617
618 \sa activatedAmbiguously()
619*/
620
621/*!
622 \fn void QAccel::activatedAmbiguously( int id )
623
624 This signal is emitted when an accelerator key is pressed. \a id
625 is a number that identifies this particular accelerator item.
626
627 \sa activated()
628*/
629
630
631/*!
632 Returns TRUE if the accelerator is enabled; otherwise returns
633 FALSE.
634
635 \sa setEnabled(), isItemEnabled()
636*/
637
638bool QAccel::isEnabled() const
639{
640 return d->enabled;
641}
642
643
644/*!
645 Enables the accelerator if \a enable is TRUE, or disables it if \a
646 enable is FALSE.
647
648 Individual keys can also be enabled or disabled using
649 setItemEnabled(). To work, a key must be an enabled item in an
650 enabled QAccel.
651
652 \sa isEnabled(), setItemEnabled()
653*/
654
655void QAccel::setEnabled( bool enable )
656{
657 d->enabled = enable;
658}
659
660
661/*!
662 Returns the number of accelerator items in this accelerator.
663*/
664
665uint QAccel::count() const
666{
667 return d->aitems.count();
668}
669
670
671static int get_seq_id()
672{
673 static int seq_no = -2; // -1 is used as return value in findKey()
674 return seq_no--;
675}
676
677/*!
678 Inserts an accelerator item and returns the item's identifier.
679
680 \a key is a key code and an optional combination of SHIFT, CTRL
681 and ALT. \a id is the accelerator item id.
682
683 If \a id is negative, then the item will be assigned a unique
684 negative identifier less than -1.
685
686 \code
687 QAccel *a = new QAccel( myWindow ); // create accels for myWindow
688 a->insertItem( CTRL + Key_P, 200 ); // Ctrl+P, e.g. to print document
689 a->insertItem( ALT + Key_X, 201 ); // Alt+X, e.g. to quit
690 a->insertItem( UNICODE_ACCEL + 'q', 202 ); // Unicode 'q', e.g. to quit
691 a->insertItem( Key_D ); // gets a unique negative id < -1
692 a->insertItem( CTRL + SHIFT + Key_P ); // gets a unique negative id < -1
693 \endcode
694*/
695
696int QAccel::insertItem( const QKeySequence& key, int id )
697{
698 if ( id == -1 )
699 id = get_seq_id();
700 d->aitems.insert( 0, new QAccelItem(key,id) );
701 return id;
702}
703
704/*!
705 Removes the accelerator item with the identifier \a id.
706*/
707
708void QAccel::removeItem( int id )
709{
710 if ( find_id( d->aitems, id) )
711 d->aitems.remove();
712}
713
714
715/*!
716 Removes all accelerator items.
717*/
718
719void QAccel::clear()
720{
721 d->aitems.clear();
722}
723
724
725/*!
726 Returns the key sequence of the accelerator item with identifier
727 \a id, or an invalid key sequence (0) if the id cannot be found.
728*/
729
730QKeySequence QAccel::key( int id )
731{
732 QAccelItem *item = find_id( d->aitems, id);
733 return item ? item->key : QKeySequence( 0 );
734}
735
736
737/*!
738 Returns the identifier of the accelerator item with the key code
739 \a key, or -1 if the item cannot be found.
740*/
741
742int QAccel::findKey( const QKeySequence& key ) const
743{
744 QAccelItem *item = find_key( d->aitems, key );
745 return item ? item->id : -1;
746}
747
748
749/*!
750 Returns TRUE if the accelerator item with the identifier \a id is
751 enabled. Returns FALSE if the item is disabled or cannot be found.
752
753 \sa setItemEnabled(), isEnabled()
754*/
755
756bool QAccel::isItemEnabled( int id ) const
757{
758 QAccelItem *item = find_id( d->aitems, id);
759 return item ? item->enabled : FALSE;
760}
761
762
763/*!
764 Enables the accelerator item with the identifier \a id if \a
765 enable is TRUE, and disables item \a id if \a enable is FALSE.
766
767 To work, an item must be enabled and be in an enabled QAccel.
768
769 \sa isItemEnabled(), isEnabled()
770*/
771
772void QAccel::setItemEnabled( int id, bool enable )
773{
774 QAccelItem *item = find_id( d->aitems, id);
775 if ( item )
776 item->enabled = enable;
777}
778
779
780/*!
781 Connects the accelerator item \a id to the slot \a member of \a
782 receiver.
783
784 \code
785 a->connectItem( 201, mainView, SLOT(quit()) );
786 \endcode
787
788 Of course, you can also send a signal as \a member.
789
790 Normally accelerators are connected to slots which then receive
791 the \c activated(int id) signal with the id of the accelerator
792 item that was activated. If you choose to connect a specific
793 accelerator item using this function, the \c activated() signal is
794 emitted if the associated key sequence is pressed but no \c
795 activated(int id) signal is emitted.
796
797 \sa disconnectItem()
798*/
799
800bool QAccel::connectItem( int id, const QObject *receiver, const char *member )
801{
802 QAccelItem *item = find_id( d->aitems, id);
803 if ( item ) {
804 if ( !item->signal ) {
805 item->signal = new QSignal;
806 Q_CHECK_PTR( item->signal );
807 }
808 return item->signal->connect( receiver, member );
809 }
810 return FALSE;
811}
812
813/*!
814 Disconnects an accelerator item with id \a id from the function
815 called \a member in the \a receiver object.
816
817 \sa connectItem()
818*/
819
820bool QAccel::disconnectItem( int id, const QObject *receiver,
821 const char *member )
822{
823 QAccelItem *item = find_id( d->aitems, id);
824 if ( item && item->signal )
825 return item->signal->disconnect( receiver, member );
826 return FALSE;
827}
828
829void QAccelPrivate::activate( QAccelItem* item )
830{
831#ifndef QT_NO_WHATSTHIS
832 if ( QWhatsThis::inWhatsThisMode() && !ignorewhatsthis ) {
833 QWhatsThis::leaveWhatsThisMode( item->whatsthis );
834 return;
835 }
836#endif
837 if ( item->signal )
838 item->signal->activate();
839 else
840 emit parent->activated( item->id );
841}
842
843void QAccelPrivate::activateAmbiguously( QAccelItem* item )
844{
845 if ( item->signal )
846 item->signal->activate();
847 else
848 emit parent->activatedAmbiguously( item->id );
849}
850
851
852/*!
853 Returns the shortcut key sequence for \a str, or an invalid key
854 sequence (0) if \a str has no shortcut sequence.
855
856 For example, shortcutKey("E&amp;xit") returns ALT+Key_X,
857 shortcutKey("&amp;Quit") returns ALT+Key_Q and shortcutKey("Quit")
858 returns 0. (In code that does not inherit the Qt namespace class,
859 you must write e.g. Qt::ALT+Qt::Key_Q.)
860
861 We provide a \link accelerators.html list of common accelerators
862 \endlink in English. At the time of writing, Microsoft and Open
863 Group do not appear to have issued equivalent recommendations for
864 other languages.
865*/
866
867QKeySequence QAccel::shortcutKey( const QString &str )
868{
869 if(qt_accel_no_shortcuts)
870 return QKeySequence();
871
872 int p = 0;
873 while ( p >= 0 ) {
874 p = str.find( '&', p ) + 1;
875 if ( p <= 0 || p >= (int)str.length() )
876 return 0;
877 if ( str[p] != '&' ) {
878 QChar c = str[p];
879 if ( c.isPrint() ) {
880 c = c.upper();
881 return QKeySequence( c.unicode() + ALT + UNICODE_ACCEL );
882 }
883 }
884 p++;
885 }
886 return QKeySequence();
887}
888
889/*! \obsolete
890
891 Creates an accelerator string for the key \a k.
892 For instance CTRL+Key_O gives "Ctrl+O". The "Ctrl" etc.
893 are translated (using QObject::tr()) in the "QAccel" context.
894
895 The function is superfluous. Cast the QKeySequence \a k to a
896 QString for the same effect.
897*/
898QString QAccel::keyToString( QKeySequence k )
899{
900 return (QString) k;
901}
902
903/*!\obsolete
904
905 Returns an accelerator code for the string \a s. For example
906 "Ctrl+O" gives CTRL+UNICODE_ACCEL+'O'. The strings "Ctrl",
907 "Shift", "Alt" are recognized, as well as their translated
908 equivalents in the "QAccel" context (using QObject::tr()). Returns 0
909 if \a s is not recognized.
910
911 This function is typically used with \link QObject::tr() tr
912 \endlink(), so that accelerator keys can be replaced in
913 translations:
914
915 \code
916 QPopupMenu *file = new QPopupMenu( this );
917 file->insertItem( p1, tr("&Open..."), this, SLOT(open()),
918 QAccel::stringToKey(tr("Ctrl+O", "File|Open")) );
919 \endcode
920
921 Notice the \c "File|Open" translator comment. It is by no means
922 necessary, but it provides some context for the human translator.
923
924
925 The function is superfluous. Construct a QKeySequence from the
926 string \a s for the same effect.
927
928 \sa QObject::tr()
929 \link i18n.html Internationalization with Qt \endlink
930*/
931QKeySequence QAccel::stringToKey( const QString & s )
932{
933 return QKeySequence( s );
934}
935
936
937/*!
938 Sets a What's This help text for the accelerator item \a id to \a
939 text.
940
941 The text will be shown when the application is in What's This mode
942 and the user hits the accelerator key.
943
944 To set What's This help on a menu item (with or without an
945 accelerator key), use QMenuData::setWhatsThis().
946
947 \sa whatsThis(), QWhatsThis::inWhatsThisMode(),
948 QMenuData::setWhatsThis(), QAction::setWhatsThis()
949*/
950void QAccel::setWhatsThis( int id, const QString& text )
951{
952
953 QAccelItem *item = find_id( d->aitems, id);
954 if ( item )
955 item->whatsthis = text;
956}
957
958/*!
959 Returns the What's This help text for the specified item \a id or
960 QString::null if no text has been specified.
961
962 \sa setWhatsThis()
963*/
964QString QAccel::whatsThis( int id ) const
965{
966
967 QAccelItem *item = find_id( d->aitems, id);
968 return item? item->whatsthis : QString::null;
969}
970
971/*!\internal */
972void QAccel::setIgnoreWhatsThis( bool b)
973{
974 d->ignorewhatsthis = b;
975}
976
977/*!\internal */
978bool QAccel::ignoreWhatsThis() const
979{
980 return d->ignorewhatsthis;
981}
982
983
984/*!
985
986\page accelerators.html
987
988\title Standard Accelerator Keys
989
990Applications invariably need to define accelerator keys for actions.
991Qt fully supports accelerators, for example with \l QAccel::shortcutKey().
992
993Here are Microsoft's recommendations for accelerator keys, with
994comments about the Open Group's recommendations where they exist
995and differ. For most commands, the Open Group either has no advice or
996agrees with Microsoft.
997
998The emboldened letter plus Alt is Microsoft's recommended choice, and
999we recommend supporting it. For an Apply button, for example, we
1000recommend QButton::setText( \link QWidget::tr() tr \endlink("&amp;Apply") );
1001
1002If you have conflicting commands (e.g. About and Apply buttons in the
1003same dialog), you must decide for yourself.
1004
1005\list
1006\i <b><u>A</u></b>bout
1007\i Always on <b><u>T</u></b>op
1008\i <b><u>A</u></b>pply
1009\i <b><u>B</u></b>ack
1010\i <b><u>B</u></b>rowse
1011\i <b><u>C</u></b>lose (CDE: Alt+F4; Alt+F4 is "close window" in Windows)
1012\i <b><u>C</u></b>opy (CDE: Ctrl+C, Ctrl+Insert)
1013\i <b><u>C</u></b>opy Here
1014\i Create <b><u>S</u></b>hortcut
1015\i Create <b><u>S</u></b>hortcut Here
1016\i Cu<b><u>t</u></b>
1017\i <b><u>D</u></b>elete
1018\i <b><u>E</u></b>dit
1019\i <b><u>E</u></b>xit (CDE: E<b><u>x</u></b>it)
1020\i <b><u>E</u></b>xplore
1021\i <b><u>F</u></b>ile
1022\i <b><u>F</u></b>ind
1023\i <b><u>H</u></b>elp
1024\i Help <b><u>T</u></b>opics
1025\i <b><u>H</u></b>ide
1026\i <b><u>I</u></b>nsert
1027\i Insert <b><u>O</u></b>bject
1028\i <b><u>L</u></b>ink Here
1029\i Ma<b><u>x</u></b>imize
1030\i Mi<b><u>n</u></b>imize
1031\i <b><u>M</u></b>ove
1032\i <b><u>M</u></b>ove Here
1033\i <b><u>N</u></b>ew
1034\i <b><u>N</u></b>ext
1035\i <b><u>N</u></b>o
1036\i <b><u>O</u></b>pen
1037\i Open <b><u>W</u></b>ith
1038\i Page Set<b><u>u</u></b>p
1039\i <b><u>P</u></b>aste
1040\i Paste <b><u>L</u></b>ink
1041\i Paste <b><u>S</u></b>hortcut
1042\i Paste <b><u>S</u></b>pecial
1043\i <b><u>P</u></b>ause
1044\i <b><u>P</u></b>lay
1045\i <b><u>P</u></b>rint
1046\i <b><u>P</u></b>rint Here
1047\i P<b><u>r</u></b>operties
1048\i <b><u>Q</u></b>uick View
1049\i <b><u>R</u></b>edo (CDE: Ctrl+Y, Shift+Alt+Backspace)
1050\i <b><u>R</u></b>epeat
1051\i <b><u>R</u></b>estore
1052\i <b><u>R</u></b>esume
1053\i <b><u>R</u></b>etry
1054\i <b><u>R</u></b>un
1055\i <b><u>S</u></b>ave
1056\i Save <b><u>A</u></b>s
1057\i Select <b><u>A</u></b>ll
1058\i Se<b><u>n</u></b>d To
1059\i <b><u>S</u></b>how
1060\i <b><u>S</u></b>ize
1061\i S<b><u>p</u></b>lit
1062\i <b><u>S</u></b>top
1063\i <b><u>U</u></b>ndo (CDE: Ctrl+Z or Alt+Backspace)
1064\i <b><u>V</u></b>iew
1065\i <b><u>W</u></b>hat's This?
1066\i <b><u>W</u></b>indow
1067\i <b><u>Y</u></b>es
1068\endlist
1069
1070There are also a lot of other keys and actions (that use other
1071modifier keys than Alt). See the Microsoft and The Open Group
1072documentation for details.
1073
1074The \link http://www.amazon.com/exec/obidos/ASIN/0735605661/trolltech/t
1075Microsoft book \endlink has ISBN 0735605661. The corresponding Open Group
1076book is very hard to find, rather expensive and we cannot recommend
1077it. However, if you really want it, OGPubs@opengroup.org might be able
1078to help. Ask them for ISBN 1859121047.
1079
1080*/
1081
1082/*! \obsolete serves no purpose anymore */
1083void QAccel::repairEventFilter() {}
1084/*! \obsolete serves no purpose anymore */
1085bool QAccel::eventFilter( QObject *, QEvent * ) { return FALSE; }
1086#endif // QT_NO_ACCEL
Note: See TracBrowser for help on using the repository browser.