/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

#include <private/qt_mac_p.h>
#include <qdebug.h>
#include <qevent.h>
#include <private/qevent_p.h>
#include <qtextcodec.h>
#include <qapplication.h>
#include <qinputcontext.h>
#include <private/qkeymapper_p.h>
#include <private/qapplication_p.h>
#include <private/qmacinputcontext_p.h>

QT_BEGIN_NAMESPACE

QT_USE_NAMESPACE

/*****************************************************************************
  QKeyMapper debug facilities
 *****************************************************************************/
//#define DEBUG_KEY_BINDINGS
//#define DEBUG_KEY_BINDINGS_MODIFIERS
//#define DEBUG_KEY_MAPS

/*****************************************************************************
  Internal variables and functions
 *****************************************************************************/
bool qt_mac_eat_unicode_key = false;
extern bool qt_sendSpontaneousEvent(QObject *obj, QEvent *event); //qapplication_mac.cpp

Q_GUI_EXPORT void qt_mac_secure_keyboard(bool b)
{
    static bool secure = false;
    if (b != secure){
        b ? EnableSecureEventInput() : DisableSecureEventInput();
        secure = b;
    }
}

/*
    \internal
    A Mac KeyboardLayoutItem has 8 possible states:
        1. Unmodified
        2. Shift
        3. Control
        4. Control + Shift
        5. Alt
        6. Alt + Shift
        7. Alt + Control
        8. Alt + Control + Shift
        9. Meta
        10. Meta + Shift
        11. Meta + Control
        12. Meta + Control + Shift
        13. Meta + Alt
        14. Meta + Alt + Shift
        15. Meta + Alt + Control
        16. Meta + Alt + Control + Shift
*/
struct KeyboardLayoutItem {
    bool dirty;
    quint32 qtKey[16]; // Can by any Qt::Key_<foo>, or unicode character
};

// Possible modifier states.
// NOTE: The order of these states match the order in QKeyMapperPrivate::updatePossibleKeyCodes()!
static const Qt::KeyboardModifiers ModsTbl[] = {
    Qt::NoModifier,                                             // 0
    Qt::ShiftModifier,                                          // 1
    Qt::ControlModifier,                                        // 2
    Qt::ControlModifier | Qt::ShiftModifier,                    // 3
    Qt::AltModifier,                                            // 4
    Qt::AltModifier | Qt::ShiftModifier,                        // 5
    Qt::AltModifier | Qt::ControlModifier,                      // 6
    Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier,  // 7
    Qt::MetaModifier,                                           // 8
    Qt::MetaModifier | Qt::ShiftModifier,                       // 9
    Qt::MetaModifier | Qt::ControlModifier,                    // 10
    Qt::MetaModifier | Qt::ControlModifier | Qt::ShiftModifier,// 11
    Qt::MetaModifier | Qt::AltModifier,                        // 12
    Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier,    // 13
    Qt::MetaModifier | Qt::AltModifier | Qt::ControlModifier,  // 14
    Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier,  // 15
};

/* key maps */
struct qt_mac_enum_mapper
{
    int mac_code;
    int qt_code;
#if defined(DEBUG_KEY_BINDINGS)
#   define QT_MAC_MAP_ENUM(x) x, #x
    const char *desc;
#else
#   define QT_MAC_MAP_ENUM(x) x
#endif
};

