src/gui/inputmethod/qmacinputcontext_mac.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include <qvarlengtharray.h>
       
    43 #include <qwidget.h>
       
    44 #include <private/qmacinputcontext_p.h>
       
    45 #include "qtextformat.h"
       
    46 #include <qdebug.h>
       
    47 #include <private/qapplication_p.h>
       
    48 #include <private/qkeymapper_p.h>
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 extern bool qt_sendSpontaneousEvent(QObject*, QEvent*);
       
    53 
       
    54 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
       
    55 #  define typeRefCon typeSInt32
       
    56 #  define typeByteCount typeSInt32
       
    57 #endif
       
    58 
       
    59 QMacInputContext::QMacInputContext(QObject *parent)
       
    60     : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0),
       
    61       keydownEvent(0)
       
    62 {
       
    63 //    createTextDocument();
       
    64 }
       
    65 
       
    66 QMacInputContext::~QMacInputContext()
       
    67 {
       
    68 #ifndef QT_MAC_USE_COCOA
       
    69     if(textDocument)
       
    70         DeleteTSMDocument(textDocument);
       
    71 #endif
       
    72 }
       
    73 
       
    74 void
       
    75 QMacInputContext::createTextDocument()
       
    76 {
       
    77 #ifndef QT_MAC_USE_COCOA
       
    78     if(!textDocument) {
       
    79         InterfaceTypeList itl = { kUnicodeDocument };
       
    80         NewTSMDocument(1, itl, &textDocument, SRefCon(this));
       
    81     }
       
    82 #endif
       
    83 }
       
    84 
       
    85 
       
    86 QString QMacInputContext::language()
       
    87 {
       
    88     return QString();
       
    89 }
       
    90 
       
    91 
       
    92 void QMacInputContext::mouseHandler(int pos, QMouseEvent *e)
       
    93 {
       
    94 #ifndef QT_MAC_USE_COCOA
       
    95     if(e->type() != QEvent::MouseButtonPress)
       
    96         return;
       
    97 
       
    98     if (!composing)
       
    99         return;
       
   100     if (pos < 0 || pos > currentText.length())
       
   101         reset();
       
   102     // ##### handle mouse position
       
   103 #else
       
   104     Q_UNUSED(pos);
       
   105     Q_UNUSED(e);
       
   106 #endif
       
   107 }
       
   108 
       
   109 #if !defined QT_MAC_USE_COCOA
       
   110 
       
   111 static QTextFormat qt_mac_compose_format()
       
   112 {
       
   113     QTextCharFormat ret;
       
   114     ret.setFontUnderline(true);
       
   115     return ret;
       
   116 }
       
   117 
       
   118 void QMacInputContext::reset()
       
   119 {
       
   120     if (recursionGuard)
       
   121         return;
       
   122     if (!currentText.isEmpty()){
       
   123         QInputMethodEvent e;
       
   124         e.setCommitString(currentText);
       
   125         qt_sendSpontaneousEvent(focusWidget(), &e);
       
   126         currentText = QString();
       
   127     }
       
   128     recursionGuard = true;
       
   129     createTextDocument();
       
   130     composing = false;
       
   131     ActivateTSMDocument(textDocument);
       
   132     FixTSMDocument(textDocument);
       
   133     recursionGuard = false;
       
   134 }
       
   135 
       
   136 bool QMacInputContext::isComposing() const
       
   137 {
       
   138     return composing;
       
   139 }
       
   140 #endif
       
   141 
       
   142 void QMacInputContext::setFocusWidget(QWidget *w)
       
   143 {
       
   144     createTextDocument();
       
   145 #ifndef QT_MAC_USE_COCOA
       
   146     if(w)
       
   147         ActivateTSMDocument(textDocument);
       
   148     else
       
   149         DeactivateTSMDocument(textDocument);
       
   150 #endif
       
   151     QInputContext::setFocusWidget(w);
       
   152 }
       
   153 
       
   154 
       
   155 #ifndef QT_MAC_USE_COCOA
       
   156 static EventTypeSpec input_events[] = {
       
   157     { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
       
   158     { kEventClassTextInput, kEventTextInputOffsetToPos },
       
   159     { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }
       
   160 };
       
   161 static EventHandlerUPP input_proc_handlerUPP = 0;
       
   162 static EventHandlerRef input_proc_handler = 0;
       
   163 #endif
       
   164 
       
   165 void
       
   166 QMacInputContext::initialize()
       
   167 {
       
   168 #ifndef QT_MAC_USE_COCOA
       
   169     if(!input_proc_handler) {
       
   170         input_proc_handlerUPP = NewEventHandlerUPP(QMacInputContext::globalEventProcessor);
       
   171         InstallEventHandler(GetApplicationEventTarget(), input_proc_handlerUPP,
       
   172                             GetEventTypeCount(input_events), input_events,
       
   173                             0, &input_proc_handler);
       
   174     }
       
   175 #endif
       
   176 }
       
   177 
       
   178 void
       
   179 QMacInputContext::cleanup()
       
   180 {
       
   181 #ifndef QT_MAC_USE_COCOA
       
   182     if(input_proc_handler) {
       
   183         RemoveEventHandler(input_proc_handler);
       
   184         input_proc_handler = 0;
       
   185     }
       
   186     if(input_proc_handlerUPP) {
       
   187         DisposeEventHandlerUPP(input_proc_handlerUPP);
       
   188         input_proc_handlerUPP = 0;
       
   189     }
       
   190 #endif
       
   191 }
       
   192 
       
   193 void QMacInputContext::setLastKeydownEvent(EventRef event)
       
   194 {
       
   195     EventRef tmpEvent = keydownEvent;
       
   196     keydownEvent = event;
       
   197     if (keydownEvent)
       
   198         RetainEvent(keydownEvent);
       
   199     if (tmpEvent)
       
   200         ReleaseEvent(tmpEvent);
       
   201 }
       
   202 
       
   203 OSStatus
       
   204 QMacInputContext::globalEventProcessor(EventHandlerCallRef, EventRef event, void *)
       
   205 {
       
   206 #ifndef QT_MAC_USE_COCOA
       
   207     QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
       
   208 
       
   209     SRefCon refcon = 0;
       
   210     GetEventParameter(event, kEventParamTextInputSendRefCon, typeRefCon, 0,
       
   211                       sizeof(refcon), 0, &refcon);
       
   212     QMacInputContext *context = reinterpret_cast<QMacInputContext*>(refcon);
       
   213 
       
   214     bool handled_event=true;
       
   215     UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
       
   216     switch(eclass) {
       
   217     case kEventClassTextInput: {
       
   218         handled_event = false;
       
   219         QWidget *widget = QApplicationPrivate::focus_widget;
       
   220         bool canCompose = widget && (!context || widget->inputContext() == context)
       
   221                 && !(widget->inputMethodHints() & Qt::ImhDigitsOnly
       
   222                 || widget->inputMethodHints() & Qt::ImhFormattedNumbersOnly
       
   223                 || widget->inputMethodHints() & Qt::ImhHiddenText);
       
   224         if(!canCompose) {
       
   225             handled_event = false;
       
   226         } else if(ekind == kEventTextInputOffsetToPos) {
       
   227             if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
       
   228                 handled_event = false;
       
   229                 break;
       
   230             }
       
   231 
       
   232             QRect mr(widget->inputMethodQuery(Qt::ImMicroFocus).toRect());
       
   233             QPoint mp(widget->mapToGlobal(QPoint(mr.topLeft())));
       
   234             Point pt;
       
   235             pt.h = mp.x();
       
   236             pt.v = mp.y() + mr.height();
       
   237             SetEventParameter(event, kEventParamTextInputReplyPoint, typeQDPoint,
       
   238                               sizeof(pt), &pt);
       
   239             handled_event = true;
       
   240         } else if(ekind == kEventTextInputUpdateActiveInputArea) {
       
   241             if(!widget->testAttribute(Qt::WA_InputMethodEnabled)) {
       
   242                 handled_event = false;
       
   243                 break;
       
   244             }
       
   245 
       
   246             if (context->recursionGuard)
       
   247                 break;
       
   248 
       
   249             ByteCount unilen = 0;
       
   250             GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
       
   251                               0, 0, &unilen, 0);
       
   252             UniChar *unicode = (UniChar*)NewPtr(unilen);
       
   253             GetEventParameter(event, kEventParamTextInputSendText, typeUnicodeText,
       
   254                               0, unilen, 0, unicode);
       
   255             QString text((QChar*)unicode, unilen / sizeof(UniChar));
       
   256             DisposePtr((char*)unicode);
       
   257 
       
   258             ByteCount fixed_length = 0;
       
   259             GetEventParameter(event, kEventParamTextInputSendFixLen, typeByteCount, 0,
       
   260                               sizeof(fixed_length), 0, &fixed_length);
       
   261             if(fixed_length == ULONG_MAX || fixed_length == unilen) {
       
   262                 QInputMethodEvent e;
       
   263                 e.setCommitString(text);
       
   264                 context->currentText = QString();
       
   265                 qt_sendSpontaneousEvent(context->focusWidget(), &e);
       
   266                 handled_event = true;
       
   267                 context->reset();
       
   268             } else {
       
   269                 ByteCount rngSize = 0;
       
   270                 OSStatus err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
       
   271                                                  0, &rngSize, 0);
       
   272                 QVarLengthArray<TextRangeArray> highlight(rngSize);
       
   273                 if (noErr == err) {
       
   274                     err = GetEventParameter(event, kEventParamTextInputSendHiliteRng, typeTextRangeArray, 0,
       
   275                                             rngSize, &rngSize, highlight.data());
       
   276                 }
       
   277                 context->composing = true;
       
   278                 if(fixed_length > 0) {
       
   279                     const int qFixedLength = fixed_length / sizeof(UniChar);
       
   280                     QList<QInputMethodEvent::Attribute> attrs;
       
   281                     attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
       
   282                                                           qFixedLength, text.length()-qFixedLength,
       
   283                                                           qt_mac_compose_format());
       
   284                     QInputMethodEvent e(text, attrs);
       
   285                     context->currentText = text;
       
   286                     e.setCommitString(text.left(qFixedLength), 0, qFixedLength);
       
   287                     qt_sendSpontaneousEvent(widget, &e);
       
   288                     handled_event = true;
       
   289                 } else {
       
   290                     /* Apple's enums that they have removed from Tiger :(
       
   291                     enum {
       
   292                         kCaretPosition = 1,
       
   293                         kRawText = 2,
       
   294                         kSelectedRawText = 3,
       
   295                         kConvertedText = 4,
       
   296                         kSelectedConvertedText = 5,
       
   297                         kBlockFillText = 6,
       
   298                         kOutlineText = 7,
       
   299                         kSelectedText = 8
       
   300                     };
       
   301                     */
       
   302 #ifndef kConvertedText
       
   303 #define kConvertedText 4
       
   304 #endif
       
   305 #ifndef kCaretPosition
       
   306 #define kCaretPosition 1
       
   307 #endif
       
   308                     QList<QInputMethodEvent::Attribute> attrs;
       
   309                     if (!highlight.isEmpty()) {
       
   310                         TextRangeArray *data = highlight.data();
       
   311                         for (int i = 0; i < data->fNumOfRanges; ++i) {
       
   312                             int start = data->fRange[i].fStart / sizeof(UniChar);
       
   313                             int len = (data->fRange[i].fEnd - data->fRange[i].fStart) / sizeof(UniChar);
       
   314                             if (data->fRange[i].fHiliteStyle == kCaretPosition) {
       
   315                                 attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, start, 0, QVariant());
       
   316                                 continue;
       
   317                             }
       
   318                             QTextCharFormat format;
       
   319                             format.setFontUnderline(true);
       
   320                             if (data->fRange[i].fHiliteStyle == kConvertedText)
       
   321                                 format.setUnderlineColor(Qt::gray);
       
   322                             else
       
   323                                 format.setUnderlineColor(Qt::black);
       
   324                             attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, len, format);
       
   325                         }
       
   326                     } else {
       
   327                         attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
       
   328                                 0, text.length(), qt_mac_compose_format());
       
   329                     }
       
   330                     context->currentText = text;
       
   331                     QInputMethodEvent e(text, attrs);
       
   332                     qt_sendSpontaneousEvent(widget, &e);
       
   333                     handled_event = true;
       
   334                 }
       
   335             }
       
   336 #if 0
       
   337             if(!context->composing)
       
   338                 handled_event = false;
       
   339 #endif
       
   340 
       
   341             extern bool qt_mac_eat_unicode_key; //qapplication_mac.cpp
       
   342             qt_mac_eat_unicode_key = handled_event;
       
   343         } else if(ekind == kEventTextInputUnicodeForKeyEvent) {
       
   344             EventRef key_ev = 0;
       
   345             GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, 0,
       
   346                               sizeof(key_ev), 0, &key_ev);
       
   347             QString text;
       
   348             ByteCount unilen = 0;
       
   349             if(GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, 0, &unilen, 0) == noErr) {
       
   350                 UniChar *unicode = (UniChar*)NewPtr(unilen);
       
   351                 GetEventParameter(key_ev, kEventParamKeyUnicodes, typeUnicodeText, 0, unilen, 0, unicode);
       
   352                 text = QString((QChar*)unicode, unilen / sizeof(UniChar));
       
   353                 DisposePtr((char*)unicode);
       
   354             }
       
   355             unsigned char chr = 0;
       
   356             GetEventParameter(key_ev, kEventParamKeyMacCharCodes, typeChar, 0, sizeof(chr), 0, &chr);
       
   357             if(!chr || chr >= 128 || (text.length() > 0 && (text.length() > 1 || text.at(0) != QLatin1Char(chr))))
       
   358                 handled_event = !widget->testAttribute(Qt::WA_InputMethodEnabled);
       
   359             QMacInputContext *context = qobject_cast<QMacInputContext*>(qApp->inputContext());
       
   360             if (context && context->lastKeydownEvent()) {
       
   361                 qt_keymapper_private()->translateKeyEvent(widget, 0, context->lastKeydownEvent(),
       
   362                                                           0, false);
       
   363                 context->setLastKeydownEvent(0);
       
   364             }
       
   365         }
       
   366         break; }
       
   367     default:
       
   368         break;
       
   369     }
       
   370     if(!handled_event) //let the event go through
       
   371         return eventNotHandledErr;
       
   372 #else
       
   373     Q_UNUSED(event);
       
   374 #endif
       
   375     return noErr; //we eat the event
       
   376 }
       
   377 
       
   378 QT_END_NAMESPACE