1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 /****************************************************************************
43 **
44 ** Implementation of QXIMInputContext class
45 **
46 ** Copyright (C) 2003-2004 immodule for Qt Project.  All rights reserved.
47 **
48 ** This file is written to contribute to Nokia Corporation and/or its subsidiary(-ies) under their own
49 ** license. You may use this file under your Qt license. Following
50 ** description is copied from their original file headers. Contact
51 ** immodule-qt@freedesktop.org if any conditions of this licensing are
52 ** not clear to you.
53 **
54 ****************************************************************************/
55 
56 #include "qplatformdefs.h"
57 #include "qdebug.h"
58 #include "qximinputcontext_p.h"
59 
60 #if !defined(QT_NO_IM)
61 
62 QT_BEGIN_NAMESPACE
63 
64 #if !defined(QT_NO_XIM)
65 
66 QT_BEGIN_INCLUDE_NAMESPACE
67 #include "qplatformdefs.h"
68 
69 #include "qapplication.h"
70 #include "qwidget.h"
71 #include "qstring.h"
72 #include "qlist.h"
73 #include "qtextcodec.h"
74 #include "qevent.h"
75 #include "qtextformat.h"
76 
77 #include "qx11info_x11.h"
78 
79 #include <stdlib.h>
80 #include <limits.h>
81 QT_END_INCLUDE_NAMESPACE
82 
83 // #define QT_XIM_DEBUG
84 #ifdef QT_XIM_DEBUG
85 #define XIM_DEBUG qDebug
86 #else
87 #define XIM_DEBUG if (0) qDebug
88 #endif
89 
90 // from qapplication_x11.cpp
91 // #### move to X11 struct
92 extern XIMStyle	qt_xim_preferred_style;
93 extern char    *qt_ximServer;
94 extern int qt_ximComposingKeycode;
95 extern QTextCodec * qt_input_mapper;
96 
97 XIMStyle QXIMInputContext::xim_style = 0;
98 // moved from qapplication_x11.cpp
99 static const XIMStyle xim_default_style = XIMPreeditCallbacks | XIMStatusNothing;
100 
101 
102 extern "C" {
103 #ifdef USE_X11R6_XIM
xim_create_callback(XIM,XPointer client_data,XPointer)104     static void xim_create_callback(XIM /*im*/,
105                                     XPointer client_data,
106                                     XPointer /*call_data*/)
107     {
108         QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
109         // qDebug("xim_create_callback");
110         qic->create_xim();
111     }
112 
xim_destroy_callback(XIM,XPointer client_data,XPointer)113     static void xim_destroy_callback(XIM /*im*/,
114                                      XPointer client_data,
115                                      XPointer /*call_data*/)
116     {
117         QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
118         // qDebug("xim_destroy_callback");
119         qic->close_xim();
120         XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
121                                        (XIMProc) xim_create_callback, reinterpret_cast<char *>(qic));
122     }
123 #endif // USE_X11R6_XIM
124 
xic_start_callback(XIC,XPointer client_data,XPointer)125     static int xic_start_callback(XIC, XPointer client_data, XPointer) {
126 	QXIMInputContext *qic = (QXIMInputContext *) client_data;
127 	if (!qic) {
128 	    XIM_DEBUG("xic_start_callback: no qic");
129 	    return 0;
130 	}
131         QXIMInputContext::ICData *data = qic->icData();
132         if (!data) {
133             XIM_DEBUG("xic_start_callback: no ic data");
134             return 0;
135         }
136 	XIM_DEBUG("xic_start_callback");
137 
138 	data->clear();
139         data->composing = true;
140 
141 	return 0;
142     }
143 
xic_draw_callback(XIC,XPointer client_data,XPointer call_data)144     static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) {
145 	QXIMInputContext *qic = (QXIMInputContext *) client_data;
146 	if (!qic) {
147 	    XIM_DEBUG("xic_draw_callback: no qic");
148 	    return 0;
149 	}
150         QXIMInputContext::ICData *data = qic->icData();
151 	if (!data) {
152 	    XIM_DEBUG("xic_draw_callback: no ic data");
153 	    return 0;
154 	}
155         XIM_DEBUG("xic_draw_callback");
156 
157 
158 	if(!data->composing) {
159 	    data->clear();
160             data->composing = true;
161         }
162 
163 	XIMPreeditDrawCallbackStruct *drawstruct = (XIMPreeditDrawCallbackStruct *) call_data;
164 	XIMText *text = (XIMText *) drawstruct->text;
165 	int cursor = drawstruct->caret, sellen = 0, selstart = 0;
166 
167 	if (!drawstruct->caret && !drawstruct->chg_first && !drawstruct->chg_length && !text) {
168 	    if(data->text.isEmpty()) {
169 		XIM_DEBUG("compose emptied");
170 		// if the composition string has been emptied, we need
171 		// to send an InputMethodEnd event
172                 QInputMethodEvent e;
173 		qic->sendEvent(e);
174 		data->clear();
175 
176 		// if the commit string has coming after here, InputMethodStart
177 		// will be sent dynamically
178 	    }
179 	    return 0;
180 	}
181 
182 	if (text) {
183 	    char *str = 0;
184 	    if (text->encoding_is_wchar) {
185 		int l = wcstombs(NULL, text->string.wide_char, text->length);
186 		if (l != -1) {
187 		    str = new char[l + 1];
188 		    wcstombs(str, text->string.wide_char, l);
189 		    str[l] = 0;
190 		}
191 	    } else
192 		str = text->string.multi_byte;
193 
194 	    if (!str)
195 		return 0;
196 
197 	    QString s = QString::fromLocal8Bit(str);
198 
199 	    if (text->encoding_is_wchar)
200 		delete [] str;
201 
202 	    if (drawstruct->chg_length < 0)
203 		data->text.replace(drawstruct->chg_first, INT_MAX, s);
204 	    else
205 		data->text.replace(drawstruct->chg_first, drawstruct->chg_length, s);
206 
207 	    if (data->selectedChars.size() < data->text.length()) {
208 		// expand the selectedChars array if the compose string is longer
209 		int from = data->selectedChars.size();
210 		data->selectedChars.resize(data->text.length());
211                 for (int x = from; x < data->selectedChars.size(); ++x)
212                     data->selectedChars.clearBit(x);
213 	    }
214 
215             // determine if the changed chars are selected based on text->feedback
216             for (int x = 0; x < text->length; ++x)
217                 data->selectedChars.setBit(x + drawstruct->chg_first,
218                                            (text->feedback ? (text->feedback[x] & XIMReverse) : 0));
219 
220             // figure out where the selection starts, and how long it is
221             bool started = false;
222             for (int x = 0; x < qMin(data->selectedChars.size(), data->text.length()); ++x) {
223                 if (started) {
224                     if (data->selectedChars.testBit(x)) ++sellen;
225                     else break;
226                 } else {
227                     if (data->selectedChars.testBit(x)) {
228                         selstart = x;
229                         started = true;
230                         sellen = 1;
231                     }
232                 }
233             }
234 	} else {
235 	    if (drawstruct->chg_length == 0)
236 		drawstruct->chg_length = -1;
237 
238 	    data->text.remove(drawstruct->chg_first, drawstruct->chg_length);
239 	    bool qt_compose_emptied = data->text.isEmpty();
240 	    if (qt_compose_emptied) {
241 		XIM_DEBUG("compose emptied 2 text=%s", data->text.toUtf8().constData());
242 		// if the composition string has been emptied, we need
243 		// to send an InputMethodEnd event
244                 QInputMethodEvent e;
245 		qic->sendEvent(e);
246 		data->clear();
247 		// if the commit string has coming after here, InputMethodStart
248 		// will be sent dynamically
249 		return 0;
250 	    }
251 	}
252 
253         XIM_DEBUG("sending compose: '%s', cursor=%d, sellen=%d",
254                   data->text.toUtf8().constData(), cursor, sellen);
255         QList<QInputMethodEvent::Attribute> attrs;
256         if (selstart > 0)
257             attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, selstart,
258                                                   qic->standardFormat(QInputContext::PreeditFormat));
259         if (sellen)
260             attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, selstart, sellen,
261                                                   qic->standardFormat(QInputContext::SelectionFormat));
262         if (selstart + sellen < data->text.length())
263             attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
264                                                   selstart + sellen, data->text.length() - selstart - sellen,
265                                                   qic->standardFormat(QInputContext::PreeditFormat));
266         attrs << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, sellen ? 0 : 1, QVariant());
267         QInputMethodEvent e(data->text, attrs);
268         data->preeditEmpty = data->text.isEmpty();
269 	qic->sendEvent(e);
270 
271 	return 0;
272     }
273 
xic_done_callback(XIC,XPointer client_data,XPointer)274     static int xic_done_callback(XIC, XPointer client_data, XPointer) {
275 	QXIMInputContext *qic = (QXIMInputContext *) client_data;
276 	if (!qic)
277 	    return 0;
278 
279         XIM_DEBUG("xic_done_callback");
280 	// Don't send InputMethodEnd here. QXIMInputContext::x11FilterEvent()
281 	// handles InputMethodEnd with commit string.
282 	return 0;
283     }
284 }
285 
clear()286 void QXIMInputContext::ICData::clear()
287 {
288     text = QString();
289     selectedChars.clear();
290     composing = false;
291     preeditEmpty = true;
292 }
293 
icData() const294 QXIMInputContext::ICData *QXIMInputContext::icData() const
295 {
296     if (QWidget *w = focusWidget())
297         return ximData.value(w->effectiveWinId());
298     return 0;
299 }
300 /* The cache here is needed, as X11 leaks a few kb for every
301    XFreeFontSet call, so we avoid creating and deletion of fontsets as
302    much as possible
303 */
304 static XFontSet fontsetCache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
305 static int fontsetRefCount = 0;
306 
307 static const char * const fontsetnames[] = {
308     "-*-fixed-medium-r-*-*-16-*,-*-*-medium-r-*-*-16-*",
309     "-*-fixed-medium-i-*-*-16-*,-*-*-medium-i-*-*-16-*",
310     "-*-fixed-bold-r-*-*-16-*,-*-*-bold-r-*-*-16-*",
311     "-*-fixed-bold-i-*-*-16-*,-*-*-bold-i-*-*-16-*",
312     "-*-fixed-medium-r-*-*-24-*,-*-*-medium-r-*-*-24-*",
313     "-*-fixed-medium-i-*-*-24-*,-*-*-medium-i-*-*-24-*",
314     "-*-fixed-bold-r-*-*-24-*,-*-*-bold-r-*-*-24-*",
315     "-*-fixed-bold-i-*-*-24-*,-*-*-bold-i-*-*-24-*"
316 };
317 
getFontSet(const QFont & f)318 static XFontSet getFontSet(const QFont &f)
319 {
320     int i = 0;
321     if (f.italic())
322         i |= 1;
323     if (f.bold())
324         i |= 2;
325 
326     if (f.pointSize() > 20)
327         i += 4;
328 
329     if (!fontsetCache[i]) {
330         Display* dpy = X11->display;
331         int missCount;
332         char** missList;
333         fontsetCache[i] = XCreateFontSet(dpy, fontsetnames[i], &missList, &missCount, 0);
334         if(missCount > 0)
335             XFreeStringList(missList);
336         if (!fontsetCache[i]) {
337             fontsetCache[i] = XCreateFontSet(dpy, "-*-fixed-*-*-*-*-16-*", &missList, &missCount, 0);
338             if(missCount > 0)
339                 XFreeStringList(missList);
340             if (!fontsetCache[i])
341                 fontsetCache[i] = (XFontSet)-1;
342         }
343     }
344     return (fontsetCache[i] == (XFontSet)-1) ? 0 : fontsetCache[i];
345 }
346 
347 extern bool qt_use_rtl_extensions; // from qapplication_x11.cpp
348 #ifndef QT_NO_XKB
349 extern QLocale q_getKeyboardLocale(const QByteArray &layoutName, const QByteArray &variantName);
350 #endif
351 
QXIMInputContext()352 QXIMInputContext::QXIMInputContext()
353 {
354     if (!qt_xim_preferred_style) // no configured input style, use the default
355         qt_xim_preferred_style = xim_default_style;
356 
357     xim = 0;
358     QByteArray ximServerName(qt_ximServer);
359     if (qt_ximServer)
360         ximServerName.prepend("@im=");
361     else
362         ximServerName = "";
363 
364     if (!XSupportsLocale())
365 #ifndef QT_NO_DEBUG
366         qWarning("Qt: Locale not supported on X server")
367 #endif
368             ;
369 #ifdef USE_X11R6_XIM
370     else if (XSetLocaleModifiers (ximServerName.constData()) == 0)
371         qWarning("Qt: Cannot set locale modifiers: %s", ximServerName.constData());
372     else
373         XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
374                                        (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
375 #else // !USE_X11R6_XIM
376     else if (XSetLocaleModifiers ("") == 0)
377         qWarning("Qt: Cannot set locale modifiers");
378     else
379         QXIMInputContext::create_xim();
380 #endif // USE_X11R6_XIM
381 
382 #ifndef QT_NO_XKB
383     if (X11->use_xkb) {
384         QByteArray layoutName;
385         QByteArray variantName;
386 
387         Atom type = XNone;
388         int format = 0;
389         ulong nitems = 0;
390         ulong bytesAfter = 0;
391         uchar *data = 0;
392         if (XGetWindowProperty(X11->display, RootWindow(X11->display, 0), ATOM(_XKB_RULES_NAMES), 0, 1024,
393                                false, XA_STRING, &type, &format, &nitems, &bytesAfter, &data) == Success
394             && type == XA_STRING && format == 8 && nitems > 2) {
395 
396             char *names[5] = { 0, 0, 0, 0, 0 };
397             char *p = reinterpret_cast<char *>(data), *end = p + nitems;
398             int i = 0;
399             do {
400                 names[i++] = p;
401                 p += qstrlen(p) + 1;
402             } while (p < end);
403 
404             QList<QByteArray> layoutNames = QByteArray::fromRawData(names[2], qstrlen(names[2])).split(',');
405             QList<QByteArray> variantNames = QByteArray::fromRawData(names[3], qstrlen(names[3])).split(',');
406             for (int i = 0; i < qMin(layoutNames.count(), variantNames.count()); ++i  ) {
407                 QByteArray variantName = variantNames.at(i);
408                 const int dashPos = variantName.indexOf("-");
409                 if (dashPos >= 0)
410                     variantName.truncate(dashPos);
411                 QLocale keyboardInputLocale = q_getKeyboardLocale(layoutNames.at(i), variantName);
412                 if (keyboardInputLocale.textDirection() == Qt::RightToLeft)
413                     qt_use_rtl_extensions = true;
414             }
415         }
416 
417         if (data)
418             XFree(data);
419     }
420 #endif // QT_NO_XKB
421 
422 }
423 
424 
425 /*!\internal
426   Creates the application input method.
427 */
create_xim()428 void QXIMInputContext::create_xim()
429 {
430     ++fontsetRefCount;
431 #ifndef QT_NO_XIM
432     xim = XOpenIM(X11->display, 0, 0, 0);
433     if (xim) {
434 
435 #ifdef USE_X11R6_XIM
436         XIMCallback destroy;
437         destroy.callback = (XIMProc) xim_destroy_callback;
438         destroy.client_data = XPointer(this);
439         if (XSetIMValues(xim, XNDestroyCallback, &destroy, (char *) 0) != 0)
440             qWarning("Xlib doesn't support destroy callback");
441 #endif // USE_X11R6_XIM
442 
443         XIMStyles *styles = 0;
444         XGetIMValues(xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0);
445         if (styles) {
446             int i;
447             for (i = 0; !xim_style && i < styles->count_styles; i++) {
448                 if (styles->supported_styles[i] == qt_xim_preferred_style) {
449                     xim_style = qt_xim_preferred_style;
450                     break;
451                 }
452             }
453             // if the preferred input style couldn't be found, look for
454             // Nothing
455             for (i = 0; !xim_style && i < styles->count_styles; i++) {
456                 if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
457                     xim_style = XIMPreeditNothing | XIMStatusNothing;
458                     break;
459                 }
460             }
461             // ... and failing that, None.
462             for (i = 0; !xim_style && i < styles->count_styles; i++) {
463                 if (styles->supported_styles[i] == (XIMPreeditNone |
464                                                     XIMStatusNone)) {
465                     xim_style = XIMPreeditNone | XIMStatusNone;
466                     break;
467                 }
468             }
469 
470             // qDebug("QApplication: using im style %lx", xim_style);
471             XFree((char *)styles);
472         }
473 
474         if (xim_style) {
475 
476 #ifdef USE_X11R6_XIM
477             XUnregisterIMInstantiateCallback(X11->display, 0, 0, 0,
478                                              (XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
479 #endif // USE_X11R6_XIM
480 
481             if (QWidget *focusWidget = QApplication::focusWidget()) {
482                 // reinitialize input context after the input method
483                 // server (like SCIM) has been launched without
484                 // requiring the user to manually switch focus.
485                 if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled)
486                     && focusWidget->testAttribute(Qt::WA_WState_Created)
487                     && focusWidget->isEnabled())
488                     setFocusWidget(focusWidget);
489             }
490             // following code fragment is not required for immodule
491             // version of XIM
492 #if 0
493             QWidgetList list = qApp->topLevelWidgets();
494             for (int i = 0; i < list.size(); ++i) {
495                 QWidget *w = list.at(i);
496                 w->d->createTLSysExtra();
497             }
498 #endif
499         } else {
500             // Give up
501             qWarning("No supported input style found."
502                      "  See InputMethod documentation.");
503             close_xim();
504         }
505     }
506 #endif // QT_NO_XIM
507 }
508 
509 /*!\internal
510   Closes the application input method.
511 */
close_xim()512 void QXIMInputContext::close_xim()
513 {
514     for(QHash<WId, ICData *>::const_iterator i = ximData.constBegin(),
515                                              e = ximData.constEnd(); i != e; ++i) {
516         ICData *data = i.value();
517         if (data->ic)
518             XDestroyIC(data->ic);
519         delete data;
520     }
521     ximData.clear();
522 
523     if ( --fontsetRefCount == 0 ) {
524 	Display *dpy = X11->display;
525 	for ( int i = 0; i < 8; i++ ) {
526 	    if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) {
527 		XFreeFontSet(dpy, fontsetCache[i]);
528 		fontsetCache[i] = 0;
529 	    }
530 	}
531     }
532 
533     setFocusWidget(0);
534     xim = 0;
535 }
536 
537 
538 
~QXIMInputContext()539 QXIMInputContext::~QXIMInputContext()
540 {
541     XIM old_xim = xim; // close_xim clears xim pointer.
542     close_xim();
543     if (old_xim)
544         XCloseIM(old_xim);
545 }
546 
547 
identifierName()548 QString QXIMInputContext::identifierName()
549 {
550     // the name should be "xim" rather than "XIM" to be consistent
551     // with corresponding immodule of GTK+
552     return QLatin1String("xim");
553 }
554 
555 
language()556 QString QXIMInputContext::language()
557 {
558     QString language;
559     if (xim) {
560         QByteArray locale(XLocaleOfIM(xim));
561 
562         if (locale.startsWith("zh")) {
563             // Chinese language should be formed as "zh_CN", "zh_TW", "zh_HK"
564             language = QLatin1String(locale.left(5));
565         } else {
566             // other languages should be two-letter ISO 639 language code
567             language = QLatin1String(locale.left(2));
568         }
569     }
570     return language;
571 }
572 
reset()573 void QXIMInputContext::reset()
574 {
575     QWidget *w = focusWidget();
576     if (!w)
577         return;
578 
579     ICData *data = ximData.value(w->effectiveWinId());
580     if (!data)
581         return;
582 
583     if (data->ic) {
584         char *mb = XmbResetIC(data->ic);
585         QInputMethodEvent e;
586         if (mb) {
587             e.setCommitString(QString::fromLocal8Bit(mb));
588             XFree(mb);
589             data->preeditEmpty = false; // force sending an event
590         }
591         if (!data->preeditEmpty) {
592             sendEvent(e);
593             update();
594         }
595     }
596     data->clear();
597 }
598 
widgetDestroyed(QWidget * w)599 void QXIMInputContext::widgetDestroyed(QWidget *w)
600 {
601     QInputContext::widgetDestroyed(w);
602     ICData *data = ximData.take(w->effectiveWinId());
603     if (!data)
604         return;
605 
606     data->clear();
607     if (data->ic)
608         XDestroyIC(data->ic);
609     delete data;
610 }
611 
mouseHandler(int pos,QMouseEvent * e)612 void QXIMInputContext::mouseHandler(int pos, QMouseEvent *e)
613 {
614     if(e->type() != QEvent::MouseButtonPress)
615         return;
616 
617     XIM_DEBUG("QXIMInputContext::mouseHandler pos=%d", pos);
618     if (QWidget *w = focusWidget()) {
619         ICData *data = ximData.value(w->effectiveWinId());
620         if (!data)
621             return;
622         if (pos < 0 || pos > data->text.length())
623             reset();
624         // ##### handle mouse position
625     }
626 }
627 
isComposing() const628 bool QXIMInputContext::isComposing() const
629 {
630     QWidget *w = focusWidget();
631     if (!w)
632         return false;
633 
634     ICData *data = ximData.value(w->effectiveWinId());
635     if (!data)
636         return false;
637     return data->composing;
638 }
639 
setFocusWidget(QWidget * w)640 void QXIMInputContext::setFocusWidget(QWidget *w)
641 {
642     if (!xim)
643         return;
644     QWidget *oldFocus = focusWidget();
645     if (oldFocus == w)
646         return;
647 
648     if (language() != QLatin1String("ja"))
649         reset();
650 
651     if (oldFocus) {
652         ICData *data = ximData.value(oldFocus->effectiveWinId());
653         if (data && data->ic)
654             XUnsetICFocus(data->ic);
655     }
656 
657     QInputContext::setFocusWidget(w);
658 
659     if (!w || w->inputMethodHints() & (Qt::ImhExclusiveInputMask | Qt::ImhHiddenText))
660         return;
661 
662     ICData *data = ximData.value(w->effectiveWinId());
663     if (!data)
664         data = createICData(w);
665 
666     if (data->ic)
667         XSetICFocus(data->ic);
668 
669     update();
670 }
671 
672 
x11FilterEvent(QWidget * keywidget,XEvent * event)673 bool QXIMInputContext::x11FilterEvent(QWidget *keywidget, XEvent *event)
674 {
675     int xkey_keycode = event->xkey.keycode;
676     if (!keywidget->testAttribute(Qt::WA_WState_Created))
677         return false;
678     if (XFilterEvent(event, keywidget->effectiveWinId())) {
679         qt_ximComposingKeycode = xkey_keycode; // ### not documented in xlib
680 
681         update();
682 
683         return true;
684     }
685     if (event->type != XKeyPress || event->xkey.keycode != 0)
686         return false;
687 
688     QWidget *w = focusWidget();
689     if (keywidget != w)
690         return false;
691     ICData *data = ximData.value(w->effectiveWinId());
692     if (!data)
693         return false;
694 
695     // input method has sent us a commit string
696     QByteArray string;
697     string.resize(513);
698     KeySym key;    // unused
699     Status status; // unused
700     QString text;
701     int count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
702                                 &key, &status);
703 
704     if (status == XBufferOverflow) {
705         string.resize(count + 1);
706         count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
707                                 &key, &status);
708     }
709     if (count > 0) {
710         // XmbLookupString() gave us some text, convert it to unicode
711         text = qt_input_mapper->toUnicode(string.constData() , count);
712         if (text.isEmpty()) {
713             // codec couldn't convert to unicode? this can happen when running in the
714             // C locale (or with no LANG set). try converting from latin-1
715             text = QString::fromLatin1(string.constData(), count);
716         }
717     }
718 
719 #if 0
720     if (!(xim_style & XIMPreeditCallbacks) || !isComposing()) {
721         // ############### send a regular key event here!
722         ;
723     }
724 #endif
725 
726     QInputMethodEvent e;
727     e.setCommitString(text);
728     sendEvent(e);
729     data->clear();
730 
731     update();
732 
733     return true;
734 }
735 
736 
createICData(QWidget * w)737 QXIMInputContext::ICData *QXIMInputContext::createICData(QWidget *w)
738 {
739     ICData *data = new ICData;
740     data->widget = w;
741     data->preeditEmpty = true;
742 
743     XVaNestedList preedit_attr = 0;
744     XIMCallback startcallback, drawcallback, donecallback;
745 
746     QFont font = w->font();
747     data->fontset = getFontSet(font);
748 
749     if (xim_style & XIMPreeditArea) {
750         XRectangle rect;
751         rect.x = 0;
752         rect.y = 0;
753         rect.width = w->width();
754         rect.height = w->height();
755 
756         preedit_attr = XVaCreateNestedList(0,
757                                            XNArea, &rect,
758                                            XNFontSet, data->fontset,
759                                            (char *) 0);
760     } else if (xim_style & XIMPreeditPosition) {
761         XPoint spot;
762         spot.x = 1;
763         spot.y = 1;
764 
765         preedit_attr = XVaCreateNestedList(0,
766                                            XNSpotLocation, &spot,
767                                            XNFontSet, data->fontset,
768                                            (char *) 0);
769     } else if (xim_style & XIMPreeditCallbacks) {
770         startcallback.client_data = (XPointer) this;
771         startcallback.callback = (XIMProc) xic_start_callback;
772         drawcallback.client_data = (XPointer) this;
773         drawcallback.callback = (XIMProc)xic_draw_callback;
774         donecallback.client_data = (XPointer) this;
775         donecallback.callback = (XIMProc) xic_done_callback;
776 
777         preedit_attr = XVaCreateNestedList(0,
778                                            XNPreeditStartCallback, &startcallback,
779                                            XNPreeditDrawCallback, &drawcallback,
780                                            XNPreeditDoneCallback, &donecallback,
781                                            (char *) 0);
782     }
783 
784     if (preedit_attr) {
785         data->ic = XCreateIC(xim,
786                              XNInputStyle, xim_style,
787                              XNClientWindow, w->effectiveWinId(),
788                              XNPreeditAttributes, preedit_attr,
789                              (char *) 0);
790         XFree(preedit_attr);
791     } else {
792         data->ic = XCreateIC(xim,
793                              XNInputStyle, xim_style,
794                              XNClientWindow, w->effectiveWinId(),
795                              (char *) 0);
796     }
797 
798     if (data->ic) {
799         // when resetting the input context, preserve the input state
800         (void) XSetICValues(data->ic, XNResetState, XIMPreserveState, (char *) 0);
801     } else {
802         qWarning("Failed to create XIC");
803     }
804 
805     ximData[w->effectiveWinId()] = data;
806     return data;
807 }
808 
update()809 void QXIMInputContext::update()
810 {
811     QWidget *w = focusWidget();
812     if (!w)
813         return;
814 
815     ICData *data = ximData.value(w->effectiveWinId());
816     if (!data || !data->ic)
817         return;
818 
819     QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
820     QPoint p;
821     if (w->nativeParentWidget())
822         p = w->mapTo(w->nativeParentWidget(), QPoint((r.left() + r.right() + 1)/2, r.bottom()));
823     else
824         p = QPoint((r.left() + r.right() + 1)/2, r.bottom());
825     XPoint spot;
826     spot.x = p.x();
827     spot.y = p.y();
828 
829     r = w->rect();
830     XRectangle area;
831     area.x = r.x();
832     area.y = r.y();
833     area.width = r.width();
834     area.height = r.height();
835 
836     XFontSet fontset = getFontSet(qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont)));
837     if (data->fontset == fontset)
838         fontset = 0;
839     else
840         data->fontset = fontset;
841 
842     XVaNestedList preedit_attr;
843     if (fontset)
844         preedit_attr = XVaCreateNestedList(0,
845                                            XNSpotLocation, &spot,
846                                            XNArea, &area,
847                                            XNFontSet, fontset,
848                                            (char *) 0);
849     else
850         preedit_attr = XVaCreateNestedList(0,
851                                            XNSpotLocation, &spot,
852                                            XNArea, &area,
853                                            (char *) 0);
854 
855     XSetICValues(data->ic, XNPreeditAttributes, preedit_attr, (char *) 0);
856     XFree(preedit_attr);
857 }
858 
859 
860 #else
861 /*
862     When QT_NO_XIM is defined, we provide a dummy implementation for
863     this class. The reason for this is that the header file is moc'ed
864     regardless of QT_NO_XIM. The best would be to remove the file
865     completely from the pri file is QT_NO_XIM was defined, or for moc
866     to understand this preprocessor directive. Since the header does
867     not declare this class when QT_NO_XIM is defined, this is dead
868     code.
869 */
870 bool QXIMInputContext::isComposing() const { return false; }
871 QString QXIMInputContext::identifierName() { return QString(); }
872 void QXIMInputContext::mouseHandler(int, QMouseEvent *) {}
873 void QXIMInputContext::setFocusWidget(QWidget *) {}
874 void QXIMInputContext::reset() {}
875 void QXIMInputContext::update() {}
876 QXIMInputContext::~QXIMInputContext() {}
877 void QXIMInputContext::widgetDestroyed(QWidget *) {}
878 QString QXIMInputContext::language() { return QString(); }
879 bool QXIMInputContext::x11FilterEvent(QWidget *, XEvent *) { return true; }
880 
881 #endif //QT_NO_XIM
882 
883 QT_END_NAMESPACE
884 
885 #endif //QT_NO_IM
886