//modifiers
static qt_mac_enum_mapper qt_mac_modifier_symbols[] = {
    { shiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
    { rightShiftKey, QT_MAC_MAP_ENUM(Qt::ShiftModifier) },
    { controlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
    { rightControlKey, QT_MAC_MAP_ENUM(Qt::MetaModifier) },
    { cmdKey, QT_MAC_MAP_ENUM(Qt::ControlModifier) },
    { optionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
    { rightOptionKey, QT_MAC_MAP_ENUM(Qt::AltModifier) },
    { kEventKeyModifierNumLockMask, QT_MAC_MAP_ENUM(Qt::KeypadModifier) },
    { 0, QT_MAC_MAP_ENUM(0) }
};
Qt::KeyboardModifiers qt_mac_get_modifiers(int keys)
{
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
    qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", keys, keys);
#endif
    Qt::KeyboardModifiers ret = Qt::NoModifier;
    for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
        if (keys & qt_mac_modifier_symbols[i].mac_code) {
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
            qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
#endif
            ret |= Qt::KeyboardModifier(qt_mac_modifier_symbols[i].qt_code);
        }
    }
    if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
        Qt::KeyboardModifiers oldModifiers = ret;
        ret &= ~(Qt::MetaModifier | Qt::ControlModifier);
        if (oldModifiers & Qt::ControlModifier)
            ret |= Qt::MetaModifier;
        if (oldModifiers & Qt::MetaModifier)
            ret |= Qt::ControlModifier;
    }
    return ret;
}
static int qt_mac_get_mac_modifiers(Qt::KeyboardModifiers keys)
{
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
    qDebug("Qt: internal: **Mapping modifiers: %d (0x%04x)", (int)keys, (int)keys);
#endif
    int ret = 0;
    for (int i = 0; qt_mac_modifier_symbols[i].qt_code; i++) {
        if (keys & qt_mac_modifier_symbols[i].qt_code) {
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
            qDebug("Qt: internal: got modifier: %s", qt_mac_modifier_symbols[i].desc);
#endif
            ret |= qt_mac_modifier_symbols[i].mac_code;
        }
    }

    if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
        int oldModifiers = ret;
        ret &= ~(controlKeyBit | cmdKeyBit);
        if (oldModifiers & controlKeyBit)
            ret |= cmdKeyBit;
        if (oldModifiers & cmdKeyBit)
            ret |= controlKeyBit;
    }
    return ret;
}
void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object)
{
    static quint32 cachedModifiers = 0;
    quint32 lastModifiers = cachedModifiers,
          changedModifiers = lastModifiers ^ modifiers;
    cachedModifiers = modifiers;

    //check the bits
    static qt_mac_enum_mapper modifier_key_symbols[] = {
        { shiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) },
        { rightShiftKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Shift) }, //???
        { controlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) },
        { rightControlKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Meta) }, //???
        { cmdKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Control) },
        { optionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) },
        { rightOptionKeyBit, QT_MAC_MAP_ENUM(Qt::Key_Alt) }, //???
        { alphaLockBit, QT_MAC_MAP_ENUM(Qt::Key_CapsLock) },
        { kEventKeyModifierNumLockBit, QT_MAC_MAP_ENUM(Qt::Key_NumLock) },
        {   0, QT_MAC_MAP_ENUM(0) } };
    for (int i = 0; i <= 32; i++) { //just check each bit
        if (!(changedModifiers & (1 << i)))
            continue;
        QEvent::Type etype = QEvent::KeyPress;
        if (lastModifiers & (1 << i))
            etype = QEvent::KeyRelease;
        int key = 0;
        for (uint x = 0; modifier_key_symbols[x].mac_code; x++) {
            if (modifier_key_symbols[x].mac_code == i) {
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
                qDebug("got modifier changed: %s", modifier_key_symbols[x].desc);
#endif
                key = modifier_key_symbols[x].qt_code;
                break;
            }
        }
        if (!key) {
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
            qDebug("could not get modifier changed: %d", i);
#endif
            continue;
        }
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
        qDebug("KeyEvent (modif): Sending %s to %s::%s: %d - 0x%08x",
               etype == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
               object ? object->metaObject()->className() : "none",
               object ? object->objectName().toLatin1().constData() : "",
               key, (int)modifiers);
#endif
        QKeyEvent ke(etype, key, qt_mac_get_modifiers(modifiers ^ (1 << i)), QLatin1String(""));
        qt_sendSpontaneousEvent(object, &ke);
    }
}

//keyboard keys (non-modifiers)
static qt_mac_enum_mapper qt_mac_keyboard_symbols[] = {
    { kHomeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Home) },
    { kEnterCharCode, QT_MAC_MAP_ENUM(Qt::Key_Enter) },
    { kEndCharCode, QT_MAC_MAP_ENUM(Qt::Key_End) },
    { kBackspaceCharCode, QT_MAC_MAP_ENUM(Qt::Key_Backspace) },
    { kTabCharCode, QT_MAC_MAP_ENUM(Qt::Key_Tab) },
    { kPageUpCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageUp) },
    { kPageDownCharCode, QT_MAC_MAP_ENUM(Qt::Key_PageDown) },
    { kReturnCharCode, QT_MAC_MAP_ENUM(Qt::Key_Return) },
    { kEscapeCharCode, QT_MAC_MAP_ENUM(Qt::Key_Escape) },
    { kLeftArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Left) },
    { kRightArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Right) },
    { kUpArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Up) },
    { kDownArrowCharCode, QT_MAC_MAP_ENUM(Qt::Key_Down) },
    { kHelpCharCode, QT_MAC_MAP_ENUM(Qt::Key_Help) },
    { kDeleteCharCode, QT_MAC_MAP_ENUM(Qt::Key_Delete) },
