/****************************************************************************
**
** 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$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** 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.
**
** 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