src/gui/inputmethod/qmacinputcontext_mac.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/inputmethod/qmacinputcontext_mac.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 <qvarlengtharray.h>
+#include <qwidget.h>
+#include <private/qmacinputcontext_p.h>
+#include "qtextformat.h"
+#include <qdebug.h>
+#include <private/qapplication_p.h>
+#include <private/qkeymapper_p.h>
+
+QT_BEGIN_NAMESPACE
+
+extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
+
+#if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
+#  define typeRefCon typeSInt32
+#  define typeByteCount typeSInt32
+#endif
+
+QMacInputContext::QMacInputContext(QObject *parent)
+    : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0),
+      keydownEvent(0)
+{
+//    createTextDocument();
+}
+
+QMacInputContext::~QMacInputContext()
+{
+#ifndef QT_MAC_USE_COCOA
+    if(textDocument)
+        DeleteTSMDocument(textDocument);
+#endif
+}
+
+void
+QMacInputContext::createTextDocument()
+{
+#ifndef QT_MAC_USE_COCOA
+    if(!textDocument) {
+        InterfaceTypeList itl = { kUnicodeDocument };
+        NewTSMDocument(1, itl, &textDocument, SRefCon(this));
+    }
+#endif
+}
+
+
+QString QMacInputContext::language()
+{
+    return QString();
+}
+
+
+void QMacInputContext::mouseHandler(int pos, QMouseEvent *e)
+{
+#ifndef QT_MAC_USE_COCOA
+    if(e->type() != QEvent::MouseButtonPress)
+        return;
+
+    if (!composing)
+        return;
+    if (pos < 0 || pos > currentText.length())
+        reset();
+    // ##### handle mouse position
+#else
+    Q_UNUSED(pos);
+    Q_UNUSED(e);
+#endif
+}
+
+#if !defined QT_MAC_USE_COCOA
+
+static QTextFormat qt_mac_compose_format()
+{
+    QTextCharFormat ret;
+    ret.setFontUnderline(true);
+    return ret;
+}
+
+void QMacInputContext::reset()
+{
+    if (recursionGuard)
+        return;
+    if (!currentText.isEmpty()){
+        QInputMethodEvent e;
+        e.setCommitString(currentText);
+        qt_sendSpontaneousEvent(focusWidget(), &e);
+        currentText = QString();
+    }
+    recursionGuard = true;
+    createTextDocument();
+    composing = false;
+    ActivateTSMDocument(textDocument);
+    FixTSMDocument(textDocument);
+    recursionGuard = false;
+}
+
+bool QMacInputContext::isComposing() const
+{
+    return composing;
+}
+#endif
+
+void QMacInputContext::setFocusWidget(QWidget *w)
+{
+    createTextDocument();
+#ifndef QT_MAC_USE_COCOA
+    if(w)
+        ActivateTSMDocument(textDocument);
+    else
+        DeactivateTSMDocument(textDocument);
+#endif
+    QInputContext::setFocusWidget(w);
+}
+
+
+#ifndef QT_MAC_USE_COCOA
+static EventTypeSpec input_events[] = {
+    { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
+    { kEventClassTextInput, kEventTextInputOffsetToPos },
+    { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }
+};
+static EventHandlerUPP input_proc_handlerUPP = 0;
+static EventHandlerRef input_proc_handler = 0;
+#endif
+
+void
+QMacInputContext::initialize()
+{
+#ifndef QT_MAC_USE_COCOA
+    if(!input_proc_handler) {
+        input_proc_handlerUPP = NewEventHandlerUPP(QMacInputContext::globalEventProcessor);
+        InstallEventHandler(GetApplicationEventTarget(), input_proc_handlerUPP,
+                            GetEventTypeCount(input_events), input_events,
+                            0, &input_proc_handler);
+    }
+#endif
+}
+
+void
+QMacInputContext::cleanup()
+{
+#ifndef QT_MAC_USE_COCOA
+    if(input_proc_handler) {
+        RemoveEventHandler(input_proc_handler);
+        input_proc_handler = 0;
+    }
+    if(input_proc_handlerUPP) {
+        DisposeEventHandlerUPP(input_proc_handlerUPP);
+        input_proc_handlerUPP = 0;
+    }
+#endif
+}
+
+void QMacInputContext::setLastKeydownEvent(EventRef event)
+{
+    EventRef tmpEvent = keydownEvent;
+    keydownEvent = event;
+    if (keydownEvent)
+        RetainEvent(keydownEvent);
+    if (tmpEvent)
+        ReleaseEvent(tmpEvent);
+}
+
+OSStatus
+QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void *)
+{
+#ifndef QT_MAC_USE_COCOA
+    QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
+
+    SRefCon refcon = 0;
+    GetEventParameter(event, kEventParamTextInputSendRefCon, typeRefCon, 0,
+                      sizeof(refcon), 0, &refcon);
+    QMacInputContext *context = reinterpret_cast<QMacInputContext*>(refcon);
+
+    bool handled_event=true;
+    UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
+    switch(eclass) {
+    case kEventClassTextInput: {
+        handled_event = false;
+        QWidget *widget = QApplicationPrivate::focus_widget;
+        bool canCompose = widget && (!context || widget->inputContext() == context)
+                && !(widget->inputMethodHints() & Qt::ImhDigitsOnly
+                || widget->inputMethodHints() & Qt::ImhFormattedNumbersOnly
+                || widget->inputMethodHints() & Qt::ImhHiddenText);
+        if(!canCompose) {
+            handled_event = false;
+        } else if(ekind == kEventTextInputOffsetToPos) {
+            if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+                handled_event = false;
+                break;
+            }
+
+            QRect mr(widget->inputMethodQuery(Qt::ImMicroFocus).toRect());
+            QPoint mp(widget->mapToGlobal(QPoint(mr.topLeft())));
+            Point pt;
+            pt.h = mp.x();
+            pt.v = mp.y() + mr.height();
+            SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint,
+                              sizeof(pt), &pt);
+            handled_event = true;
+        } else if(ekind == kEventTextInputUpdateActiveInputArea) {
+            if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
+                handled_event = false;
+                break;
+            }
+
+            if (context->recursionGuard)
+                break;
+
+            ByteCount unilen = 0;
+            GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+                              0, 0, &unilen, 0);
+            UniChar *unicode = (UniChar*)NewPtr(unilen);
+            GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
+                              0, unilen, 0, unicode);
+            QString text((QChar*)unicode, unilen / sizeof(UniChar));
+            DisposePtr((char*)unicode);
+
+            ByteCount fixed_length = 0;
+            GetEventParameter(event, kEventParamTextInputSendFixLen, typeByteCount, 0,
+                              sizeof(fixed_length), 0, &fixed_length);
+            if(fixed_length == ULONG_MAX || fixed_length == unilen) {
+                QInputMethodEvent e;
+                e.setCommitString(text);
+                context->currentText = QString();
+                qt_sendSpontaneousEvent(context->focusWidget(), &e);
+                handled_event = true;
+                context->reset();
+            } else {
+                ByteCount rngSize = 0;
+                OSStatus err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+                                                 0, &rngSize, 0);
+                QVarLengthArray<TextRangeArray> highlight(rngSize);
+                if (noErr == err) {
+                    err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
+                                            rngSize, &rngSize, highlight.data());
+                }
+                context->composing = true;
+                if(fixed_length > 0) {
+                    const int qFixedLength = fixed_length / sizeof(UniChar);
+                    QList<QInputMethodEvent::Attribute> attrs;
+                    attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+                                                          qFixedLength, text.length()-qFixedLength,
+                                                          qt_mac_compose_format());
+                    QInputMethodEvent e(text, attrs);
+                    context->currentText = text;
+                    e.setCommitString(text.left(qFixedLength), 0, qFixedLength);
+                    qt_sendSpontaneousEvent(widget, &e);
+                    handled_event = true;
+                } else {
+                    /* Apple's enums that they have removed from Tiger :(
+                    enum {
+                        kCaretPosition = 1,
+                        kRawText = 2,
+                        kSelectedRawText = 3,
+                        kConvertedText = 4,
+                        kSelectedConvertedText = 5,
+                        kBlockFillText = 6,
+                        kOutlineText = 7,
+                        kSelectedText = 8
+                    };
+                    */
+#ifndef kConvertedText
+#define kConvertedText 4
+#endif
+#ifndef kCaretPosition
+#define kCaretPosition 1
+#endif
+                    QList<QInputMethodEvent::Attribute> attrs;
+                    if (!highlight.isEmpty()) {
+                        TextRangeArray *data = highlight.data();
+                        for (int i = 0; i < data->fNumOfRanges; ++i) {
+                            int start = data->fRange[i].fStart / sizeof(UniChar);
+                            int len = (data->fRange[i].fEnd - data->fRange[i].fStart) / sizeof(UniChar);
+                            if (data->fRange[i].fHiliteStyle == kCaretPosition) {
+                                attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, start, 0, QVariant());
+                                continue;
+                            }
+                            QTextCharFormat format;
+                            format.setFontUnderline(true);
+                            if (data->fRange[i].fHiliteStyle == kConvertedText)
+                                format.setUnderlineColor(Qt::gray);
+                            else
+                                format.setUnderlineColor(Qt::black);
+                            attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, len, format);
+                        }
+                    } else {
+                        attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+                                0, text.length(), qt_mac_compose_format());
+                    }
+                    context->currentText = text;
+                    QInputMethodEvent e(text, attrs);
+                    qt_sendSpontaneousEvent(widget, &e);
+                    handled_event = true;
+                }
+            }
+#if 0
+            if(!context->composing)
+                handled_event = false;
+#endif
+
+            extern bool qt_mac_eat_unicode_key; //qapplication_mac.cpp
+            qt_mac_eat_unicode_key = handled_event;
+        } else if(ekind == kEventTextInputUnicodeForKeyEvent) {
+            EventRef key_ev = 0;
+            GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
+                              sizeof(key_ev), 0, &key_ev);
+            QString text;
+            ByteCount unilen = 0;
+            if(GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0) == noErr) {
+                UniChar *unicode = (UniChar*)NewPtr(unilen);
+                GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, unicode);
+                text = QString((QChar*)unicode, unilen / sizeof(UniChar));
+                DisposePtr((char*)unicode);
+            }
+            unsigned char chr = 0;
+            GetEventParameter(key_ev, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(chr), 0, &chr);
+            if(!chr || chr >= 128 || (text.length() > 0 && (text.length() > 1 || text.at(0) != QLatin1Char(chr))))
+                handled_event = !widget->testAttribute(Qt::WA_InputMethodEnabled);
+            QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
+            if (context && context->lastKeydownEvent()) {
+                qt_keymapper_private()->translateKeyEvent(widget, 0, context->lastKeydownEvent(),
+                                                          0, false);
+                context->setLastKeydownEvent(0);
+            }
+        }
+        break; }
+    default:
+        break;
+    }
+    if(!handled_event) //let the event go through
+        return eventNotHandledErr;
+#else
+    Q_UNUSED(event);
+#endif
+    return noErr; //we eat the event
+}
+
+QT_END_NAMESPACE