//ascii maps, for debug
    { ':', QT_MAC_MAP_ENUM(Qt::Key_Colon) },
    { ';', QT_MAC_MAP_ENUM(Qt::Key_Semicolon) },
    { '<', QT_MAC_MAP_ENUM(Qt::Key_Less) },
    { '=', QT_MAC_MAP_ENUM(Qt::Key_Equal) },
    { '>', QT_MAC_MAP_ENUM(Qt::Key_Greater) },
    { '?', QT_MAC_MAP_ENUM(Qt::Key_Question) },
    { '@', QT_MAC_MAP_ENUM(Qt::Key_At) },
    { ' ', QT_MAC_MAP_ENUM(Qt::Key_Space) },
    { '!', QT_MAC_MAP_ENUM(Qt::Key_Exclam) },
    { '"', QT_MAC_MAP_ENUM(Qt::Key_QuoteDbl) },
    { '#', QT_MAC_MAP_ENUM(Qt::Key_NumberSign) },
    { '$', QT_MAC_MAP_ENUM(Qt::Key_Dollar) },
    { '%', QT_MAC_MAP_ENUM(Qt::Key_Percent) },
    { '&', QT_MAC_MAP_ENUM(Qt::Key_Ampersand) },
    { '\'', QT_MAC_MAP_ENUM(Qt::Key_Apostrophe) },
    { '(', QT_MAC_MAP_ENUM(Qt::Key_ParenLeft) },
    { ')', QT_MAC_MAP_ENUM(Qt::Key_ParenRight) },
    { '*', QT_MAC_MAP_ENUM(Qt::Key_Asterisk) },
    { '+', QT_MAC_MAP_ENUM(Qt::Key_Plus) },
    { ',', QT_MAC_MAP_ENUM(Qt::Key_Comma) },
    { '-', QT_MAC_MAP_ENUM(Qt::Key_Minus) },
    { '.', QT_MAC_MAP_ENUM(Qt::Key_Period) },
    { '/', QT_MAC_MAP_ENUM(Qt::Key_Slash) },
    { '[', QT_MAC_MAP_ENUM(Qt::Key_BracketLeft) },
    { ']', QT_MAC_MAP_ENUM(Qt::Key_BracketRight) },
    { '\\', QT_MAC_MAP_ENUM(Qt::Key_Backslash) },
    { '_', QT_MAC_MAP_ENUM(Qt::Key_Underscore) },
    { '`', QT_MAC_MAP_ENUM(Qt::Key_QuoteLeft) },
    { '{', QT_MAC_MAP_ENUM(Qt::Key_BraceLeft) },
    { '}', QT_MAC_MAP_ENUM(Qt::Key_BraceRight) },
    { '|', QT_MAC_MAP_ENUM(Qt::Key_Bar) },
    { '~', QT_MAC_MAP_ENUM(Qt::Key_AsciiTilde) },
    { '^', QT_MAC_MAP_ENUM(Qt::Key_AsciiCircum) },
    {   0, QT_MAC_MAP_ENUM(0) }
};

static qt_mac_enum_mapper qt_mac_keyvkey_symbols[] = { //real scan codes
    { 122, QT_MAC_MAP_ENUM(Qt::Key_F1) },
    { 120, QT_MAC_MAP_ENUM(Qt::Key_F2) },
    { 99,  QT_MAC_MAP_ENUM(Qt::Key_F3) },
    { 118, QT_MAC_MAP_ENUM(Qt::Key_F4) },
    { 96,  QT_MAC_MAP_ENUM(Qt::Key_F5) },
    { 97,  QT_MAC_MAP_ENUM(Qt::Key_F6) },
    { 98,  QT_MAC_MAP_ENUM(Qt::Key_F7) },
    { 100, QT_MAC_MAP_ENUM(Qt::Key_F8) },
    { 101, QT_MAC_MAP_ENUM(Qt::Key_F9) },
    { 109, QT_MAC_MAP_ENUM(Qt::Key_F10) },
    { 103, QT_MAC_MAP_ENUM(Qt::Key_F11) },
    { 111, QT_MAC_MAP_ENUM(Qt::Key_F12) },
    { 105, QT_MAC_MAP_ENUM(Qt::Key_F13) },
    { 107, QT_MAC_MAP_ENUM(Qt::Key_F14) },
    { 113, QT_MAC_MAP_ENUM(Qt::Key_F15) },
    { 106, QT_MAC_MAP_ENUM(Qt::Key_F16) },
    {   0, QT_MAC_MAP_ENUM(0) }
};

