|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt3Support module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "q3accel.h" |
|
43 |
|
44 #include "q3signal.h" |
|
45 #include "qapplication.h" |
|
46 #include "qwidget.h" |
|
47 #include "q3ptrlist.h" |
|
48 #include "qwhatsthis.h" |
|
49 #include "qpointer.h" |
|
50 #include "qstatusbar.h" |
|
51 #include "qdockwidget.h" |
|
52 #include "qevent.h" |
|
53 #include "qkeysequence.h" |
|
54 #include "private/qapplication_p.h" |
|
55 |
|
56 QT_BEGIN_NAMESPACE |
|
57 |
|
58 using namespace Qt; |
|
59 |
|
60 /*! |
|
61 \class Q3Accel |
|
62 \brief The Q3Accel class handles keyboard accelerator and shortcut keys. |
|
63 |
|
64 \compat |
|
65 |
|
66 A keyboard accelerator triggers an action when a certain key |
|
67 combination is pressed. The accelerator handles all keyboard |
|
68 activity for all the children of one top-level widget, so it is |
|
69 not affected by the keyboard focus. |
|
70 |
|
71 In most cases, you will not need to use this class directly. Use |
|
72 the QAction class to create actions with accelerators that can be |
|
73 used in both menus and toolbars. If you're only interested in |
|
74 menus use Q3MenuData::insertItem() or Q3MenuData::setAccel() to make |
|
75 accelerators for operations that are also available on menus. Many |
|
76 widgets automatically generate accelerators, such as QAbstractButton, |
|
77 QGroupBox, QLabel (with QLabel::setBuddy()), QMenuBar, and QTabBar. |
|
78 Example: |
|
79 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 0 |
|
80 |
|
81 A Q3Accel contains a list of accelerator items that can be |
|
82 manipulated using insertItem(), removeItem(), clear(), key() and |
|
83 findKey(). |
|
84 |
|
85 Each accelerator item consists of an identifier and a \l |
|
86 QKeySequence. A single key sequence consists of a keyboard code |
|
87 combined with modifiers (Qt::SHIFT, Qt::CTRL, Qt::ALT, or |
|
88 Qt::UNICODE_ACCEL). For example, Qt::CTRL + Qt::Key_P could be a shortcut |
|
89 for printing a document. As an alternative, use Qt::UNICODE_ACCEL with the |
|
90 unicode code point of the character. For example, Qt::UNICODE_ACCEL |
|
91 + 'A' gives the same accelerator as Qt::Key_A. |
|
92 |
|
93 When an accelerator key is pressed, the accelerator sends out the |
|
94 signal activated() with a number that identifies this particular |
|
95 accelerator item. Accelerator items can also be individually |
|
96 connected, so that two different keys will activate two different |
|
97 slots (see connectItem() and disconnectItem()). |
|
98 |
|
99 The activated() signal is \e not emitted when two or more |
|
100 accelerators match the same key. Instead, the first matching |
|
101 accelerator sends out the activatedAmbiguously() signal. By |
|
102 pressing the key multiple times, users can navigate between all |
|
103 matching accelerators. Some standard controls like QPushButton and |
|
104 QCheckBox connect the activatedAmbiguously() signal to the |
|
105 harmless setFocus() slot, whereas activated() is connected to a |
|
106 slot invoking the button's action. Most controls, like QLabel and |
|
107 QTabBar, treat activated() and activatedAmbiguously() as |
|
108 equivalent. |
|
109 |
|
110 Use setEnabled() to enable or disable all the items in an |
|
111 accelerator, or setItemEnabled() to enable or disable individual |
|
112 items. An item is active only when both the Q3Accel and the item |
|
113 itself are enabled. |
|
114 |
|
115 The function setWhatsThis() specifies a help text that appears |
|
116 when the user presses an accelerator key in What's This mode. |
|
117 |
|
118 The accelerator will be deleted when \e parent is deleted, |
|
119 and will consume relevant key events until then. |
|
120 |
|
121 Please note that the accelerator |
|
122 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 1 |
|
123 can be triggered with both the 'M' key, and with Shift+M, |
|
124 unless a second accelerator is defined for the Shift+M |
|
125 combination. |
|
126 |
|
127 |
|
128 Example: |
|
129 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 2 |
|
130 |
|
131 \sa QKeyEvent QWidget::keyPressEvent() |
|
132 QAbstractButton::setAccel() QLabel::setBuddy() QKeySequence |
|
133 */ |
|
134 |
|
135 |
|
136 struct Q3AccelItem { // internal accelerator item |
|
137 Q3AccelItem(const QKeySequence &k, int i) |
|
138 { key=k; id=i; enabled=true; signal=0; } |
|
139 ~Q3AccelItem() { delete signal; } |
|
140 int id; |
|
141 QKeySequence key; |
|
142 bool enabled; |
|
143 Q3Signal *signal; |
|
144 QString whatsthis; |
|
145 }; |
|
146 |
|
147 |
|
148 typedef Q3PtrList<Q3AccelItem> Q3AccelList; // internal accelerator list |
|
149 |
|
150 class Q3AccelPrivate { |
|
151 public: |
|
152 Q3AccelPrivate(Q3Accel* p); |
|
153 ~Q3AccelPrivate(); |
|
154 Q3AccelList aitems; |
|
155 bool enabled; |
|
156 QPointer<QWidget> watch; |
|
157 bool ignorewhatsthis; |
|
158 Q3Accel* parent; |
|
159 |
|
160 void activate(Q3AccelItem* item); |
|
161 void activateAmbiguously(Q3AccelItem* item); |
|
162 }; |
|
163 |
|
164 class Q3AccelManager { |
|
165 public: |
|
166 static Q3AccelManager* self() { return self_ptr ? self_ptr : new Q3AccelManager; } |
|
167 void registerAccel(Q3AccelPrivate* a) { accels.append(a); } |
|
168 void unregisterAccel(Q3AccelPrivate* a) { accels.removeRef(a); if (accels.isEmpty()) delete this; } |
|
169 bool tryAccelEvent(QWidget* w, QKeyEvent* e); |
|
170 bool dispatchAccelEvent(QWidget* w, QKeyEvent* e); |
|
171 bool tryComposeUnicode(QWidget* w, QKeyEvent* e); |
|
172 |
|
173 private: |
|
174 Q3AccelManager() |
|
175 : currentState(QKeySequence::NoMatch), clash(-1), metaComposeUnicode(false),composedUnicode(0) |
|
176 { setFuncPtr(); self_ptr = this; } |
|
177 ~Q3AccelManager() { self_ptr = 0; } |
|
178 void setFuncPtr(); |
|
179 |
|
180 bool correctSubWindow(QWidget *w, Q3AccelPrivate* d); |
|
181 QKeySequence::SequenceMatch match(QKeyEvent* e, Q3AccelItem* item, QKeySequence& temp); |
|
182 int translateModifiers(ButtonState state); |
|
183 |
|
184 Q3PtrList<Q3AccelPrivate> accels; |
|
185 static Q3AccelManager* self_ptr; |
|
186 QKeySequence::SequenceMatch currentState; |
|
187 QKeySequence intermediate; |
|
188 int clash; |
|
189 bool metaComposeUnicode; |
|
190 int composedUnicode; |
|
191 }; |
|
192 Q3AccelManager* Q3AccelManager::self_ptr = 0; |
|
193 |
|
194 bool Q_COMPAT_EXPORT qt_tryAccelEvent(QWidget* w, QKeyEvent* e){ |
|
195 return Q3AccelManager::self()->tryAccelEvent(w, e); |
|
196 } |
|
197 |
|
198 bool Q_COMPAT_EXPORT qt_dispatchAccelEvent(QWidget* w, QKeyEvent* e){ |
|
199 return Q3AccelManager::self()->dispatchAccelEvent(w, e); |
|
200 } |
|
201 |
|
202 bool Q_COMPAT_EXPORT qt_tryComposeUnicode(QWidget* w, QKeyEvent* e){ |
|
203 return Q3AccelManager::self()->tryComposeUnicode(w, e); |
|
204 } |
|
205 |
|
206 void Q3AccelManager::setFuncPtr() { |
|
207 if (qApp->d_func()->qt_compat_used) |
|
208 return; |
|
209 QApplicationPrivate *data = static_cast<QApplicationPrivate*>(qApp->d_ptr.data()); |
|
210 data->qt_tryAccelEvent = qt_tryAccelEvent; |
|
211 data->qt_tryComposeUnicode = qt_tryComposeUnicode; |
|
212 data->qt_dispatchAccelEvent = qt_dispatchAccelEvent; |
|
213 data->qt_compat_used = true; |
|
214 } |
|
215 |
|
216 #ifdef Q_WS_MAC |
|
217 static bool qt_accel_no_shortcuts = true; |
|
218 #else |
|
219 static bool qt_accel_no_shortcuts = false; |
|
220 #endif |
|
221 void Q_COMPAT_EXPORT qt_set_accel_auto_shortcuts(bool b) { qt_accel_no_shortcuts = b; } |
|
222 |
|
223 /* |
|
224 \internal |
|
225 Returns true if the accel is in the current subwindow, else false. |
|
226 */ |
|
227 bool Q3AccelManager::correctSubWindow(QWidget* w, Q3AccelPrivate* d) { |
|
228 #if !defined (Q_OS_MACX) |
|
229 if (!d->watch || !d->watch->isVisible() || !d->watch->isEnabled()) |
|
230 #else |
|
231 if (!d->watch || (!d->watch->isVisible() && !d->watch->inherits("QMenuBar")) || !d->watch->isEnabled()) |
|
232 #endif |
|
233 return false; |
|
234 QWidget* tlw = w->window(); |
|
235 QWidget* wtlw = d->watch->window(); |
|
236 |
|
237 /* if we live in a floating dock window, keep our parent's |
|
238 * accelerators working */ |
|
239 #ifndef QT_NO_MAINWINDOW |
|
240 if ((tlw->windowType() == Qt::Dialog) && tlw->parentWidget() && qobject_cast<QDockWidget*>(tlw)) |
|
241 return tlw->parentWidget()->window() == wtlw; |
|
242 |
|
243 if (wtlw != tlw) |
|
244 return false; |
|
245 #endif |
|
246 /* if we live in a MDI subwindow, ignore the event if we are |
|
247 not the active document window */ |
|
248 QWidget* sw = d->watch; |
|
249 while (sw && sw->windowType() != Qt::SubWindow) |
|
250 sw = sw->parentWidget(true); |
|
251 if (sw) { // we are in a subwindow indeed |
|
252 QWidget* fw = w; |
|
253 while (fw && fw != sw) |
|
254 fw = fw->parentWidget(true); |
|
255 if (fw != sw) // focus widget not in our subwindow |
|
256 return false; |
|
257 } |
|
258 return true; |
|
259 } |
|
260 |
|
261 inline int Q3AccelManager::translateModifiers(ButtonState state) |
|
262 { |
|
263 int result = 0; |
|
264 if (state & ShiftButton) |
|
265 result |= SHIFT; |
|
266 if (state & ControlButton) |
|
267 result |= CTRL; |
|
268 if (state & MetaButton) |
|
269 result |= META; |
|
270 if (state & AltButton) |
|
271 result |= ALT; |
|
272 return result; |
|
273 } |
|
274 |
|
275 /* |
|
276 \internal |
|
277 Matches the current intermediate key sequence + the latest |
|
278 keyevent, with and AccelItem. Returns Identical, |
|
279 PartialMatch or NoMatch, and fills \a temp with the |
|
280 resulting key sequence. |
|
281 */ |
|
282 QKeySequence::SequenceMatch Q3AccelManager::match(QKeyEvent *e, Q3AccelItem* item, QKeySequence& temp) |
|
283 { |
|
284 QKeySequence::SequenceMatch result = QKeySequence::NoMatch; |
|
285 int index = intermediate.count(); |
|
286 temp = intermediate; |
|
287 |
|
288 int modifier = translateModifiers(e->state()); |
|
289 |
|
290 if (e->key() && e->key() != Key_unknown) { |
|
291 int key = e->key() | modifier; |
|
292 if (e->key() == Key_BackTab) { |
|
293 /* |
|
294 In QApplication, we map shift+tab to shift+backtab. |
|
295 This code here reverts the mapping in a way that keeps |
|
296 backtab and shift+tab accelerators working, in that |
|
297 order, meaning backtab has priority.*/ |
|
298 key &= ~SHIFT; |
|
299 |
|
300 temp.setKey(key, index); |
|
301 if (QKeySequence::NoMatch != (result = temp.matches(item->key))) |
|
302 return result; |
|
303 if (e->state() & ShiftButton) |
|
304 key |= SHIFT; |
|
305 key = Key_Tab | (key & MODIFIER_MASK); |
|
306 temp.setKey(key, index); |
|
307 if (QKeySequence::NoMatch != (result = temp.matches(item->key))) |
|
308 return result; |
|
309 } else { |
|
310 temp.setKey(key, index); |
|
311 if (QKeySequence::NoMatch != (result = temp.matches(item->key))) |
|
312 return result; |
|
313 } |
|
314 |
|
315 if (key == Key_BackTab) { |
|
316 if (e->state() & ShiftButton) |
|
317 key |= SHIFT; |
|
318 temp.setKey(key, index); |
|
319 if (QKeySequence::NoMatch != (result = temp.matches(item->key))) |
|
320 return result; |
|
321 } |
|
322 } |
|
323 if (!e->text().isEmpty()) { |
|
324 temp.setKey((int)e->text()[0].unicode() | UNICODE_ACCEL | modifier, index); |
|
325 result = temp.matches(item->key); |
|
326 } |
|
327 return result; |
|
328 } |
|
329 |
|
330 bool Q3AccelManager::tryAccelEvent(QWidget* w, QKeyEvent* e) |
|
331 { |
|
332 if (QKeySequence::NoMatch == currentState) { |
|
333 e->t = QEvent::AccelOverride; |
|
334 e->ignore(); |
|
335 QApplication::sendSpontaneousEvent(w, e); |
|
336 if (e->isAccepted()) |
|
337 return false; |
|
338 } |
|
339 e->t = QEvent::Accel; |
|
340 e->ignore(); |
|
341 QApplication::sendSpontaneousEvent(w, e); |
|
342 return e->isAccepted(); |
|
343 } |
|
344 |
|
345 bool Q3AccelManager::tryComposeUnicode(QWidget* w, QKeyEvent* e) |
|
346 { |
|
347 if (metaComposeUnicode) { |
|
348 int value = e->key() - Key_0; |
|
349 // Ignore acceloverrides so we don't trigger |
|
350 // accels on keypad when Meta compose is on |
|
351 if ((e->type() == QEvent::AccelOverride) && |
|
352 (e->state() == Qt::Keypad + Qt::MetaButton)) { |
|
353 e->accept(); |
|
354 // Meta compose start/continue |
|
355 } else if ((e->type() == QEvent::KeyPress) && |
|
356 (e->state() == Qt::Keypad + Qt::MetaButton)) { |
|
357 if (value >= 0 && value <= 9) { |
|
358 composedUnicode *= 10; |
|
359 composedUnicode += value; |
|
360 return true; |
|
361 } else { |
|
362 // Composing interrupted, dispatch! |
|
363 if (composedUnicode) { |
|
364 QChar ch(composedUnicode); |
|
365 QString s(ch); |
|
366 QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s); |
|
367 QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s); |
|
368 QApplication::sendEvent(w, &kep); |
|
369 QApplication::sendEvent(w, &ker); |
|
370 } |
|
371 composedUnicode = 0; |
|
372 return true; |
|
373 } |
|
374 // Meta compose end, dispatch |
|
375 } else if ((e->type() == QEvent::KeyRelease) && |
|
376 (e->key() == Key_Meta) && |
|
377 (composedUnicode != 0)) { |
|
378 if ((composedUnicode > 0) && |
|
379 (composedUnicode < 0xFFFE)) { |
|
380 QChar ch(composedUnicode); |
|
381 QString s(ch); |
|
382 QKeyEvent kep(QEvent::KeyPress, 0, ch.row() ? 0 : ch.cell(), 0, s); |
|
383 QKeyEvent ker(QEvent::KeyRelease, 0, ch.row() ? 0 : ch.cell(), 0, s); |
|
384 QApplication::sendEvent(w, &kep); |
|
385 QApplication::sendEvent(w, &ker); |
|
386 } |
|
387 composedUnicode = 0; |
|
388 return true; |
|
389 } |
|
390 } |
|
391 return false; |
|
392 } |
|
393 |
|
394 /* |
|
395 \internal |
|
396 Checks for possible accelerators, if no widget |
|
397 ate the keypres, or we are in the middle of a |
|
398 partial key sequence. |
|
399 */ |
|
400 bool Q3AccelManager::dispatchAccelEvent(QWidget* w, QKeyEvent* e) |
|
401 { |
|
402 #ifndef QT_NO_STATUSBAR |
|
403 // Needs to be declared and used here because of "goto doclash" |
|
404 QStatusBar* mainStatusBar = 0; |
|
405 #endif |
|
406 |
|
407 // Modifiers can NOT be accelerators... |
|
408 if (e->key() >= Key_Shift && |
|
409 e->key() <= Key_Alt) |
|
410 return false; |
|
411 |
|
412 QKeySequence::SequenceMatch result = QKeySequence::NoMatch; |
|
413 QKeySequence tocheck, partial; |
|
414 Q3AccelPrivate* accel = 0; |
|
415 Q3AccelItem* item = 0; |
|
416 Q3AccelPrivate* firstaccel = 0; |
|
417 Q3AccelItem* firstitem = 0; |
|
418 Q3AccelPrivate* lastaccel = 0; |
|
419 Q3AccelItem* lastitem = 0; |
|
420 |
|
421 QKeyEvent pe = *e; |
|
422 int n = -1; |
|
423 int hasShift = (e->state()&Qt::ShiftButton)?1:0; |
|
424 bool identicalDisabled = false; |
|
425 bool matchFound = false; |
|
426 do { |
|
427 accel = accels.first(); |
|
428 matchFound = false; |
|
429 while (accel) { |
|
430 if (correctSubWindow(w, accel)) { |
|
431 if (accel->enabled) { |
|
432 item = accel->aitems.last(); |
|
433 while(item) { |
|
434 if (QKeySequence::Identical == (result = match(&pe, item, tocheck))) { |
|
435 if (item->enabled) { |
|
436 if (!firstaccel) { |
|
437 firstaccel = accel; |
|
438 firstitem = item; |
|
439 } |
|
440 lastaccel = accel; |
|
441 lastitem = item; |
|
442 n++; |
|
443 matchFound = true; |
|
444 if (n > QMAX(clash,0)) |
|
445 goto doclash; |
|
446 } else { |
|
447 identicalDisabled = true; |
|
448 } |
|
449 } |
|
450 if (item->enabled && QKeySequence::PartialMatch == result) { |
|
451 partial = tocheck; |
|
452 matchFound = true; |
|
453 } |
|
454 item = accel->aitems.prev(); |
|
455 } |
|
456 } else { |
|
457 item = accel->aitems.last(); |
|
458 while(item) { |
|
459 if (QKeySequence::Identical == match(&pe, item, tocheck)) |
|
460 identicalDisabled = true; |
|
461 item = accel->aitems.prev(); |
|
462 } |
|
463 } |
|
464 } |
|
465 accel = accels.next(); |
|
466 } |
|
467 pe = QKeyEvent(QEvent::Accel, pe.key(), pe.ascii(), pe.state()&~Qt::ShiftButton, pe.text()); |
|
468 } while (hasShift-- && !matchFound && !identicalDisabled); |
|
469 |
|
470 #ifndef QT_NO_STATUSBAR |
|
471 mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar"); |
|
472 #endif |
|
473 if (n < 0) { // no match found |
|
474 currentState = partial.count() ? QKeySequence::PartialMatch : QKeySequence::NoMatch; |
|
475 #ifndef QT_NO_STATUSBAR |
|
476 // Only display message if we are, or were, in a partial match |
|
477 if (mainStatusBar && (QKeySequence::PartialMatch == currentState || intermediate.count())) { |
|
478 if (currentState == QKeySequence::PartialMatch) { |
|
479 mainStatusBar->showMessage((QString)partial + QLatin1String(", ...")); |
|
480 } else if (!identicalDisabled) { |
|
481 QString message = Q3Accel::tr("%1, %2 not defined"). |
|
482 arg((QString)intermediate). |
|
483 arg(QKeySequence::encodeString(e->key() | translateModifiers(e->state()))); |
|
484 mainStatusBar->showMessage(message, 2000); |
|
485 // Since we're a NoMatch, reset the clash count |
|
486 clash = -1; |
|
487 } else { |
|
488 mainStatusBar->clearMessage(); |
|
489 } |
|
490 } |
|
491 #endif |
|
492 |
|
493 bool eatKey = (QKeySequence::PartialMatch == currentState || intermediate.count()); |
|
494 intermediate = partial; |
|
495 if (eatKey) |
|
496 e->accept(); |
|
497 return eatKey; |
|
498 } else if (n == 0) { // found exactly one match |
|
499 clash = -1; // reset |
|
500 #ifndef QT_NO_STATUSBAR |
|
501 if (currentState == QKeySequence::PartialMatch && mainStatusBar) |
|
502 mainStatusBar->clearMessage(); |
|
503 #endif |
|
504 currentState = QKeySequence::NoMatch; // Free sequence keylock |
|
505 intermediate = QKeySequence(); |
|
506 lastaccel->activate(lastitem); |
|
507 e->accept(); |
|
508 return true; |
|
509 } |
|
510 |
|
511 doclash: // found more than one match |
|
512 #ifndef QT_NO_STATUSBAR |
|
513 if (!mainStatusBar) // if "goto doclash", we need to get status bar again. |
|
514 mainStatusBar = (QStatusBar*) w->window()->child(0, "QStatusBar"); |
|
515 #endif |
|
516 |
|
517 QString message = Q3Accel::tr("Ambiguous %1 not handled").arg((QString)tocheck); |
|
518 if (clash >= 0 && n > clash) { // pick next match |
|
519 intermediate = QKeySequence(); |
|
520 currentState = QKeySequence::NoMatch; // Free sequence keylock |
|
521 clash++; |
|
522 #ifndef QT_NO_STATUSBAR |
|
523 if (mainStatusBar && |
|
524 !lastitem->signal && |
|
525 !(lastaccel->parent->receivers(SIGNAL(activatedAmbiguously(int))))) |
|
526 mainStatusBar->showMessage(message, 2000); |
|
527 #endif |
|
528 lastaccel->activateAmbiguously(lastitem); |
|
529 } else { // start (or wrap) with the first matching |
|
530 intermediate = QKeySequence(); |
|
531 currentState = QKeySequence::NoMatch; // Free sequence keylock |
|
532 clash = 0; |
|
533 #ifndef QT_NO_STATUSBAR |
|
534 if (mainStatusBar && |
|
535 !firstitem->signal && |
|
536 !(firstaccel->parent->receivers(SIGNAL(activatedAmbiguously(int))))) |
|
537 mainStatusBar->showMessage(message, 2000); |
|
538 #endif |
|
539 firstaccel->activateAmbiguously(firstitem); |
|
540 } |
|
541 e->accept(); |
|
542 return true; |
|
543 } |
|
544 |
|
545 Q3AccelPrivate::Q3AccelPrivate(Q3Accel* p) |
|
546 : parent(p) |
|
547 { |
|
548 Q3AccelManager::self()->registerAccel(this); |
|
549 aitems.setAutoDelete(true); |
|
550 ignorewhatsthis = false; |
|
551 } |
|
552 |
|
553 Q3AccelPrivate::~Q3AccelPrivate() |
|
554 { |
|
555 Q3AccelManager::self()->unregisterAccel(this); |
|
556 } |
|
557 |
|
558 static Q3AccelItem *find_id(Q3AccelList &list, int id) |
|
559 { |
|
560 register Q3AccelItem *item = list.first(); |
|
561 while (item && item->id != id) |
|
562 item = list.next(); |
|
563 return item; |
|
564 } |
|
565 |
|
566 static Q3AccelItem *find_key(Q3AccelList &list, const QKeySequence &key) |
|
567 { |
|
568 register Q3AccelItem *item = list.first(); |
|
569 while (item && !(item->key == key)) |
|
570 item = list.next(); |
|
571 return item; |
|
572 } |
|
573 |
|
574 /*! |
|
575 Constructs a Q3Accel object called \a name, with parent \a parent. |
|
576 The accelerator operates on \a parent. |
|
577 */ |
|
578 |
|
579 Q3Accel::Q3Accel(QWidget *parent, const char *name) |
|
580 : QObject(parent, name) |
|
581 { |
|
582 d = new Q3AccelPrivate(this); |
|
583 d->enabled = true; |
|
584 d->watch = parent; |
|
585 #if defined(QT_CHECK_NULL) |
|
586 if (!d->watch) |
|
587 qWarning("Q3Accel: An accelerator must have a parent or a watch widget"); |
|
588 #endif |
|
589 } |
|
590 |
|
591 /*! |
|
592 Constructs a Q3Accel object called \a name, that operates on \a |
|
593 watch, and is a child of \a parent. |
|
594 |
|
595 This constructor is not needed for normal application programming. |
|
596 */ |
|
597 Q3Accel::Q3Accel(QWidget* watch, QObject *parent, const char *name) |
|
598 : QObject(parent, name) |
|
599 { |
|
600 d = new Q3AccelPrivate(this); |
|
601 d->enabled = true; |
|
602 d->watch = watch; |
|
603 #if defined(QT_CHECK_NULL) |
|
604 if (!d->watch) |
|
605 qWarning("Q3Accel: An accelerator must have a parent or a watch widget"); |
|
606 #endif |
|
607 } |
|
608 |
|
609 /*! |
|
610 Destroys the accelerator object and frees all allocated resources. |
|
611 */ |
|
612 |
|
613 Q3Accel::~Q3Accel() |
|
614 { |
|
615 delete d; |
|
616 } |
|
617 |
|
618 |
|
619 /*! |
|
620 \fn void Q3Accel::activated(int id) |
|
621 |
|
622 This signal is emitted when the user types the shortcut's key |
|
623 sequence. \a id is a number that identifies this particular |
|
624 accelerator item. |
|
625 |
|
626 \sa activatedAmbiguously() |
|
627 */ |
|
628 |
|
629 /*! |
|
630 \fn void Q3Accel::activatedAmbiguously(int id) |
|
631 |
|
632 This signal is emitted when the user types a shortcut key |
|
633 sequence that is ambiguous. For example, if one key sequence is a |
|
634 "prefix" for another and the user types these keys it isn't clear |
|
635 if they want the shorter key sequence, or if they're about to |
|
636 type more to complete the longer key sequence. \a id is a number |
|
637 that identifies this particular accelerator item. |
|
638 |
|
639 \sa activated() |
|
640 */ |
|
641 |
|
642 /*! |
|
643 Returns true if the accelerator is enabled; otherwise returns |
|
644 false. |
|
645 |
|
646 \sa setEnabled(), isItemEnabled() |
|
647 */ |
|
648 |
|
649 bool Q3Accel::isEnabled() const |
|
650 { |
|
651 return d->enabled; |
|
652 } |
|
653 |
|
654 |
|
655 /*! |
|
656 Enables the accelerator if \a enable is true, or disables it if \a |
|
657 enable is false. |
|
658 |
|
659 Individual keys can also be enabled or disabled using |
|
660 setItemEnabled(). To work, a key must be an enabled item in an |
|
661 enabled Q3Accel. |
|
662 |
|
663 \sa isEnabled(), setItemEnabled() |
|
664 */ |
|
665 |
|
666 void Q3Accel::setEnabled(bool enable) |
|
667 { |
|
668 d->enabled = enable; |
|
669 } |
|
670 |
|
671 |
|
672 /*! |
|
673 Returns the number of accelerator items in this accelerator. |
|
674 */ |
|
675 |
|
676 uint Q3Accel::count() const |
|
677 { |
|
678 return d->aitems.count(); |
|
679 } |
|
680 |
|
681 |
|
682 static int get_seq_id() |
|
683 { |
|
684 static int seq_no = -2; // -1 is used as return value in findKey() |
|
685 return seq_no--; |
|
686 } |
|
687 |
|
688 /*! |
|
689 Inserts an accelerator item and returns the item's identifier. |
|
690 |
|
691 \a key is a key code and an optional combination of SHIFT, CTRL |
|
692 and ALT. \a id is the accelerator item id. |
|
693 |
|
694 If \a id is negative, then the item will be assigned a unique |
|
695 negative identifier less than -1. |
|
696 |
|
697 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 3 |
|
698 */ |
|
699 |
|
700 int Q3Accel::insertItem(const QKeySequence& key, int id) |
|
701 { |
|
702 if (id == -1) |
|
703 id = get_seq_id(); |
|
704 d->aitems.insert(0, new Q3AccelItem(key,id)); |
|
705 return id; |
|
706 } |
|
707 |
|
708 /*! |
|
709 Removes the accelerator item with the identifier \a id. |
|
710 */ |
|
711 |
|
712 void Q3Accel::removeItem(int id) |
|
713 { |
|
714 if (find_id(d->aitems, id)) |
|
715 d->aitems.remove(); |
|
716 } |
|
717 |
|
718 |
|
719 /*! |
|
720 Removes all accelerator items. |
|
721 */ |
|
722 |
|
723 void Q3Accel::clear() |
|
724 { |
|
725 d->aitems.clear(); |
|
726 } |
|
727 |
|
728 |
|
729 /*! |
|
730 Returns the key sequence of the accelerator item with identifier |
|
731 \a id, or an invalid key sequence (0) if the id cannot be found. |
|
732 */ |
|
733 |
|
734 QKeySequence Q3Accel::key(int id) |
|
735 { |
|
736 Q3AccelItem *item = find_id(d->aitems, id); |
|
737 return item ? item->key : QKeySequence(0); |
|
738 } |
|
739 |
|
740 |
|
741 /*! |
|
742 Returns the identifier of the accelerator item with the key code |
|
743 \a key, or -1 if the item cannot be found. |
|
744 */ |
|
745 |
|
746 int Q3Accel::findKey(const QKeySequence& key) const |
|
747 { |
|
748 Q3AccelItem *item = find_key(d->aitems, key); |
|
749 return item ? item->id : -1; |
|
750 } |
|
751 |
|
752 |
|
753 /*! |
|
754 Returns true if the accelerator item with the identifier \a id is |
|
755 enabled. Returns false if the item is disabled or cannot be found. |
|
756 |
|
757 \sa setItemEnabled(), isEnabled() |
|
758 */ |
|
759 |
|
760 bool Q3Accel::isItemEnabled(int id) const |
|
761 { |
|
762 Q3AccelItem *item = find_id(d->aitems, id); |
|
763 return item ? item->enabled : false; |
|
764 } |
|
765 |
|
766 |
|
767 /*! |
|
768 Enables the accelerator item with the identifier \a id if \a |
|
769 enable is true, and disables item \a id if \a enable is false. |
|
770 |
|
771 To work, an item must be enabled and be in an enabled Q3Accel. |
|
772 |
|
773 \sa isItemEnabled(), isEnabled() |
|
774 */ |
|
775 |
|
776 void Q3Accel::setItemEnabled(int id, bool enable) |
|
777 { |
|
778 Q3AccelItem *item = find_id(d->aitems, id); |
|
779 if (item) |
|
780 item->enabled = enable; |
|
781 } |
|
782 |
|
783 |
|
784 /*! |
|
785 Connects the accelerator item \a id to the slot \a member of \a |
|
786 receiver. Returns true if the connection is successful. |
|
787 |
|
788 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 4 |
|
789 |
|
790 Of course, you can also send a signal as \a member. |
|
791 |
|
792 Normally accelerators are connected to slots which then receive |
|
793 the \c activated(int id) signal with the id of the accelerator |
|
794 item that was activated. If you choose to connect a specific |
|
795 accelerator item using this function, the \c activated() signal is |
|
796 emitted if the associated key sequence is pressed but no \c |
|
797 activated(int id) signal is emitted. |
|
798 |
|
799 \sa disconnectItem(), QObject::connect() |
|
800 */ |
|
801 |
|
802 bool Q3Accel::connectItem(int id, const QObject *receiver, const char *member) |
|
803 { |
|
804 Q3AccelItem *item = find_id(d->aitems, id); |
|
805 if (item) { |
|
806 if (!item->signal) { |
|
807 item->signal = new Q3Signal; |
|
808 Q_CHECK_PTR(item->signal); |
|
809 } |
|
810 return item->signal->connect(receiver, member); |
|
811 } |
|
812 return false; |
|
813 } |
|
814 |
|
815 /*! |
|
816 Disconnects the accelerator item identified by \a id from |
|
817 the function called \a member in the \a receiver object. |
|
818 Returns true if the connection existed and the disconnect |
|
819 was successful. |
|
820 |
|
821 \sa connectItem(), QObject::disconnect() |
|
822 */ |
|
823 |
|
824 bool Q3Accel::disconnectItem(int id, const QObject *receiver, |
|
825 const char *member) |
|
826 { |
|
827 Q3AccelItem *item = find_id(d->aitems, id); |
|
828 if (item && item->signal) |
|
829 return item->signal->disconnect(receiver, member); |
|
830 return false; |
|
831 } |
|
832 |
|
833 void Q3AccelPrivate::activate(Q3AccelItem* item) |
|
834 { |
|
835 #ifndef QT_NO_WHATSTHIS |
|
836 if (QWhatsThis::inWhatsThisMode() && !ignorewhatsthis) { |
|
837 QWhatsThis::showText(QCursor::pos(), item->whatsthis); |
|
838 return; |
|
839 } |
|
840 #endif |
|
841 if (item->signal) |
|
842 item->signal->activate(); |
|
843 else |
|
844 emit parent->activated(item->id); |
|
845 } |
|
846 |
|
847 void Q3AccelPrivate::activateAmbiguously(Q3AccelItem* item) |
|
848 { |
|
849 if (item->signal) |
|
850 item->signal->activate(); |
|
851 else |
|
852 emit parent->activatedAmbiguously(item->id); |
|
853 } |
|
854 |
|
855 |
|
856 /*! |
|
857 Returns the shortcut key sequence for \a str, or an invalid key |
|
858 sequence (0) if \a str has no shortcut sequence. |
|
859 |
|
860 For example, shortcutKey("E&xit") returns QKeySequence(Qt::ALT + |
|
861 Qt::Key_X), shortcutKey("&Quit") returns QKeySequence(Qt::ALT + |
|
862 Qt::Key_Q), and shortcutKey("Quit") returns QKeySequence(). |
|
863 */ |
|
864 |
|
865 QKeySequence Q3Accel::shortcutKey(const QString &str) |
|
866 { |
|
867 if(qt_accel_no_shortcuts) |
|
868 return QKeySequence(); |
|
869 |
|
870 int p = 0; |
|
871 while (p >= 0) { |
|
872 p = str.find(QLatin1Char('&'), p) + 1; |
|
873 if (p <= 0 || p >= (int)str.length()) |
|
874 return 0; |
|
875 if (str[p] != QLatin1Char('&')) { |
|
876 QChar c = str[p]; |
|
877 if (c.isPrint()) { |
|
878 char ltr = c.upper().latin1(); |
|
879 if (ltr >= (char)Key_A && ltr <= (char)Key_Z) |
|
880 c = QLatin1Char(ltr); |
|
881 else |
|
882 c = c.lower(); |
|
883 return QKeySequence(c.unicode() + ALT + UNICODE_ACCEL); |
|
884 } |
|
885 } |
|
886 p++; |
|
887 } |
|
888 return QKeySequence(); |
|
889 } |
|
890 |
|
891 /*! \obsolete |
|
892 |
|
893 Creates an accelerator string for the key \a k. |
|
894 For instance CTRL+Key_O gives "Ctrl+O". The "Ctrl" etc. |
|
895 are translated (using QObject::tr()) in the "Q3Accel" context. |
|
896 |
|
897 The function is superfluous. Cast the QKeySequence \a k to a |
|
898 QString for the same effect. |
|
899 */ |
|
900 QString Q3Accel::keyToString(QKeySequence k) |
|
901 { |
|
902 return (QString) k; |
|
903 } |
|
904 |
|
905 /*!\obsolete |
|
906 |
|
907 Returns an accelerator code for the string \a s. For example |
|
908 "Ctrl+O" gives CTRL+UNICODE_ACCEL+'O'. The strings "Ctrl", |
|
909 "Shift", "Alt" are recognized, as well as their translated |
|
910 equivalents in the "Q3Accel" context (using QObject::tr()). Returns 0 |
|
911 if \a s is not recognized. |
|
912 |
|
913 This function is typically used with \link QObject::tr() tr |
|
914 \endlink(), so that accelerator keys can be replaced in |
|
915 translations: |
|
916 |
|
917 \snippet doc/src/snippets/code/src_qt3support_other_q3accel.cpp 5 |
|
918 |
|
919 Notice the "File|Open" translator comment. It is by no means |
|
920 necessary, but it provides some context for the human translator. |
|
921 |
|
922 The function is superfluous. Construct a QKeySequence from the |
|
923 string \a s for the same effect. |
|
924 |
|
925 \sa QObject::tr(), {Internationalization with Qt} |
|
926 */ |
|
927 QKeySequence Q3Accel::stringToKey(const QString & s) |
|
928 { |
|
929 return QKeySequence(s); |
|
930 } |
|
931 |
|
932 |
|
933 /*! |
|
934 Sets a What's This help text for the accelerator item \a id to \a |
|
935 text. |
|
936 |
|
937 The text will be shown when the application is in What's This mode |
|
938 and the user hits the accelerator key. |
|
939 |
|
940 To set What's This help on a menu item (with or without an |
|
941 accelerator key), use Q3MenuData::setWhatsThis(). |
|
942 |
|
943 \sa whatsThis(), QWhatsThis::inWhatsThisMode(), QAction::setWhatsThis() |
|
944 */ |
|
945 void Q3Accel::setWhatsThis(int id, const QString& text) |
|
946 { |
|
947 Q3AccelItem *item = find_id(d->aitems, id); |
|
948 if (item) |
|
949 item->whatsthis = text; |
|
950 } |
|
951 |
|
952 /*! |
|
953 Returns the What's This help text for the specified item \a id or |
|
954 an empty string if no text has been specified. |
|
955 |
|
956 \sa setWhatsThis() |
|
957 */ |
|
958 QString Q3Accel::whatsThis(int id) const |
|
959 { |
|
960 |
|
961 Q3AccelItem *item = find_id(d->aitems, id); |
|
962 return item? item->whatsthis : QString(); |
|
963 } |
|
964 |
|
965 /*!\internal */ |
|
966 void Q3Accel::setIgnoreWhatsThis(bool b) |
|
967 { |
|
968 d->ignorewhatsthis = b; |
|
969 } |
|
970 |
|
971 /*!\internal */ |
|
972 bool Q3Accel::ignoreWhatsThis() const |
|
973 { |
|
974 return d->ignorewhatsthis; |
|
975 } |
|
976 |
|
977 /*! |
|
978 \fn void Q3Accel::repairEventFilter() |
|
979 \internal |
|
980 */ |
|
981 |
|
982 QT_END_NAMESPACE |