blob: a5838ae223e8baea6dc9211e87968ed9673a8fd4 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 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$
** GNU Lesser General Public License Usage
** 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $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