static int qt_mac_get_key(int modif, const QChar &key, int virtualKey)
{
#ifdef DEBUG_KEY_BINDINGS
    qDebug("**Mapping key: %d (0x%04x) - %d (0x%04x)", key.unicode(), key.unicode(), virtualKey, virtualKey);
#endif

    if (key == kClearCharCode && virtualKey == 0x47)
        return Qt::Key_Clear;

    if (key.isDigit()) {
#ifdef DEBUG_KEY_BINDINGS
            qDebug("%d: got key: %d", __LINE__, key.digitValue());
#endif
        return key.digitValue() + Qt::Key_0;
    }

    if (key.isLetter()) {
#ifdef DEBUG_KEY_BINDINGS
        qDebug("%d: got key: %d", __LINE__, (key.toUpper().unicode() - 'A'));
#endif
        return (key.toUpper().unicode() - 'A') + Qt::Key_A;
    }
    if (key.isSymbol()) {
#ifdef DEBUG_KEY_BINDINGS
        qDebug("%d: got key: %d", __LINE__, (key.unicode()));
#endif
        return key.unicode();
    }

    for (int i = 0; qt_mac_keyboard_symbols[i].qt_code; i++) {
        if (qt_mac_keyboard_symbols[i].mac_code == key) {
            /* To work like Qt for X11 we issue Backtab when Shift + Tab are pressed */
            if (qt_mac_keyboard_symbols[i].qt_code == Qt::Key_Tab && (modif & Qt::ShiftModifier)) {
#ifdef DEBUG_KEY_BINDINGS
                qDebug("%d: got key: Qt::Key_Backtab", __LINE__);
#endif
                return Qt::Key_Backtab;
            }

#ifdef DEBUG_KEY_BINDINGS
            qDebug("%d: got key: %s", __LINE__, qt_mac_keyboard_symbols[i].desc);
#endif
            return qt_mac_keyboard_symbols[i].qt_code;
        }
    }

    //last ditch try to match the scan code
    for (int i = 0; qt_mac_keyvkey_symbols[i].qt_code; i++) {
        if (qt_mac_keyvkey_symbols[i].mac_code == virtualKey) {
#ifdef DEBUG_KEY_BINDINGS
            qDebug("%d: got key: %s", __LINE__, qt_mac_keyvkey_symbols[i].desc);
#endif
            return qt_mac_keyvkey_symbols[i].qt_code;
        }
    }

    //oh well
#ifdef DEBUG_KEY_BINDINGS
    qDebug("Unknown case.. %s:%d %d[%d] %d", __FILE__, __LINE__, key.unicode(), key.toLatin1(), virtualKey);
#endif
    return Qt::Key_unknown;
}

static Boolean qt_KeyEventComparatorProc(EventRef inEvent, void *data)
{
    UInt32 ekind = GetEventKind(inEvent),
           eclass = GetEventClass(inEvent);
    return (eclass == kEventClassKeyboard && (void *)ekind == data);
}

static bool translateKeyEventInternal(EventHandlerCallRef er, EventRef keyEvent, int *qtKey,
                                      QChar *outChar, Qt::KeyboardModifiers *outModifiers, bool *outHandled)
{
#if !defined(QT_MAC_USE_COCOA) || defined(Q_OS_MAC64)
    Q_UNUSED(er);
    Q_UNUSED(outHandled);
#endif
    const UInt32 ekind = GetEventKind(keyEvent);
    {
        UInt32 mac_modifiers = 0;
        GetEventParameter(keyEvent, kEventParamKeyModifiers, typeUInt32, 0,
                          sizeof(mac_modifiers), 0, &mac_modifiers);
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
        qDebug("************ Mapping modifiers and key ***********");
#endif
        *outModifiers = qt_mac_get_modifiers(mac_modifiers);
#ifdef DEBUG_KEY_BINDINGS_MODIFIERS
        qDebug("------------ Mapping modifiers and key -----------");
#endif
    }

    //get keycode
    UInt32 keyCode = 0;
    GetEventParameter(keyEvent, kEventParamKeyCode, typeUInt32, 0, sizeof(keyCode), 0, &keyCode);

    //get mac mapping
    static UInt32 tmp_unused_state = 0L;
    const UCKeyboardLayout *uchrData = 0;
#if defined(Q_OS_MAC32)
    KeyboardLayoutRef keyLayoutRef = 0;
    KLGetCurrentKeyboardLayout(&keyLayoutRef);
    OSStatus err;
    if (keyLayoutRef != 0) {
        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
                                  (reinterpret_cast<const void **>(&uchrData)));
        if (err != noErr) {
            qWarning("Qt::internal::unable to get keyboardlayout %ld %s:%d",
                     long(err), __FILE__, __LINE__);
        }
    }
#else
    QCFType<TISInputSourceRef> inputSource = TISCopyCurrentKeyboardInputSource();
    Q_ASSERT(inputSource != 0);
    CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(inputSource,
                                                                 kTISPropertyUnicodeKeyLayoutData));
    uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
#endif
    *qtKey = Qt::Key_unknown;
    if (uchrData) {
        // The easy stuff; use the unicode stuff!
        UniChar string[4];
        UniCharCount actualLength;
        UInt32 currentModifiers = GetCurrentEventKeyModifiers();
        UInt32 currentModifiersWOAltOrControl = currentModifiers & ~(controlKey | optionKey);
        int keyAction;
        switch (ekind) {
        default:
        case kEventRawKeyDown:
            keyAction = kUCKeyActionDown;
            break;
        case kEventRawKeyUp:
            keyAction = kUCKeyActionUp;
            break;
        case kEventRawKeyRepeat:
            keyAction = kUCKeyActionAutoKey;
            break;
        }
        OSStatus err = UCKeyTranslate(uchrData, keyCode, keyAction,
                                  ((currentModifiersWOAltOrControl >> 8) & 0xff), LMGetKbdType(),
                                  kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
                                  string);
        if (err == noErr) {
            *outChar = QChar(string[0]);
            *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
            if (currentModifiersWOAltOrControl != currentModifiers) {
                // Now get the real char.
                err = UCKeyTranslate(uchrData, keyCode, keyAction,
                                     ((currentModifiers >> 8) & 0xff), LMGetKbdType(),
                                      kUCKeyTranslateNoDeadKeysMask, &tmp_unused_state, 4, &actualLength,
                                      string);
                if (err == noErr)
                    *outChar = QChar(string[0]);
            }
        } else {
            qWarning("Qt::internal::UCKeyTranslate is returnining %ld %s:%d",
                     long(err), __FILE__, __LINE__);
        }
    }
