1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the input methods of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
/****************************************************************************
31
** Implementation of QXIMInputContext class
33
** Copyright (C) 2003-2004 immodule for Qt Project. All rights reserved.
35
** This file is written to contribute to Trolltech AS under their own
36
** licence. You may use this file under your Qt license. Following
37
** description is copied from their original file headers. Contact
38
** immodule-qt@freedesktop.org if any conditions of this licensing are
41
****************************************************************************/
43
#include "qximinputcontext_p.h"
45
#if !defined(QT_NO_IM)
47
#if !defined(QT_NO_XIM)
49
#include "qplatformdefs.h"
51
#include "qapplication.h"
55
#include "qtextcodec.h"
57
#include "qtextformat.h"
59
#include "qx11info_x11.h"
65
// #define QT_XIM_DEBUG
67
#define XIM_DEBUG qDebug
69
#define XIM_DEBUG if (0) qDebug
72
// from qapplication_x11.cpp
73
// #### move to X11 struct
74
extern XIMStyle qt_xim_preferred_style;
75
extern char *qt_ximServer;
76
extern int qt_ximComposingKeycode;
77
extern QTextCodec * qt_input_mapper;
79
XIMStyle QXIMInputContext::xim_style = 0;
80
// moved from qapplication_x11.cpp
81
static const XIMStyle xim_default_style = XIMPreeditCallbacks | XIMStatusNothing;
86
static void xim_create_callback(XIM /*im*/,
88
XPointer /*call_data*/)
90
QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
91
// qDebug("xim_create_callback");
95
static void xim_destroy_callback(XIM /*im*/,
97
XPointer /*call_data*/)
99
QXIMInputContext *qic = reinterpret_cast<QXIMInputContext *>(client_data);
100
// qDebug("xim_destroy_callback");
102
XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
103
(XIMProc) xim_create_callback, reinterpret_cast<char *>(qic));
105
#endif // USE_X11R6_XIM
107
static int xic_start_callback(XIC, XPointer client_data, XPointer) {
108
QXIMInputContext *qic = (QXIMInputContext *) client_data;
110
XIM_DEBUG("xic_start_callback: no qic");
113
QXIMInputContext::ICData *data = qic->icData();
115
XIM_DEBUG("xic_start_callback: no ic data");
118
XIM_DEBUG("xic_start_callback");
121
data->composing = true;
126
static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) {
127
QXIMInputContext *qic = (QXIMInputContext *) client_data;
129
XIM_DEBUG("xic_draw_callback: no qic");
132
QXIMInputContext::ICData *data = qic->icData();
134
XIM_DEBUG("xic_draw_callback: no ic data");
137
XIM_DEBUG("xic_draw_callback");
140
if(!data->composing) {
142
data->composing = true;
145
XIMPreeditDrawCallbackStruct *drawstruct = (XIMPreeditDrawCallbackStruct *) call_data;
146
XIMText *text = (XIMText *) drawstruct->text;
147
int cursor = drawstruct->caret, sellen = 0;
149
if (!drawstruct->caret && !drawstruct->chg_first && !drawstruct->chg_length && !text) {
150
if(data->text.isEmpty()) {
151
XIM_DEBUG("compose emptied");
152
// if the composition string has been emptied, we need
153
// to send an InputMethodEnd event
158
// if the commit string has coming after here, InputMethodStart
159
// will be sent dynamically
166
if (text->encoding_is_wchar) {
167
int l = wcstombs(NULL, text->string.wide_char, text->length);
169
str = new char[l + 1];
170
wcstombs(str, text->string.wide_char, l);
174
str = text->string.multi_byte;
179
QString s = QString::fromLocal8Bit(str);
181
if (text->encoding_is_wchar)
184
if (drawstruct->chg_length < 0)
185
data->text.replace(drawstruct->chg_first, UINT_MAX, s);
187
data->text.replace(drawstruct->chg_first, drawstruct->chg_length, s);
189
if (data->selectedChars.size() < data->text.length()) {
190
// expand the selectedChars array if the compose string is longer
191
int from = data->selectedChars.size();
192
data->selectedChars.resize(data->text.length());
193
for (int x = from; x < data->selectedChars.size(); ++x)
194
data->selectedChars.clearBit(x);
197
// determine if the changed chars are selected based on text->feedback
198
for (int x = 0; x < text->length; ++x)
199
data->selectedChars.setBit(x + drawstruct->chg_first,
200
(text->feedback ? (text->feedback[x] & XIMReverse) : 0));
202
// figure out where the selection starts, and how long it is
203
bool started = false;
204
for (int x = 0; x < qMin(data->selectedChars.size(), data->text.length()); ++x) {
206
if (data->selectedChars.testBit(x)) ++sellen;
209
if (data->selectedChars.testBit(x)) {
217
if (drawstruct->chg_length == 0)
218
drawstruct->chg_length = -1;
220
data->text.remove(drawstruct->chg_first, drawstruct->chg_length);
221
bool qt_compose_emptied = data->text.isEmpty();
222
if (qt_compose_emptied) {
223
XIM_DEBUG("compose emptied 2 text=%s", data->text.toUtf8().constData());
224
// if the composition string has been emptied, we need
225
// to send an InputMethodEnd event
229
// if the commit string has coming after here, InputMethodStart
230
// will be sent dynamically
236
cursor = data->text.length();
237
XIM_DEBUG("sending compose: '%s', cursor=%d, sellen=%d",
238
data->text.toUtf8().constData(), cursor, sellen);
239
QList<QInputMethodEvent::Attribute> attrs;
241
attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, cursor,
242
qic->standardFormat(QInputContext::PreeditFormat));
244
attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, cursor, sellen,
245
qic->standardFormat(QInputContext::SelectionFormat));
246
if (cursor + sellen < data->text.length())
247
attrs << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
248
cursor + sellen, data->text.length() - cursor - sellen,
249
qic->standardFormat(QInputContext::PreeditFormat));
250
QInputMethodEvent e(data->text, attrs);
256
static int xic_done_callback(XIC, XPointer client_data, XPointer) {
257
QXIMInputContext *qic = (QXIMInputContext *) client_data;
261
XIM_DEBUG("xic_done_callback");
262
// Don't send InputMethodEnd here. QXIMInputContext::x11FilterEvent()
263
// handles InputMethodEnd with commit string.
268
void QXIMInputContext::ICData::clear()
271
selectedChars.clear();
275
QXIMInputContext::ICData *QXIMInputContext::icData() const
277
return ximData.value(focusWidget());
279
/* The cache here is needed, as X11 leaks a few kb for every
280
XFreeFontSet call, so we avoid creating and deletion of fontsets as
283
static XFontSet fontsetCache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
284
static int fontsetRefCount = 0;
286
static const char * const fontsetnames[] = {
287
"-*-fixed-medium-r-*-*-16-*,-*-*-medium-r-*-*-16-*",
288
"-*-fixed-medium-i-*-*-16-*,-*-*-medium-i-*-*-16-*",
289
"-*-fixed-bold-r-*-*-16-*,-*-*-bold-r-*-*-16-*",
290
"-*-fixed-bold-i-*-*-16-*,-*-*-bold-i-*-*-16-*",
291
"-*-fixed-medium-r-*-*-24-*,-*-*-medium-r-*-*-24-*",
292
"-*-fixed-medium-i-*-*-24-*,-*-*-medium-i-*-*-24-*",
293
"-*-fixed-bold-r-*-*-24-*,-*-*-bold-r-*-*-24-*",
294
"-*-fixed-bold-i-*-*-24-*,-*-*-bold-i-*-*-24-*"
297
static XFontSet getFontSet(const QFont &f)
305
if (f.pointSize() > 20)
308
if (!fontsetCache[i]) {
309
Display* dpy = X11->display;
312
fontsetCache[i] = XCreateFontSet(dpy, fontsetnames[i], &missList, &missCount, 0);
314
XFreeStringList(missList);
315
if (!fontsetCache[i]) {
316
fontsetCache[i] = XCreateFontSet(dpy, "-*-fixed-*-*-*-*-16-*", &missList, &missCount, 0);
318
XFreeStringList(missList);
319
if (!fontsetCache[i])
320
fontsetCache[i] = (XFontSet)-1;
323
return (fontsetCache[i] == (XFontSet)-1) ? 0 : fontsetCache[i];
328
QXIMInputContext::QXIMInputContext()
330
if (!qt_xim_preferred_style) // no configured input style, use the default
331
qt_xim_preferred_style = xim_default_style;
334
QByteArray ximServerName(qt_ximServer);
336
ximServerName.prepend("@im=");
340
if (!XSupportsLocale())
341
qWarning("Qt: Locales not supported on X server");
344
else if (XSetLocaleModifiers (ximServerName.constData()) == 0)
345
qWarning("Qt: Cannot set locale modifiers: %s", ximServerName.constData());
346
else /* if (!noxim) */
347
XRegisterIMInstantiateCallback(X11->display, 0, 0, 0,
348
(XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
349
#else // !USE_X11R6_XIM
350
else if (XSetLocaleModifiers ("") == 0)
351
qWarning("Qt: Cannot set locale modifiers");
352
else /* if (!noxim) */
353
QXIMInputContext::create_xim();
354
#endif // USE_X11R6_XIM
359
Creates the application input method.
361
void QXIMInputContext::create_xim()
365
xim = XOpenIM(X11->display, 0, 0, 0);
370
destroy.callback = (XIMProc) xim_destroy_callback;
371
destroy.client_data = 0;
372
if (XSetIMValues(xim, XNDestroyCallback, &destroy, (char *) 0) != 0)
373
qWarning("Xlib dosn't support destroy callback");
374
#endif // USE_X11R6_XIM
376
XIMStyles *styles = 0;
377
XGetIMValues(xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0);
380
for (i = 0; !xim_style && i < styles->count_styles; i++) {
381
if (styles->supported_styles[i] == qt_xim_preferred_style) {
382
xim_style = qt_xim_preferred_style;
386
// if the preferred input style couldn't be found, look for
388
for (i = 0; !xim_style && i < styles->count_styles; i++) {
389
if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) {
390
xim_style = XIMPreeditNothing | XIMStatusNothing;
394
// ... and failing that, None.
395
for (i = 0; !xim_style && i < styles->count_styles; i++) {
396
if (styles->supported_styles[i] == (XIMPreeditNone |
398
xim_style = XIMPreeditNone | XIMStatusNone;
403
// qDebug("QApplication: using im style %lx", xim_style);
404
XFree((char *)styles);
410
XUnregisterIMInstantiateCallback(X11->display, 0, 0, 0,
411
(XIMProc) xim_create_callback, reinterpret_cast<char *>(this));
412
#endif // USE_X11R6_XIM
414
// following code fragment is not required for immodule
417
QWidgetList list = qApp->topLevelWidgets();
418
for (int i = 0; i < list.size(); ++i) {
419
QWidget *w = list.at(i);
420
w->d->createTLSysExtra();
425
qWarning("No supported input style found."
426
" See InputMethod documentation.");
434
Closes the application input method.
436
void QXIMInputContext::close_xim()
438
QString errMsg("QXIMInputContext::close_xim() has been called");
440
// ###clean up ximData!
443
// ############## check this!
444
// Calling XCloseIM gives a Purify FMR error
446
// We prefer a less serious memory leak
449
if ( --fontsetRefCount == 0 ) {
450
Display *dpy = X11->display;
451
for ( int i = 0; i < 8; i++ ) {
452
if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) {
453
XFreeFontSet(dpy, fontsetCache[i]);
462
QXIMInputContext::~QXIMInputContext()
468
QString QXIMInputContext::identifierName()
470
// the name should be "xim" rather than "XIM" to be consistent
471
// with corresponding immodule of GTK+
476
QString QXIMInputContext::language()
480
QByteArray locale(XLocaleOfIM(xim));
482
if (locale.startsWith("zh")) {
483
// Chinese language should be formed as "zh_CN", "zh_TW", "zh_HK"
484
language = locale.left(5);
486
// other languages should be two-letter ISO 639 language code
487
language = locale.left(2);
493
void QXIMInputContext::reset()
495
QWidget *w = focusWidget();
499
ICData *data = ximData.value(w);
504
char *mb = XmbResetIC(data->ic);
507
e.setCommitString(QString::fromLocal8Bit(mb));
515
void QXIMInputContext::widgetDestroyed(QWidget *w)
517
QInputContext::widgetDestroyed(w);
518
ICData *data = ximData.take(w);
524
XDestroyIC(data->ic);
528
void QXIMInputContext::mouseHandler(int pos, QMouseEvent *)
530
XIM_DEBUG("QXIMInputContext::mouseHandler pos=%d", pos);
531
ICData *data = ximData.value(focusWidget());
534
if (pos < 0 || pos > data->text.length())
536
// ##### handle mouse position
539
bool QXIMInputContext::isComposing() const
541
QWidget *w = focusWidget();
545
ICData *data = ximData.value(w);
548
return data->composing;
551
void QXIMInputContext::setFocusWidget(QWidget *w)
553
QWidget *oldFocus = focusWidget();
557
if (language() != QLatin1String("ja"))
561
ICData *data = ximData.value(oldFocus);
562
if (data && data->ic)
563
XUnsetICFocus(data->ic);
566
QInputContext::setFocusWidget(w);
571
ICData *data = ximData.value(w);
573
data = createICData(w);
576
XSetICFocus(data->ic);
582
bool QXIMInputContext::x11FilterEvent(QWidget *keywidget, XEvent *event)
584
int xkey_keycode = event->xkey.keycode;
585
if (XFilterEvent(event, keywidget->winId())) {
586
qt_ximComposingKeycode = xkey_keycode; // ### not documented in xlib
590
if (event->type != XKeyPress || event->xkey.keycode != 0)
593
QWidget *w = focusWidget();
596
ICData *data = ximData.value(w);
600
// input method has sent us a commit string
603
KeySym key; // unused
604
Status status; // unused
606
int count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
609
if (status == XBufferOverflow) {
610
string.resize(count + 1);
611
count = XmbLookupString(data->ic, &event->xkey, string.data(), string.size(),
615
text = qt_input_mapper->toUnicode(string.constData() , count);
618
if (!(xim_style & XIMPreeditCallbacks) || !isComposing()) {
619
// ############### send a regular key event here!
625
e.setCommitString(text);
632
QXIMInputContext::ICData *QXIMInputContext::createICData(QWidget *w)
634
ICData *data = new ICData;
637
XVaNestedList preedit_attr = 0;
638
XIMCallback startcallback, drawcallback, donecallback;
640
QFont font = w->font();
641
data->fontset = getFontSet(font);
643
if (xim_style & XIMPreeditArea) {
647
rect.width = w->width();
648
rect.height = w->height();
650
preedit_attr = XVaCreateNestedList(0,
652
XNFontSet, data->fontset,
654
} else if (xim_style & XIMPreeditPosition) {
659
preedit_attr = XVaCreateNestedList(0,
660
XNSpotLocation, &spot,
661
XNFontSet, data->fontset,
663
} else if (xim_style & XIMPreeditCallbacks) {
664
startcallback.client_data = (XPointer) this;
665
startcallback.callback = (XIMProc) xic_start_callback;
666
drawcallback.client_data = (XPointer) this;
667
drawcallback.callback = (XIMProc)xic_draw_callback;
668
donecallback.client_data = (XPointer) this;
669
donecallback.callback = (XIMProc) xic_done_callback;
671
preedit_attr = XVaCreateNestedList(0,
672
XNPreeditStartCallback, &startcallback,
673
XNPreeditDrawCallback, &drawcallback,
674
XNPreeditDoneCallback, &donecallback,
679
data->ic = XCreateIC(xim,
680
XNInputStyle, xim_style,
681
XNClientWindow, w->winId(),
682
XNPreeditAttributes, preedit_attr,
686
data->ic = XCreateIC(xim,
687
XNInputStyle, xim_style,
688
XNClientWindow, w->winId(),
693
// when resetting the input context, preserve the input state
694
(void) XSetICValues(data->ic, XNResetState, XIMPreserveState, (char *) 0);
696
qWarning("Failed to create XIC");
703
void QXIMInputContext::update()
705
QWidget *w = focusWidget();
709
ICData *data = ximData.value(w);
710
if (!data || !data->ic)
713
QRect r = w->inputMethodQuery(Qt::ImMicroFocus).toRect();
714
QPoint p = r.bottomLeft();
723
area.width = r.width();
724
area.height = r.height();
726
XFontSet fontset = getFontSet(qvariant_cast<QFont>(w->inputMethodQuery(Qt::ImFont)));
727
if (data->fontset == fontset)
730
data->fontset = fontset;
732
XVaNestedList preedit_attr;
734
preedit_attr = XVaCreateNestedList(0,
735
XNSpotLocation, &spot,
740
preedit_attr = XVaCreateNestedList(0,
741
XNSpotLocation, &spot,
745
XSetICValues(data->ic, XNPreeditAttributes, preedit_attr, (char *) 0);
752
When QT_NO_XIM is defined, we provide a dummy implementation for
753
this class. The reason for this is that the header file is moc'ed
754
regardless of QT_NO_XIM. The best would be to remove the file
755
completely from the pri file is QT_NO_XIM was defined, or for moc
756
to understand this preprocessor directive. Since the header does
757
not declare this class when QT_NO_XIM is defined, this is dead
760
bool QXIMInputContext::isComposing() const { return false; }
761
QString QXIMInputContext::identifierName() { return QString(); }
762
void QXIMInputContext::mouseHandler(int, QMouseEvent *) {}
763
void QXIMInputContext::setFocusWidget(QWidget *) {}
764
void QXIMInputContext::reset() {}
765
void QXIMInputContext::update() {}
766
QXIMInputContext::~QXIMInputContext() {}
767
void QXIMInputContext::widgetDestroyed(QWidget *) {}
768
QString QXIMInputContext::language() { return QString(); }
769
bool QXIMInputContext::x11FilterEvent(QWidget *, XEvent *) { return true; }