source: trunk/src/gui/inputmethod/qmacinputcontext_mac.cpp@ 605

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 13.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
50QT_BEGIN_NAMESPACE
51
52extern 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
59QMacInputContext::QMacInputContext(QObject *parent)
60 : QInputContext(parent), composing(false), recursionGuard(false), textDocument(0),
61 keydownEvent(0)
62{
63// createTextDocument();
64}
65
66QMacInputContext::~QMacInputContext()
67{
68#ifndef QT_MAC_USE_COCOA
69 if(textDocument)
70 DeleteTSMDocument(textDocument);
71#endif
72}
73
74void
75QMacInputContext::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
86QString QMacInputContext::language()
87{
88 return QString();
89}
90
91
92void 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
111static QTextFormat qt_mac_compose_format()
112{
113 QTextCharFormat ret;
114 ret.setFontUnderline(true);
115 return ret;
116}
117
118void 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
136bool QMacInputContext::isComposing() const
137{
138 return composing;
139}
140#endif
141
142void 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
156static EventTypeSpec input_events[] = {
157 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent },
158 { kEventClassTextInput, kEventTextInputOffsetToPos },
159 { kEventClassTextInput, kEventTextInputUpdateActiveInputArea }
160};
161static EventHandlerUPP input_proc_handlerUPP = 0;
162static EventHandlerRef input_proc_handler = 0;
163#endif
164
165void
166QMacInputContext::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
178void
179QMacInputContext::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
193void 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
203OSStatus
204QMacInputContext::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
378QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.