#ifdef Q_OS_MAC32
    else {
        // The road less travelled; use KeyTranslate
        const void *keyboard_layout;
        KeyboardLayoutRef keyLayoutRef = 0;
        KLGetCurrentKeyboardLayout(&keyLayoutRef);
        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
                                  reinterpret_cast<const void **>(&keyboard_layout));

        int translatedChar = KeyTranslate(keyboard_layout, (GetCurrentEventKeyModifiers() &
                                                             (kEventKeyModifierNumLockMask|shiftKey|cmdKey|
                                                              rightShiftKey|alphaLock)) | keyCode,
                                           &tmp_unused_state);
        if (!translatedChar) {
#ifdef QT_MAC_USE_COCOA
            if (outHandled) {
                qt_mac_eat_unicode_key = false;
                if (er)
                    CallNextEventHandler(er, keyEvent);
                *outHandled = qt_mac_eat_unicode_key;
            }
#endif
            return false;
        }

        //map it into qt keys
        *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
        if (*outModifiers & (Qt::AltModifier | Qt::ControlModifier)) {
            if (translatedChar & (1 << 7)) //high ascii
                translatedChar = 0;
        } else {          //now get the real ascii value
            UInt32 tmp_mod = 0L;
            static UInt32 tmp_state = 0L;
            if (*outModifiers & Qt::ShiftModifier)
                tmp_mod |= shiftKey;
            if (*outModifiers & Qt::MetaModifier)
                tmp_mod |= controlKey;
            if (*outModifiers & Qt::ControlModifier)
                tmp_mod |= cmdKey;
            if (GetCurrentEventKeyModifiers() & alphaLock) //no Qt mapper
                tmp_mod |= alphaLock;
            if (*outModifiers & Qt::AltModifier)
                tmp_mod |= optionKey;
            if (*outModifiers & Qt::KeypadModifier)
                tmp_mod |= kEventKeyModifierNumLockMask;
            translatedChar = KeyTranslate(keyboard_layout, tmp_mod | keyCode, &tmp_state);
        }
        {
            ByteCount unilen = 0;
            if (GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0)
                    == noErr && unilen == 2) {
                GetEventParameter(keyEvent, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, outChar);
            } else if (translatedChar) {
                static QTextCodec *c = 0;
                if (!c)
                    c = QTextCodec::codecForName("Apple Roman");
		char tmpChar = (char)translatedChar; // **sigh**
                *outChar = c->toUnicode(&tmpChar, 1).at(0);
            } else {
                *qtKey = qt_mac_get_key(*outModifiers, QChar(translatedChar), keyCode);
            }
        }
    }
#endif
    if (*qtKey == Qt::Key_unknown)
        *qtKey = qt_mac_get_key(*outModifiers, *outChar, keyCode);
    return true;
}

QKeyMapperPrivate::QKeyMapperPrivate()
{
    memset(keyLayout, 0, sizeof(keyLayout));
    keyboard_layout_format.unicode = 0;
#ifdef Q_OS_MAC32
    keyboard_mode = NullMode;
#else
    currentInputSource = 0;
#endif
}

QKeyMapperPrivate::~QKeyMapperPrivate()
{
    deleteLayouts();
}

bool
QKeyMapperPrivate::updateKeyboard()
{
    const UCKeyboardLayout *uchrData = 0;
#ifdef Q_OS_MAC32
    KeyboardLayoutRef keyLayoutRef = 0;
    KLGetCurrentKeyboardLayout(&keyLayoutRef);

    if (keyboard_mode != NullMode && currentKeyboardLayout == keyLayoutRef)
        return false;

    OSStatus err;
    if (keyLayoutRef != 0) {
        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLuchrData,
                                  const_cast<const void **>(reinterpret_cast<const void **>(&uchrData)));
        if (err != noErr) {
            qWarning("Qt::internal::unable to get unicode keyboardlayout %ld %s:%d",
                     long(err), __FILE__, __LINE__);
        }
    }
#else
    QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource();
    if (keyboard_mode != NullMode && source == currentInputSource) {
        return false;
    }
    Q_ASSERT(source != 0);
    CFDataRef data = static_cast<CFDataRef>(TISGetInputSourceProperty(source,
                                                                 kTISPropertyUnicodeKeyLayoutData));
    uchrData = data ? reinterpret_cast<const UCKeyboardLayout *>(CFDataGetBytePtr(data)) : 0;
