VirtualBox

Changeset 35730 in vbox for trunk


Ignore:
Timestamp:
Jan 27, 2011 10:49:44 AM (14 years ago)
Author:
vboxsync
Message:

FE/Qt: 5245: GUI support for complex host-key combinations. Initial build for Win/X11.

Location:
trunk/src/VBox/Frontends/VirtualBox
Files:
11 edited
2 moved

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk

    r35686 r35730  
    276276        src/extensions/QIDialog.h \
    277277        src/extensions/QIFileDialog.h \
    278         src/extensions/QIHotKeyEdit.h \
    279278        src/extensions/QIHttp.h \
    280279        src/extensions/QILabel.h \
     
    328327        src/widgets/UIDownloaderAdditions.h \
    329328        src/widgets/UIDownloaderUserManual.h \
     329        src/widgets/UIHotKeyEditor.h \
    330330        src/widgets/UIPopupBox.h \
    331331        src/widgets/UIProgressDialog.h \
     
    443443        src/extensions/QIDialogButtonBox.cpp \
    444444        src/extensions/QIFileDialog.cpp \
    445         src/extensions/QIHotKeyEdit.cpp \
    446445        src/extensions/QILabel.cpp \
    447446        src/extensions/QILabelSeparator.cpp \
     
    493492        src/widgets/UIDownloaderAdditions.cpp \
    494493        src/widgets/UIDownloaderUserManual.cpp \
     494        src/widgets/UIHotKeyEditor.cpp \
    495495        src/widgets/UIPopupBox.cpp \
    496496        src/widgets/UIProgressDialog.cpp \
  • trunk/src/VBox/Frontends/VirtualBox/src/VBoxGlobalSettings.cpp

    r33540 r35730  
    77
    88/*
    9  * Copyright (C) 2006-2007 Oracle Corporation
     9 * Copyright (C) 2006-2011 Oracle Corporation
    1010 *
    1111 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2727
    2828#include "VBoxGlobalSettings.h"
    29 #include "QIHotKeyEdit.h"
     29#include "UIHotKeyEditor.h"
    3030#include "COMDefs.h"
    3131#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
     
    4040{
    4141    /* default settings */
    42 #if defined (Q_WS_WIN32)
    43     hostkey = 0xA3; // VK_RCONTROL
    44 //    hostkey = 165; // VK_RMENU
    45 #elif defined (Q_WS_PM)
    46     hostkey = VK_CTRL;
     42#if defined (Q_WS_WIN)
     43    hostCombo = "163"; // VK_RCONTROL
    4744#elif defined (Q_WS_X11)
    48     hostkey = 0xffe4; // XK_Control_R
    49 //    hostkey = 65514; // XK_Alt_R
     45    hostCombo = "65508"; // XK_Control_R
    5046#elif defined (Q_WS_MAC)
    51 //    hostkey = 0x36; // QZ_RMETA
    52     hostkey = 0x37; // QZ_LMETA
    53 //    hostkey = 0x3e; // QZ_RCTRL
    54 //    hostkey = 0x3d; // QZ_RALT
     47    hostCombo = "55"; // QZ_LMETA
    5548#else
    5649# warning "port me!"
    57     hostkey = 0;
    5850#endif
    5951    autoCapture = true;
     
    6961VBoxGlobalSettingsData::VBoxGlobalSettingsData (const VBoxGlobalSettingsData &that)
    7062{
    71     hostkey = that.hostkey;
     63    hostCombo = that.hostCombo;
    7264    autoCapture = that.autoCapture;
    7365    guiFeatures = that.guiFeatures;
     
    8779{
    8880    return this == &that ||
    89         (hostkey == that.hostkey &&
     81        (hostCombo == that.hostCombo &&
    9082         autoCapture == that.autoCapture &&
    9183         guiFeatures == that.guiFeatures &&
     
    117109gPropertyMap[] =
    118110{
    119     { "GUI/Input/HostKey",                         "hostKey",                 "0|\\d*[1-9]\\d*", true },
     111    { "GUI/Input/HostCombo",                       "hostCombo",               "0|\\d*[1-9]\\d*(,\\d*[1-9]\\d*)?(,\\d*[1-9]\\d*)?", true },
    120112    { "GUI/Input/AutoCapture",                     "autoCapture",             "true|false", true },
    121113    { "GUI/Customizations",                        "guiFeatures",             "\\S+", true },
     
    127119    { VBoxDefs::GUI_PresentationModeEnabled,       "presentationModeEnabled", "true|false", true },
    128120#endif /* Q_WS_MAC */
    129     { "GUI/HostScreenSaverDisabled",               "hostScreenSaverDisabled",     "true|false", true }
     121    { "GUI/HostScreenSaverDisabled",               "hostScreenSaverDisabled", "true|false", true }
    130122};
    131123
    132 void VBoxGlobalSettings::setHostKey (int key)
    133 {
    134     if (!QIHotKeyEdit::isValidKey (key))
    135     {
    136         last_err = tr ("'%1 (0x%2)' is an invalid host key code.")
    137                        .arg (key).arg (key, 0, 16);
     124void VBoxGlobalSettings::setHostCombo (const QString &hostCombo)
     125{
     126    if (!UIHotKeyCombination::isValidKeyCombo (hostCombo))
     127    {
     128        last_err = tr ("'%1' is an invalid host-combination code-sequence.").arg (hostCombo);
    138129        return;
    139130    }
    140 
    141     mData()->hostkey = key;
     131    mData()->hostCombo = hostCombo;
    142132    resetError();
    143133}
  • trunk/src/VBox/Frontends/VirtualBox/src/VBoxGlobalSettings.h

    r32479 r35730  
    66
    77/*
    8  * Copyright (C) 2006-2007 Oracle Corporation
     8 * Copyright (C) 2006-2011 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    3838private:
    3939
    40     int hostkey;
     40    QString hostCombo;
    4141    bool autoCapture;
    4242    QString guiFeatures;
     
    5656{
    5757    Q_OBJECT
    58     Q_PROPERTY (int hostKey READ hostKey WRITE setHostKey)
     58    Q_PROPERTY (QString hostCombo READ hostCombo WRITE setHostCombo)
    5959    Q_PROPERTY (bool autoCapture READ autoCapture WRITE setAutoCapture)
    6060    Q_PROPERTY (QString guiFeatures READ guiFeatures WRITE setGuiFeatures)
     
    7979    // Properties
    8080
    81     int hostKey() const { return data()->hostkey; }
    82     void setHostKey (int key);
     81    QString hostCombo() const { return data()->hostCombo; }
     82    void setHostCombo (const QString &hostCombo);
    8383
    8484    bool autoCapture() const { return data()->autoCapture; }
  • trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxGlobal.cpp

    r35722 r35730  
    2525#include "VBoxSelectorWnd.h"
    2626#include "VBoxProblemReporter.h"
    27 #include "QIHotKeyEdit.h"
    2827#include "QIMessageBox.h"
    2928#include "QIDialogButtonBox.h"
     
    7372
    7473#ifdef Q_WS_X11
     74# include "UIHotKeyEditor.h"
    7575# ifndef VBOX_OSE
    7676#  include "VBoxLicenseViewer.h"
     
    30163016        it->refresh();
    30173017
    3018 #if defined (Q_WS_PM) || defined (Q_WS_X11)
    3019     /* As PM and X11 do not (to my knowledge) have functionality for providing
    3020      * human readable key names, we keep a table of them, which must be
    3021      * updated when the language is changed. */
    3022     QIHotKeyEdit::retranslateUi();
    3023 #endif
     3018#ifdef Q_WS_X11
     3019    /* As X11 do not have functionality for providing human readable key names,
     3020     * we keep a table of them, which must be updated when the language is changed. */
     3021    UIHotKey::retranslateKeyNames();
     3022#endif /* Q_WS_X11 */
    30243023}
    30253024
  • trunk/src/VBox/Frontends/VirtualBox/src/globals/VBoxProblemReporter.cpp

    r35587 r35730  
    3939#include "UIMachine.h"
    4040#include "VBoxAboutDlg.h"
    41 #include "QIHotKeyEdit.h"
     41#include "UIHotKeyEditor.h"
    4242#ifdef Q_WS_MAC
    4343# include "VBoxUtils-darwin.h"
     
    16771677        tr ("<p>The host key is currently defined as <b>%1</b>.</p>",
    16781678            "additional message box paragraph")
    1679             .arg (QIHotKeyEdit::keyName (vboxGlobal().settings().hostKey())),
     1679            .arg (UIHotKeyCombination::toReadableString (vboxGlobal().settings().hostCombo())),
    16801680        "confirmInputCapture",
    16811681        QIMessageBox::Ok | QIMessageBox::Default,
     
    17101710        tr ("<p>The host key is currently defined as <b>%1</b>.</p>",
    17111711            "additional message box paragraph")
    1712             .arg (QIHotKeyEdit::keyName (vboxGlobal().settings().hostKey())),
     1712            .arg (UIHotKeyCombination::toReadableString (vboxGlobal().settings().hostCombo())),
    17131713        "remindAboutAutoCapture");
    17141714}
     
    18781878            "You can access it by pressing <b>Host+Home</b>.</p>")
    18791879            .arg (aHotKey)
    1880             .arg (QIHotKeyEdit::keyName (vboxGlobal().settings().hostKey())),
     1880            .arg (UIHotKeyCombination::toReadableString (vboxGlobal().settings().hostCombo())),
    18811881        "confirmGoingFullscreen",
    18821882        tr ("Switch", "fullscreen"));
     
    18981898            "You can access it by pressing <b>Host+Home</b>.</p>")
    18991899            .arg (aHotKey)
    1900             .arg (QIHotKeyEdit::keyName (vboxGlobal().settings().hostKey())),
     1900            .arg (UIHotKeyCombination::toReadableString (vboxGlobal().settings().hostCombo())),
    19011901        "confirmGoingSeamless",
    19021902        tr ("Switch", "seamless"));
     
    19181918            "You can access it by pressing <b>Host+Home</b>.</p>")
    19191919            .arg (aHotKey)
    1920             .arg (QIHotKeyEdit::keyName (vboxGlobal().settings().hostKey())),
     1920            .arg (UIHotKeyCombination::toReadableString (vboxGlobal().settings().hostCombo())),
    19211921        "confirmGoingScale",
    19221922        tr ("Switch", "scale"));
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp

    r35686 r35730  
    3434#include "UIMachineWindow.h"
    3535#include "UIMachineView.h"
     36#include "UIHotKeyEditor.h"
    3637
    3738#ifdef Q_WS_X11
     
    6465
    6566#ifdef Q_WS_WIN
    66 UIKeyboardHandler* UIKeyboardHandler::m_pKeyboardHandler = 0;
     67UIKeyboardHandler* UIKeyboardHandler::m_spKeyboardHandler = 0;
    6768#endif /* Q_WS_WIN */
    6869
     
    9495    /* Its required to have static pointer to created handler
    9596     * because windows keyboard-hook works only with static members: */
    96     m_pKeyboardHandler = pKeyboardHandler;
     97    m_spKeyboardHandler = pKeyboardHandler;
    9798#endif /* Q_WS_WIN */
    9899    /* Return prepared keyboard-handler: */
     
    105106    /* Delete keyboard-handler: */
    106107#ifdef Q_WS_WIN
    107     m_pKeyboardHandler = 0;
     108    m_spKeyboardHandler = 0;
    108109#endif /* Q_WS_WIN */
    109110    delete pKeyboardHandler;
     
    307308
    308309    if (aReleaseHostKey)
    309         m_bIsHostkeyPressed = false;
     310        m_bIsHostComboPressed = false;
    310311
    311312#ifdef Q_WS_MAC
    312     /* Clear most of the modifiers: */
    313     m_darwinKeyModifiers &=
    314         alphaLock | kEventKeyModifierNumLockMask |
    315         (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask(m_globalSettings.hostKey()));
     313    // TODO: To NaN: Please fix:
     314//    /* Clear most of the modifiers: */
     315//    m_darwinKeyModifiers &=
     316//        alphaLock | kEventKeyModifierNumLockMask |
     317//        (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask(m_globalSettings.hostKey()));
    316318#endif
    317319
     
    323325{
    324326    return (m_fIsKeyboardCaptured ? UIViewStateType_KeyboardCaptured : 0) |
    325            (m_bIsHostkeyPressed ? UIViewStateType_HostKeyPressed : 0);
     327           (m_bIsHostComboPressed ? UIViewStateType_HostKeyPressed : 0);
    326328}
    327329
     
    368370             * on the host, the scancode in lParam might be 0x71/0x72 or 0xF1/0xF2.
    369371             * In either case, we must deliver 0xF1/0xF2 scancode to the guest when
    370              * the key is pressed and nothing when it's released.
    371              */
     372             * the key is pressed and nothing when it's released. */
    372373            if (scan == 0x71 || scan == 0x72)
    373374            {
     
    393394                case VK_MENU:
    394395                {
    395                     /* Overcome stupid Win32 modifier key generalization: */
     396                    /* Overcome Win32 modifier key generalization: */
    396397                    int keyscan = scan;
    397398                    if (flags & KeyExtended)
     
    631632    , m_globalSettings(vboxGlobal().settings())
    632633    , m_fIsKeyboardCaptured(false)
    633     , m_bIsHostkeyPressed(false)
    634     , m_bIsHostkeyAlone (false)
    635     , m_bIsHostkeyInCapture(false)
     634    , m_bIsHostComboPressed(false)
     635    , m_bIsHostComboAlone (false)
    636636    , m_fPassCAD(false)
    637637#if defined(Q_WS_WIN)
     638    , m_bIsHostkeyInCapture(false)
    638639    , m_iKeyboardHookViewIndex(-1)
    639640#elif defined(Q_WS_MAC)
     
    846847                QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
    847848
    848                 if (m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyPress)
     849                if (m_bIsHostComboPressed && pEvent->type() == QEvent::KeyPress)
    849850                {
    850851                    /* Passing F1-F12 keys to the guest: */
     
    873874                    machineLogic()->actionsPool()->processHotKey(QKeySequence(pKeyEvent->key()));
    874875                }
    875                 else if (!m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyRelease)
     876                else if (!m_bIsHostComboPressed && pEvent->type() == QEvent::KeyRelease)
    876877                {
    877878                    /* Show a possible warning on key release which seems to be more expected by the end user: */
     
    899900LRESULT CALLBACK UIKeyboardHandler::lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
    900901{
    901     if (nCode == HC_ACTION && m_pKeyboardHandler && m_pKeyboardHandler->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
     902    if (nCode == HC_ACTION && m_spKeyboardHandler && m_spKeyboardHandler->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
    902903        return 1;
    903904
     
    909910    /* Check what related machine-view was NOT unregistered yet: */
    910911    if (!m_views.contains(m_iKeyboardHookViewIndex))
     912        return false;
     913
     914    if (!m_fIsKeyboardCaptured)
    911915        return false;
    912916
     
    929933    }
    930934
    931     if (!m_fIsKeyboardCaptured)
    932         return false;
    933 
    934935    /* It's possible that a key has been pressed while the keyboard was not
    935936     * captured, but is being released under the capture. Detect this situation
     
    938939    uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT) ? IsExtKeyPressed : IsKeyPressed;
    939940    if ((event.flags & 0x80) /* released */ &&
    940         ((event.vkCode == m_globalSettings.hostKey() && !m_bIsHostkeyInCapture) ||
     941        ((UIHotKeyCombination::toKeyCodeList(m_globalSettings.hostCombo()).contains(event.vkCode) && !m_bIsHostkeyInCapture) ||
    941942         (m_pressedKeys[event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed))
    942943        return false;
     
    11021103#endif
    11031104
    1104 bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = NULL */)
    1105 {
    1106     const bool isHostKey = iKey == m_globalSettings.hostKey();
    1107 
    1108     LONG buf[16];
    1109     LONG *codes = buf;
    1110     uint count = 0;
    1111     uint8_t whatPressed = 0;
    1112 
    1113     if (!isHostKey && !m_bIsHostkeyPressed)
    1114     {
    1115         if (fFlags & KeyPrint)
    1116         {
    1117             static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
    1118             static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
    1119             if (fFlags & KeyPressed)
    1120             {
    1121                 codes = PrintMake;
    1122                 count = SIZEOF_ARRAY(PrintMake);
    1123             }
    1124             else
    1125             {
    1126                 codes = PrintBreak;
    1127                 count = SIZEOF_ARRAY(PrintBreak);
    1128             }
    1129         }
    1130         else if (fFlags & KeyPause)
    1131         {
    1132             if (fFlags & KeyPressed)
    1133             {
    1134                 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
    1135                 codes = Pause;
    1136                 count = SIZEOF_ARRAY(Pause);
    1137             }
    1138             else
    1139             {
    1140                 /* Pause shall not produce a break code */
     1105bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = 0 */)
     1106{
     1107    /* Get host-combo key list: */
     1108    QSet<int> allHostComboKeys = UIHotKeyCombination::toKeyCodeList(m_globalSettings.hostCombo()).toSet();
     1109
     1110    /* Update the map of pressed host-combo keys: */
     1111    if (fFlags & KeyPressed)
     1112    {
     1113        if (allHostComboKeys.contains(iKey))
     1114        {
     1115            if (!m_pressedHostComboKeys.contains(iKey))
     1116                m_pressedHostComboKeys.insert(iKey, uScan);
     1117            else if (m_bIsHostComboPressed)
    11411118                return true;
    1142             }
    1143         }
    1144         else
    1145         {
    1146             if (fFlags & KeyPressed)
    1147             {
    1148                 /* Check if the guest has the same view on the modifier keys (NumLock,
    1149                  * CapsLock, ScrollLock) as the X server. If not, send KeyPress events
    1150                  * to synchronize the state. */
    1151                 fixModifierState(codes, &count);
    1152             }
    1153 
    1154             /* Check if it's C-A-D and GUI/PassCAD is not true */
    1155             if (!m_fPassCAD &&
    1156                 uScan == 0x53 /* Del */ &&
    1157                 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
    1158                  (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
    1159                 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
    1160                  (m_pressedKeys[0x1d] & IsExtKeyPressed)))
    1161             {
    1162                 /* Use the C-A-D combination as a last resort to get the
    1163                  * keyboard and mouse back to the host when the user forgets
    1164                  * the Host Key. Note that it's always possible to send C-A-D
    1165                  * to the guest using the Host+Del combination. BTW, it would
    1166                  * be preferable to completely ignore C-A-D in guests, but
    1167                  * that's not possible because we cannot predict what other
    1168                  * keys will be pressed next when one of C, A, D is held. */
    1169                 if (uisession()->isRunning() && m_fIsKeyboardCaptured)
    1170                 {
    1171                     releaseKeyboard();
    1172                     if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
    1173                         machineLogic()->mouseHandler()->releaseMouse();
    1174                 }
    1175                 return true;
    1176             }
    1177 
    1178             /* Process the scancode and update the table of pressed keys: */
    1179             whatPressed = IsKeyPressed;
    1180 
    1181             if (fFlags & KeyExtended)
    1182             {
    1183                 codes[count++] = 0xE0;
    1184                 whatPressed = IsExtKeyPressed;
    1185             }
    1186 
    1187             if (fFlags & KeyPressed)
    1188             {
    1189                 codes[count++] = uScan;
    1190                 m_pressedKeys[uScan] |= whatPressed;
    1191             }
    1192             else
    1193             {
    1194                 /* If we haven't got this key's press message, we ignore its release: */
    1195                 if (!(m_pressedKeys[uScan] & whatPressed))
    1196                     return true;
    1197                 codes[count++] = uScan | 0x80;
    1198                 m_pressedKeys[uScan] &= ~whatPressed;
    1199             }
    1200 
    1201             if (m_fIsKeyboardCaptured)
    1202                 m_pressedKeys[uScan] |= IsKbdCaptured;
    1203             else
    1204                 m_pressedKeys[uScan] &= ~IsKbdCaptured;
    12051119        }
    12061120    }
    12071121    else
     1122    {
     1123        if (allHostComboKeys.contains(iKey) && m_pressedHostComboKeys.contains(iKey))
     1124            m_pressedHostComboKeys.remove(iKey);
     1125    }
     1126    /* Check if we are currently holding FULL host-combo: */
     1127    bool fIsFullHostComboPresent = allHostComboKeys == m_pressedHostComboKeys.keys().toSet();
     1128    /* Check if currently pressed/released key had changed host-combo state: */
     1129    const bool isHostComboStateChanged = (!m_bIsHostComboPressed && fIsFullHostComboPresent) ||
     1130                                         (m_bIsHostComboPressed && !fIsFullHostComboPresent);
     1131
     1132#ifdef Q_WS_WIN
     1133    if (m_bIsHostComboPressed || isHostComboStateChanged)
    12081134    {
    12091135        /* Currently this is used in winLowKeyboardEvent() only: */
    12101136        m_bIsHostkeyInCapture = m_fIsKeyboardCaptured;
    12111137    }
    1212 
    1213     bool emitSignal = false;
    1214     int hotkey = 0;
    1215 
    1216     /* Process the host key: */
     1138#endif /* Q_WS_WIN */
     1139
     1140    /* Check if it's C-A-D and GUI/PassCAD is not true: */
     1141    if (!m_fPassCAD &&
     1142        uScan == 0x53 /* Del */ &&
     1143        ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
     1144         (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
     1145        ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
     1146         (m_pressedKeys[0x1d] & IsExtKeyPressed)))
     1147    {
     1148        /* Use the C-A-D combination as a last resort to get the keyboard and mouse back
     1149         * to the host when the user forgets the Host Key. Note that it's always possible
     1150         * to send C-A-D to the guest using the Host+Del combination: */
     1151        if (uisession()->isRunning() && m_fIsKeyboardCaptured)
     1152        {
     1153            releaseKeyboard();
     1154            if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
     1155                machineLogic()->mouseHandler()->releaseMouse();
     1156        }
     1157        return true;
     1158    }
     1159
     1160    /* Prepare empty code-buffer: */
     1161    LONG aCodesBuffer[16];
     1162    LONG *pCodes = aCodesBuffer;
     1163    uint uCodesCount = 0;
     1164    /* Processing usual key-presses/releases without host-key being held: */
     1165    if (!m_bIsHostComboPressed || isHostComboStateChanged)
     1166    {
     1167        /* Special flags handling (KeyPrint): */
     1168        if (fFlags & KeyPrint)
     1169        {
     1170            if (fFlags & KeyPressed)
     1171            {
     1172                static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
     1173                pCodes = PrintMake;
     1174                uCodesCount = SIZEOF_ARRAY(PrintMake);
     1175            }
     1176            else
     1177            {
     1178                static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
     1179                pCodes = PrintBreak;
     1180                uCodesCount = SIZEOF_ARRAY(PrintBreak);
     1181            }
     1182        }
     1183        /* Special flags handling (KeyPause): */
     1184        else if (fFlags & KeyPause)
     1185        {
     1186            if (fFlags & KeyPressed)
     1187            {
     1188                static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
     1189                pCodes = Pause;
     1190                uCodesCount = SIZEOF_ARRAY(Pause);
     1191            }
     1192            else
     1193            {
     1194                /* Pause shall not produce a break code: */
     1195                return true;
     1196            }
     1197        }
     1198        /* Common flags handling: */
     1199        else
     1200        {
     1201            /* Get the type of key - simple or extended: */
     1202            uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
     1203
     1204            /* If some key was pressed or some previously pressed key was released =>
     1205             * we are updating the list of pressed keys and preparing scancodes: */
     1206            if ((fFlags & KeyPressed) || (m_pressedKeys[uScan] & uWhatPressed))
     1207            {
     1208                /* Check if the guest has the same view on the modifier keys
     1209                 * (NumLock, CapsLock, ScrollLock) as the X server.
     1210                 * If not, send KeyPress events to synchronize the state: */
     1211                if (fFlags & KeyPressed)
     1212                    fixModifierState(pCodes, &uCodesCount);
     1213
     1214                /* Prepend 'extended' scancode if needed: */
     1215                if (fFlags & KeyExtended)
     1216                    pCodes[uCodesCount++] = 0xE0;
     1217
     1218                /* Process key-press: */
     1219                if (fFlags & KeyPressed)
     1220                {
     1221                    /* Append scancode: */
     1222                    pCodes[uCodesCount++] = uScan;
     1223                    m_pressedKeys[uScan] |= uWhatPressed;
     1224                }
     1225                /* Process key-release if that key was pressed before: */
     1226                else if (m_pressedKeys[uScan] & uWhatPressed)
     1227                {
     1228                    /* Append scancode: */
     1229                    pCodes[uCodesCount++] = uScan | 0x80;
     1230                    m_pressedKeys[uScan] &= ~uWhatPressed;
     1231                }
     1232
     1233                /* Update keyboard-captured flag: */
     1234                if (m_fIsKeyboardCaptured)
     1235                    m_pressedKeys[uScan] |= IsKbdCaptured;
     1236                else
     1237                    m_pressedKeys[uScan] &= ~IsKbdCaptured;
     1238            }
     1239            /* Ignore key-release if that key was NOT pressed before,
     1240             * but only if thats not one of the host-combination keys: */
     1241            else if (!allHostComboKeys.contains(iKey))
     1242                return true;
     1243        }
     1244    }
     1245
     1246    /* Process the host-combo funtionality: */
    12171247    if (fFlags & KeyPressed)
    12181248    {
    1219         if (isHostKey)
    1220         {
    1221             if (!m_bIsHostkeyPressed)
    1222             {
    1223                 m_bIsHostkeyPressed = m_bIsHostkeyAlone = true;
     1249        if (isHostComboStateChanged)
     1250        {
     1251            if (!m_bIsHostComboPressed)
     1252            {
     1253                m_bIsHostComboPressed = true;
     1254                m_bIsHostComboAlone = true;
    12241255                if (uisession()->isRunning())
    12251256                    saveKeyStates();
    1226                 emitSignal = true;
    12271257            }
    12281258        }
    12291259        else
    12301260        {
    1231             if (m_bIsHostkeyPressed)
    1232             {
    1233                 if (m_bIsHostkeyAlone)
    1234                 {
    1235                     hotkey = iKey;
    1236                     m_bIsHostkeyAlone = false;
     1261            if (m_bIsHostComboPressed)
     1262            {
     1263                if (m_bIsHostComboAlone)
     1264                {
     1265                    m_bIsHostComboAlone = false;
     1266                    m_pressedHostComboKeys.clear();
     1267                    /* Process Host+<key> shortcuts.
     1268                     * Currently, <key> is limited to alphanumeric chars.
     1269                     * Other Host+<key> combinations are handled in Qt event(): */
     1270                    return processHotKey(iKey, pUniKey);
    12371271                }
    12381272            }
     
    12411275    else
    12421276    {
    1243         if (isHostKey)
    1244         {
    1245             if (m_bIsHostkeyPressed)
    1246             {
    1247                 m_bIsHostkeyPressed = false;
    1248 
    1249                 if (m_bIsHostkeyAlone)
    1250                 {
    1251                     if (uisession()->isPaused())
    1252                     {
    1253                         vboxProblem().remindAboutPausedVMInput();
    1254                     }
    1255                     else if (uisession()->isRunning())
     1277        if (isHostComboStateChanged)
     1278        {
     1279            if (m_bIsHostComboPressed)
     1280            {
     1281                m_bIsHostComboPressed = false;
     1282                if (m_bIsHostComboAlone)
     1283                {
     1284                    /* Capturing/releasing keyboard/mouse: */
     1285                    if (uisession()->isRunning())
    12561286                    {
    12571287                        bool ok = true;
    12581288                        if (!m_fIsKeyboardCaptured)
    12591289                        {
    1260                             /* Temporarily disable auto capture that will take
    1261                              * place after this dialog is dismissed because
    1262                              * the capture state is to be defined by the
    1263                              * dialog result itself */
     1290                            /* Temporarily disable auto-capture that will take place after
     1291                             * this dialog is dismissed because the capture state is to be
     1292                             * defined by the dialog result itself: */
    12641293                            uisession()->setAutoCaptureDisabled(true);
    1265                             bool autoConfirmed = false;
    1266                             ok = vboxProblem().confirmInputCapture(&autoConfirmed);
    1267                             if (autoConfirmed)
     1294                            bool fIsAutoConfirmed = false;
     1295                            ok = vboxProblem().confirmInputCapture(&fIsAutoConfirmed);
     1296                            if (fIsAutoConfirmed)
    12681297                                uisession()->setAutoCaptureDisabled(false);
    1269                             /* Otherwise, the disable flag will be reset in
    1270                              * the next console view's focus in event (since
    1271                              * may happen asynchronously on some platforms,
    1272                              * after we return from this code) */
     1298                            else
     1299                                m_pressedHostComboKeys.clear();
     1300                            /* Otherwise, the disable flag will be reset in the next
     1301                             * machine-view's focus-in event (since may happen asynchronously
     1302                             * on some platforms, after we return from this code): */
    12731303                        }
    1274 
    12751304                        if (ok)
    12761305                        {
     
    12841313                                /* Make sure that pending FocusOut events from the
    12851314                                 * previous message box are handled, otherwise the
    1286                                  * mouse is immediately ungrabbed. */
     1315                                 * mouse is immediately ungrabbed: */
    12871316                                qApp->processEvents();
    1288 #endif
     1317#endif /* Q_WS_X11 */
    12891318                                if (m_fIsKeyboardCaptured)
    12901319                                    machineLogic()->mouseHandler()->captureMouse(uScreenId);
     
    12951324                    }
    12961325                }
    1297 
    12981326                if (uisession()->isRunning())
    12991327                    sendChangedKeyStates();
    1300 
    1301                 emitSignal = true;
    13021328            }
    13031329        }
    13041330        else
    13051331        {
    1306             if (m_bIsHostkeyPressed)
    1307                 m_bIsHostkeyAlone = false;
     1332            if (m_bIsHostComboPressed)
     1333                m_bIsHostComboAlone = false;
    13081334        }
    13091335    }
     
    13121338    emit keyboardStateChanged(keyboardState());
    13131339
    1314     /* Process Host+<key> shortcuts. currently, <key> is limited to
    1315      * alphanumeric chars. Other Host+<key> combinations are handled in
    1316      * event(). */
    1317     if (hotkey)
    1318     {
    1319         bool processed = false;
    1320 #if defined (Q_WS_WIN)
    1321         NOREF(pUniKey);
    1322         int n = GetKeyboardLayoutList(0, NULL);
    1323         Assert(n);
    1324         HKL *list = new HKL[n];
    1325         GetKeyboardLayoutList(n, list);
    1326         for (int i = 0; i < n && !processed; i++)
    1327         {
    1328             wchar_t ch;
    1329             static BYTE keys[256] = {0};
    1330             if (!ToUnicodeEx(hotkey, 0, keys, &ch, 1, 0, list[i]) == 1)
    1331                 ch = 0;
    1332             if (ch)
    1333                 processed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(ch).toUpper().unicode())));
    1334         }
    1335         delete[] list;
    1336 #elif defined (Q_WS_X11)
    1337         NOREF(pUniKey);
    1338         Display *display = QX11Info::display();
    1339         int keysyms_per_keycode = getKeysymsPerKeycode();
    1340         KeyCode kc = XKeysymToKeycode (display, iKey);
    1341         for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)
    1342         {
    1343             KeySym ks = XKeycodeToKeysym(display, kc, i);
    1344             char ch = 0;
    1345             if (!XkbTranslateKeySym(display, &ks, 0, &ch, 1, NULL) == 1)
    1346                 ch = 0;
    1347             if (ch)
    1348             {
    1349                 QChar c = QString::fromLocal8Bit(&ch, 1)[0];
    1350                 processed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(c).toUpper().unicode())));
    1351             }
    1352         }
    1353 #elif defined (Q_WS_MAC)
    1354         if (pUniKey && pUniKey[0] && !pUniKey[1])
    1355             processed = machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pUniKey[0]).toUpper().unicode()));
    1356 
    1357         /* Don't consider the hot key as pressed since the guest never saw
    1358          * it. (probably a generic thing) */
    1359         m_pressedKeys[uScan] &= ~whatPressed;
    1360 #endif
    1361         /* Grab the key from Qt if processed, or pass it to Qt otherwise
    1362          * in order to process non-alphanumeric keys in event(), after they are
    1363          * converted to Qt virtual keys. */
    1364         return processed;
    1365     }
    1366 
    1367     /* No more to do, if the host key is in action or the VM is paused: */
    1368     if (m_bIsHostkeyPressed || isHostKey || uisession()->isPaused())
    1369     {
    1370         /* Grab the key from Qt and from VM if it's a host key,
    1371          * otherwise just pass it to Qt */
    1372         return isHostKey;
    1373     }
    1374 
    1375     CKeyboard keyboard = session().GetConsole().GetKeyboard();
    1376     Assert(!keyboard.isNull());
     1340    /* If the VM is NOT paused and there are scancodes to send: */
     1341    if (!uisession()->isPaused() && uCodesCount)
     1342    {
     1343        /* Get the  VM keyboard to pass key in there: */
     1344        CKeyboard keyboard = session().GetConsole().GetKeyboard();
     1345        Assert(!keyboard.isNull());
     1346
     1347        /* Pass this key to the guest: */
     1348        std::vector<LONG> scancodes(pCodes, &pCodes[uCodesCount]);
     1349        keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes));
     1350
     1351        /* If full host-key sequence was just finalized
     1352         * and the last key of host-combination was just sent to the guest =>
     1353         * we have to notify guest to make it release keys from the host-combination: */
     1354        if (isHostComboStateChanged && m_bIsHostComboPressed)
     1355        {
     1356            QList<uint8_t> hostComboScans = m_pressedHostComboKeys.values();
     1357            for (int i = 0 ; i < hostComboScans.size(); ++i)
     1358            {
     1359                uint8_t uScan = hostComboScans[i];
     1360                if (m_pressedKeys[uScan] & IsKeyPressed)
     1361                {
     1362                    keyboard.PutScancode(uScan | 0x80);
     1363                }
     1364                else if (m_pressedKeys[uScan] & IsExtKeyPressed)
     1365                {
     1366                    QVector<LONG> scancodes(2);
     1367                    scancodes[0] = 0xE0;
     1368                    scancodes[1] = uScan | 0x80;
     1369                    keyboard.PutScancodes(scancodes);
     1370                }
     1371                m_pressedKeys[uScan] = 0;
     1372            }
     1373        }
     1374    }
     1375
     1376    /* Prevent the key from going to Qt: */
     1377    return true;
     1378}
     1379
     1380bool UIKeyboardHandler::processHotKey(int iHotKey, wchar_t *pHotKey)
     1381{
     1382    /* Prepare processing result: */
     1383    bool fWasProcessed = false;
    13771384
    13781385#ifdef Q_WS_WIN
    1379     /* Send pending WM_PAINT events: */
    1380     ::UpdateWindow(m_views[uScreenId]->viewport()->winId());
     1386    Q_UNUSED(pHotKey);
     1387    int iKeyboardLayout = GetKeyboardLayoutList(0, NULL);
     1388    Assert(iKeyboardLayout);
     1389    HKL *pList = new HKL[iKeyboardLayout];
     1390    GetKeyboardLayoutList(iKeyboardLayout, pList);
     1391    for (int i = 0; i < iKeyboardLayout && !fWasProcessed; ++i)
     1392    {
     1393        wchar_t symbol;
     1394        static BYTE keys[256] = {0};
     1395        if (!ToUnicodeEx(iHotKey, 0, keys, &symbol, 1, 0, pList[i]) == 1)
     1396            symbol = 0;
     1397        if (symbol)
     1398            fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(symbol).toUpper().unicode())));
     1399    }
     1400    delete[] pList;
    13811401#endif /* Q_WS_WIN */
    13821402
    1383     std::vector <LONG> scancodes(codes, &codes[count]);
    1384     keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes));
    1385 
    1386     /* Grab the key from Qt: */
    1387     return true;
     1403#ifdef Q_WS_X11
     1404    Q_UNUSED(pHotKey);
     1405    Display *pDisplay = QX11Info::display();
     1406    int iKeysymsPerKeycode = getKeysymsPerKeycode();
     1407    KeyCode keyCode = XKeysymToKeycode(pDisplay, iHotKey);
     1408    for (int i = 0; i < iKeysymsPerKeycode && !fWasProcessed; i += 2)
     1409    {
     1410        KeySym ks = XKeycodeToKeysym(pDisplay, keyCode, i);
     1411        char symbol = 0;
     1412        if (!XkbTranslateKeySym(pDisplay, &ks, 0, &symbol, 1, NULL) == 1)
     1413            symbol = 0;
     1414        if (symbol)
     1415        {
     1416            QChar qtSymbol = QString::fromLocal8Bit(&symbol, 1)[0];
     1417            fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + qtSymbol.toUpper().unicode())));
     1418        }
     1419    }
     1420#endif /* Q_WS_X11 */
     1421
     1422#ifdef Q_WS_MAC
     1423    Q_UNUSED(iHotKey);
     1424    if (pHotKey && pHotKey[0] && !pHotKey[1])
     1425        fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pHotKey[0]).toUpper().unicode()));
     1426#endif /* Q_WS_MAC */
     1427
     1428    /* Grab the key from the Qt if it was processed, or pass it to the Qt otherwise
     1429     * in order to process non-alphanumeric keys in event(), after they are converted to Qt virtual keys: */
     1430    return fWasProcessed;
    13881431}
    13891432
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h

    r35686 r35730  
    7171
    7272    /* Some getters required by side-code: */
    73     bool isHostKeyPressed() const { return m_bIsHostkeyPressed; }
     73    bool isHostKeyPressed() const { return m_bIsHostComboPressed; }
    7474#ifdef Q_WS_MAC
    75     bool isHostKeyAlone() const { return m_bIsHostkeyAlone; }
     75    bool isHostKeyAlone() const { return m_bIsHostComboAlone; }
    7676    bool isKeyboardGrabbed() const { return m_fKeyboardGrabbed; }
    7777#endif /* Q_WS_MAC */
     
    126126    /* Separate function to handle most of existing keyboard-events: */
    127127    bool keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey = 0);
     128    bool processHotKey(int iHotKey, wchar_t *pUniKey);
    128129
    129130    /* Private helpers: */
     
    150151    uint8_t m_pressedKeysCopy[128];
    151152
     153    QMap<int, uint8_t> m_pressedHostComboKeys;
     154
    152155    bool m_fIsKeyboardCaptured : 1;
    153     bool m_bIsHostkeyPressed : 1;
    154     bool m_bIsHostkeyAlone : 1;
    155     bool m_bIsHostkeyInCapture : 1;
     156    bool m_bIsHostComboPressed : 1;
     157    bool m_bIsHostComboAlone : 1;
    156158    bool m_fPassCAD : 1;
    157159
    158160#if defined(Q_WS_WIN)
     161    /* Currently this is used in winLowKeyboardEvent() only: */
     162    bool m_bIsHostkeyInCapture;
    159163    /* Keyboard hook required to capture keyboard event under windows. */
    160     static UIKeyboardHandler *m_pKeyboardHandler;
     164    static UIKeyboardHandler *m_spKeyboardHandler;
    161165    HHOOK m_keyboardHook;
    162166    int m_iKeyboardHookViewIndex;
  • trunk/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp

    r35424 r35730  
    4545#include "QIStatusBar.h"
    4646#include "QIStateIndicator.h"
    47 #include "QIHotKeyEdit.h"
     47#include "UIHotKeyEditor.h"
    4848
    4949UIMachineWindowNormal::UIMachineWindowNormal(UIMachineLogic *pMachineLogic, ulong uScreenId)
     
    262262void UIMachineWindowNormal::sltProcessGlobalSettingChange(const char * /* aPublicName */, const char * /* aName */)
    263263{
    264     m_pNameHostkey->setText(QIHotKeyEdit::keyName(vboxGlobal().settings().hostKey()));
     264    m_pNameHostkey->setText(UIHotKeyCombination::toReadableString(vboxGlobal().settings().hostCombo()));
    265265}
    266266
     
    275275           "capture state. It can also be used in combination with other keys "
    276276           "to quickly perform actions from the main menu."));
    277     m_pNameHostkey->setText(QIHotKeyEdit::keyName(vboxGlobal().settings().hostKey()));
     277    m_pNameHostkey->setText(UIHotKeyCombination::toReadableString(vboxGlobal().settings().hostCombo()));
    278278}
    279279
     
    443443    pIndicatorBoxHLayout->addWidget(m_pCntHostkey);
    444444    pHostkeyLedContainerLayout->addWidget(indicatorsPool()->indicator(UIIndicatorIndex_Hostkey));
    445     m_pNameHostkey = new QLabel(QIHotKeyEdit::keyName(vboxGlobal().settings().hostKey()));
     445    m_pNameHostkey = new QLabel(UIHotKeyCombination::toReadableString(vboxGlobal().settings().hostCombo()));
    446446    pHostkeyLedContainerLayout->addWidget(m_pNameHostkey);
    447447
  • trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.cpp

    r33960 r35730  
    4040
    4141    /* Load to cache: */
    42     m_cache.m_iHostKey = m_settings.hostKey();
     42    m_cache.m_strHostCombo = m_settings.hostCombo();
    4343    m_cache.m_fAutoCapture = m_settings.autoCapture();
    4444
     
    5252{
    5353    /* Fetch from cache: */
    54     m_pHostKeyEditor->setKey(m_cache.m_iHostKey);
     54    m_pHostKeyEditor->setCombo(m_cache.m_strHostCombo);
    5555    m_pEnableAutoGrabCheckbox->setChecked(m_cache.m_fAutoCapture);
    5656}
     
    6161{
    6262    /* Upload to cache: */
    63     m_cache.m_iHostKey = m_pHostKeyEditor->key();
     63    m_cache.m_strHostCombo = m_pHostKeyEditor->combo();
    6464    m_cache.m_fAutoCapture = m_pEnableAutoGrabCheckbox->isChecked();
    6565}
     
    7373
    7474    /* Save from cache: */
    75     m_settings.setHostKey(m_cache.m_iHostKey);
     75    m_settings.setHostCombo(m_cache.m_strHostCombo);
    7676    m_settings.setAutoCapture(m_cache.m_fAutoCapture);
    7777
     
    8484{
    8585    setTabOrder(pWidget, m_pHostKeyEditor);
    86     setTabOrder(m_pHostKeyEditor, m_pResetHostKeyButton);
    87     setTabOrder(m_pResetHostKeyButton, m_pEnableAutoGrabCheckbox);
     86    setTabOrder(m_pHostKeyEditor, m_pEnableAutoGrabCheckbox);
    8887}
    8988
  • trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.h

    r33960 r35730  
    2727struct UISettingsCacheGlobalInput
    2828{
    29     int m_iHostKey;
     29    QString m_strHostCombo;
    3030    bool m_fAutoCapture;
    3131};
  • trunk/src/VBox/Frontends/VirtualBox/src/settings/global/UIGlobalSettingsInput.ui

    r34160 r35730  
    5454   </item>
    5555   <item row="0" column="2">
    56     <widget class="QIHotKeyEdit" name="m_pHostKeyEditor">
     56    <widget class="UIHotKeyEditor" name="m_pHostKeyEditor">
    5757     <property name="whatsThis">
    5858      <string>Displays the key used as a Host Key in the VM window. Activate the entry field and press a new Host Key. Note that alphanumeric, cursor movement and editing keys cannot be used.</string>
     
    6666    </widget>
    6767   </item>
    68    <item row="0" column="3">
    69     <widget class="UIResetButton" name="m_pResetHostKeyButton">
    70      <property name="focusPolicy">
    71       <enum>Qt::StrongFocus</enum>
    72      </property>
    73      <property name="toolTip">
    74       <string>Reset Host Key</string>
    75      </property>
    76      <property name="whatsThis">
    77       <string>Resets the key used as a Host Key in the VM window.</string>
    78      </property>
    79      <property name="icon">
    80       <iconset><normaloff>:/delete_16px.png</normaloff>:/delete_16px.png</iconset>
    81      </property>
    82     </widget>
    83    </item>
    84    <item row="0" column="4">
    85     <spacer>
    86      <property name="orientation">
    87       <enum>Qt::Horizontal</enum>
    88      </property>
    89      <property name="sizeType">
    90       <enum>QSizePolicy::Fixed</enum>
    91      </property>
    92      <property name="sizeHint" stdset="0">
    93       <size>
    94        <width>40</width>
    95        <height>20</height>
    96       </size>
    97      </property>
    98     </spacer>
    99    </item>
    100    <item row="1" column="2" colspan="3">
     68   <item row="1" column="2">
    10169    <widget class="QCheckBox" name="m_pEnableAutoGrabCheckbox">
    10270     <property name="whatsThis">
     
    10876    </widget>
    10977   </item>
    110    <item row="2" column="0" colspan="5">
     78   <item row="2" column="0" colspan="3">
    11179    <spacer>
    11280     <property name="orientation">
     
    12593 <customwidgets>
    12694  <customwidget>
    127    <class>QIHotKeyEdit</class>
     95   <class>UIHotKeyEditor</class>
    12896   <extends>QLabel</extends>
    129    <header>QIHotKeyEdit.h</header>
     97   <header>UIHotKeyEditor.h</header>
    13098  </customwidget>
    13199  <customwidget>
  • trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.cpp

    r35698 r35730  
    33 *
    44 * VBox frontends: Qt GUI ("VirtualBox"):
    5  * VirtualBox Qt extensions: QIHotKeyEdit class implementation
     5 * VirtualBox Qt extensions: UIHotKeyEditor class implementation
    66 */
    77
     
    1919
    2020/* Local includes */
    21 #include "QIHotKeyEdit.h"
     21#include "UIHotKeyEditor.h"
    2222#include "VBoxDefs.h"
    2323#include "VBoxGlobal.h"
     
    2727#include <QStyleOption>
    2828#include <QStylePainter>
     29#include <QKeyEvent>
    2930
    3031#ifdef Q_WS_WIN
    31 /* VBox/cdefs.h defines these: */
    3232# undef LOWORD
    3333# undef HIWORD
     
    3838
    3939#ifdef Q_WS_X11
    40 /* We need to capture some X11 events directly which
    41  * requires the XEvent structure to be defined. However,
    42  * including the Xlib header file will cause some nasty
    43  * conflicts with Qt. Therefore we use the following hack
    44  * to redefine those conflicting identifiers. */
    45 # define XK_XKB_KEYS
    46 # define XK_MISCELLANY
    47 # include <X11/Xlib.h>
     40# include <QX11Info>
    4841# include <X11/Xutil.h>
    49 # include <X11/keysymdef.h>
     42# include "XKeyboard.h"
    5043# ifdef KeyPress
    51 const int XFocusOut = FocusOut;
    52 const int XFocusIn = FocusIn;
    53 const int XKeyPress = KeyPress;
    54 const int XKeyRelease = KeyRelease;
     44   const int XKeyPress = KeyPress;
     45   const int XKeyRelease = KeyRelease;
     46#  undef KeyPress
    5547#  undef KeyRelease
    56 #  undef KeyPress
    57 #  undef FocusOut
    58 #  undef FocusIn
    5948# endif /* KeyPress */
    60 # include "XKeyboard.h"
    61 QMap<QString, QString> QIHotKeyEdit::s_keyNames;
    62 # include <QX11Info>
    6349#endif /* Q_WS_X11 */
    6450
     
    6854# include "VBoxUtils.h"
    6955# include <Carbon/Carbon.h>
    70 #endif
    71 
     56#endif /* Q_WS_MAC */
     57
     58
     59#ifdef Q_WS_X11
     60namespace UIHotKey
     61{
     62    QMap<QString, QString> m_keyNames;
     63}
     64#endif /* Q_WS_X11 */
     65
     66QString UIHotKey::toString(int iKeyCode)
     67{
     68    QString strKeyName;
    7269
    7370#ifdef Q_WS_WIN
    74 /* Returns the correct modifier vkey for the *last* keyboard message,
    75  * distinguishing between left and right keys. If both are pressed
    76  * the left key wins. If the pressed key not a modifier, wParam is returned
    77  * unchanged. */
    78 int qi_distinguish_modifier_vkey(WPARAM wParam)
    79 {
    80     int keyval = wParam;
    81     switch (wParam)
    82     {
    83         case VK_SHIFT:
    84             if (::GetKeyState(VK_LSHIFT) & 0x8000) keyval = VK_LSHIFT;
    85             else if (::GetKeyState(VK_RSHIFT) & 0x8000) keyval = VK_RSHIFT;
    86             break;
    87         case VK_CONTROL:
    88             if (::GetKeyState(VK_LCONTROL) & 0x8000) keyval = VK_LCONTROL;
    89             else if (::GetKeyState(VK_RCONTROL) & 0x8000) keyval = VK_RCONTROL;
    90             break;
    91         case VK_MENU:
    92             if (::GetKeyState(VK_LMENU) & 0x8000) keyval = VK_LMENU;
    93             else if (::GetKeyState(VK_RMENU) & 0x8000) keyval = VK_RMENU;
    94             break;
    95     }
    96     return keyval;
    97 }
     71    /* MapVirtualKey doesn't distinguish between right and left vkeys,
     72     * even under XP, despite that it stated in MSDN. Do it by hands.
     73     * Besides that it can't recognize such virtual keys as
     74     * VK_DIVIDE & VK_PAUSE, this is also known bug. */
     75    int iScan;
     76    switch (iKeyCode)
     77    {
     78        /* Processing special keys... */
     79        case VK_PAUSE: iScan = 0x45 << 16; break;
     80        case VK_RSHIFT: iScan = 0x36 << 16; break;
     81        case VK_RCONTROL: iScan = (0x1D << 16) | (1 << 24); break;
     82        case VK_RMENU: iScan = (0x38 << 16) | (1 << 24); break;
     83        /* Processing extended keys... */
     84        case VK_APPS:
     85        case VK_LWIN:
     86        case VK_RWIN:
     87        case VK_NUMLOCK: iScan = (::MapVirtualKey(iKeyCode, 0) | 256) << 16; break;
     88        default: iScan = ::MapVirtualKey(iKeyCode, 0) << 16;
     89    }
     90    TCHAR *pKeyName = new TCHAR[256];
     91    if (::GetKeyNameText(iScan, pKeyName, 256))
     92    {
     93        strKeyName = QString::fromUtf16(pKeyName);
     94    }
     95    else
     96    {
     97        AssertMsgFailed(("That key have no name!\n"));
     98        strKeyName = UIHotKeyEditor::tr("<key_%1>").arg(iKeyCode);
     99    }
     100    delete[] pKeyName;
    98101#endif /* Q_WS_WIN */
    99102
    100 
    101 const char *QIHotKeyEdit::m_spNoneSymbName = "None";
    102 
    103 QIHotKeyEdit::QIHotKeyEdit(QWidget *pParent)
    104     : QLabel(pParent)
    105 {
    106103#ifdef Q_WS_X11
    107     /* Initialize the X keyboard subsystem: */
    108     initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes"));
     104    if (char *pNativeKeyName = ::XKeysymToString((KeySym)iKeyCode))
     105    {
     106        strKeyName = m_keyNames[pNativeKeyName].isEmpty() ?
     107                     QString(pNativeKeyName) : m_keyNames[pNativeKeyName];
     108    }
     109    else
     110    {
     111        AssertMsgFailed(("That key have no name!\n"));
     112        strKeyName = UIHotKeyEditor::tr("<key_%1>").arg(iKeyCode);
     113    }
    109114#endif /* Q_WS_X11 */
    110115
    111     clear();
    112 
     116#ifdef Q_WS_MAC
     117    UInt32 modMask = DarwinKeyCodeToDarwinModifierMask(iKeyCode);
     118    switch (modMask)
     119    {
     120        case shiftKey:
     121        case optionKey:
     122        case controlKey:
     123        case cmdKey:
     124            strKeyName = tr("Left ");
     125            break;
     126        case rightShiftKey:
     127        case rightOptionKey:
     128        case rightControlKey:
     129        case kEventKeyModifierRightCmdKeyMask:
     130            strKeyName = tr("Right ");
     131            break;
     132        default:
     133            AssertMsgFailedReturn(("modMask=%#x\n", modMask), QString());
     134    }
     135    switch (modMask)
     136    {
     137        case shiftKey:
     138        case rightShiftKey:
     139            strKeyName += QChar(kShiftUnicode);
     140            break;
     141        case optionKey:
     142        case rightOptionKey:
     143            strKeyName += QChar(kOptionUnicode);
     144            break;
     145        case controlKey:
     146        case rightControlKey:
     147            strKeyName += QChar(kControlUnicode);
     148            break;
     149        case cmdKey:
     150        case kEventKeyModifierRightCmdKeyMask:
     151            strKeyName += QChar(kCommandUnicode);
     152            break;
     153    }
     154#endif /* Q_WS_MAC */
     155
     156    return strKeyName;
     157}
     158
     159bool UIHotKey::isValidKey(int iKeyCode)
     160{
    113161#ifdef Q_WS_WIN
    114     /* Qt documentation hasn't mentioned this is windows-only flag,
    115      * but looks like that is so, anyway it is required for winEvent() handler only: */
    116     setAttribute(Qt::WA_NativeWindow);
     162    return ((iKeyCode >= VK_SHIFT && iKeyCode <= VK_CAPITAL) ||
     163            (iKeyCode >= VK_LSHIFT && iKeyCode <= VK_RMENU) ||
     164            (iKeyCode >= VK_F1 && iKeyCode <= VK_F24) ||
     165            iKeyCode == VK_NUMLOCK || iKeyCode == VK_SCROLL ||
     166            iKeyCode == VK_LWIN || iKeyCode == VK_RWIN ||
     167            iKeyCode == VK_APPS ||
     168            iKeyCode == VK_PRINT);
    117169#endif /* Q_WS_WIN */
    118     setFrameStyle(QFrame::StyledPanel | Sunken);
    119     setAlignment(Qt::AlignCenter);
    120     setFocusPolicy(Qt::StrongFocus);
    121     setAutoFillBackground(true);
    122 
    123     QPalette p = palette();
    124     p.setColor(QPalette::Active, QPalette::Foreground, p.color(QPalette::Active, QPalette::Text));
    125     p.setColor(QPalette::Active, QPalette::Background, p.color(QPalette::Active, QPalette::Base));
    126     setPalette(p);
     170
     171#ifdef Q_WS_X11
     172    return (IsModifierKey(iKeyCode) /* allow modifiers */ ||
     173            IsFunctionKey(iKeyCode) /* allow function keys */ ||
     174            IsMiscFunctionKey(iKeyCode) /* allow miscellaneous function keys */ ||
     175            iKeyCode == XK_Scroll_Lock /* allow 'Scroll Lock' missed in IsModifierKey() */) &&
     176           (iKeyCode != NoSymbol /* ignore some special symbol */ &&
     177            iKeyCode != XK_Insert /* ignore 'insert' included into IsMiscFunctionKey */);
     178#endif /* Q_WS_X11 */
    127179
    128180#ifdef Q_WS_MAC
    129     m_uDarwinKeyModifiers = GetCurrentEventKeyModifiers();
    130     UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown  | NSKeyUp | | NSFlagsChanged */, QIHotKeyEdit::darwinEventHandlerProc, this);
    131     ::DarwinGrabKeyboard(false /* just modifiers */);
    132 #endif /* Q_WS_MAC */
    133 }
    134 
    135 QIHotKeyEdit::~QIHotKeyEdit()
    136 {
    137 #ifdef Q_WS_MAC
    138     ::DarwinReleaseKeyboard();
    139     UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown  | NSKeyUp | | NSFlagsChanged */, QIHotKeyEdit::darwinEventHandlerProc, this);
    140 #endif
    141 }
    142 
    143 /**
    144  *  Set the hot key value. O means there is no hot key.
    145  *
    146  *  @note
    147  *      The key value is platform-dependent. On Win32 it is the
    148  *      virtual key, on Linux it is the first (0) keysym corresponding
    149  *      to the keycode.
    150  */
    151 void QIHotKeyEdit::setKey(int iKeyVal)
    152 {
    153     m_iKeyVal = iKeyVal;
    154     m_strSymbName = QIHotKeyEdit::keyName(iKeyVal);
    155     updateText();
    156 }
    157 
    158 /**@@ QIHotKeyEdit::key() const
    159  *
    160  *  Returns the value of the last recorded hot key.
    161  *  O means there is no hot key.
    162  *
    163  *  @note
    164  *      The key value is platform-dependent. On Win32 it is the
    165  *      virtual key, on Linux it is the first (0) keysym corresponding
    166  *      to the keycode.
    167  */
    168 
    169 QSize QIHotKeyEdit::sizeHint() const
    170 {
    171     ensurePolished();
    172     QFontMetrics fm(font());
    173     int h = qMax(fm.lineSpacing(), 14) + 2;
    174     int w = fm.width('x') * 17;
    175     int m = frameWidth() * 2;
    176     QStyleOption option;
    177     option.initFrom(this);
    178     return (style()->sizeFromContents(QStyle::CT_LineEdit, &option,
    179                                       QSize(w + m, h + m)
    180                                       .expandedTo(QApplication::globalStrut()),
    181                                       this));
    182 }
    183 
    184 QSize QIHotKeyEdit::minimumSizeHint() const
    185 {
    186     ensurePolished();
    187     QFontMetrics fm = fontMetrics();
    188     int h = fm.height() + qMax(2, fm.leading());
    189     int w = fm.maxWidth();
    190     int m = frameWidth() * 2;
    191     return QSize(w + m, h + m);
    192 }
    193 
    194 /**
    195  *  Returns the string representation of a given key.
    196  *
    197  *  @note
    198  *      The key value is platform-dependent. On Win32 it is the
    199  *      virtual key, on Linux it is the first (0) keysym corresponding
    200  *      to the keycode.
    201  */
    202 /* static */
    203 QString QIHotKeyEdit::keyName(int iKeyVal)
    204 {
    205     QString strName;
    206 
    207     if (!iKeyVal)
    208     {
    209         strName = tr(m_spNoneSymbName);
    210     }
    211     else
    212     {
    213 #if defined (Q_WS_WIN)
    214         /* Stupid MapVirtualKey doesn't distinguish between right and left
    215          * vkeys, even under XP, despite that it stated in msdn. Do it by
    216          * hands. Besides that it can't recognize such virtual keys as
    217          * VK_DIVIDE & VK_PAUSE, this is also known bug. */
    218         int scan;
    219         switch (iKeyVal)
    220         {
    221             /* Processing special keys... */
    222             case VK_PAUSE: scan = 0x45 << 16; break;
    223             case VK_RSHIFT: scan = 0x36 << 16; break;
    224             case VK_RCONTROL: scan = (0x1D << 16) | (1 << 24); break;
    225             case VK_RMENU: scan = (0x38 << 16) | (1 << 24); break;
    226             /* Processing extended keys... */
    227             case VK_APPS:
    228             case VK_LWIN:
    229             case VK_RWIN:
    230             case VK_NUMLOCK: scan = (::MapVirtualKey(iKeyVal, 0) | 256) << 16; break;
    231             default: scan = ::MapVirtualKey(iKeyVal, 0) << 16;
    232         }
    233         TCHAR *str = new TCHAR[256];
    234         if (::GetKeyNameText(scan, str, 256))
    235         {
    236             strName = QString::fromUtf16(str);
    237         }
    238         else
    239         {
    240             AssertFailed();
    241             strName = QString(tr("<key_%1>")).arg(iKeyVal);
    242         }
    243         delete[] str;
    244 #elif defined (Q_WS_X11)
    245         char *sn = ::XKeysymToString((KeySym)iKeyVal);
    246         if (sn)
    247         {
    248             strName = s_keyNames[sn];
    249             if (strName.isEmpty())
    250                 strName = sn;
    251         }
    252         else
    253         {
    254             AssertFailed();
    255             strName = QString(tr("<key_%1>")).arg(iKeyVal);
    256         }
    257 #elif defined(Q_WS_MAC)
    258         UInt32 modMask = DarwinKeyCodeToDarwinModifierMask(iKeyVal);
    259         switch (modMask)
    260         {
    261             case shiftKey:
    262             case optionKey:
    263             case controlKey:
    264             case cmdKey:
    265                 strName = tr("Left ");
    266                 break;
    267             case rightShiftKey:
    268             case rightOptionKey:
    269             case rightControlKey:
    270             case kEventKeyModifierRightCmdKeyMask:
    271                 strName = tr("Right ");
    272                 break;
    273             default:
    274                 AssertMsgFailedReturn(("modMask=%#x\n", modMask), QString());
    275         }
    276         switch (modMask)
    277         {
    278             case shiftKey:
    279             case rightShiftKey:
    280                 strName += QChar(kShiftUnicode);
    281                 break;
    282             case optionKey:
    283             case rightOptionKey:
    284                 strName += QChar(kOptionUnicode);
    285                 break;
    286             case controlKey:
    287             case rightControlKey:
    288                 strName += QChar(kControlUnicode);
    289                 break;
    290             case cmdKey:
    291             case kEventKeyModifierRightCmdKeyMask:
    292                 strName += QChar(kCommandUnicode);
    293                 break;
    294         }
    295 #else
    296         AssertFailed();
    297         strName = QString(tr("<key_%1>")).arg(iKeyVal);
    298 #endif
    299     }
    300 
    301     return strName;
    302 }
    303 
    304 /* static */
    305 bool QIHotKeyEdit::isValidKey(int iKeyVal)
    306 {
    307     /* Empty value is correct: */
    308     if (iKeyVal == 0)
    309         return true;
    310 #if defined(Q_WS_WIN)
    311     return ((iKeyVal >= VK_SHIFT && iKeyVal <= VK_CAPITAL) ||
    312             iKeyVal == VK_PRINT ||
    313             iKeyVal == VK_LWIN || iKeyVal == VK_RWIN ||
    314             iKeyVal == VK_APPS ||
    315             (iKeyVal >= VK_F1 && iKeyVal <= VK_F24) ||
    316             iKeyVal == VK_NUMLOCK || iKeyVal == VK_SCROLL ||
    317             (iKeyVal >= VK_LSHIFT && iKeyVal <= VK_RMENU));
    318 #elif defined(Q_WS_X11)
    319     KeySym ks = (KeySym)iKeyVal;
    320     return (ks != NoSymbol && ks != XK_Insert) &&
    321            (ks == XK_Scroll_Lock || IsModifierKey(ks) || IsFunctionKey(ks) || IsMiscFunctionKey(ks));
    322 #elif defined(Q_WS_MAC)
    323     UInt32 modMask = ::DarwinKeyCodeToDarwinModifierMask(iKeyVal);
     181    UInt32 modMask = ::DarwinKeyCodeToDarwinModifierMask(iKeyCode);
    324182    switch (modMask)
    325183    {
     
    336194            return false;
    337195    }
    338 #else
    339     Q_UNUSED(iKeyVal);
     196#endif /* Q_WS_MAC */
     197
     198    return false;
     199}
     200
     201#ifdef Q_WS_WIN
     202int UIHotKey::distinguishModifierVKey(int wParam)
     203{
     204    int iKeyCode = wParam;
     205    switch (iKeyCode)
     206    {
     207        case VK_SHIFT:
     208            if (::GetKeyState(VK_LSHIFT) & 0x8000) iKeyCode = VK_LSHIFT;
     209            else if (::GetKeyState(VK_RSHIFT) & 0x8000) iKeyCode = VK_RSHIFT;
     210            break;
     211        case VK_CONTROL:
     212            if (::GetKeyState(VK_LCONTROL) & 0x8000) iKeyCode = VK_LCONTROL;
     213            else if (::GetKeyState(VK_RCONTROL) & 0x8000) iKeyCode = VK_RCONTROL;
     214            break;
     215        case VK_MENU:
     216            if (::GetKeyState(VK_LMENU) & 0x8000) iKeyCode = VK_LMENU;
     217            else if (::GetKeyState(VK_RMENU) & 0x8000) iKeyCode = VK_RMENU;
     218            break;
     219    }
     220    return iKeyCode;
     221}
     222#endif /* Q_WS_WIN */
     223
     224#ifdef Q_WS_X11
     225void UIHotKey::retranslateKeyNames()
     226{
     227    m_keyNames["Shift_L"]          = UIHotKeyEditor::tr("Left Shift");
     228    m_keyNames["Shift_R"]          = UIHotKeyEditor::tr("Right Shift");
     229    m_keyNames["Control_L"]        = UIHotKeyEditor::tr("Left Ctrl");
     230    m_keyNames["Control_R"]        = UIHotKeyEditor::tr("Right Ctrl");
     231    m_keyNames["Alt_L"]            = UIHotKeyEditor::tr("Left Alt");
     232    m_keyNames["Alt_R"]            = UIHotKeyEditor::tr("Right Alt");
     233    m_keyNames["Super_L"]          = UIHotKeyEditor::tr("Left WinKey");
     234    m_keyNames["Super_R"]          = UIHotKeyEditor::tr("Right WinKey");
     235    m_keyNames["Menu"]             = UIHotKeyEditor::tr("Menu key");
     236    m_keyNames["ISO_Level3_Shift"] = UIHotKeyEditor::tr("Alt Gr");
     237    m_keyNames["Caps_Lock"]        = UIHotKeyEditor::tr("Caps Lock");
     238    m_keyNames["Scroll_Lock"]      = UIHotKeyEditor::tr("Scroll Lock");
     239}
     240#endif /* Q_WS_X11 */
     241
     242
     243QString UIHotKeyCombination::toReadableString(const QString &strKeyCombo)
     244{
     245    QStringList encodedKeyList = strKeyCombo.split(',');
     246    QStringList readableKeyList;
     247    for (int i = 0; i < encodedKeyList.size(); ++i)
     248        if (int iKeyCode = encodedKeyList[i].toInt())
     249            readableKeyList << UIHotKey::toString(iKeyCode);
     250    return readableKeyList.isEmpty() ? UIHotKeyEditor::tr("None") : readableKeyList.join(" + ");
     251}
     252
     253QList<int> UIHotKeyCombination::toKeyCodeList(const QString &strKeyCombo)
     254{
     255    QStringList encodedKeyList = strKeyCombo.split(',');
     256    QList<int> keyCodeList;
     257    for (int i = 0; i < encodedKeyList.size(); ++i)
     258        if (int iKeyCode = encodedKeyList[i].toInt())
     259            keyCodeList << iKeyCode;
     260    return keyCodeList;
     261}
     262
     263bool UIHotKeyCombination::isValidKeyCombo(const QString &strKeyCombo)
     264{
     265    QList<int> keyCodeList = toKeyCodeList(strKeyCombo);
     266    for (int i = 0; i < keyCodeList.size(); ++i)
     267        if (!UIHotKey::isValidKey(keyCodeList[i]))
     268            return false;
    340269    return true;
    341 #endif
    342 }
     270}
     271
     272
     273UIHotKeyEditor::UIHotKeyEditor(QWidget *pParent)
     274    : QLabel(pParent)
     275    , m_fStartNewSequence(true)
     276{
     277    /* Configure widget: */
     278    setAttribute(Qt::WA_NativeWindow);
     279    setFrameStyle(QFrame::StyledPanel | Sunken);
     280    setAlignment(Qt::AlignCenter);
     281    setFocusPolicy(Qt::StrongFocus);
     282    setAutoFillBackground(true);
     283
     284    /* Setup palette: */
     285    QPalette p = palette();
     286    p.setColor(QPalette::Active, QPalette::Foreground, p.color(QPalette::Active, QPalette::Text));
     287    p.setColor(QPalette::Active, QPalette::Background, p.color(QPalette::Active, QPalette::Base));
     288    setPalette(p);
    343289
    344290#ifdef Q_WS_X11
    345 /* Updates the associative array containing the translations
    346  * of X11 key strings to human readable key names. */
    347 void QIHotKeyEdit::retranslateUi()
    348 {
    349     /* Note: strings for the same key must match strings in retranslateUi()
    350      * versions for all platforms, to keep translators happy. */
    351 
    352     s_keyNames["Shift_L"]          = tr("Left Shift");
    353     s_keyNames["Shift_R"]          = tr("Right Shift");
    354     s_keyNames["Control_L"]        = tr("Left Ctrl");
    355     s_keyNames["Control_R"]        = tr("Right Ctrl");
    356     s_keyNames["Alt_L"]            = tr("Left Alt");
    357     s_keyNames["Alt_R"]            = tr("Right Alt");
    358     s_keyNames["Super_L"]          = tr("Left WinKey");
    359     s_keyNames["Super_R"]          = tr("Right WinKey");
    360     s_keyNames["Menu"]             = tr("Menu key");
    361     s_keyNames["ISO_Level3_Shift"] = tr("Alt Gr");
    362     s_keyNames["Caps_Lock"]        = tr("Caps Lock");
    363     s_keyNames["Scroll_Lock"]      = tr("Scroll Lock");
    364 }
     291    /* Initialize the X keyboard subsystem: */
     292    initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes"));
    365293#endif /* Q_WS_X11 */
    366294
    367 void QIHotKeyEdit::clear()
    368 {
    369     m_iKeyVal = 0;
    370     m_strSymbName = tr(m_spNoneSymbName);
     295#ifdef Q_WS_MAC
     296    m_uDarwinKeyModifiers = GetCurrentEventKeyModifiers();
     297    UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown  | NSKeyUp | | NSFlagsChanged */, UIHotKeyEditor::darwinEventHandlerProc, this);
     298    ::DarwinGrabKeyboard(false /* just modifiers */);
     299#endif /* Q_WS_MAC */
     300}
     301
     302UIHotKeyEditor::~UIHotKeyEditor()
     303{
     304#ifdef Q_WS_MAC
     305    ::DarwinReleaseKeyboard();
     306    UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown  | NSKeyUp | | NSFlagsChanged */, UIHotKeyEditor::darwinEventHandlerProc, this);
     307#endif /* Q_WS_MAC */
     308}
     309
     310void UIHotKeyEditor::setCombo(const QString &strKeyCombo)
     311{
     312    /* Cleanup old combo: */
     313    m_shownKeys.clear();
     314    /* Parse newly passed combo: */
     315    QList<int> keyCodeList = UIHotKeyCombination::toKeyCodeList(strKeyCombo);
     316    for (int i = 0; i < keyCodeList.size(); ++i)
     317        if (int iKeyCode = keyCodeList[i])
     318            m_shownKeys.insert(iKeyCode, UIHotKey::toString(iKeyCode));
     319    /* Update text: */
    371320    updateText();
    372321}
    373322
    374 #if defined (Q_WS_WIN)
    375 
    376 bool QIHotKeyEdit::winEvent(MSG *pMsg, long* /* pResult */)
    377 {
    378     if (!(pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN ||
    379           pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP ||
    380           pMsg->message == WM_CHAR || pMsg->message == WM_SYSCHAR ||
    381           pMsg->message == WM_DEADCHAR || pMsg->message == WM_SYSDEADCHAR ||
    382           pMsg->message == WM_CONTEXTMENU))
    383         return false;
    384 
    385     /* Ignore if not a valid hot key: */
    386     if (!isValidKey(pMsg->wParam))
    387         return false;
    388 
    389 #if 0
    390     LogFlow(("%WM_%04X: vk=%04X rep=%05d scan=%02X ext=%01d"
    391              "rzv=%01X ctx=%01d prev=%01d tran=%01d\n",
    392              pMsg->message, pMsg->wParam,
    393              (pMsg->lParam & 0xFFFF),
    394              ((pMsg->lParam >> 16) & 0xFF),
    395              ((pMsg->lParam >> 24) & 0x1),
    396              ((pMsg->lParam >> 25) & 0xF),
    397              ((pMsg->lParam >> 29) & 0x1),
    398              ((pMsg->lParam >> 30) & 0x1),
    399              ((pMsg->lParam >> 31) & 0x1)));
    400 #endif
    401 
    402     if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
    403     {
    404         /* Determine platform-dependent key: */
    405         m_iKeyVal = qi_distinguish_modifier_vkey(pMsg->wParam);
    406         /* Determine symbolic name: */
    407         TCHAR *pStr = new TCHAR[256];
    408         if (::GetKeyNameText(pMsg->lParam, pStr, 256))
     323QString UIHotKeyEditor::combo() const
     324{
     325    /* Compose current combination: */
     326    QStringList keyCodeStringList;
     327    QList<int> keyCodeList = m_shownKeys.keys();
     328    for (int i = 0; i < keyCodeList.size(); ++i)
     329        keyCodeStringList << QString::number(keyCodeList[i]);
     330    /* Return current combination or "0" for "None": */
     331    return keyCodeStringList.isEmpty() ? "0" : keyCodeStringList.join(",");
     332}
     333
     334QSize UIHotKeyEditor::sizeHint() const
     335{
     336    ensurePolished();
     337    QFontMetrics fm(font());
     338    int h = qMax(fm.lineSpacing(), 14) + 2;
     339    int w = fm.width('x') * 17;
     340    int m = frameWidth() * 2;
     341    QStyleOption option;
     342    option.initFrom(this);
     343    return (style()->sizeFromContents(QStyle::CT_LineEdit, &option,
     344                                      QSize(w + m, h + m).expandedTo(QApplication::globalStrut()),
     345                                      this));
     346}
     347
     348QSize UIHotKeyEditor::minimumSizeHint() const
     349{
     350    ensurePolished();
     351    QFontMetrics fm = fontMetrics();
     352    int h = fm.height() + qMax(2, fm.leading());
     353    int w = fm.maxWidth();
     354    int m = frameWidth() * 2;
     355    return QSize(w + m, h + m);
     356}
     357
     358#ifdef Q_WS_WIN
     359bool UIHotKeyEditor::winEvent(MSG *pMsg, long* /* pResult */)
     360{
     361    switch (pMsg->message)
     362    {
     363        case WM_KEYDOWN:
     364        case WM_SYSKEYDOWN:
     365        case WM_KEYUP:
     366        case WM_SYSKEYUP:
    409367        {
    410             m_strSymbName = QString::fromUtf16(pStr);
     368            /* Get key-code: */
     369            int iKeyCode = UIHotKey::distinguishModifierVKey((int)pMsg->wParam);
     370
     371            /* Check if symbol is valid else pass it to Qt: */
     372            if (!UIHotKey::isValidKey(iKeyCode))
     373                return false;
     374
     375            /* Key press: */
     376            if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
     377            {
     378                /* Clear reflected symbols if new sequence started: */
     379                if (m_fStartNewSequence)
     380                    m_shownKeys.clear();
     381
     382                /* Remember pressed symbol: */
     383                m_pressedKeys << pMsg->wParam;
     384                m_shownKeys.insert(iKeyCode, UIHotKey::toString(iKeyCode));
     385
     386                /* Remember what we already started a sequence: */
     387                m_fStartNewSequence = false;
     388            }
     389            /* Key release: */
     390            else if (pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP)
     391            {
     392                /* Remove pressed symbol: */
     393                m_pressedKeys.remove(pMsg->wParam);
     394
     395                /* If pressed keys map is empty => start new sequence: */
     396                if (m_pressedKeys.isEmpty())
     397                    m_fStartNewSequence = true;
     398            }
     399
     400            /* Update text: */
     401            updateText();
     402
     403            /* Prevent passing to Qt: */
     404            return true;
    411405        }
    412         else
    413         {
    414             AssertFailed();
    415             m_strSymbName = QString(tr("<key_%1>")).arg(m_iKeyVal);
    416         }
    417         delete[] pStr;
    418         /* Update the display: */
    419         updateText();
    420     }
    421 
    422     return true;
    423 }
    424 
    425 #elif defined (Q_WS_X11)
    426 
    427 bool QIHotKeyEdit::x11Event(XEvent *pEvent)
     406        default:
     407            break;
     408    }
     409
     410    return false;
     411}
     412#endif /* Q_WS_WIN */
     413
     414#ifdef Q_WS_X11
     415bool UIHotKeyEditor::x11Event(XEvent *pEvent)
    428416{
    429417    switch (pEvent->type)
     
    432420        case XKeyRelease:
    433421        {
     422            /* Get key-code: */
    434423            XKeyEvent *pKeyEvent = (XKeyEvent*)pEvent;
    435424            KeySym ks = ::XKeycodeToKeysym(pKeyEvent->display, pKeyEvent->keycode, 0);
    436             /* Ignore if not a valid hot key: */
    437             if (!isValidKey((int)ks))
     425            int iKeySym = (int)ks;
     426
     427            /* Check if symbol is valid else pass it to Qt: */
     428            if (!UIHotKey::isValidKey(iKeySym))
    438429                return false;
    439430
    440             /* Skip key releases: */
    441             if (pEvent->type == XKeyRelease)
    442                 return true;
    443 
    444             /* Determine platform-dependent key: */
    445             m_iKeyVal = (int)ks;
    446             /* Determine symbolic name: */
    447             m_strSymbName = QIHotKeyEdit::keyName(m_iKeyVal);
    448             /* Update the display: */
     431            /* Key press: */
     432            if (pEvent->type == XKeyPress)
     433            {
     434                /* Clear reflected symbols if new sequence started: */
     435                if (m_fStartNewSequence)
     436                    m_shownKeys.clear();
     437
     438                /* Remember pressed symbol: */
     439                m_pressedKeys << iKeySym;
     440                m_shownKeys.insert(iKeySym, UIHotKey::toString(iKeySym));
     441
     442                /* Remember what we already started a sequence: */
     443                m_fStartNewSequence = false;
     444            }
     445            /* Key release: */
     446            else if (pEvent->type == XKeyRelease)
     447            {
     448                /* Remove pressed symbol: */
     449                m_pressedKeys.remove(iKeySym);
     450
     451                /* If pressed keys map is empty => start new sequence: */
     452                if (m_pressedKeys.isEmpty())
     453                    m_fStartNewSequence = true;
     454            }
     455
     456            /* Update text: */
    449457            updateText();
    450 #if 0
    451             LogFlow(("%s: state=%08X keycode=%08X keysym=%08X symb=%s\n",
    452                      pEvent->type == XKeyPress ? "XKeyPress" : "XKeyRelease",
    453                      pKeyEvent->state, pKeyEvent->keycode, ks,
    454                      symbname.latin1()));
    455 #endif
     458
     459            /* Prevent passing to Qt: */
    456460            return true;
    457461        }
     
    460464    return false;
    461465}
    462 
    463 #elif defined (Q_WS_MAC)
    464 
     466#endif /* Q_WS_X11 */
     467
     468#ifdef Q_WS_MAC
    465469/* static */
    466 bool QIHotKeyEdit::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
    467 {
    468     QIHotKeyEdit *edit = (QIHotKeyEdit*)pvUser;
     470bool UIHotKeyEditor::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
     471{
     472    UIHotKeyEditor *edit = (UIHotKeyEditor*)pvUser;
    469473    EventRef inEvent = (EventRef)pvCarbonEvent;
    470474    UInt32 EventClass = ::GetEventClass(inEvent);
     
    474478}
    475479
    476 bool QIHotKeyEdit::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent)
    477 {
    478 #if 0 /* for debugging */
    479     ::darwinDebugPrintEvent("QIHotKeyEdit", inEvent);
    480 #endif
    481 
     480bool UIHotKeyEditor::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent)
     481{
    482482    /* Ignore key changes unless we're the focus widget: */
    483483    if (!hasFocus())
     
    487487    switch (eventKind)
    488488    {
    489         /*case kEventRawKeyDown:
    490         case kEventRawKeyUp:
    491         case kEventRawKeyRepeat:*/
     489        //case kEventRawKeyDown:
     490        //case kEventRawKeyUp:
     491        //case kEventRawKeyRepeat:
    492492        case kEventRawKeyModifiersChanged:
    493493        {
     494            /* Get modifier mask: */
    494495            UInt32 modifierMask = 0;
    495496            ::GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, NULL,
    496497                                sizeof(modifierMask), NULL, &modifierMask);
    497 
    498498            modifierMask = ::DarwinAdjustModifierMask(modifierMask, pvCocoaEvent);
    499499            UInt32 changed = m_uDarwinKeyModifiers ^ modifierMask;
    500500            m_uDarwinKeyModifiers = modifierMask;
    501501
    502             /* Skip key releases: */
    503             if (changed && (changed & modifierMask))
     502            /* Convert to keycode: */
     503            int iKeyCode = ::DarwinModifierMaskToDarwinKeycode(changed);
     504
     505            /* Check if symbol is valid else pass it to Qt: */
     506            if (!iKeyCode || iKeyCode == ~0U || !UIHotKey::isValidKey(iKeyCode))
    504507                break;
    505508
    506             /* Convert to keycode and skip keycodes we don't care about. */
    507             unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode(changed);
    508             if (!keyCode || keyCode == ~0U || !isValidKey(keyCode))
    509                 break;
    510 
    511             /* Update key current key: */
    512             m_iKeyVal = keyCode;
    513             m_strSymbName = QIHotKeyEdit::keyName(keyCode);
     509            if (changed)
     510            {
     511                /* Key release: */
     512                if (changed & modifierMask)
     513                {
     514                    /* Remove pressed symbol: */
     515                    m_pressedKeys.remove(iKeyCode);
     516
     517                    /* If pressed key map is empty => start new sequence: */
     518                    if (m_pressedKeys.isEmpty())
     519                        m_fStartNewSequence = true;
     520                }
     521                /* Key press: */
     522                else
     523                {
     524                    /* Clear reflected symbols if new sequence started: */
     525                    if (m_fStartNewSequence)
     526                        m_shownKeys.clear();
     527
     528                    /* Remember pressed symbol: */
     529                    m_pressedKeys << iKeyCode;
     530                    m_shownKeys.insert(iKeyCode, UIHotKey::toString(iKeyCode));
     531
     532                    /* Remember what we already started a sequence: */
     533                    m_fStartNewSequence = false;
     534                }
     535            }
     536
     537            /* Update text: */
    514538            updateText();
    515             break; //return true;
    516539        }
    517540        break;
     
    519542    return false;
    520543}
    521 
    522 #else
    523 # warning "Port me!"
    524 #endif
    525 
    526 void QIHotKeyEdit::focusInEvent(QFocusEvent *pEvent)
     544#endif /* Q_WS_MAC */
     545
     546void UIHotKeyEditor::keyPressEvent(QKeyEvent *pEvent)
     547{
     548    switch (pEvent->key())
     549    {
     550        case Qt::Key_Delete:
     551        case Qt::Key_Backspace:
     552            m_shownKeys.clear();
     553            updateText();
     554            break;
     555        default:
     556            QWidget::keyPressEvent(pEvent);
     557            break;
     558    }
     559}
     560
     561void UIHotKeyEditor::focusInEvent(QFocusEvent *pEvent)
    527562{
    528563    QLabel::focusInEvent(pEvent);
     
    534569}
    535570
    536 void QIHotKeyEdit::focusOutEvent(QFocusEvent *pEvent)
     571void UIHotKeyEditor::focusOutEvent(QFocusEvent *pEvent)
    537572{
    538573    QLabel::focusOutEvent(pEvent);
     
    544579}
    545580
    546 void QIHotKeyEdit::paintEvent(QPaintEvent *pEvent)
     581void UIHotKeyEditor::paintEvent(QPaintEvent *pEvent)
    547582{
    548583    if (hasFocus())
     
    558593}
    559594
    560 void QIHotKeyEdit::updateText()
    561 {
    562     setText(QString(" %1 ").arg(m_strSymbName));
    563 }
    564 
     595void UIHotKeyEditor::updateText()
     596{
     597    QStringList shownKeyNames(m_shownKeys.values());
     598    setText(shownKeyNames.isEmpty() ? tr("None") : shownKeyNames.join(" + "));
     599}
     600
  • trunk/src/VBox/Frontends/VirtualBox/src/widgets/UIHotKeyEditor.h

    r35698 r35730  
    22 *
    33 * VBox frontends: Qt GUI ("VirtualBox"):
    4  * VirtualBox Qt extensions: QIHotKeyEdit class declaration
     4 * VirtualBox Qt extensions: UIHotKeyEditor class declaration
    55 */
    66
     
    1717 */
    1818
    19 #ifndef ___QIHotKeyEdit_h___
    20 #define ___QIHotKeyEdit_h___
     19#ifndef ___UIHotKeyEditor_h___
     20#define ___UIHotKeyEditor_h___
    2121
    2222/* Global includes */
    2323#include <QLabel>
     24#include <QMap>
     25#include <QSet>
    2426
     27/* Hot-key namespace to unify
     28 * all the related hot-key processing stuff: */
     29namespace UIHotKey
     30{
     31    QString toString(int iKeyCode);
     32    bool isValidKey(int iKeyCode);
     33#ifdef Q_WS_WIN
     34    int distinguishModifierVKey(int wParam);
     35#endif /* Q_WS_WIN */
    2536#ifdef Q_WS_X11
    26 # include <QMap>
     37    void retranslateKeyNames();
    2738#endif /* Q_WS_X11 */
     39}
    2840
    29 class QIHotKeyEdit : public QLabel
     41/* Hot-combo namespace to unify
     42 * all the related hot-combo processing stuff: */
     43namespace UIHotKeyCombination
    3044{
    31     Q_OBJECT
     45    QString toReadableString(const QString &strKeyCombo);
     46    QList<int> toKeyCodeList(const QString &strKeyCombo);
     47    bool isValidKeyCombo(const QString &strKeyCombo);
     48}
     49
     50class UIHotKeyEditor : public QLabel
     51{
     52    Q_OBJECT;
    3253
    3354public:
    3455
    35     QIHotKeyEdit(QWidget *pParent);
    36     virtual ~QIHotKeyEdit();
     56    UIHotKeyEditor(QWidget *pParent);
     57    virtual ~UIHotKeyEditor();
    3758
    38     void setKey(int iKeyVal);
    39     int key() const { return m_iKeyVal; }
    40 
    41     QString symbolicName() const { return m_strSymbName; }
     59    void setCombo(const QString &strKeyCombo);
     60    QString combo() const;
    4261
    4362    QSize sizeHint() const;
    4463    QSize minimumSizeHint() const;
    4564
    46     static QString keyName(int iKeyVal);
    47     static bool isValidKey(int iKeyVal);
    48 #ifdef Q_WS_X11
    49     static void retranslateUi();
    50 #endif /* Q_WS_X11 */
    51 
    52 public slots:
    53 
    54     void clear();
    55 
    5665protected:
    5766
    58 #if defined (Q_WS_WIN32)
     67#ifdef Q_WS_WIN
    5968    bool winEvent(MSG *pMsg, long *pResult);
    60 #elif defined (Q_WS_X11)
     69#endif /* Q_WS_WIN */
     70#ifdef Q_WS_X11
    6171    bool x11Event(XEvent *pEvent);
    62 #elif defined (Q_WS_MAC)
     72#endif /* Q_WS_X11 */
     73#ifdef Q_WS_MAC
    6374    static bool darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);
    6475    bool darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent);
    65 #endif
     76#endif /* Q_WS_MAC */
    6677
     78    void keyPressEvent(QKeyEvent *pEvent);
    6779    void focusInEvent(QFocusEvent *pEvent);
    6880    void focusOutEvent(QFocusEvent *pEvent);
     
    7385    void updateText();
    7486
    75     int m_iKeyVal;
    76     QString m_strSymbName;
     87    QSet<int> m_pressedKeys;
     88    QMap<int, QString> m_shownKeys;
    7789
    78 #ifdef Q_WS_X11
    79     static QMap<QString, QString> s_keyNames;
    80 #endif /* Q_WS_X11 */
     90    bool m_fStartNewSequence;
    8191
    8292#ifdef Q_WS_MAC
     
    8595    uint32_t m_uDarwinKeyModifiers;
    8696#endif /* Q_WS_MAC */
    87 
    88     static const char *m_spNoneSymbName;
    8997};
    9098
    91 #endif // !___QIHotKeyEdit_h___
     99#endif // !___UIHotKeyEditor_h___
    92100
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette