VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIHostComboEditor.cpp@ 76553

Last change on this file since 76553 was 76553, checked in by vboxsync, 5 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.7 KB
Line 
1/* $Id: UIHostComboEditor.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIHostComboEditor class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifdef VBOX_WITH_PRECOMPILED_HEADERS
19# include <precomp.h>
20#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
21
22/* Qt includes: */
23# include <QApplication>
24# include <QHBoxLayout>
25# include <QKeyEvent>
26# include <QStyleOption>
27# include <QStylePainter>
28# include <QTimer>
29# ifdef VBOX_WS_X11
30# include <QX11Info>
31# endif
32
33/* GUI includes: */
34# include "QIToolButton.h"
35# include "VBoxGlobal.h"
36# include "UIExtraDataManager.h"
37# include "UIHostComboEditor.h"
38# include "UIIconPool.h"
39# ifdef VBOX_WS_MAC
40# include "UICocoaApplication.h"
41# include "VBoxUtils-darwin.h"
42# endif
43
44#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
45
46/* Qt includes: */
47#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
48# include <QAbstractNativeEventFilter>
49#endif
50
51/* GUI includes: */
52#if defined(VBOX_WS_MAC)
53# include "DarwinKeyboard.h"
54#elif defined(VBOX_WS_WIN)
55# include "WinKeyboard.h"
56#elif defined(VBOX_WS_X11)
57# include "XKeyboard.h"
58#endif
59
60/* Other VBox includes: */
61#if defined(VBOX_WS_X11)
62# include <VBox/VBoxKeyboard.h>
63#endif
64
65/* External includes: */
66#if defined(VBOX_WS_MAC)
67# include <Carbon/Carbon.h>
68#elif defined(VBOX_WS_X11)
69# include <X11/Xlib.h>
70# include <X11/Xutil.h>
71# include <X11/keysym.h>
72# include <xcb/xcb.h>
73#endif /* VBOX_WS_X11 */
74
75/* Namespaces: */
76using namespace UIExtraDataDefs;
77
78
79#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
80/** QAbstractNativeEventFilter extension
81 * allowing to handle native platform events.
82 * Why do we need it? It's because Qt5 have unhandled
83 * well .. let's call it 'a bug' about native keyboard events
84 * which come to top-level widget (window) instead of focused sub-widget
85 * which actually supposed to get them. The strange thing is that target of
86 * those events on at least Windows host (MSG::hwnd) is indeed window itself,
87 * not the sub-widget we expect, so that's probably the reason Qt devs
88 * haven't fixed that bug so far for Windows and Mac OS X hosts. */
89class ComboEditorEventFilter : public QAbstractNativeEventFilter
90{
91public:
92
93 /** Constructor which takes the passed @a pParent to redirect events to. */
94 ComboEditorEventFilter(UIHostComboEditorPrivate *pParent)
95 : m_pParent(pParent)
96 {}
97
98 /** Handles all native events. */
99 virtual bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pResult) /* override */
100 {
101 /* Redirect event to parent: */
102 return m_pParent->nativeEvent(eventType, pMessage, pResult);
103 }
104
105private:
106
107 /** Holds the passed parent reference. */
108 UIHostComboEditorPrivate *m_pParent;
109};
110#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
111
112
113/*********************************************************************************************************************************
114* Namespace UINativeHotKey implementation. *
115*********************************************************************************************************************************/
116
117#ifdef VBOX_WS_X11
118namespace UINativeHotKey
119{
120 QMap<QString, QString> m_keyNames;
121}
122#endif /* VBOX_WS_X11 */
123
124QString UINativeHotKey::toString(int iKeyCode)
125{
126 QString strKeyName;
127
128#if defined(VBOX_WS_MAC)
129
130 UInt32 modMask = DarwinKeyCodeToDarwinModifierMask(iKeyCode);
131 switch (modMask)
132 {
133 case shiftKey:
134 case optionKey:
135 case controlKey:
136 case cmdKey:
137 strKeyName = UIHostComboEditor::tr("Left %1");
138 break;
139 case rightShiftKey:
140 case rightOptionKey:
141 case rightControlKey:
142 case kEventKeyModifierRightCmdKeyMask:
143 strKeyName = UIHostComboEditor::tr("Right %1");
144 break;
145 default:
146 AssertMsgFailedReturn(("modMask=%#x\n", modMask), QString());
147 }
148 switch (modMask)
149 {
150 case shiftKey:
151 case rightShiftKey:
152 strKeyName = strKeyName.arg(QChar(kShiftUnicode));
153 break;
154 case optionKey:
155 case rightOptionKey:
156 strKeyName = strKeyName.arg(QChar(kOptionUnicode));
157 break;
158 case controlKey:
159 case rightControlKey:
160 strKeyName = strKeyName.arg(QChar(kControlUnicode));
161 break;
162 case cmdKey:
163 case kEventKeyModifierRightCmdKeyMask:
164 strKeyName = strKeyName.arg(QChar(kCommandUnicode));
165 break;
166 }
167
168#elif defined(VBOX_WS_WIN)
169
170 // WORKAROUND:
171 // MapVirtualKey doesn't distinguish between right and left vkeys,
172 // even under XP, despite that it stated in MSDN. Do it by hands.
173 // Besides that it can't recognize such virtual keys as
174 // VK_DIVIDE & VK_PAUSE, this is also known bug.
175 int iScan;
176 switch (iKeyCode)
177 {
178 /* Processing special keys... */
179 case VK_PAUSE: iScan = 0x45 << 16; break;
180 case VK_RSHIFT: iScan = 0x36 << 16; break;
181 case VK_RCONTROL: iScan = (0x1D << 16) | (1 << 24); break;
182 case VK_RMENU: iScan = (0x38 << 16) | (1 << 24); break;
183 /* Processing extended keys... */
184 case VK_APPS:
185 case VK_LWIN:
186 case VK_RWIN:
187 case VK_NUMLOCK: iScan = (::MapVirtualKey(iKeyCode, 0) | 256) << 16; break;
188 default: iScan = ::MapVirtualKey(iKeyCode, 0) << 16;
189 }
190 TCHAR *pKeyName = new TCHAR[256];
191 if (::GetKeyNameText(iScan, pKeyName, 256))
192 {
193 strKeyName = QString::fromUtf16(pKeyName);
194 }
195 else
196 {
197 AssertMsgFailed(("That key have no name!\n"));
198 strKeyName = UIHostComboEditor::tr("<key_%1>").arg(iKeyCode);
199 }
200 delete[] pKeyName;
201
202#elif defined(VBOX_WS_X11)
203
204 if (char *pNativeKeyName = ::XKeysymToString((KeySym)iKeyCode))
205 {
206 strKeyName = m_keyNames[pNativeKeyName].isEmpty() ?
207 QString(pNativeKeyName) : m_keyNames[pNativeKeyName];
208 }
209 else
210 {
211 AssertMsgFailed(("That key have no name!\n"));
212 strKeyName = UIHostComboEditor::tr("<key_%1>").arg(iKeyCode);
213 }
214
215#else
216
217# warning "port me!"
218
219#endif
220
221 return strKeyName;
222}
223
224bool UINativeHotKey::isValidKey(int iKeyCode)
225{
226#if defined(VBOX_WS_MAC)
227
228 UInt32 modMask = ::DarwinKeyCodeToDarwinModifierMask(iKeyCode);
229 switch (modMask)
230 {
231 case shiftKey:
232 case optionKey:
233 case controlKey:
234 case rightShiftKey:
235 case rightOptionKey:
236 case rightControlKey:
237 case cmdKey:
238 case kEventKeyModifierRightCmdKeyMask:
239 return true;
240 default:
241 return false;
242 }
243
244#elif defined(VBOX_WS_WIN)
245
246 return (iKeyCode >= VK_SHIFT && iKeyCode <= VK_CAPITAL)
247 || (iKeyCode >= VK_LSHIFT && iKeyCode <= VK_RMENU)
248 || (iKeyCode >= VK_F1 && iKeyCode <= VK_F24)
249 || iKeyCode == VK_NUMLOCK
250 || iKeyCode == VK_SCROLL
251 || iKeyCode == VK_LWIN
252 || iKeyCode == VK_RWIN
253 || iKeyCode == VK_APPS
254 || iKeyCode == VK_PRINT;
255
256#elif defined(VBOX_WS_X11)
257
258 return (IsModifierKey(iKeyCode) /* allow modifiers */ ||
259 IsFunctionKey(iKeyCode) /* allow function keys */ ||
260 IsMiscFunctionKey(iKeyCode) /* allow miscellaneous function keys */ ||
261 iKeyCode == XK_Scroll_Lock /* allow 'Scroll Lock' missed in IsModifierKey() */) &&
262 (iKeyCode != NoSymbol /* ignore some special symbol */ &&
263 iKeyCode != XK_Insert /* ignore 'insert' included into IsMiscFunctionKey */);
264
265#else
266
267# warning "port me!"
268
269 return false;
270
271#endif
272}
273
274unsigned UINativeHotKey::modifierToSet1ScanCode(int iKeyCode)
275{
276 switch(iKeyCode)
277 {
278#if defined(VBOX_WS_MAC)
279
280 case controlKey: return 0x1D;
281 case rightControlKey: return 0x11D;
282 case shiftKey: return 0x2A;
283 case rightShiftKey: return 0x36;
284 case optionKey: return 0x38;
285 case rightOptionKey: return 0x138;
286 case cmdKey: return 0x15B;
287 case kEventKeyModifierRightCmdKeyMask: return 0x15C;
288 default: return 0;
289
290#elif defined(VBOX_WS_WIN)
291
292 case VK_CONTROL:
293 case VK_LCONTROL: return 0x1D;
294 case VK_RCONTROL: return 0x11D;
295 case VK_SHIFT:
296 case VK_LSHIFT: return 0x2A;
297 case VK_RSHIFT: return 0x36;
298 case VK_MENU:
299 case VK_LMENU: return 0x38;
300 case VK_RMENU: return 0x138;
301 case VK_LWIN: return 0x15B;
302 case VK_RWIN: return 0x15C;
303 case VK_APPS: return 0x15D;
304 default: return 0;
305
306#elif defined(VBOX_WS_X11)
307
308 case XK_Control_L: return 0x1D;
309 case XK_Control_R: return 0x11D;
310 case XK_Shift_L: return 0x2A;
311 case XK_Shift_R: return 0x36;
312 case XK_Alt_L: return 0x38;
313 case XK_ISO_Level3_Shift:
314 case XK_Alt_R: return 0x138;
315 case XK_Meta_L:
316 case XK_Super_L: return 0x15B;
317 case XK_Meta_R:
318 case XK_Super_R: return 0x15C;
319 case XK_Menu: return 0x15D;
320 default: return 0;
321
322#else
323
324# warning "port me!"
325
326 default: return 0;
327
328#endif
329 }
330}
331
332#if defined(VBOX_WS_WIN)
333
334int UINativeHotKey::distinguishModifierVKey(int wParam, int lParam)
335{
336 int iKeyCode = wParam;
337 switch (iKeyCode)
338 {
339 case VK_SHIFT:
340 {
341 UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
342 UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
343 if (uCurrentScanCode == uLeftScanCode)
344 iKeyCode = VK_LSHIFT;
345 else
346 iKeyCode = VK_RSHIFT;
347 break;
348 }
349 case VK_CONTROL:
350 {
351 UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
352 UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
353 if (uCurrentScanCode == uLeftScanCode)
354 iKeyCode = VK_LCONTROL;
355 else
356 iKeyCode = VK_RCONTROL;
357 break;
358 }
359 case VK_MENU:
360 {
361 UINT uCurrentScanCode = (lParam & 0x01FF0000) >> 16;
362 UINT uLeftScanCode = ::MapVirtualKey(iKeyCode, 0);
363 if (uCurrentScanCode == uLeftScanCode)
364 iKeyCode = VK_LMENU;
365 else
366 iKeyCode = VK_RMENU;
367 break;
368 }
369 }
370 return iKeyCode;
371}
372
373#elif defined(VBOX_WS_X11)
374
375void UINativeHotKey::retranslateKeyNames()
376{
377 m_keyNames["Shift_L"] = UIHostComboEditor::tr("Left Shift");
378 m_keyNames["Shift_R"] = UIHostComboEditor::tr("Right Shift");
379 m_keyNames["Control_L"] = UIHostComboEditor::tr("Left Ctrl");
380 m_keyNames["Control_R"] = UIHostComboEditor::tr("Right Ctrl");
381 m_keyNames["Alt_L"] = UIHostComboEditor::tr("Left Alt");
382 m_keyNames["Alt_R"] = UIHostComboEditor::tr("Right Alt");
383 m_keyNames["Super_L"] = UIHostComboEditor::tr("Left WinKey");
384 m_keyNames["Super_R"] = UIHostComboEditor::tr("Right WinKey");
385 m_keyNames["Menu"] = UIHostComboEditor::tr("Menu key");
386 m_keyNames["ISO_Level3_Shift"] = UIHostComboEditor::tr("Alt Gr");
387 m_keyNames["Caps_Lock"] = UIHostComboEditor::tr("Caps Lock");
388 m_keyNames["Scroll_Lock"] = UIHostComboEditor::tr("Scroll Lock");
389}
390
391#endif /* VBOX_WS_X11 */
392
393
394/*********************************************************************************************************************************
395* Namespace UIHostCombo implementation. *
396*********************************************************************************************************************************/
397
398namespace UIHostCombo
399{
400 int m_iMaxComboSize = 3;
401}
402
403int UIHostCombo::hostComboModifierIndex()
404{
405 return -1;
406}
407
408QString UIHostCombo::hostComboModifierName()
409{
410 return UIHostComboEditor::tr("Host+");
411}
412
413QString UIHostCombo::hostComboCacheKey()
414{
415 return QString(GUI_Input_MachineShortcuts) + "/" + "HostCombo";
416}
417
418QString UIHostCombo::toReadableString(const QString &strKeyCombo)
419{
420 QStringList encodedKeyList = strKeyCombo.split(',');
421 QStringList readableKeyList;
422 for (int i = 0; i < encodedKeyList.size(); ++i)
423 if (int iKeyCode = encodedKeyList[i].toInt())
424 readableKeyList << UINativeHotKey::toString(iKeyCode);
425 return readableKeyList.isEmpty() ? UIHostComboEditor::tr("None") : readableKeyList.join(" + ");
426}
427
428QList<int> UIHostCombo::toKeyCodeList(const QString &strKeyCombo)
429{
430 QStringList encodedKeyList = strKeyCombo.split(',');
431 QList<int> keyCodeList;
432 for (int i = 0; i < encodedKeyList.size(); ++i)
433 if (int iKeyCode = encodedKeyList[i].toInt())
434 keyCodeList << iKeyCode;
435 return keyCodeList;
436}
437
438QList<unsigned> UIHostCombo::modifiersToScanCodes(const QString &strKeyCombo)
439{
440 QStringList encodedKeyList = strKeyCombo.split(',');
441 QList<unsigned> scanCodeList;
442 for (int i = 0; i < encodedKeyList.size(); ++i)
443 if (unsigned idxScanCode = UINativeHotKey::modifierToSet1ScanCode(encodedKeyList[i].toInt()))
444 if (idxScanCode != 0)
445 scanCodeList << idxScanCode;
446 return scanCodeList;
447}
448
449bool UIHostCombo::isValidKeyCombo(const QString &strKeyCombo)
450{
451 QList<int> keyCodeList = toKeyCodeList(strKeyCombo);
452 if (keyCodeList.size() > m_iMaxComboSize)
453 return false;
454 for (int i = 0; i < keyCodeList.size(); ++i)
455 if (!UINativeHotKey::isValidKey(keyCodeList[i]))
456 return false;
457 return true;
458}
459
460
461/*********************************************************************************************************************************
462* Class UIHostComboEditor implementation. *
463*********************************************************************************************************************************/
464
465UIHostComboEditor::UIHostComboEditor(QWidget *pParent)
466 : QIWithRetranslateUI<QWidget>(pParent)
467{
468 /* Prepare: */
469 prepare();
470}
471
472void UIHostComboEditor::retranslateUi()
473{
474 /* Translate 'clear' tool-button: */
475 m_pButtonClear->setToolTip(QApplication::translate("UIHotKeyEditor", "Unset shortcut"));
476}
477
478void UIHostComboEditor::sltCommitData()
479{
480 /* Commit data to the listener: */
481 emit sigCommitData(this);
482}
483
484void UIHostComboEditor::prepare()
485{
486 /* Configure self: */
487 setAutoFillBackground(true);
488 /* Create layout: */
489 QHBoxLayout *pLayout = new QHBoxLayout(this);
490 {
491 /* Configure layout: */
492#ifdef VBOX_WS_MAC
493 pLayout->setSpacing(5);
494#else
495 pLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
496#endif
497 pLayout->setContentsMargins(0, 0, 0, 0);
498 /* Create UIHostComboEditorPrivate instance: */
499 m_pEditor = new UIHostComboEditorPrivate;
500 {
501 /* Configure UIHostComboEditorPrivate instance: */
502 setFocusProxy(m_pEditor);
503 connect(m_pEditor, SIGNAL(sigDataChanged()), this, SLOT(sltCommitData()));
504 }
505 /* Create 'clear' tool-button: */
506 m_pButtonClear = new QIToolButton;
507 {
508 /* Configure 'clear' tool-button: */
509 m_pButtonClear->removeBorder();
510 m_pButtonClear->setIcon(UIIconPool::iconSet(":/eraser_16px.png"));
511 connect(m_pButtonClear, SIGNAL(clicked(bool)), m_pEditor, SLOT(sltClear()));
512 }
513 /* Add widgets to layout: */
514 pLayout->addWidget(m_pEditor);
515 pLayout->addWidget(m_pButtonClear);
516 }
517 /* Translate finally: */
518 retranslateUi();
519}
520
521void UIHostComboEditor::setCombo(const UIHostComboWrapper &strCombo)
522{
523 /* Pass combo to child: */
524 m_pEditor->setCombo(strCombo);
525}
526
527UIHostComboWrapper UIHostComboEditor::combo() const
528{
529 /* Acquire combo from child: */
530 return m_pEditor->combo();
531}
532
533
534/*********************************************************************************************************************************
535* Class UIHostComboEditorPrivate implementation. *
536*********************************************************************************************************************************/
537
538UIHostComboEditorPrivate::UIHostComboEditorPrivate()
539 : m_pReleaseTimer(0)
540 , m_fStartNewSequence(true)
541#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
542 , m_pPrivateEventFilter(0)
543#endif
544#ifdef VBOX_WS_WIN
545 , m_pAltGrMonitor(0)
546#endif
547{
548 /* Configure widget: */
549 setAttribute(Qt::WA_NativeWindow);
550 setContextMenuPolicy(Qt::NoContextMenu);
551 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
552 connect(this, SIGNAL(selectionChanged()), this, SLOT(sltDeselect()));
553
554 /* Setup release-pending-keys timer: */
555 m_pReleaseTimer = new QTimer(this);
556 m_pReleaseTimer->setInterval(200);
557 connect(m_pReleaseTimer, SIGNAL(timeout()), this, SLOT(sltReleasePendingKeys()));
558
559#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
560 /* Prepare private event filter: */
561 m_pPrivateEventFilter = new ComboEditorEventFilter(this);
562 qApp->installNativeEventFilter(m_pPrivateEventFilter);
563#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
564
565#if defined(VBOX_WS_MAC)
566 m_uDarwinKeyModifiers = 0;
567#elif defined(VBOX_WS_WIN)
568 /* Prepare AltGR monitor: */
569 m_pAltGrMonitor = new WinAltGrMonitor;
570#elif defined(VBOX_WS_X11)
571 /* Initialize the X keyboard subsystem: */
572 initMappedX11Keyboard(QX11Info::display(), gEDataManager->remappedScanCodes());
573#endif /* VBOX_WS_X11 */
574}
575
576UIHostComboEditorPrivate::~UIHostComboEditorPrivate()
577{
578#if defined(VBOX_WS_WIN)
579 /* Cleanup AltGR monitor: */
580 delete m_pAltGrMonitor;
581 m_pAltGrMonitor = 0;
582#endif /* VBOX_WS_WIN */
583
584#if defined(VBOX_WS_MAC) || defined(VBOX_WS_WIN)
585 /* Cleanup private event filter: */
586 qApp->removeNativeEventFilter(m_pPrivateEventFilter);
587 delete m_pPrivateEventFilter;
588 m_pPrivateEventFilter = 0;
589#endif /* VBOX_WS_MAC || VBOX_WS_WIN */
590}
591
592void UIHostComboEditorPrivate::setCombo(const UIHostComboWrapper &strCombo)
593{
594 /* Cleanup old combo: */
595 m_shownKeys.clear();
596 /* Parse newly passed combo: */
597 QList<int> keyCodeList = UIHostCombo::toKeyCodeList(strCombo.toString());
598 for (int i = 0; i < keyCodeList.size(); ++i)
599 if (int iKeyCode = keyCodeList[i])
600 m_shownKeys.insert(iKeyCode, UINativeHotKey::toString(iKeyCode));
601 /* Update text: */
602 updateText();
603}
604
605UIHostComboWrapper UIHostComboEditorPrivate::combo() const
606{
607 /* Compose current combination: */
608 QStringList keyCodeStringList;
609 QList<int> keyCodeList = m_shownKeys.keys();
610 for (int i = 0; i < keyCodeList.size(); ++i)
611 keyCodeStringList << QString::number(keyCodeList[i]);
612 /* Return current combination or "0" for "None": */
613 return keyCodeStringList.isEmpty() ? "0" : keyCodeStringList.join(",");
614}
615
616void UIHostComboEditorPrivate::sltDeselect()
617{
618 deselect();
619}
620
621void UIHostComboEditorPrivate::sltClear()
622{
623 /* Cleanup combo: */
624 m_shownKeys.clear();
625 /* Update text: */
626 updateText();
627 /* Move the focus to text-field: */
628 setFocus();
629 /* Notify data changed: */
630 emit sigDataChanged();
631}
632
633bool UIHostComboEditorPrivate::nativeEvent(const QByteArray &eventType, void *pMessage, long *pResult)
634{
635# if defined(VBOX_WS_MAC)
636
637 /* Make sure it's generic NSEvent: */
638 if (eventType != "mac_generic_NSEvent")
639 return QLineEdit::nativeEvent(eventType, pMessage, pResult);
640 EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
641
642 /* Check if some NSEvent should be filtered out: */
643 // Returning @c true means filtering-out,
644 // Returning @c false means passing event to Qt.
645 switch(::GetEventClass(event))
646 {
647 /* Watch for keyboard-events: */
648 case kEventClassKeyboard:
649 {
650 switch(::GetEventKind(event))
651 {
652 /* Watch for keyboard-modifier-events: */
653 case kEventRawKeyModifiersChanged:
654 {
655 /* Get modifier mask: */
656 UInt32 modifierMask = 0;
657 ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
658 NULL, sizeof(modifierMask), NULL, &modifierMask);
659 modifierMask = ::DarwinAdjustModifierMask(modifierMask, pMessage);
660
661 /* Do not handle unchanged masks: */
662 UInt32 uChanged = m_uDarwinKeyModifiers ^ modifierMask;
663 if (!uChanged)
664 break;
665
666 /* Convert to keycode: */
667 unsigned uKeyCode = ::DarwinModifierMaskToDarwinKeycode(uChanged);
668
669 /* Do not handle empty and multiple modifier changes: */
670 if (!uKeyCode || uKeyCode == ~0U)
671 break;
672
673 /* Handle key-event: */
674 if (processKeyEvent(uKeyCode, uChanged & modifierMask))
675 {
676 /* Save the new modifier mask state: */
677 m_uDarwinKeyModifiers = modifierMask;
678 return true;
679 }
680 break;
681 }
682 default:
683 break;
684 }
685 break;
686 }
687 default:
688 break;
689 }
690
691# elif defined(VBOX_WS_WIN)
692
693 /* Make sure it's generic MSG event: */
694 if (eventType != "windows_generic_MSG")
695 return QLineEdit::nativeEvent(eventType, pMessage, pResult);
696 MSG *pEvent = static_cast<MSG*>(pMessage);
697
698 /* Check if some MSG event should be filtered out: */
699 // Returning @c true means filtering-out,
700 // Returning @c false means passing event to Qt.
701 switch (pEvent->message)
702 {
703 /* Watch for key-events: */
704 case WM_KEYDOWN:
705 case WM_SYSKEYDOWN:
706 case WM_KEYUP:
707 case WM_SYSKEYUP:
708 {
709 /* Parse key-event: */
710 int iKeyCode = UINativeHotKey::distinguishModifierVKey((int)pEvent->wParam, (int)pEvent->lParam);
711 unsigned iDownScanCode = (pEvent->lParam >> 16) & 0x7F;
712 const bool fPressed = !(pEvent->lParam & 0x80000000);
713 const bool fExtended = pEvent->lParam & 0x1000000;
714
715 /* If present - why not just assert this? */
716 if (m_pAltGrMonitor)
717 {
718 /* Update AltGR monitor state from key-event: */
719 m_pAltGrMonitor->updateStateFromKeyEvent(iDownScanCode, fPressed, fExtended);
720 /* And release left Ctrl key early (if required): */
721 if (m_pAltGrMonitor->isLeftControlReleaseNeeded())
722 {
723 m_pressedKeys.remove(VK_LCONTROL);
724 m_shownKeys.remove(VK_LCONTROL);
725 }
726 // WORKAROUND:
727 // Fake LCtrl release events can also end up in the released
728 // key set. Detect them on the immediately following RAlt up.
729 if (!m_pressedKeys.contains(VK_LCONTROL))
730 m_releasedKeys.remove(VK_LCONTROL);
731 }
732
733 /* Handle key-event: */
734 return processKeyEvent(iKeyCode, (pEvent->message == WM_KEYDOWN || pEvent->message == WM_SYSKEYDOWN));
735 }
736 default:
737 break;
738 }
739
740# elif defined(VBOX_WS_X11)
741
742 /* Make sure it's generic XCB event: */
743 if (eventType != "xcb_generic_event_t")
744 return QLineEdit::nativeEvent(eventType, pMessage, pResult);
745 xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
746
747 /* Check if some XCB event should be filtered out: */
748 // Returning @c true means filtering-out,
749 // Returning @c false means passing event to Qt.
750 switch (pEvent->response_type & ~0x80)
751 {
752 /* Watch for key-events: */
753 case XCB_KEY_PRESS:
754 case XCB_KEY_RELEASE:
755 {
756 /* Parse key-event: */
757 xcb_key_press_event_t *pKeyEvent = static_cast<xcb_key_press_event_t*>(pMessage);
758 RT_GCC_NO_WARN_DEPRECATED_BEGIN
759 const KeySym ks = ::XKeycodeToKeysym(QX11Info::display(), pKeyEvent->detail, 0);
760 RT_GCC_NO_WARN_DEPRECATED_END
761 const int iKeySym = static_cast<int>(ks);
762
763 /* Handle key-event: */
764 return processKeyEvent(iKeySym, (pEvent->response_type & ~0x80) == XCB_KEY_PRESS);
765 }
766 default:
767 break;
768 }
769
770# else
771
772# warning "port me!"
773
774# endif
775
776 /* Call to base-class: */
777 return QLineEdit::nativeEvent(eventType, pMessage, pResult);
778}
779
780void UIHostComboEditorPrivate::keyPressEvent(QKeyEvent *pEvent)
781{
782 /* Ignore most of key presses... */
783 switch (pEvent->key())
784 {
785 case Qt::Key_Enter:
786 case Qt::Key_Return:
787 case Qt::Key_Tab:
788 case Qt::Key_Backtab:
789 case Qt::Key_Escape:
790 return QLineEdit::keyPressEvent(pEvent);
791 case Qt::Key_Up:
792 case Qt::Key_Down:
793 case Qt::Key_Left:
794 case Qt::Key_Right:
795 pEvent->ignore();
796 return;
797 default:
798 break;
799 }
800}
801
802void UIHostComboEditorPrivate::keyReleaseEvent(QKeyEvent *pEvent)
803{
804 /* Ignore most of key presses... */
805 switch (pEvent->key())
806 {
807 case Qt::Key_Tab:
808 case Qt::Key_Backtab:
809 case Qt::Key_Escape:
810 return QLineEdit::keyReleaseEvent(pEvent);
811 case Qt::Key_Up:
812 case Qt::Key_Down:
813 case Qt::Key_Left:
814 case Qt::Key_Right:
815 pEvent->ignore();
816 return;
817 default:
818 break;
819 }
820}
821
822void UIHostComboEditorPrivate::mousePressEvent(QMouseEvent *pEvent)
823{
824 /* Handle like for usual QWidget: */
825 QWidget::mousePressEvent(pEvent);
826}
827
828void UIHostComboEditorPrivate::mouseReleaseEvent(QMouseEvent *pEvent)
829{
830 /* Handle like for usual QWidget: */
831 QWidget::mouseReleaseEvent(pEvent);
832}
833
834void UIHostComboEditorPrivate::sltReleasePendingKeys()
835{
836 /* Stop the timer, we process all pending keys at once: */
837 m_pReleaseTimer->stop();
838 /* Something to do? */
839 if (!m_releasedKeys.isEmpty())
840 {
841 /* Remove every key: */
842 QSetIterator<int> iterator(m_releasedKeys);
843 while (iterator.hasNext())
844 {
845 int iKeyCode = iterator.next();
846 m_pressedKeys.remove(iKeyCode);
847 m_shownKeys.remove(iKeyCode);
848 }
849 m_releasedKeys.clear();
850 if (m_pressedKeys.isEmpty())
851 m_fStartNewSequence = true;
852 /* Notify data changed: */
853 emit sigDataChanged();
854 }
855 /* Make sure the user see what happens: */
856 updateText();
857}
858
859bool UIHostComboEditorPrivate::processKeyEvent(int iKeyCode, bool fKeyPress)
860{
861 /* Check if symbol is valid else pass it to Qt: */
862 if (!UINativeHotKey::isValidKey(iKeyCode))
863 return false;
864
865 /* Stop the release-pending-keys timer: */
866 m_pReleaseTimer->stop();
867
868 /* Key press: */
869 if (fKeyPress)
870 {
871 /* Clear reflected symbols if new sequence started: */
872 if (m_fStartNewSequence)
873 m_shownKeys.clear();
874 /* Make sure any keys pending for releasing are processed: */
875 sltReleasePendingKeys();
876 /* Check maximum combo size: */
877 if (m_shownKeys.size() < UIHostCombo::m_iMaxComboSize)
878 {
879 /* Remember pressed symbol: */
880 m_pressedKeys << iKeyCode;
881 m_shownKeys.insert(iKeyCode, UINativeHotKey::toString(iKeyCode));
882 /* Remember what we already started a sequence: */
883 m_fStartNewSequence = false;
884 /* Notify data changed: */
885 emit sigDataChanged();
886 }
887 }
888 /* Key release: */
889 else
890 {
891 /* Queue released symbol for processing: */
892 m_releasedKeys << iKeyCode;
893
894 /* If all pressed keys are now pending for releasing we should stop further handling.
895 * Now we have the status the user want: */
896 if (m_pressedKeys == m_releasedKeys)
897 {
898 m_pressedKeys.clear();
899 m_releasedKeys.clear();
900 m_fStartNewSequence = true;
901 }
902 else
903 m_pReleaseTimer->start();
904 }
905
906 /* Update text: */
907 updateText();
908
909 /* Prevent passing to Qt: */
910 return true;
911}
912
913void UIHostComboEditorPrivate::updateText()
914{
915 QStringList shownKeyNames(m_shownKeys.values());
916 setText(shownKeyNames.isEmpty() ? UIHostComboEditor::tr("None") : shownKeyNames.join(" + "));
917}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use