#endif

    keyboard_kind = LMGetKbdType();
    if (uchrData) {
        keyboard_layout_format.unicode = uchrData;
        keyboard_mode = UnicodeMode;
    }
#ifdef Q_OS_MAC32
    else {
        void *happy;
        err = KLGetKeyboardLayoutProperty(keyLayoutRef, kKLKCHRData,
                                  const_cast<const void **>(reinterpret_cast<void **>(&happy)));
        if (err != noErr) {
            qFatal("Qt::internal::unable to get non-unicode layout, cannot procede %ld %s:%d",
                     long(err), __FILE__, __LINE__);
        }
        keyboard_layout_format.other = happy;
        keyboard_mode = OtherMode;
    }

    currentKeyboardLayout = keyLayoutRef;
#else
    currentInputSource = source;
#endif
    keyboard_dead = 0;
    CFStringRef iso639Code;
#ifdef Q_OS_MAC32
# ifndef kKLLanguageCode
# define kKLLanguageCode 9
# endif
    KLGetKeyboardLayoutProperty(currentKeyboardLayout, kKLLanguageCode,
                                reinterpret_cast<const void **>(&iso639Code));
#else
    CFArrayRef array = static_cast<CFArrayRef>(TISGetInputSourceProperty(currentInputSource, kTISPropertyInputSourceLanguages));
    iso639Code = static_cast<CFStringRef>(CFArrayGetValueAtIndex(array, 0)); // Actually a RFC3066bis, but it's close enough
#endif
    if (iso639Code) {
        keyboardInputLocale = QLocale(QCFString::toQString(iso639Code));
        QString monday = keyboardInputLocale.dayName(1);
        bool rtl = false;
        for (int i = 0; i < monday.length(); ++i) {
            switch (monday.at(i).direction()) {
            default:
                break;
            case QChar::DirR:
            case QChar::DirAL:
            case QChar::DirRLE:
            case QChar::DirRLO:
                rtl = true;
                break;
            }
            if (rtl)
                break;
        }
        keyboardInputDirection = rtl ? Qt::RightToLeft : Qt::LeftToRight;
    } else {
        keyboardInputLocale = QLocale::c();
        keyboardInputDirection = Qt::LeftToRight;
    }
    return true;
}

void
QKeyMapperPrivate::deleteLayouts()
{
    keyboard_mode = NullMode;
    for (int i = 0; i < 255; ++i) {
        if (keyLayout[i]) {
            delete keyLayout[i];
            keyLayout[i] = 0;
        }
    }
}

void
QKeyMapperPrivate::clearMappings()
{
    deleteLayouts();
    updateKeyboard();
}

QList<int>
QKeyMapperPrivate::possibleKeys(QKeyEvent *e)
{
    QList<int> ret;

    KeyboardLayoutItem *kbItem = keyLayout[e->nativeVirtualKey()];
    if (!kbItem) // Key is not in any keyboard layout (e.g. eisu-key on Japanese keyboard) 
        return ret;

    int baseKey = kbItem->qtKey[0];
    Qt::KeyboardModifiers keyMods = e->modifiers();
    ret << int(baseKey + keyMods); // The base key is _always_ valid, of course

    for (int i = 1; i < 8; ++i) {
        Qt::KeyboardModifiers neededMods = ModsTbl[i];
        int key = kbItem->qtKey[i];
        if (key && key != baseKey && ((keyMods & neededMods) == neededMods))
            ret << int(key + (keyMods & ~neededMods));
    }

    return ret;
}

