source: trunk/src/qt3support/other/q3accel.cpp@ 603

Last change on this file since 603 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 30.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt3Support module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
56QT_BEGIN_NAMESPACE
57
58using 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
136struct 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
148typedef Q3PtrList<Q3AccelItem> Q3AccelList; // internal accelerator list
149
150class Q3AccelPrivate {
151public:
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
164class Q3AccelManager {
165public:
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
173private:
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};
192Q3AccelManager* Q3AccelManager::self_ptr = 0;
193
194bool Q_COMPAT_EXPORT qt_tryAccelEvent(QWidget* w, QKeyEvent* e){
195 return Q3AccelManager::self()->tryAccelEvent(w, e);
196}
197
198bool Q_COMPAT_EXPORT qt_dispatchAccelEvent(QWidget* w, QKeyEvent* e){
199 return Q3AccelManager::self()->dispatchAccelEvent(w, e);
200}
201
202bool Q_COMPAT_EXPORT qt_tryComposeUnicode(QWidget* w, QKeyEvent* e){
203 return Q3AccelManager::self()->tryComposeUnicode(w, e);
204}
205
206void 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
217static bool qt_accel_no_shortcuts = true;
218#else
219static bool qt_accel_no_shortcuts = false;
220#endif
221void 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*/
227bool 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
261inline 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*/
282QKeySequence::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
330bool Q3AccelManager::tryAccelEvent(QWidget* w, QKeyEvent* e)
331{