Changeset 434 for trunk/src


Ignore:
Timestamp:
Dec 22, 2009, 2:34:55 AM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: Implemented proper generation of possible key and modifier combinations for a given key event. This in particular enables Ctrl+<letter> shortcuts for non-Latin languages and also makes Ctrl+<letter>, Alt+<letter> and similar shortcuts language-neutral. See #50 for details.

Location:
trunk/src/gui/kernel
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/kernel/qapplication_pm.cpp

    r432 r434  
    6565#include "qcursor_p.h"
    6666
     67#define WM_KBDLAYERCHANGED   0x0BD4 // defined in OS2TK45/h/pmbidi.h
     68
    6769//#define QT_DEBUGMSGFLOW
    6870
     
    944946
    945947            case WM_CHAR: { // keyboard event
     948                if (!(CHARMSG(&qmsg.msg)->fs & KC_KEYUP))
     949                    qt_keymapper_private()->updateKeyMap(qmsg);
     950
    946951                QWidget *g = QWidget::keyboardGrabber();
    947952                if (g)
     
    966971                        return (MRESULT)TRUE;
    967972                }
     973                break;
     974            }
     975
     976            case WM_KBDLAYERCHANGED: { // Keyboard layout change
     977                QKeyMapper::changeKeyboard();
    968978                break;
    969979            }
     
    25292539    myCaseEnd()
    25302540
     2541    myCaseBegin(WM_KBDLAYERCHANGED)
     2542        str += QString().sprintf(" mp1 %p mp2 %p", qmsg.mp1, qmsg.mp2);
     2543        break;
     2544    myCaseEnd()
     2545
    25312546    myCaseBegin(WM_PAINT)
    25322547        break;
  • trunk/src/gui/kernel/qkeymapper.cpp

    r2 r434  
    9191void QKeyMapper::changeKeyboard()
    9292{
     93#ifdef Q_WS_PM
     94    // don't clear key mappings on layout change on OS/2 since we keep track of
     95    // both layouts (Latin and National) in parallel
     96#else
    9397    instance()->d_func()->clearMappings();
     98#endif
    9499
    95100    // inform all toplevel widgets of the change
  • trunk/src/gui/kernel/qkeymapper_p.h

    r136 r434  
    166166#elif defined(Q_WS_PM)
    167167
     168    void updateKeyMap(const QMSG &qmsg);
    168169    bool translateKeyEvent(QWidget *receiver, const QMSG &qmsg, bool grab);
    169170
    170171    int extraKeyState;
     172    KeyboardLayoutItem *keyLayout[256];
     173    enum { KeyLayoutSize = sizeof(keyLayout) / sizeof(keyLayout[0]) };
    171174
    172175#elif defined(Q_WS_X11)
  • trunk/src/gui/kernel/qkeymapper_pm.cpp

    r433 r434  
    4949#include "qt_os2.h"
    5050
     51//#define DEBUG_KEYMAPPER
     52
     53// BIDI API --------------------------------------------------------[ start ] --
     54// copied from pmbidi.h from the OS/2 toolkit as Innotek GCC headers lack this
     55
     56#define KL_LATIN     0x00000000
     57#define KL_NATIONAL  0x00000001
     58
     59ULONG   APIENTRY WinSetKbdLayer     (HWND hwnd,
     60                                     ULONG idKbdLayer,
     61                                     ULONG flFlags);
     62
     63ULONG   APIENTRY WinQueryKbdLayer   (HWND hwnd);
     64
     65ULONG   APIENTRY WinQueryKbdLayout  (HWND hwndDesktop);
     66
     67BOOL    APIENTRY WinSetKbdLayout    (HWND hwndDesktop,
     68                                     ULONG idKbdLayout);
     69
     70// BIDI API ----------------------------------------------------------[ end ] --
     71
    5172QT_BEGIN_NAMESPACE
    52 
    53 //#define DEBUG_KEYMAPPER
    5473
    5574// Key recorder ----------------------------------------------------[ start ] --
     
    246265// Keyboard map private --------------------------------------------[ start ]---
    247266
     267/*
     268    \internal
     269
     270    An OS/2 KeyboardLayoutItem has 8 possible states meaningful for Qt:
     271        1. Unmodified
     272        2. Shift
     273        3. Control
     274        4. Control + Shift
     275        5. Alt
     276        6. Alt + Shift
     277        7. Alt + Control
     278        8. Alt + Control + Shift
     279*/
     280struct KeyboardLayoutItem {
     281    int qtKey[8][2];    // Can be any Qt::Key_<foo>, or unicode character.
     282                        // The second index is for different keyboard layouts
     283    ULONG layoutIds[2]; // Latin layout ID + National layout ID
     284    enum { QtKeySize = sizeof(qtKey) / sizeof(qtKey[0]) };
     285};
     286
     287// Possible Qt modifier states. Must match KbdModsTbl
     288static const Qt::KeyboardModifiers QtModsTbl[KeyboardLayoutItem::QtKeySize] = {
     289    Qt::NoModifier,                                             // 0
     290    Qt::ShiftModifier,                                          // 1
     291    Qt::ControlModifier,                                        // 2
     292    Qt::ControlModifier | Qt::ShiftModifier,                    // 3
     293    Qt::AltModifier,                                            // 4
     294    Qt::AltModifier | Qt::ShiftModifier,                        // 5
     295    Qt::AltModifier | Qt::ControlModifier,                      // 6
     296    Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier,  // 7
     297};
     298
     299// Possible OS/2 keyboard modifier states. Must match QtModsTbl
     300static const USHORT KbdModsTbl[] = {
     301    0,                                                          // 0
     302    KBDSTF_RIGHTSHIFT,                                          // 1
     303    KBDSTF_CONTROL,                                             // 2
     304    KBDSTF_CONTROL | KBDSTF_RIGHTSHIFT,                         // 3
     305    KBDSTF_ALT,                                                 // 4
     306    KBDSTF_ALT | KBDSTF_RIGHTSHIFT,                             // 5
     307    KBDSTF_ALT | KBDSTF_CONTROL,                                // 6
     308    KBDSTF_ALT | KBDSTF_RIGHTSHIFT | KBDSTF_CONTROL,            // 7
     309};
     310
    248311QKeyMapperPrivate::QKeyMapperPrivate()
    249312{
     
    252315    extraKeyState = 0;
    253316
    254    // @todo implement
     317    memset(keyLayout, 0, sizeof(keyLayout));
    255318}
    256319
     
    262325void QKeyMapperPrivate::clearMappings()
    263326{
    264     // @todo implement
     327    for (int i = 0; i < KeyLayoutSize; ++i) {
     328        if (keyLayout[i]) {
     329            delete keyLayout[i];
     330            keyLayout[i] = 0;
     331        }
     332    }
    265333}
    266334
     
    269337    QList<int> result;
    270338
    271     // @todo implement; so far do the same as QKeyMapper::possibleKeys()
    272     if (e->key() && (e->key() != Qt::Key_unknown))
    273         result << int(e->key() + e->modifiers());
    274     else if (!e->text().isEmpty())
    275         result << int(e->text().at(0).unicode() + e->modifiers());
     339    KeyboardLayoutItem *kbItem = keyLayout[e->nativeScanCode()];
     340    if(!kbItem) {
     341#ifdef DEBUG_KEYMAPPER
     342        qDebug("QKeyMapperPrivate::possibleKeys: none");
     343#endif
     344        return result;
     345    }
     346
     347    int baseKey0 = kbItem->qtKey[0][0] ? kbItem->qtKey[0][0] :
     348                   e->key() && e->key() != Qt::Key_unknown ? e->key() :
     349                   e->text().at(0).unicode();
     350
     351    int baseKey1 = baseKey0;
     352    if (kbItem->layoutIds[1])
     353        kbItem->qtKey[0][1] ? kbItem->qtKey[0][1] :
     354                   e->key() && e->key() != Qt::Key_unknown ? e->key() :
     355                   e->text().at(0).unicode();
     356
     357    Qt::KeyboardModifiers keyMods = e->modifiers();
     358
     359    // The base key is _always_ valid, of course
     360    result << int(baseKey0 + keyMods);
     361    if (baseKey1 != baseKey0)
     362        result << int(baseKey1 + keyMods);
     363
     364    // go through both keyboard layouts
     365    for (int j = 0; j < 2; ++j) {
     366        // check if we skipped the layout in updateKeyMap() and skip too if so
     367        if (!kbItem->layoutIds[j])
     368            continue;
     369        // go through all modifiers
     370        for(int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
     371            Qt::KeyboardModifiers neededMods = QtModsTbl[i];
     372            int key = kbItem->qtKey[i][j];
     373            if (key && key != baseKey0 && key != baseKey1 &&
     374                ((keyMods & neededMods) == neededMods)) {
     375                int k = int(key + (keyMods & ~neededMods));
     376                if (!result.contains(k))
     377                    result << k;
     378            }
     379        }
     380    }
     381
     382#ifdef DEBUG_KEYMAPPER
     383    qDebug("QKeyMapperPrivate::possibleKeys:");
     384    foreach(int k, result)
     385        qDebug("    0x%x", k);
     386#endif
     387
    276388    return result;
     389}
     390
     391void QKeyMapperPrivate::updateKeyMap(const QMSG &qmsg)
     392{
     393    CHRMSG chm = *CHARMSG(&qmsg.msg);
     394
     395    // it may be a keyboard layout change message, see translateKeyEvent()
     396    // for details
     397    if ((chm.fs & KC_VIRTUALKEY) && chm.vkey == 0) {
     398        if (chm.chr == 0xF0 || chm.chr == 0xF1) {
     399            chm.fs |= KC_ALT | KC_SHIFT;
     400            chm.vkey = VK_SHIFT;
     401            chm.scancode = chm.chr == 0xF1 ? 0x2A : 0x36;
     402            chm.chr = 0;
     403        }
     404    }
     405
     406    if (!chm.scancode)
     407        return;
     408
     409    if (!keyLayout[chm.scancode])
     410        keyLayout[chm.scancode] = new KeyboardLayoutItem;
     411
     412    KBDTRANS kt;
     413    ULONG curLayerId = WinQueryKbdLayer(qmsg.hwnd);
     414
     415#ifdef DEBUG_KEYMAPPER
     416    int layoutsChanged = 0;
     417#endif
     418
     419    // go through both keyboard layouts
     420    for (int j = 0; j < 2; ++j) {
     421        WinSetKbdLayer(qmsg.hwnd, j ? KL_NATIONAL : KL_LATIN, 0);
     422        // check if the data is still valid and skip if so. Also skip the
     423        // National layout if it's the same as Latin
     424        ULONG layoutId = WinQueryKbdLayout(HWND_DESKTOP);
     425        if (keyLayout[chm.scancode]->layoutIds[j] == layoutId ||
     426            (j && keyLayout[chm.scancode]->layoutIds[0] == layoutId))
     427            continue;
     428        keyLayout[chm.scancode]->layoutIds[j] = layoutId;
     429
     430        // now go through all modifiers
     431        for (int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
     432            // reset all kbd states and modifiers
     433            memset(&kt, 0, sizeof(KBDTRANS));
     434            kt.chScan = chm.scancode;
     435            kt.fsState = KbdModsTbl[i];
     436            APIRET arc = KbdXlate(&kt, 0);
     437            Q_ASSERT(arc == NO_ERROR);
     438            keyLayout[chm.scancode]->qtKey[i][j] =
     439                QString::fromLocal8Bit((char*)&kt.chChar, 1)[0].toUpper().unicode();
     440        }
     441#ifdef DEBUG_KEYMAPPER
     442        ++layoutsChanged;
     443#endif
     444    }
     445
     446    // restore the layout
     447    WinSetKbdLayer(qmsg.hwnd, curLayerId, 0);
     448
     449#ifdef DEBUG_KEYMAPPER
     450    if (layoutsChanged) {
     451        qDebug("QKeyMapperPrivate::updateKeyMap: scancode 0x%02x", chm.scancode);
     452        for (int i = 0; i < KeyboardLayoutItem::QtKeySize; ++i) {
     453            qDebug("    [%d] (0x%04x, '%lc') (0x%04x, '%lc')", i,
     454                   keyLayout[chm.scancode]->qtKey[i][0],
     455                   keyLayout[chm.scancode]->qtKey[i][0] < 0x20 ? ' ' :
     456                   keyLayout[chm.scancode]->qtKey[i][0],
     457                   keyLayout[chm.scancode]->qtKey[i][1],
     458                   keyLayout[chm.scancode]->qtKey[i][1] < 0x20 ? ' ' :
     459                   keyLayout[chm.scancode]->qtKey[i][1]);
     460        }
     461    }
     462#endif
    277463}
    278464
     
    334520    // char code in the high byte of chm.chr (probably this is less
    335521    // device-dependent than scancode)
    336     switch (chm.chr) {
    337         case 0xEC00: // LWIN
    338         case 0xED00: // RWIN
    339             code = Qt::Key_Meta;
    340             if (!(chm.fs & KC_KEYUP))
    341                 extraKeyState |= Qt::MetaModifier;
    342             else
    343                 extraKeyState &= ~Qt::MetaModifier;
    344             break;
    345         case 0xEE00: // WINAPP (menu with arrow)
    346             code = Qt::Key_Menu;
    347             break;
    348         case 0x5600: // additional '\' (0x56 is actually its scancode)
    349             chm.chr = state & Qt::ShiftModifier ? '|' : '\\';
    350             break;
    351         case 0xFA00: // Back
    352             code = Qt::Key_Back;
    353             break;
    354         case 0xF900: // Forward
    355             code = Qt::Key_Forward;
    356             break;
    357         case 0x2064: // Volume Mute
    358             code = Qt::Key_VolumeMute;
    359             break;
    360         case 0x2E63: // Volume Down
    361             code = Qt::Key_VolumeDown;
    362             break;
    363         case 0x3062: // Volume Up
    364             code = Qt::Key_VolumeUp;
    365             break;
    366         case 0x2267: // Play/Pause
    367             code = Qt::Key_MediaPlay;
    368             break;
    369         case 0x326D: // Web/Home
    370             code = Qt::Key_HomePage;
    371             break;
    372         case 0xF500: // Search
    373             code = Qt::Key_Search;
    374             break;
    375         case 0xF600: // Favorites
    376             code = Qt::Key_Favorites;
    377             break;
    378         }
     522    if (!code){
     523        switch (chm.chr) {
     524            case 0xEC00: // LWIN
     525            case 0xED00: // RWIN
     526                code = Qt::Key_Meta;
     527                if (!(chm.fs & KC_KEYUP))
     528                    extraKeyState |= Qt::MetaModifier;
     529                else
     530                    extraKeyState &= ~Qt::MetaModifier;
     531                break;
     532            case 0xEE00: // WINAPP (menu with arrow)
     533                code = Qt::Key_Menu;
     534                break;
     535            case 0x5600: // additional '\' (0x56 is actually its scancode)
     536                chm.chr = state & Qt::ShiftModifier ? '|' : '\\';
     537                break;
     538            case 0xFA00: // Back
     539                code = Qt::Key_Back;
     540                break;
     541            case 0xF900: // Forward
     542                code = Qt::Key_Forward;
     543                break;
     544            case 0x2064: // Volume Mute
     545                code = Qt::Key_VolumeMute;
     546                break;
     547            case 0x2E63: // Volume Down
     548                code = Qt::Key_VolumeDown;
     549                break;
     550            case 0x3062: // Volume Up
     551                code = Qt::Key_VolumeUp;
     552                break;
     553            case 0x2267: // Play/Pause
     554                code = Qt::Key_MediaPlay;
     555                break;
     556            case 0x326D: // Web/Home
     557                code = Qt::Key_HomePage;
     558                break;
     559            case 0xF500: // Search
     560                code = Qt::Key_Search;
     561                break;
     562            case 0xF600: // Favorites
     563                code = Qt::Key_Favorites;
     564                break;
     565        }
     566    }
    379567
    380568    // update state after updating extraKeyState
Note: See TracChangeset for help on using the changeset viewer.