bool QKeyMapperPrivate::translateKeyEvent(QWidget *widget, EventHandlerCallRef er, EventRef event,
                                          void *info, bool grab)
{
    Q_ASSERT(GetEventClass(event) == kEventClassKeyboard);
    bool handled_event=true;
    UInt32 ekind = GetEventKind(event);

    // unfortunately modifiers changed event looks quite different, so I have a separate
    // code path
    if (ekind == kEventRawKeyModifiersChanged) {
        //figure out changed modifiers, wish Apple would just send a delta
        UInt32 modifiers = 0;
        GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
                          sizeof(modifiers), 0, &modifiers);
        qt_mac_send_modifiers_changed(modifiers, widget);
        return true;
    }

    if (qApp->inputContext() && qApp->inputContext()->isComposing()) {
        if (ekind == kEventRawKeyDown) {
            QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
            if (context)
                context->setLastKeydownEvent(event);
        }
        return false;
    }
    //get modifiers
    Qt::KeyboardModifiers modifiers;
    int qtKey;
    QChar ourChar;
    if (translateKeyEventInternal(er, event, &qtKey, &ourChar, &modifiers,
                                  &handled_event) == false)
        return handled_event;
    QString text(ourChar);
    /* This is actually wrong - but unfortunatly it is the best that can be
       done for now because of the Control/Meta mapping problems */
    if (modifiers & (Qt::ControlModifier | Qt::MetaModifier)
        && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
        text = QString();
    }


    if (widget) {
#ifndef QT_MAC_USE_COCOA
        Q_UNUSED(info);
        // Try not to call "other" event handlers if we have a popup,
        // However, if the key has text
        // then we should pass it along because otherwise then people
        // can use input method stuff.
        if (!qApp->activePopupWidget()
                || (qApp->activePopupWidget() && !text.isEmpty())) {
            //Find out if someone else wants the event, namely
            //is it of use to text services? If so we won't bother
            //with a QKeyEvent.
            qt_mac_eat_unicode_key = false;
            if (er)
                CallNextEventHandler(er, event);
            extern bool qt_mac_menubar_is_open();   
            if (qt_mac_eat_unicode_key || qt_mac_menubar_is_open()) {
                return true;
            }
        }
#endif
        // Try to compress key events.
        if (!text.isEmpty() && widget->testAttribute(Qt::WA_KeyCompression)) {
            EventTime lastTime = GetEventTime(event);
            for (;;) {
                EventRef releaseEvent = FindSpecificEventInQueue(GetMainEventQueue(),
                                                                 qt_KeyEventComparatorProc,
                                                                 (void*)kEventRawKeyUp);
                if (!releaseEvent)
                    break;
                const EventTime releaseTime = GetEventTime(releaseEvent);
                if (releaseTime < lastTime)
                    break;
                lastTime = releaseTime;

                EventRef pressEvent = FindSpecificEventInQueue(GetMainEventQueue(),
                                                               qt_KeyEventComparatorProc,
                                                               (void*)kEventRawKeyDown);
                if (!pressEvent)
                    break;
                const EventTime pressTime = GetEventTime(pressEvent);
                if (pressTime < lastTime)
                    break;
                lastTime = pressTime;

                Qt::KeyboardModifiers compressMod;
                int compressQtKey = 0;
                QChar compressChar;
                if (translateKeyEventInternal(er, pressEvent,
                                              &compressQtKey, &compressChar, &compressMod, 0)
                    == false) {
                    break;
                }
                // Copied from qapplication_x11.cpp (change both).

                bool stopCompression =
                    // 1) misc keys
                    (compressQtKey >= Qt::Key_Escape && compressQtKey <= Qt::Key_SysReq)
                    // 2) cursor movement
                    || (compressQtKey >= Qt::Key_Home && compressQtKey <= Qt::Key_PageDown)
                    // 3) extra keys
                    || (compressQtKey >= Qt::Key_Super_L && compressQtKey <= Qt::Key_Direction_R)
                    // 4) something that a) doesn't translate to text or b) translates
                    //    to newline text
                    || (compressQtKey == 0)
                    || (compressChar == QLatin1Char('\n'))
                    || (compressQtKey == Qt::Key_unknown);

                if (compressMod == modifiers && !compressChar.isNull() && !stopCompression) {
#ifdef DEBUG_KEY_BINDINGS
                    qDebug("compressing away %c", compressChar.toLatin1());
#endif
                    text += compressChar;
                    // Clean up
                    RemoveEventFromQueue(GetMainEventQueue(), releaseEvent);
                    RemoveEventFromQueue(GetMainEventQueue(), pressEvent);
                } else {
#ifdef DEBUG_KEY_BINDINGS
                    qDebug("stoping compression..");
#endif
                    break;
                }
            }
        }

        // There is no way to get the scan code from carbon. But we cannot use the value 0, since
        // it indicates that the event originates from somewhere else than the keyboard
        UInt32 macScanCode = 1;
        UInt32 macVirtualKey = 0;
        GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
        UInt32 macModifiers = 0;
        GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, 0,
                          sizeof(macModifiers), 0, &macModifiers);
        handled_event = QKeyMapper::sendKeyEvent(widget, grab,
                                                 (ekind == kEventRawKeyUp) ? QEvent::KeyRelease : QEvent::KeyPress,
                                                 qtKey, modifiers, text, ekind == kEventRawKeyRepeat, 0,
                                                 macScanCode, macVirtualKey, macModifiers
#ifdef QT_MAC_USE_COCOA
                                                 ,static_cast<bool *>(info)
#endif
                                                 );
    }
    return handled_event;
}

