/**************************************************************************** ** ** Implementation of TQInputContext class ** ** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. ** ** This file is part of the kernel module of the TQt GUI Toolkit. ** ** This file may be used under the terms of the GNU General ** Public License versions 2.0 or 3.0 as published by the Free ** Software Foundation and appearing in the files LICENSE.GPL2 ** and LICENSE.GPL3 included in the packaging of this file. ** Alternatively you may (at your option) use any later version ** of the GNU General Public License if such license has been ** publicly approved by Trolltech ASA (or its successors, if any) ** and the KDE Free TQt Foundation. ** ** Please review the following information to ensure GNU General ** Public Licensing requirements will be met: ** http://trolltech.com/products/qt/licenses/licensing/opensource/. ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview ** or contact the sales department at sales@trolltech.com. ** ** This file may be used under the terms of the Q Public License as ** defined by Trolltech ASA and appearing in the file LICENSE.TQPL ** included in the packaging of this file. Licensees holding valid TQt ** Commercial licenses may use this file in accordance with the TQt ** Commercial License Agreement provided with the Software. ** ** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, ** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted ** herein. ** **********************************************************************/ #include "tqplatformdefs.h" #include "tqapplication.h" #include "tqwidget.h" #include "tqinputcontext_p.h" #include #include bool qt_compose_emptied = FALSE; #if !defined(TQT_NO_XIM) #define XK_MISCELLANY #define XK_LATIN1 #include // #define TQT_XIM_DEBUG // from qapplication_x11.cpp extern XIM qt_xim; extern XIMStyle qt_xim_style; /* The cache here is needed, as X11 leaks a few kb for every XFreeFontSet call, so we avoid creating and deletion of fontsets as much as possible */ static XFontSet fontsetCache[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; static int fontsetRefCount = 0; static const char * const fontsetnames[] = { "-*-fixed-medium-r-*-*-16-*,-*-*-medium-r-*-*-16-*", "-*-fixed-medium-i-*-*-16-*,-*-*-medium-i-*-*-16-*", "-*-fixed-bold-r-*-*-16-*,-*-*-bold-r-*-*-16-*", "-*-fixed-bold-i-*-*-16-*,-*-*-bold-i-*-*-16-*", "-*-fixed-medium-r-*-*-24-*,-*-*-medium-r-*-*-24-*", "-*-fixed-medium-i-*-*-24-*,-*-*-medium-i-*-*-24-*", "-*-fixed-bold-r-*-*-24-*,-*-*-bold-r-*-*-24-*", "-*-fixed-bold-i-*-*-24-*,-*-*-bold-i-*-*-24-*" }; static XFontSet getFontSet( const TQFont &f ) { int i = 0; if (f.italic()) i |= 1; if (f.bold()) i |= 2; if ( f.pointSize() > 20 ) i += 4; if ( !fontsetCache[i] ) { Display* dpy = TQPaintDevice::x11AppDisplay(); int missCount; char** missList; fontsetCache[i] = XCreateFontSet(dpy, fontsetnames[i], &missList, &missCount, 0); if(missCount > 0) XFreeStringList(missList); if ( !fontsetCache[i] ) { fontsetCache[i] = XCreateFontSet(dpy, "-*-fixed-*-*-*-*-16-*", &missList, &missCount, 0); if(missCount > 0) XFreeStringList(missList); if ( !fontsetCache[i] ) fontsetCache[i] = (XFontSet)-1; } } return (fontsetCache[i] == (XFontSet)-1) ? 0 : fontsetCache[i]; } #ifdef TQ_C_CALLBACKS extern "C" { #endif // TQ_C_CALLBACKS static int xic_start_callback(XIC, XPointer client_data, XPointer) { TQInputContext *qic = (TQInputContext *) client_data; if (! qic) { #ifdef TQT_XIM_DEBUG qDebug("compose start: no qic"); #endif // TQT_XIM_DEBUG return 0; } qic->composing = TRUE; qic->text = TQString::null; qic->tqfocusWidget = 0; if ( qic->selectedChars.size() < 128 ) qic->selectedChars.resize( 128 ); qic->selectedChars.fill( 0 ); #ifdef TQT_XIM_DEBUG qDebug("compose start"); #endif // TQT_XIM_DEBUG return 0; } static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) { TQInputContext *qic = (TQInputContext *) client_data; if (! qic) { #ifdef TQT_XIM_DEBUG qDebug("compose event: invalid compose event %p", qic); #endif // TQT_XIM_DEBUG return 0; } bool send_imstart = FALSE; if (tqApp->tqfocusWidget() != qic->tqfocusWidget && qic->text.isEmpty()) { if (qic->tqfocusWidget) { #ifdef TQT_XIM_DEBUG qDebug( "sending IMEnd (empty) to %p", qic->tqfocusWidget ); #endif // TQT_XIM_DEBUG TQIMEvent endevent(TQEvent::IMEnd, TQString::null, -1); TQApplication::sendEvent(qic->tqfocusWidget, &endevent); } qic->text = TQString::null; qic->tqfocusWidget = tqApp->tqfocusWidget(); qic->composing = FALSE; if ( qic->selectedChars.size() < 128 ) qic->selectedChars.resize( 128 ); qic->selectedChars.fill( 0 ); if (qic->tqfocusWidget) { qic->composing = TRUE; send_imstart = TRUE; } } if (! qic->composing || ! qic->tqfocusWidget) { #ifdef TQT_XIM_DEBUG qDebug("compose event: invalid compose event %d %p", qic->composing, qic->tqfocusWidget); #endif // TQT_XIM_DEBUG return 0; } if ( send_imstart ) { #ifdef TQT_XIM_DEBUG qDebug( "sending IMStart to %p", qic->tqfocusWidget ); #endif // TQT_XIM_DEBUG qt_compose_emptied = FALSE; TQIMEvent startevent(TQEvent::IMStart, TQString::null, -1); TQApplication::sendEvent(qic->tqfocusWidget, &startevent); } XIMPreeditDrawCallbackStruct *drawstruct = (XIMPreeditDrawCallbackStruct *) call_data; XIMText *text = (XIMText *) drawstruct->text; int cursor = drawstruct->caret, sellen = 0; if ( ! drawstruct->caret && ! drawstruct->chg_first && ! drawstruct->chg_length && ! text ) { // nothing to do return 0; } if (text) { char *str = 0; if (text->encoding_is_wchar) { int l = wcstombs(NULL, text->string.wide_char, text->length); if (l != -1) { str = new char[l + 1]; wcstombs(str, text->string.wide_char, l); str[l] = 0; } } else str = text->string.multi_byte; if (! str) return 0; TQString s = TQString::fromLocal8Bit(str); if (text->encoding_is_wchar) delete [] str; if (drawstruct->chg_length < 0) qic->text.tqreplace(drawstruct->chg_first, UINT_MAX, s); else qic->text.tqreplace(drawstruct->chg_first, drawstruct->chg_length, s); if ( qic->selectedChars.size() < qic->text.length() ) { // expand the selectedChars array if the compose string is longer uint from = qic->selectedChars.size(); qic->selectedChars.resize( qic->text.length() ); for ( uint x = from; from < qic->selectedChars.size(); ++x ) qic->selectedChars[x] = 0; } uint x; bool *p = qic->selectedChars.data() + drawstruct->chg_first; // determine if the changed chars are selected based on text->feedback for ( x = 0; x < s.length(); ++x ) *p++ = ( text->feedback ? ( text->feedback[x] & XIMReverse ) : 0 ); // figure out where the selection starts, and how long it is p = qic->selectedChars.data(); bool started = FALSE; for ( x = 0; x < TQMIN(qic->text.length(), qic->selectedChars.size()); ++x ) { if ( started ) { if ( *p ) ++sellen; else break; } else { if ( *p ) { cursor = x; started = TRUE; sellen = 1; } } ++p; } } else { if (drawstruct->chg_length == 0) drawstruct->chg_length = -1; qic->text.remove(drawstruct->chg_first, drawstruct->chg_length); qt_compose_emptied = qic->text.isEmpty(); if ( qt_compose_emptied ) { #ifdef TQT_XIM_DEBUG qDebug( "compose emptied" ); #endif // TQT_XIM_DEBUG // don't send an empty compose, since we will send an IMEnd with // either the correct compose text (or null text if the user has // cancelled the compose or deleted all chars). return 0; } } #ifdef TQT_XIM_DEBUG qDebug( "sending IMCompose to %p with %d chars", qic->tqfocusWidget, qic->text.length() ); #endif // TQT_XIM_DEBUG TQIMComposeEvent event( TQEvent::IMCompose, qic->text, cursor, sellen ); TQApplication::sendEvent(qic->tqfocusWidget, &event); return 0; } static int xic_done_callback(XIC, XPointer client_data, XPointer) { TQInputContext *qic = (TQInputContext *) client_data; if (! qic) return 0; if (qic->composing && qic->tqfocusWidget) { #ifdef TQT_XIM_DEBUG qDebug( "sending IMEnd (empty) to %p", qic->tqfocusWidget ); #endif // TQT_XIM_DEBUG TQIMEvent event(TQEvent::IMEnd, TQString::null, -1); TQApplication::sendEvent(qic->tqfocusWidget, &event); } qic->composing = FALSE; qic->tqfocusWidget = 0; if ( qic->selectedChars.size() < 128 ) qic->selectedChars.resize( 128 ); qic->selectedChars.fill( 0 ); return 0; } #ifdef TQ_C_CALLBACKS } #endif // TQ_C_CALLBACKS #endif // !TQT_NO_XIM TQInputContext::TQInputContext(TQWidget *widget) : ic(0), tqfocusWidget(0), composing(FALSE), fontset(0) { #if !defined(TQT_NO_XIM) fontsetRefCount++; if (! qt_xim) { qWarning("TQInputContext: no input method context available"); return; } if (! widget->isTopLevel()) { qWarning("TQInputContext: cannot create input context for non-toplevel widgets"); return; } XPoint spot; XRectangle rect; XVaNestedList preedit_attr = 0; XIMCallback startcallback, drawcallback, donecallback; font = widget->font(); fontset = getFontSet( font ); if (qt_xim_style & XIMPreeditArea) { rect.x = 0; rect.y = 0; rect.width = widget->width(); rect.height = widget->height(); preedit_attr = XVaCreateNestedList(0, XNArea, &rect, XNFontSet, fontset, (char *) 0); } else if (qt_xim_style & XIMPreeditPosition) { spot.x = 1; spot.y = 1; preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, XNFontSet, fontset, (char *) 0); } else if (qt_xim_style & XIMPreeditCallbacks) { startcallback.client_data = (XPointer) this; startcallback.callback = (XIMProc) xic_start_callback; drawcallback.client_data = (XPointer) this; drawcallback.callback = (XIMProc)xic_draw_callback; donecallback.client_data = (XPointer) this; donecallback.callback = (XIMProc) xic_done_callback; preedit_attr = XVaCreateNestedList(0, XNPreeditStartCallback, &startcallback, XNPreeditDrawCallback, &drawcallback, XNPreeditDoneCallback, &donecallback, (char *) 0); } if (preedit_attr) { ic = XCreateIC(qt_xim, XNInputStyle, qt_xim_style, XNClientWindow, widget->winId(), XNPreeditAttributes, preedit_attr, (char *) 0); XFree(preedit_attr); } else ic = XCreateIC(qt_xim, XNInputStyle, qt_xim_style, XNClientWindow, widget->winId(), (char *) 0); if (! ic) qFatal("Failed to create XIM input context!"); // when resetting the input context, preserve the input state (void) XSetICValues((XIC) ic, XNResetState, XIMPreserveState, (char *) 0); #endif // !TQT_NO_XIM } TQInputContext::~TQInputContext() { #if !defined(TQT_NO_XIM) if (ic) XDestroyIC((XIC) ic); if ( --fontsetRefCount == 0 ) { Display *dpy = TQPaintDevice::x11AppDisplay(); for ( int i = 0; i < 8; i++ ) { if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) { XFreeFontSet(dpy, fontsetCache[i]); fontsetCache[i] = 0; } } } #endif // !TQT_NO_XIM ic = 0; tqfocusWidget = 0; composing = FALSE; } void TQInputContext::reset() { #if !defined(TQT_NO_XIM) if (tqfocusWidget && composing && ! text.isNull()) { #ifdef TQT_XIM_DEBUG qDebug("TQInputContext::reset: composing - sending IMEnd (empty) to %p", tqfocusWidget); #endif // TQT_XIM_DEBUG TQIMEvent endevent(TQEvent::IMEnd, TQString::null, -1); TQApplication::sendEvent(tqfocusWidget, &endevent); tqfocusWidget = 0; text = TQString::null; if ( selectedChars.size() < 128 ) selectedChars.resize( 128 ); selectedChars.fill( 0 ); char *mb = XmbResetIC((XIC) ic); if (mb) XFree(mb); } #endif // !TQT_NO_XIM } void TQInputContext::setComposePosition(int x, int y) { #if !defined(TQT_NO_XIM) if (qt_xim && ic) { XPoint point; point.x = x; point.y = y; XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, (char *) 0); XSetICValues((XIC) ic, XNPreeditAttributes, preedit_attr, (char *) 0); XFree(preedit_attr); } #endif // !TQT_NO_XIM } void TQInputContext::setComposeArea(int x, int y, int w, int h) { #if !defined(TQT_NO_XIM) if (qt_xim && ic) { XRectangle rect; rect.x = x; rect.y = y; rect.width = w; rect.height = h; XVaNestedList preedit_attr = XVaCreateNestedList(0, XNArea, &rect, (char *) 0); XSetICValues((XIC) ic, XNPreeditAttributes, preedit_attr, (char *) 0); XFree(preedit_attr); } #endif } int TQInputContext::lookupString(XKeyEvent *event, TQCString &chars, KeySym *key, Status *status) const { int count = 0; #if !defined(TQT_NO_XIM) if (qt_xim && ic) { count = XmbLookupString((XIC) ic, event, chars.data(), chars.size(), key, status); if ((*status) == XBufferOverflow ) { chars.resize(count + 1); count = XmbLookupString((XIC) ic, event, chars.data(), chars.size(), key, status); } } #endif // TQT_NO_XIM return count; } void TQInputContext::setFocus() { #if !defined(TQT_NO_XIM) if (qt_xim && ic) XSetICFocus((XIC) ic); #endif // !TQT_NO_XIM } void TQInputContext::setXFontSet(const TQFont &f) { #if !defined(TQT_NO_XIM) if (font == f) return; // nothing to do font = f; XFontSet fs = getFontSet(font); if (fontset == fs) return; // nothing to do fontset = fs; XVaNestedList preedit_attr = XVaCreateNestedList(0, XNFontSet, fontset, (char *) 0); XSetICValues((XIC) ic, XNPreeditAttributes, preedit_attr, (char *) 0); XFree(preedit_attr); #else TQ_UNUSED( f ); #endif }