void
QKeyMapperPrivate::updateKeyMap(EventHandlerCallRef, EventRef event, void *)
{
    UInt32 macVirtualKey = 0;
    GetEventParameter(event, kEventParamKeyCode, typeUInt32, 0, sizeof(macVirtualKey), 0, &macVirtualKey);
    if (updateKeyboard())
       QKeyMapper::changeKeyboard();
    else if (keyLayout[macVirtualKey])
        return;

    UniCharCount buffer_size = 10;
    UniChar buffer[buffer_size];
    keyLayout[macVirtualKey] = new KeyboardLayoutItem;
    for (int i = 0; i < 16; ++i) {
        UniCharCount out_buffer_size = 0;
        keyLayout[macVirtualKey]->qtKey[i] = 0;
#ifdef Q_WS_MAC32
        if (keyboard_mode == UnicodeMode) {
#endif
            const UInt32 keyModifier = ((qt_mac_get_mac_modifiers(ModsTbl[i]) >> 8) & 0xFF);
            OSStatus err = UCKeyTranslate(keyboard_layout_format.unicode, macVirtualKey, kUCKeyActionDown, keyModifier,
                                          keyboard_kind, 0, &keyboard_dead, buffer_size, &out_buffer_size, buffer);
            if (err == noErr && out_buffer_size) {
                const QChar unicode(buffer[0]);
                int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
                if (qtkey == Qt::Key_unknown)
                    qtkey = unicode.unicode();
                keyLayout[macVirtualKey]->qtKey[i] = qtkey;
            }
#ifdef Q_WS_MAC32            
        } else {
            const UInt32 keyModifier = (qt_mac_get_mac_modifiers(ModsTbl[i]));

            uchar translatedChar = KeyTranslate(keyboard_layout_format.other, keyModifier | macVirtualKey, &keyboard_dead);
            if (translatedChar) {
                static QTextCodec *c = 0;
                if (!c)
                    c = QTextCodec::codecForName("Apple Roman");
                const QChar unicode(c->toUnicode((const char *)&translatedChar, 1).at(0));
                int qtkey = qt_mac_get_key(keyModifier, unicode, macVirtualKey);
                if (qtkey == Qt::Key_unknown)
                    qtkey = unicode.unicode();
                keyLayout[macVirtualKey]->qtKey[i] = qtkey;
            }
        }
#endif
    }
#ifdef DEBUG_KEY_MAPS
    qDebug("updateKeyMap for virtual key = 0x%02x!", (uint)macVirtualKey);
    for (int i = 0; i < 16; ++i) {
        qDebug("    [%d] (%d,0x%02x,'%c')", i,
               keyLayout[macVirtualKey]->qtKey[i],
               keyLayout[macVirtualKey]->qtKey[i],
               keyLayout[macVirtualKey]->qtKey[i]);
    }
#endif
}

bool
QKeyMapper::sendKeyEvent(QWidget *widget, bool grab,
                         QEvent::Type type, int code, Qt::KeyboardModifiers modifiers,
                         const QString &text, bool autorepeat, int count,
                         quint32 nativeScanCode, quint32 nativeVirtualKey,
                         quint32 nativeModifiers, bool *isAccepted)
{
    Q_UNUSED(count);
    if (widget && widget->isEnabled()) {
        bool key_event = true;
#if defined(QT3_SUPPORT) && !defined(QT_NO_SHORTCUT)
        if (type == QEvent::KeyPress && !grab
           && QApplicationPrivate::instance()->use_compat()) {
               QKeyEventEx accel_ev(type, code, modifiers,
                                    text, autorepeat, qMax(1, int(text.length())),
                                    nativeScanCode, nativeVirtualKey, nativeModifiers);
            if (QApplicationPrivate::instance()->qt_tryAccelEvent(widget, &accel_ev)) {
#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
                qDebug("KeyEvent: %s::%s consumed Accel: %s",
                       widget ? widget->metaObject()->className() : "none",
                       widget ? widget->objectName().toLatin1().constData() : "",
                       text.toLatin1().constData());
#endif
                key_event = false;
            } else {
                if (accel_ev.isAccepted()) {
#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
                    qDebug("KeyEvent: %s::%s overrode Accel: %s",
                           widget ? widget->metaObject()->className() : "none",
                           widget ? widget->objectName().toLatin1().constData() : "",
                           text.toLatin1().constData());
#endif
                }
            }
        }
#else
Q_UNUSED(grab);
#endif // QT3_SUPPORT && !QT_NO_SHORTCUT
        if (key_event) {
#if defined(DEBUG_KEY_BINDINGS) || defined(DEBUG_KEY_BINDINGS_MODIFIERS)
            qDebug("KeyEvent: Sending %s to %s::%s: %s 0x%08x%s",
                   type == QEvent::KeyRelease ? "KeyRelease" : "KeyPress",
                   widget ? widget->metaObject()->className() : "none",
                   widget ? widget->objectName().toLatin1().constData() : "",
                   text.toLatin1().constData(), int(modifiers),
                   autorepeat ? " Repeat" : "");
#endif
            QKeyEventEx ke(type, code, modifiers, text, autorepeat, qMax(1, text.length()),
                           nativeScanCode, nativeVirtualKey, nativeModifiers);
            bool retMe = qt_sendSpontaneousEvent(widget,&ke);
            if (isAccepted)
                *isAccepted = ke.isAccepted();
            return retMe;
        }
    }
    return false;
}

QT_END_NAMESPACE
