diff -urN qt-x11-free-3.3.2/configure qt-x11-immodule-bc/configure --- qt-x11-free-3.3.2/configure 2004-04-01 01:40:12.000000000 +0900 +++ qt-x11-immodule-bc/configure 2004-06-23 01:10:27.058123680 +0900 @@ -217,7 +217,7 @@ fi # licensed modules depend on type of commercial license -MODULES="styles tools kernel widgets dialogs iconview workspace" +MODULES="styles tools kernel widgets dialogs iconview workspace input" [ "$PLATFORM_QWS" = "yes" ] && [ "$Products" = "qt-professional" ] && MODULES="$MODULES network" [ "$Products" != "qt-professional" ] && MODULES="$MODULES network canvas table xml opengl sql" CFG_MODULES_AVAILABLE=$MODULES @@ -2999,7 +2999,7 @@ # minimal-config small-config medium-config large-config full-config # # Modules: -# styles tools kernel widgets dialogs iconview workspace +# styles tools kernel widgets dialogs iconview workspace input # # Enterprise/Free edition modules: # network canvas table xml opengl sql @@ -3031,7 +3031,7 @@ # X11 : xftnameunparse x11sm xinerama xcursor xrandr xrender xftfreetype xkb # Embedded: embedded ft # -ALL_OPTIONS="styles tools kernel widgets dialogs iconview workspace network canvas table xml opengl sql stl" +ALL_OPTIONS="styles tools kernel widgets dialogs iconview workspace input network canvas table xml opengl sql stl" BUILD_CONFIG= BUILD_OPTIONS= @@ -3045,7 +3045,7 @@ BUILD_CONFIG="$config_option" ;; - styles|tools|kernel|widgets|dialogs|iconview|workspace|network|canvas|table|xml|opengl|sql|stl) + styles|tools|kernel|widgets|dialogs|iconview|workspace|input|network|canvas|table|xml|opengl|sql|stl) # these config options affect the Qt API/ABI. they should influence # the generation of the buildkey, so we don't skip them SKIP="no" diff -urN qt-x11-free-3.3.2/src/input/qinputcontextfactory.cpp qt-x11-immodule-bc/src/input/qinputcontextfactory.cpp --- qt-x11-free-3.3.2/src/input/qinputcontextfactory.cpp 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qinputcontextfactory.cpp 2004-06-23 01:54:29.544404704 +0900 @@ -0,0 +1,151 @@ +/**************************************************************************** +** $Id: qinputcontextfactory.cpp,v 1.2 2004/06/20 18:43:11 daisuke Exp $ +** +** Implementation of QInputContextFactory class +** +** Created : 001103 +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the widgets module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition licenses may use this +** file in accordance with the Qt Commercial License Agreement provided +** with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qinputcontextinterface_p.h" // up here for GCC 2.7.* compatibility +#include "qinputcontextfactory.h" + +#ifndef QT_NO_IM + +#include "qapplication.h" +#include "qpopupmenu.h" +#ifdef Q_WS_X11 +#include "qximinputcontext_p.h" +#endif + +#ifdef QT_THREAD_SUPPORT +#include +#endif // QT_THREAD_SUPPORT + +#include + +#include "qcleanuphandler.h" +#include +#ifndef QT_NO_COMPONENT + + +static QPluginManager *manager = 0; +static QSingleCleanupHandler< QPluginManager > cleanup_manager; + +static void create_manager() +{ + if( manager ) // already created + return; + +#ifdef QT_THREAD_SUPPORT + // protect manager creation + QMutexLocker locker( qt_global_mutexpool ? + qt_global_mutexpool->get( &manager ) : 0); + + // we check the manager pointer again to make sure that another thread + // has not created the manager before us. + + if ( manager ) // already created + return; +#endif + + manager = new QPluginManager( IID_QInputContextFactory, QApplication::libraryPaths(), "/input", FALSE ); + + Q_CHECK_PTR( manager ); + cleanup_manager.set( &manager ); +} + +#endif //QT_NO_COMPONENT + + +/*! + This function generates InputMethod with the name which is in agreement + with key of the first argument. widget of the second argument is client + widget of QInputContext. +*/ +QInputContext *QInputContextFactory::create( const QString& key, QWidget *widget ) +{ + QInputContext *ret = 0; + QString inputcontext = key; +#ifdef Q_WS_X11 + if ( inputcontext == "XIM" ) { + ret = new QXIMInputContext( widget ); + ret->setHolderWidget( widget ); + } +#endif + { } // Keep these here - they make the #ifdefery above work + +#ifndef QT_NO_COMPONENT + if(!ret) { + // make sure the manager is created + create_manager(); + + QInterfacePtr iface; + manager->queryInterface( inputcontext, &iface ); + + if ( iface ) { + ret = iface->create( inputcontext ); +#ifdef Q_WS_X11 + ret->setHolderWidget( widget ); +#endif + } + } +#endif + return ret; +} + + +/*! + This function returns the list of the names input methods. + Only input methods included in default and putted below + $QTDIR/plugins/input are listed. +*/ +#ifndef QT_NO_STRINGLIST +QStringList QInputContextFactory::keys() +{ + QStringList list; +#ifndef QT_NO_COMPONENT + // make sure the manager is created + create_manager(); + + list = manager->featureList(); +#endif //QT_NO_COMPONENT + +#ifdef Q_WS_X11 + if ( !list.contains( "XIM" ) ) + list << "XIM"; +#endif // Q_WS_X11 + + return list; +} +#endif // QT_NO_STRINGLIST + +#endif // QT_NO_IM diff -urN qt-x11-free-3.3.2/src/input/qinputcontextfactory.h qt-x11-immodule-bc/src/input/qinputcontextfactory.h --- qt-x11-free-3.3.2/src/input/qinputcontextfactory.h 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qinputcontextfactory.h 2004-06-23 01:10:27.059123528 +0900 @@ -0,0 +1,60 @@ +/**************************************************************************** +** $Id: qinputcontextfactory.h,v 1.1.1.1 2004/05/11 11:16:49 daisuke Exp $ +** +** Definition of QInputContextPlugin class +** +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +** +** This file is part of the widgets module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QINPUTCONTEXTFACTORY_H +#define QINPUTCONTEXTFACTORY_H + +#ifndef QT_H +#include "qstringlist.h" +#endif // QT_H + +#ifndef QT_NO_IM + +class QInputContext; +class QPopupMenu; + + +class Q_EXPORT QInputContextFactory +{ +public: +#ifndef QT_NO_STRINGLIST + static QStringList keys(); +#endif + static QInputContext *create( const QString&, QWidget*); // should be a toplevel widget + +}; +#endif //QT_NO_IM + +#endif //QINPUTCONTEXTFACTORY_H diff -urN qt-x11-free-3.3.2/src/input/qinputcontextinterface_p.h qt-x11-immodule-bc/src/input/qinputcontextinterface_p.h --- qt-x11-free-3.3.2/src/input/qinputcontextinterface_p.h 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qinputcontextinterface_p.h 2004-06-23 01:10:27.060123376 +0900 @@ -0,0 +1,74 @@ +/**************************************************************************** +** $Id: qinputcontextinterface_p.h,v 1.2 2004/06/20 18:43:11 daisuke Exp $ +** +** ... +** +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +** +** This file is part of the widgets module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QINPUTCONTEXTINTERFACE_P_H +#define QINPUTCONTEXTINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include +#endif // QT_H + +#ifndef QT_NO_IM +#ifndef QT_NO_COMPONENT + +class QWidget; +class QInputContext; + +// {6C2B9EDE-B63C-14c9-A729-3C7643739C4C} +#ifndef IID_QInputContextFactory +#define IID_QInputContextFactory QUuid(0x6c2b9ede, 0xb63c, 0x14c9, 0xa7, 0x29, 0x3c, 0x76, 0x43, 0x73, 0x9c, 0x4c) +#endif + +struct Q_EXPORT QInputContextFactoryInterface : public QFeatureListInterface +{ + virtual QInputContext* create( const QString& inputcontext ) = 0; +}; + +#endif //QT_NO_COMPONENT +#endif //QT_NO_IM + +#endif //QINPUTCONTEXTINTERFACE_P_H diff -urN qt-x11-free-3.3.2/src/input/qinputcontextplugin.cpp qt-x11-immodule-bc/src/input/qinputcontextplugin.cpp --- qt-x11-free-3.3.2/src/input/qinputcontextplugin.cpp 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qinputcontextplugin.cpp 2004-06-23 01:10:27.060123376 +0900 @@ -0,0 +1,158 @@ +/**************************************************************************** +** $Id: qinputcontextplugin.cpp,v 1.2 2004/06/20 18:43:11 daisuke Exp $ +** +** Implementation of QInputContextPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of the widgets module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include "qinputcontextplugin.h" + +#ifndef QT_NO_IM +#ifndef QT_NO_COMPONENT + +#include "qinputcontextinterface_p.h" + +/*! + \class QInputContextPlugin qinputcontextplugin.h + \brief The QInputContextPlugin class provides an abstract base for custom QInputContext plugins. + \reentrant + \ingroup plugins + + The input context plugin is a simple plugin interface that makes it + easy to create custom input contexts that can be loaded dynamically + into applications. + + Writing a input context plugin is achieved by subclassing this base + class, reimplementing the pure virtual functions keys(), + create(), and exporting the class with the \c Q_EXPORT_PLUGIN macro. + See the \link plugins-howto.html Qt Plugins documentation \endlink + for details. +*/ + +/*! + \fn QStringList QInputContextPlugin::keys() const + + Returns the list of InputContext keys this plugin supports. + + These keys are usually the class names of the custom input context + that are implemented in the plugin. + + \sa create() +*/ + +/*! + \fn QInputContext* QInputContextPlugin::create( const QString& key ) + + Creates and returns a QInputContext object for the InputContext key \a key. + The InputContext key is usually the class name of the required input method. + + \sa keys() +*/ + + + +class QInputContextPluginPrivate : public QInputContextFactoryInterface +{ +public: + QInputContextPluginPrivate( QInputContextPlugin *p ) + : plugin( p ) + { + } + + virtual ~QInputContextPluginPrivate(); + + QRESULT queryInterface( const QUuid &iid, QUnknownInterface **iface ); + Q_REFCOUNT; + + QStringList featureList() const; + QInputContext *create( const QString &key ); + +private: + QInputContextPlugin *plugin; +}; + +QRESULT QInputContextPluginPrivate::queryInterface( const QUuid &iid, QUnknownInterface **iface ) +{ + *iface = 0; + + if ( iid == IID_QUnknown ) + *iface = this; + else if ( iid == IID_QFeatureList ) + *iface = this; + else if ( iid == IID_QInputContextFactory ) + *iface = this; + else + return QE_NOINTERFACE; + + (*iface)->addRef(); + return QS_OK; +} + +QInputContextPluginPrivate::~QInputContextPluginPrivate() +{ + delete plugin; +} + +QStringList QInputContextPluginPrivate::featureList() const +{ + return plugin->keys(); +} + +QInputContext *QInputContextPluginPrivate::create( const QString &key ) +{ + return plugin->create( key ); +} + + +/*! + Constructs a InputContext plugin. This is invoked automatically by the + \c Q_EXPORT_PLUGIN macro. +*/ +QInputContextPlugin::QInputContextPlugin() + : QGPlugin( d = new QInputContextPluginPrivate( this ) ) +{ +} + +/*! + Destroys the InputContext plugin. + + You never have to call this explicitly. Qt destroys a plugin + automatically when it is no longer used. +*/ +QInputContextPlugin::~QInputContextPlugin() +{ + // don't delete d, as this is deleted by d +} + +#endif // QT_NO_COMPONENT +#endif // QT_NO_IM diff -urN qt-x11-free-3.3.2/src/input/qinputcontextplugin.h qt-x11-immodule-bc/src/input/qinputcontextplugin.h --- qt-x11-free-3.3.2/src/input/qinputcontextplugin.h 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qinputcontextplugin.h 2004-06-23 01:10:27.061123224 +0900 @@ -0,0 +1,64 @@ +/**************************************************************************** +** $Id: qinputcontextplugin.h,v 1.2 2004/06/20 18:43:11 daisuke Exp $ +** +** Definition of QInputContextPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001 Trolltech AS. All rights reserved. +** +** This file is part of the tools module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QINPUTCONTEXTPLUGIN_H +#define QINPUTCONTEXTPLUGIN_H + +#ifndef QT_H +#include "qgplugin.h" +#include "qstringlist.h" +#endif // QT_H + +#ifndef QT_NO_IM +class QInputContext; +class QInputContextPluginPrivate; + +class Q_EXPORT QInputContextPlugin : public QGPlugin +{ + Q_OBJECT +public: + QInputContextPlugin(); + ~QInputContextPlugin(); + + virtual QStringList keys() const = 0; + virtual QInputContext *create( const QString &key ) = 0; + +private: + QInputContextPluginPrivate *d; +}; +#endif // QT_NO_TEXTCODECPLUGIN +#endif // QTEXTCODECPLUGIN_H diff -urN qt-x11-free-3.3.2/src/input/qt_input.pri qt-x11-immodule-bc/src/input/qt_input.pri --- qt-x11-free-3.3.2/src/input/qt_input.pri 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qt_input.pri 2004-06-23 01:10:27.061123224 +0900 @@ -0,0 +1,13 @@ +# Qt inputcontext module + +input { + INPUT_P = input + HEADERS +=$$INPUT_H/qinputcontextfactory.h \ + $$INPUT_P/qinputcontextinterface_p.h \ + $$INPUT_H/qinputcontextplugin.h \ + $$INPUT_H/qximinputcontext_p.h + SOURCES +=$$INPUT_CPP/qinputcontextfactory.cpp \ + $$INPUT_CPP/qinputcontextplugin.cpp \ + $$INPUT_CPP/qximinputcontext_x11.cpp + +} diff -urN qt-x11-free-3.3.2/src/input/qximinputcontext_p.h qt-x11-immodule-bc/src/input/qximinputcontext_p.h --- qt-x11-free-3.3.2/src/input/qximinputcontext_p.h 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qximinputcontext_p.h 2004-06-23 02:07:02.819889424 +0900 @@ -0,0 +1,133 @@ +/**************************************************************************** +** $Id: qximinputcontext_p.h,v 1.6 2004/06/22 06:47:27 daisuke Exp $ +** +** Definition of QXIMInputContext +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QXIMINPUTCONTEXT_P_H +#define QXIMINPUTCONTEXT_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of internal files. This header file may change from version to version +// without notice, or even be removed. +// +// We mean it. +// +// + +#if !defined(Q_NO_IM) + +#include "qglobal.h" +#include +#include +#include + +class QKeyEvent; +class QWidget; +class QFont; +class QString; + + +#ifdef Q_WS_X11 +#include "qarray.h" +#include "qwindowdefs.h" +#include +#endif + +class QXIMInputContext : public QInputContext +{ +public: +#ifdef Q_WS_X11 + QXIMInputContext( QWidget * ); // should be a toplevel widget + ~QXIMInputContext(); + + QString name(); + QCString language(); + + bool x11FilterEvent( QWidget *keywidget, XEvent *event ); + void reset(); + + void setFocus(); + void unsetFocus(); + void setMicroFocus( int x, int y, int w, int h, QFont *f = 0 ); + void mouseHandler( int x, QEvent::Type type, + Qt::ButtonState butoon, Qt::ButtonState state ); + + bool hasFocus() const; + bool isComposing() const; + void resetClientState(); + + void sendIMEvent( QEvent::Type type, + const QString &text = QString::null, + int cursorPosition = -1, int selLength = 0 ); + + static void init_xim(); + static void create_xim(); + static void close_xim(); + + void *ic; + QString composingText; + QFont font; + XFontSet fontset; + QMemArray selectedChars; + +protected: + bool isPreeditRelocationEnabled(); + virtual bool isPreeditPreservationEnabled(); // not a QInputContext func + + QCString _language; + +private: + void setComposePosition(int, int); + void setComposeArea(int, int, int, int); + void setXFontSet(const QFont &); + + int lookupString(XKeyEvent *, QCString &, KeySym *, Status *) const; + +#endif // Q_WS_X11 +}; + + +inline QString QXIMInputContext::name() +{ + return "XIM"; +} + + +#endif //Q_NO_IM + +#endif // QXIMINPUTCONTEXT_P_H diff -urN qt-x11-free-3.3.2/src/input/qximinputcontext_x11.cpp qt-x11-immodule-bc/src/input/qximinputcontext_x11.cpp --- qt-x11-free-3.3.2/src/input/qximinputcontext_x11.cpp 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/input/qximinputcontext_x11.cpp 2004-06-23 02:10:35.916493816 +0900 @@ -0,0 +1,895 @@ +/**************************************************************************** +** $Id: qximinputcontext_x11.cpp,v 1.10 2004/06/22 06:47:27 daisuke Exp $ +** +** Implementation of QXIMInputContext class +** +** Copyright (C) 2000-2003 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses for Unix/X11 may use this file in accordance with the Qt Commercial +** License Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + + +#include "qximinputcontext_p.h" + +const int XKeyPress = KeyPress; +const int XKeyRelease = KeyRelease; +#undef KeyPress +#undef KeyRelease + +#if !defined(QT_NO_IM) + +#include "qplatformdefs.h" + +#include "qapplication.h" +#include "qwidget.h" +#include "qptrlist.h" +#include "qintdict.h" +#include "qtextcodec.h" +#include "qlocale.h" + +#include +#include + +#if !defined(QT_NO_XIM) + +#define XK_MISCELLANY +#define XK_LATIN1 +#include + +// #define QT_XIM_DEBUG + +// from qapplication_x11.cpp +static XIM qt_xim = 0; +extern XIMStyle qt_xim_style; +extern XIMStyle xim_preferred_style; +extern char *ximServer; +static bool isInitXIM = FALSE; +static QPtrList *ximContextList = 0; +#endif +extern int composingKeycode; +extern QTextCodec * input_mapper; + + +#if !defined(QT_NO_XIM) + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif // Q_C_CALLBACKS + + // These static functions should be rewritten as member of + // QXIMInputContext + +#ifdef USE_X11R6_XIM + static void xim_create_callback(XIM /*im*/, + XPointer /*client_data*/, + XPointer /*call_data*/) + { + // qDebug("xim_create_callback"); + QXIMInputContext::create_xim(); + } + + static void xim_destroy_callback(XIM /*im*/, + XPointer /*client_data*/, + XPointer /*call_data*/) + { + // qDebug("xim_destroy_callback"); + QXIMInputContext::close_xim(); + Display *dpy = QPaintDevice::x11AppDisplay(); + XRegisterIMInstantiateCallback(dpy, 0, 0, 0, + (XIMProc) xim_create_callback, 0); + } + +#endif // USE_X11R6_XIM + +#if defined(Q_C_CALLBACKS) +} +#endif // Q_C_CALLBACKS + +#endif // QT_NO_XIM + +#ifndef QT_NO_XIM + +/* 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 QFont &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 = QPaintDevice::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 Q_C_CALLBACKS +extern "C" { +#endif // Q_C_CALLBACKS + + static int xic_start_callback(XIC, XPointer client_data, XPointer) { + QXIMInputContext *qic = (QXIMInputContext *) client_data; + if (! qic) { +#ifdef QT_XIM_DEBUG + qDebug("compose start: no qic"); +#endif // QT_XIM_DEBUG + + return 0; + } + + qic->resetClientState(); + qic->sendIMEvent( QEvent::IMStart ); + +#ifdef QT_XIM_DEBUG + qDebug("compose start"); +#endif // QT_XIM_DEBUG + + return 0; + } + + static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) { + QXIMInputContext *qic = (QXIMInputContext *) client_data; + if (! qic) { +#ifdef QT_XIM_DEBUG + qDebug("compose event: invalid compose event %p", qic); +#endif // QT_XIM_DEBUG + + return 0; + } + + bool send_imstart = FALSE; + if( ! qic->isComposing() && qic->hasFocus() ) { + qic->resetClientState(); + send_imstart = TRUE; + } else if ( ! qic->isComposing() || ! qic->hasFocus() ) { +#ifdef QT_XIM_DEBUG + qDebug( "compose event: invalid compose event composing=%d hasFocus=%d", + qic->isComposing(), qic->hasFocus() ); +#endif // QT_XIM_DEBUG + + return 0; + } + + if ( send_imstart ) + qic->sendIMEvent( QEvent::IMStart ); + + 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 ) { + if( qic->composingText.isEmpty() ) { +#ifdef QT_XIM_DEBUG + qDebug( "compose emptied" ); +#endif // QT_XIM_DEBUG + // if the composition string has been emptied, we need + // to send an IMEnd event + qic->sendIMEvent( QEvent::IMEnd ); + qic->resetClientState(); + // if the commit string has coming after here, IMStart + // will be sent dynamically + } + 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; + + QString s = QString::fromLocal8Bit(str); + + if (text->encoding_is_wchar) + delete [] str; + + if (drawstruct->chg_length < 0) + qic->composingText.replace(drawstruct->chg_first, UINT_MAX, s); + else + qic->composingText.replace(drawstruct->chg_first, drawstruct->chg_length, s); + + if ( qic->selectedChars.size() < qic->composingText.length() ) { + // expand the selectedChars array if the compose string is longer + uint from = qic->selectedChars.size(); + qic->selectedChars.resize( qic->composingText.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 < 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->composingText.remove(drawstruct->chg_first, drawstruct->chg_length); + bool qt_compose_emptied = qic->composingText.isEmpty(); + if ( qt_compose_emptied ) { +#ifdef QT_XIM_DEBUG + qDebug( "compose emptied" ); +#endif // QT_XIM_DEBUG + // if the composition string has been emptied, we need + // to send an IMEnd event + qic->sendIMEvent( QEvent::IMEnd ); + qic->resetClientState(); + // if the commit string has coming after here, IMStart + // will be sent dynamically + return 0; + } + } + + qic->sendIMEvent( QEvent::IMCompose, + qic->composingText, cursor, sellen ); + + return 0; + } + + static int xic_done_callback(XIC, XPointer client_data, XPointer) { + QXIMInputContext *qic = (QXIMInputContext *) client_data; + if (! qic) + return 0; + + // Don't send IMEnd here. QXIMInputContext::x11FilterEvent() + // handles IMEnd with commit string. +#if 0 + if ( qic->isComposing() ) + qic->sendIMEvent( QEvent::IMEnd ); + qic->resetClientState(); +#endif + + return 0; + } + +#ifdef Q_C_CALLBACKS +} +#endif // Q_C_CALLBACKS + +#endif // !QT_NO_XIM + + + +QXIMInputContext::QXIMInputContext(QWidget *widget) + : QInputContext(), ic(0), fontset(0) +{ + if(!isInitXIM) + QXIMInputContext::init_xim(); + +#if !defined(QT_NO_XIM) + fontsetRefCount++; + if (! qt_xim) { + qWarning("QInputContext: no input method context available"); + return; + } + + if (! widget->isTopLevel()) { + qWarning("QInputContext: 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); + + if( ! ximContextList ) + ximContextList = new QPtrList; + ximContextList->append( this ); +#endif // !QT_NO_XIM +} + + +QXIMInputContext::~QXIMInputContext() +{ + +#if !defined(QT_NO_XIM) + if (ic) + XDestroyIC((XIC) ic); + + if ( --fontsetRefCount == 0 ) { + Display *dpy = QPaintDevice::x11AppDisplay(); + for ( int i = 0; i < 8; i++ ) { + if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) { + XFreeFontSet(dpy, fontsetCache[i]); + fontsetCache[i] = 0; + } + } + } + + if( ximContextList ) { + ximContextList->remove( this ); + if(ximContextList->isEmpty()) { + // Calling XCloseIM gives a Purify FMR error + // XCloseIM( qt_xim ); + // We prefer a less serious memory leak + if( qt_xim ) { + qt_xim = 0; + isInitXIM = FALSE; + } + + delete ximContextList; + ximContextList = 0; + } + } +#endif // !QT_NO_XIM + + ic = 0; +} + +void QXIMInputContext::init_xim() +{ +#ifndef QT_NO_XIM + if(!isInitXIM) + isInitXIM = TRUE; + + qt_xim = 0; + QString ximServerName(ximServer); + if (ximServer) + ximServerName.prepend("@im="); + else + ximServerName = ""; + + if ( !XSupportsLocale() ) + qWarning("Qt: Locales not supported on X server"); + +#ifdef USE_X11R6_XIM + else if ( XSetLocaleModifiers (ximServerName.ascii()) == 0 ) + qWarning( "Qt: Cannot set locale modifiers: %s", + ximServerName.ascii()); + else { + Display *dpy = QPaintDevice::x11AppDisplay(); + XRegisterIMInstantiateCallback(dpy, 0, 0, 0, + (XIMProc) xim_create_callback, 0); + } +#else // !USE_X11R6_XIM + else if ( XSetLocaleModifiers ("") == 0 ) + qWarning("Qt: Cannot set locale modifiers"); + else + QXIMInputContext::create_xim(); +#endif // USE_X11R6_XIM +#endif // QT_NO_XIM +} + + +/*! \internal + Creates the application input method. + */ +void QXIMInputContext::create_xim() +{ +#ifndef QT_NO_XIM + Display *appDpy = QPaintDevice::x11AppDisplay(); + qt_xim = XOpenIM( appDpy, 0, 0, 0 ); + if ( qt_xim ) { + +#ifdef USE_X11R6_XIM + XIMCallback destroy; + destroy.callback = (XIMProc) xim_destroy_callback; + destroy.client_data = 0; + if ( XSetIMValues( qt_xim, XNDestroyCallback, &destroy, (char *) 0 ) != 0 ) + qWarning( "Xlib doesn't support destroy callback"); +#endif // USE_X11R6_XIM + + XIMStyles *styles = 0; + XGetIMValues(qt_xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0); + if ( styles ) { + int i; + for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { + if ( styles->supported_styles[i] == xim_preferred_style ) { + qt_xim_style = xim_preferred_style; + break; + } + } + // if the preferred input style couldn't be found, look for + // Nothing + for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { + if ( styles->supported_styles[i] == (XIMPreeditNothing | + XIMStatusNothing) ) { + qt_xim_style = XIMPreeditNothing | XIMStatusNothing; + break; + } + } + // ... and failing that, None. + for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { + if ( styles->supported_styles[i] == (XIMPreeditNone | + XIMStatusNone) ) { + qt_xim_style = XIMPreeditNone | XIMStatusNone; + break; + } + } + + // qDebug("QApplication: using im style %lx", qt_xim_style); + XFree( (char *)styles ); + } + + if ( qt_xim_style ) { + +#ifdef USE_X11R6_XIM + XUnregisterIMInstantiateCallback(appDpy, 0, 0, 0, + (XIMProc) xim_create_callback, 0); +#endif // USE_X11R6_XIM + + } else { + // Give up + qWarning( "No supported input style found." + " See InputMethod documentation."); + QXIMInputContext::close_xim(); + } + } +#endif // QT_NO_XIM +} + + +/*! \internal + Closes the application input method. +*/ +void QXIMInputContext::close_xim() +{ +#ifndef QT_NO_XIM + QApplication::close_xim(); +#endif // QT_NO_XIM +} + + +bool QXIMInputContext::x11FilterEvent( QWidget *keywidget, XEvent *event ) +{ +#ifndef QT_NO_XIM + int xkey_keycode = event->xkey.keycode; + if ( XFilterEvent( event, keywidget->topLevelWidget()->winId() ) ) { + composingKeycode = xkey_keycode; // ### not documented in xlib + + // Cancel of the composition is realizable even if + // follwing codes don't exist +#if 0 + if ( event->type != XKeyPress || ! (qt_xim_style & XIMPreeditCallbacks) ) + return TRUE; + + /* + * The Solaris htt input method will transform a ClientMessage + * event into a filtered KeyPress event, in which case our + * keywidget is still zero. + */ + QETWidget *widget = (QETWidget*)QWidget::find( (WId)event->xany.window ); + if ( ! keywidget ) { + keywidget = (QETWidget*)QWidget::keyboardGrabber(); + if ( keywidget ) { + grabbed = TRUE; + } else { + if ( focus_widget ) + keywidget = (QETWidget*)focus_widget; + if ( !keywidget ) { + if ( qApp->inPopupMode() ) // no focus widget, see if we have a popup + keywidget = (QETWidget*) qApp->activePopupWidget(); + else if ( widget ) + keywidget = (QETWidget*)widget->topLevelWidget(); + } + } + } + + /* + if the composition string has been emptied, we need to send + an IMEnd event. however, we have no way to tell if the user + has cancelled input, or if the user has accepted the + composition. + + so, we have to look for the next keypress and see if it is + the 'commit' key press (keycode == 0). if it is, we deliver + an IMEnd event with the final text, otherwise we deliver an + IMEnd with empty text (meaning the user has cancelled the + input). + */ + if ( composing && focusWidget && qt_compose_emptied ) { + XEvent event2; + bool found = FALSE; + if ( XCheckTypedEvent( QPaintDevice::x11AppDisplay(), + XKeyPress, &event2 ) ) { + if ( event2.xkey.keycode == 0 ) { + // found a key event with the 'commit' string + found = TRUE; + XPutBackEvent( QPaintDevice::x11AppDisplay(), &event2 ); + } + } + + if ( !found ) { + // no key event, so the user must have cancelled the composition + QIMEvent endevent( QEvent::IMEnd, QString::null, -1 ); + QApplication::sendEvent( focusWidget, &endevent ); + + focusWidget = 0; + } + + qt_compose_emptied = FALSE; + } +#endif + return TRUE; + } else if ( focusWidget() ) { + if ( event->type == XKeyPress && event->xkey.keycode == 0 ) { + // input method has sent us a commit string + QCString data(513); + KeySym sym; // unused + Status status; // unused + QString inputText; + int count = lookupString( &(event->xkey), data, &sym, &status ); + if ( count > 0 ) + inputText = input_mapper->toUnicode( data, count ); + + if ( ! ( qt_xim_style & XIMPreeditCallbacks ) || ! isComposing() ) { + // there is no composing state + sendIMEvent( QEvent::IMStart ); + } + + sendIMEvent( QEvent::IMEnd, inputText ); + resetClientState(); + + return TRUE; + } + } +#endif // !QT_NO_XIM + + return FALSE; +} + + +void QXIMInputContext::sendIMEvent( QEvent::Type type, const QString &text, + int cursorPosition, int selLength ) +{ + QInputContext::sendIMEvent( type, text, cursorPosition, selLength ); + if ( type == QEvent::IMCompose ) + composingText = text; +} + + +void QXIMInputContext::reset() +{ +#if !defined(QT_NO_XIM) + if ( focusWidget() && isComposing() && ! composingText.isNull() ) { +#ifdef QT_XIM_DEBUG + qDebug("QXIMInputContext::reset: composing - sending IMEnd (empty) to %p", + focusWidget() ); +#endif // QT_XIM_DEBUG + + QInputContext::reset(); + resetClientState(); + + char *mb = XmbResetIC((XIC) ic); + if (mb) + XFree(mb); + } +#endif // !QT_NO_XIM +} + + +void QXIMInputContext::resetClientState() +{ +#if !defined(QT_NO_XIM) + composingText = QString::null; + if ( selectedChars.size() < 128 ) + selectedChars.resize( 128 ); + selectedChars.fill( 0 ); +#endif // !QT_NO_XIM +} + + +bool QXIMInputContext::hasFocus() const +{ + return ( focusWidget() != 0 ); +} + + +// to break access permission +bool QXIMInputContext::isComposing() const +{ + return QInputContext::isComposing(); +} + +void QXIMInputContext::setMicroFocus(int x, int y, int w, int h, QFont *f) +{ + QWidget *widget = focusWidget(); + if ( qt_xim && widget ) { + QPoint p( x, y ); + QPoint p2 = widget->mapTo( widget->topLevelWidget(), QPoint( 0, 0 ) ); + p = widget->topLevelWidget()->mapFromGlobal( p ); + setXFontSet( f ? *f : widget->font() ); + setComposePosition(p.x(), p.y() + h); + setComposeArea(p2.x(), p2.y(), widget->width(), widget->height()); + } + +} + +void QXIMInputContext::mouseHandler( int x, QEvent::Type type, + Qt::ButtonState button, + Qt::ButtonState state ) +{ + if ( type == QEvent::MouseButtonPress || + type == QEvent::MouseButtonDblClick ) { + // Don't reset Japanese input context here. Japanese input + // context sometimes contains a whole paragraph and has + // minutes of lifetime different to ephemeral one in other + // languages. The input context should be survived until + // focused again. + if ( ! isPreeditPreservationEnabled() ) + reset(); + } +} + +void QXIMInputContext::setComposePosition(int x, int y) +{ +#if !defined(QT_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 // !QT_NO_XIM +} + + +void QXIMInputContext::setComposeArea(int x, int y, int w, int h) +{ +#if !defined(QT_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 +} + + +void QXIMInputContext::setXFontSet(const QFont &f) +{ +#if !defined(QT_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 + Q_UNUSED( f ); +#endif +} + + +int QXIMInputContext::lookupString(XKeyEvent *event, QCString &chars, + KeySym *key, Status *status) const +{ + int count = 0; + +#if !defined(QT_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 // QT_NO_XIM + + return count; +} + +void QXIMInputContext::setFocus() +{ +#if !defined(QT_NO_XIM) + if ( qt_xim && ic ) + XSetICFocus((XIC) ic); +#endif // !QT_NO_XIM +} + +void QXIMInputContext::unsetFocus() +{ +#if !defined(QT_NO_XIM) + if (qt_xim && ic) + XUnsetICFocus((XIC) ic); +#endif // !QT_NO_XIM + + // Don't reset Japanese input context here. Japanese input context + // sometimes contains a whole paragraph and has minutes of + // lifetime different to ephemeral one in other languages. The + // input context should be survived until focused again. + if ( ! isPreeditPreservationEnabled() ) + reset(); +} + + +bool QXIMInputContext::isPreeditRelocationEnabled() +{ + return ( language() == "ja" ); +} + + +bool QXIMInputContext::isPreeditPreservationEnabled() +{ + return ( language() == "ja" ); +} + + +QCString QXIMInputContext::language() +{ +#if !defined(QT_NO_XIM) + if ( qt_xim ) { + QLocale locale = QLocale( XLocaleOfIM( qt_xim ) ); + + if ( locale.language() == QLocale::Chinese ) { + // Chinese language should be formed as "zh_CN", "zh_TW", "zh_HK" + _language = locale.name(); + } else { + // other languages should be two-letter ISO 639 language code + _language = locale.name().left( 2 ); + } + } +#endif + return _language; +} + +#endif //QT_NO_IM + diff -urN qt-x11-free-3.3.2/src/kernel/qapplication.cpp qt-x11-immodule-bc/src/kernel/qapplication.cpp --- qt-x11-free-3.3.2/src/kernel/qapplication.cpp 2004-04-19 18:36:11.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qapplication.cpp 2004-06-23 01:10:27.065122616 +0900 @@ -3498,6 +3498,7 @@ #ifdef Q_WS_WIN QInputContext::accept( tmp ); #endif + tmp->unfocusInputContext(); QApplication::sendSpontaneousEvent( tmp, &out ); } else if ( active_window ) { QWidget *w = active_window->focusWidget(); diff -urN qt-x11-free-3.3.2/src/kernel/qapplication_x11.cpp qt-x11-immodule-bc/src/kernel/qapplication_x11.cpp --- qt-x11-free-3.3.2/src/kernel/qapplication_x11.cpp 2004-04-19 18:36:02.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qapplication_x11.cpp 2004-06-23 01:10:27.069122008 +0900 @@ -89,7 +89,10 @@ #include "qfileinfo.h" // Input method stuff - UNFINISHED -#include "qinputcontext_p.h" +#ifndef QT_NO_IM +class QInputContext; +#include "qinputcontext.h" +#endif // QT_NO_IM #include "qinternal_p.h" // shared double buffer cleanup #if defined(QT_THREAD_SUPPORT) @@ -189,10 +192,9 @@ static const char *mwGeometry = 0; // main widget geometry static const char *mwTitle = 0; // main widget title //Ming-Che 10/10 -static char *ximServer = 0; // XIM Server will connect to +char *ximServer = 0; // XIM Server will connect to static bool mwIconic = FALSE; // main widget iconified //Ming-Che 10/10 -static bool noxim = FALSE; // connect to xim or not static Display *appDpy = 0; // X11 application display static char *appDpyName = 0; // X11 display name static bool appForeignDpy = FALSE; // we didn't create display @@ -386,15 +388,18 @@ +#if !defined(QT_NO_IM) +QString defaultIM = "XIM"; // default input method's name +#endif #if !defined(QT_NO_XIM) -XIM qt_xim = 0; +//XIM qt_xim = 0; XIMStyle qt_xim_style = 0; +XIMStyle xim_preferred_style = 0; static XIMStyle xim_default_style = XIMPreeditCallbacks | XIMStatusNothing; -static XIMStyle xim_preferred_style = 0; #endif -static int composingKeycode=0; -static QTextCodec * input_mapper = 0; +int composingKeycode=0; +QTextCodec * input_mapper = 0; Q_EXPORT Time qt_x_time = CurrentTime; Q_EXPORT Time qt_x_user_time = CurrentTime; @@ -503,8 +508,7 @@ void setWFlags( WFlags f ) { QWidget::setWFlags(f); } void clearWFlags( WFlags f ) { QWidget::clearWFlags(f); } bool translateMouseEvent( const XEvent * ); - bool translateKeyEventInternal( const XEvent *, int& count, QString& text, int& state, char& ascii, int &code, - QEvent::Type &type, bool willRepeat=FALSE ); + bool translateKeyEventInternal( const XEvent *, int& count, QString& text, int& state, char& ascii, int &code, QEvent::Type &type, bool willRepeat=FALSE, bool statefulTranslation=TRUE ); bool translateKeyEvent( const XEvent *, bool grab ); bool translatePaintEvent( const XEvent * ); bool translateConfigEvent( const XEvent * ); @@ -521,115 +525,27 @@ // ************************************************************************ -// X Input Method support +// Input Method support // ************************************************************************ -#if !defined(QT_NO_XIM) - -#if defined(Q_C_CALLBACKS) -extern "C" { -#endif // Q_C_CALLBACKS - -#ifdef USE_X11R6_XIM - static void xim_create_callback(XIM /*im*/, - XPointer /*client_data*/, - XPointer /*call_data*/) - { - // qDebug("xim_create_callback"); - QApplication::create_xim(); - } - - static void xim_destroy_callback(XIM /*im*/, - XPointer /*client_data*/, - XPointer /*call_data*/) - { - // qDebug("xim_destroy_callback"); - QApplication::close_xim(); - XRegisterIMInstantiateCallback(appDpy, 0, 0, 0, - (XIMProc) xim_create_callback, 0); - } - -#endif // USE_X11R6_XIM - -#if defined(Q_C_CALLBACKS) -} -#endif // Q_C_CALLBACKS - -#endif // QT_NO_XIM - - /*! \internal - Creates the application input method. - */ + Creates the application input method. +*/ void QApplication::create_xim() { #ifndef QT_NO_XIM - qt_xim = XOpenIM( appDpy, 0, 0, 0 ); - if ( qt_xim ) { - -#ifdef USE_X11R6_XIM - XIMCallback destroy; - destroy.callback = (XIMProc) xim_destroy_callback; - destroy.client_data = 0; - if ( XSetIMValues( qt_xim, XNDestroyCallback, &destroy, (char *) 0 ) != 0 ) - qWarning( "Xlib dosn't support destroy callback"); -#endif // USE_X11R6_XIM - - XIMStyles *styles = 0; - XGetIMValues(qt_xim, XNQueryInputStyle, &styles, (char *) 0, (char *) 0); - if ( styles ) { - int i; - for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { - if ( styles->supported_styles[i] == xim_preferred_style ) { - qt_xim_style = xim_preferred_style; - break; - } - } - // if the preferred input style couldn't be found, look for - // Nothing - for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { - if ( styles->supported_styles[i] == (XIMPreeditNothing | - XIMStatusNothing) ) { - qt_xim_style = XIMPreeditNothing | XIMStatusNothing; - break; - } - } - // ... and failing that, None. - for ( i = 0; !qt_xim_style && i < styles->count_styles; i++ ) { - if ( styles->supported_styles[i] == (XIMPreeditNone | - XIMStatusNone) ) { - qt_xim_style = XIMPreeditNone | XIMStatusNone; - break; - } - } - - // qDebug("QApplication: using im style %lx", qt_xim_style); - XFree( (char *)styles ); - } - - if ( qt_xim_style ) { + if ( ! xim_preferred_style ) // no configured input style, use the default + xim_preferred_style = xim_default_style; +#endif // QT_NO_XIM -#ifdef USE_X11R6_XIM - XUnregisterIMInstantiateCallback(appDpy, 0, 0, 0, - (XIMProc) xim_create_callback, 0); -#endif // USE_X11R6_XIM - - QWidgetList *list= qApp->topLevelWidgets(); - QWidgetListIt it(*list); - QWidget * w; - while( (w=it.current()) != 0 ) { - ++it; - w->createTLSysExtra(); - } - delete list; - } else { - // Give up - qWarning( "No supported input style found." - " See InputMethod documentation."); - close_xim(); - } + QWidgetList *list= qApp->topLevelWidgets(); + QWidgetListIt it(*list); + QWidget * w; + while( (w=it.current()) != 0 ) { + ++it; + w->createTLSysExtra(); } -#endif // QT_NO_XIM + delete list; } @@ -643,7 +559,10 @@ // XCloseIM( qt_xim ); // We prefer a less serious memory leak - qt_xim = 0; + // if ( qt_xim ) + // qt_xim = 0; + +#endif // QT_NO_XIM QWidgetList *list = qApp->topLevelWidgets(); QWidgetListIt it(*list); while(it.current()) { @@ -651,7 +570,6 @@ ++it; } delete list; -#endif // QT_NO_XIM } @@ -1033,6 +951,13 @@ xim_preferred_style = XIMPreeditArea | XIMStatusArea; else if ( ximInputStyle == "root" ) xim_preferred_style = XIMPreeditNothing | XIMStatusNothing; + + /* + The name of InputMethod set up as a default is acquired from + a configuration file. However, If not set up, "XIM" become + the name of default InputMethod. + */ + defaultIM = settings.readEntry( "/qt/DefaultInputMethod", QObject::trUtf8( "XIM" ) ); #endif if (update_timestamp) { @@ -1500,6 +1425,7 @@ #define XK_MISCELLANY #define XK_LATIN1 +#define XK_KOREAN #include // ### This should be static but it isn't because of the friend declaration @@ -1586,9 +1512,6 @@ } else if ( arg == "-im" ) { if ( ++i < argc ) ximServer = argv[i]; - } else if ( arg == "-noxim" ) { - noxim=TRUE; - // } else if ( arg == "-iconic" ) { mwIconic = !mwIconic; } else if ( arg == "-ncols" ) { // xv and netscape use this name @@ -2065,34 +1988,9 @@ qt_set_x11_resources( appFont, appFGCol, appBGCol, appBTNCol); -#ifndef QT_NO_XIM - if ( ! xim_preferred_style ) // no configured input style, use the default - xim_preferred_style = xim_default_style; - - qt_xim = 0; - QString ximServerName(ximServer); - if (ximServer) - ximServerName.prepend("@im="); - else - ximServerName = ""; - - if ( !XSupportsLocale() ) - qWarning("Qt: Locales not supported on X server"); - -#ifdef USE_X11R6_XIM - else if ( XSetLocaleModifiers (ximServerName.ascii()) == 0 ) - qWarning( "Qt: Cannot set locale modifiers: %s", - ximServerName.ascii()); - else if (! noxim) - XRegisterIMInstantiateCallback(appDpy, 0, 0, 0, - (XIMProc) xim_create_callback, 0); -#else // !USE_X11R6_XIM - else if ( XSetLocaleModifiers ("") == 0 ) - qWarning("Qt: Cannot set locale modifiers"); - else if (! noxim) - QApplication::create_xim(); -#endif // USE_X11R6_XIM -#endif // QT_NO_XIM +#if !defined(QT_NO_IM) + QApplication::create_xim(); +#endif #if defined (QT_TABLET_SUPPORT) int ndev, @@ -2341,9 +2239,8 @@ XCloseDevice( appDpy, devEraser ); #endif -#if !defined(QT_NO_XIM) - if ( qt_xim ) - QApplication::close_xim(); +#if !defined(QT_NO_IM) + QApplication::close_xim(); #endif if ( qt_is_gui_used ) { @@ -3198,77 +3095,45 @@ } } - int xkey_keycode = event->xkey.keycode; - if ( XFilterEvent( event, - keywidget ? keywidget->topLevelWidget()->winId() : None ) ) { - if ( keywidget ) - composingKeycode = xkey_keycode; // ### not documented in xlib - -#ifndef QT_NO_XIM - if ( event->type != XKeyPress || ! (qt_xim_style & XIMPreeditCallbacks) ) - return 1; - - /* - * The Solaris htt input method will transform a ClientMessage - * event into a filtered KeyPress event, in which case our - * keywidget is still zero. - */ - if ( ! keywidget ) { - keywidget = (QETWidget*)QWidget::keyboardGrabber(); - if ( keywidget ) { - grabbed = TRUE; - } else { - if ( focus_widget ) - keywidget = (QETWidget*)focus_widget; - if ( !keywidget ) { - if ( inPopupMode() ) // no focus widget, see if we have a popup - keywidget = (QETWidget*) activePopupWidget(); - else if ( widget ) - keywidget = (QETWidget*)widget->topLevelWidget(); - } - } - } +#ifndef QT_NO_IM + if( keywidget && keywidget->isEnabled() && keywidget->isInputMethodEnabled() ) { + if( ( event->type==XKeyPress || event->type==XKeyRelease ) && + sm_blockUserInput ) // block user interaction during session management + return TRUE; - /* - if the composition string has been emptied, we need to send - an IMEnd event. however, we have no way to tell if the user - has cancelled input, or if the user has accepted the - composition. - - so, we have to look for the next keypress and see if it is - the 'commit' key press (keycode == 0). if it is, we deliver - an IMEnd event with the final text, otherwise we deliver an - IMEnd with empty text (meaning the user has cancelled the - input). - */ QInputContext *qic = - (QInputContext *) keywidget->topLevelWidget()->topData()->xic; - extern bool qt_compose_emptied; // qinputcontext_x11.cpp - if ( qic && qic->composing && qic->focusWidget && qt_compose_emptied ) { - XEvent event2; - bool found = FALSE; - if ( XCheckTypedEvent( QPaintDevice::x11AppDisplay(), - XKeyPress, &event2 ) ) { - if ( event2.xkey.keycode == 0 ) { - // found a key event with the 'commit' string - found = TRUE; - XPutBackEvent( QPaintDevice::x11AppDisplay(), &event2 ); - } - } + (QInputContext *) keywidget->topLevelWidget()->topData()->xic; + if( qic && qic->x11FilterEvent( keywidget, event ) ) + return TRUE; - if ( !found ) { - // no key event, so the user must have cancelled the composition - QIMEvent endevent( QEvent::IMEnd, QString::null, -1 ); - QApplication::sendEvent( qic->focusWidget, &endevent ); + // filterEvent() accepts QEvent *event rather than preexpanded + // key event values. This is intended to pass other IM-related + // events in future. The IM-related events are supposed as + // QWheelEvent, QTabletEvent and so on. Other non IM-related + // events should not be forwarded to input contexts to prevent + // weird event handling. + if ( ( event->type == XKeyPress || event->type == XKeyRelease ) ) { + int code = -1; + int count = 0; + int state; + char ascii = 0; + QEvent::Type type; + QString text; + + keywidget->translateKeyEventInternal( event, count, text, + state, ascii, code, type, + FALSE, FALSE ); - qic->focusWidget = 0; - } + QKeyEvent keyevent( type, code, ascii, state, text, FALSE, count ); - qt_compose_emptied = FALSE; + if( qic && qic->filterEvent( &keyevent ) ) + return TRUE; } -#endif // QT_NO_XIM - - return 1; + } else +#endif // QT_NO_IM + { + if ( XFilterEvent( event, None ) ) + return TRUE; } if ( qt_x11EventFilter(event) ) // send through app filter @@ -3419,34 +3284,8 @@ case XKeyRelease: { if ( keywidget && keywidget->isEnabled() ) { // should always exist -#ifndef QT_NO_XIM - QInputContext *qic = - (QInputContext *) keywidget->topLevelWidget()->topData()->xic; - - if ((qt_xim_style & XIMPreeditCallbacks) && event->xkey.keycode == 0 && - qic && qic->composing && qic->focusWidget) { - // input method has sent us a commit string - QCString data(513); - KeySym sym; // unused - Status status; // unused - QString text; - int count = qic->lookupString( &(event->xkey), data, - &sym, &status ); - if ( count > 0 ) - text = input_mapper->toUnicode( data, count ); - - // qDebug( "sending IMEnd with %d chars", text.length() ); - QIMEvent endevent( QEvent::IMEnd, text, -1 ); - QApplication::sendEvent( qic->focusWidget, &endevent ); - - qic->focusWidget = 0; - qic->text = QString::null; - } else -#endif // !QT_NO_XIM - { - // qDebug( "sending key event" ); - keywidget->translateKeyEvent( event, grabbed ); - } + // qDebug( "sending key event" ); + keywidget->translateKeyEvent( event, grabbed ); } break; } @@ -4733,6 +4572,61 @@ XK_Help, Qt::Key_Help, 0x1000FF74, Qt::Key_BackTab, // hardcoded HP backtab + // International input method support keys + + // International & multi-key character composition + XK_Multi_key, Qt::Key_Multi_key, + XK_Codeinput, Qt::Key_Codeinput, + XK_SingleCandidate, Qt::Key_SingleCandidate, + XK_MultipleCandidate, Qt::Key_MultipleCandidate, + XK_PreviousCandidate, Qt::Key_PreviousCandidate, + + // Misc Functions + XK_Mode_switch, Qt::Key_Mode_switch, + //XK_script_switch, Qt::Key_script_switch, + + // Japanese keyboard support + XK_Kanji, Qt::Key_Kanji, + XK_Muhenkan, Qt::Key_Muhenkan, + //XK_Henkan_Mode, Qt::Key_Henkan_Mode, + XK_Henkan, Qt::Key_Henkan, + XK_Romaji, Qt::Key_Romaji, + XK_Hiragana, Qt::Key_Hiragana, + XK_Katakana, Qt::Key_Katakana, + XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, + XK_Zenkaku, Qt::Key_Zenkaku, + XK_Hankaku, Qt::Key_Hankaku, + XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, + XK_Touroku, Qt::Key_Touroku, + XK_Massyo, Qt::Key_Massyo, + XK_Kana_Lock, Qt::Key_Kana_Lock, + XK_Kana_Shift, Qt::Key_Kana_Shift, + XK_Eisu_Shift, Qt::Key_Eisu_Shift, + XK_Eisu_toggle, Qt::Key_Eisu_toggle, + //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou, + //XK_Zen_Koho, Qt::Key_Zen_Koho, + //XK_Mae_Koho, Qt::Key_Mae_Koho, + +#ifdef XK_KOREAN + // Korean support + XK_Hangul, Qt::Key_Hangul, + XK_Hangul_Start, Qt::Key_Hangul_Start, + XK_Hangul_End, Qt::Key_Hangul_End, + XK_Hangul_Hanja, Qt::Key_Hangul_Hanja, + XK_Hangul_Jamo, Qt::Key_Hangul_Jamo, + XK_Hangul_Romaja, Qt::Key_Hangul_Romaja, + XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, + XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, + XK_Hangul_Banja, Qt::Key_Hangul_Banja, + XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, + XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, + XK_Hangul_SingleCandidate, Qt::Key_Hangul_SingleCandidate, + XK_Hangul_MultipleCandidate, Qt::Key_Hangul_MultipleCandidate, + XK_Hangul_PreviousCandidate, Qt::Key_Hangul_PreviousCandidate, + XK_Hangul_Special, Qt::Key_Hangul_Special, + //XK_Hangul_switch, Qt::Key_Hangul_switch, +#endif // XK_KOREAN + // Special multimedia keys // currently only tested with MS internet keyboard @@ -4950,7 +4844,7 @@ bool QETWidget::translateKeyEventInternal( const XEvent *event, int& count, QString& text, int& state, - char& ascii, int& code, QEvent::Type &type, bool willRepeat ) + char& ascii, int& code, QEvent::Type &type, bool willRepeat, bool statefulTranslation ) { QTextCodec *mapper = input_mapper; // some XmbLookupString implementations don't return buffer overflow correctly, @@ -4999,6 +4893,11 @@ if ( type == QEvent::KeyPress ) { bool mb=FALSE; + // commit string handling is done by + // QXIMInputContext::x11FilterEvent() and are passed to + // widgets via QIMEvent regardless of XIM style, so the + // following code is commented out. +#if 0 if ( qt_xim ) { QTLWExtra* xd = tlw->topData(); QInputContext *qic = (QInputContext *) xd->xic; @@ -5007,6 +4906,7 @@ count = qic->lookupString(&xkeyevent, chars, &key, &status); } } +#endif if ( !mb ) { count = XLookupString( &xkeyevent, chars.data(), chars.size(), &key, 0 ); @@ -5090,7 +4990,7 @@ state = translateButtonState( keystate ); static int directionKeyEvent = 0; - if ( qt_use_rtl_extensions && type == QEvent::KeyRelease ) { + if ( qt_use_rtl_extensions && type == QEvent::KeyRelease && statefulTranslation ) { if (directionKeyEvent == Key_Direction_R || directionKeyEvent == Key_Direction_L ) { type = QEvent::KeyPress; code = directionKeyEvent; @@ -5108,7 +5008,7 @@ // (to figure out whether the Ctrl modifier is held while Shift is pressed, // or Shift is held while Ctrl is pressed) since the 'state' doesn't tell // us whether the modifier held is Left or Right. - if (qt_use_rtl_extensions && type == QEvent::KeyPress) + if ( qt_use_rtl_extensions && type == QEvent::KeyPress && statefulTranslation ) if (key == XK_Control_L || key == XK_Control_R || key == XK_Shift_L || key == XK_Shift_R) { if (!directionKeyEvent) directionKeyEvent = key; @@ -5176,7 +5076,7 @@ chars[0] = 0; } - if ( qt_use_rtl_extensions && type == QEvent::KeyPress ) { + if ( qt_use_rtl_extensions && type == QEvent::KeyPress && statefulTranslation ) { if ( directionKeyEvent ) { if ( key == XK_Shift_L && directionKeyEvent == XK_Control_L || key == XK_Control_L && directionKeyEvent == XK_Shift_L ) { @@ -5330,8 +5230,18 @@ translateKeyEventInternal( event, count, text, state, ascii, code, type ); } +#ifndef QT_NO_IM + QInputContext *qic = + (QInputContext *) topLevelWidget()->topData()->xic; +#endif + // compress keys if ( !text.isEmpty() && testWState(WState_CompressKeys) && +#ifndef QT_NO_IM + // input methods need discrete key events + // TODO: describe design decision + ! qic && +#endif // QT_NO_IM // do not compress keys if the key event we just got above matches // one of the key ranges used to compute stopCompression ! ( ( code >= Key_Escape && code <= Key_SysReq ) || @@ -5419,7 +5329,13 @@ // autorepeat compression makes sense for all widgets (Windows // does it automatically .... ) - if ( event->type == XKeyPress && text.length() <= 1 ) { + if ( event->type == XKeyPress && text.length() <= 1 +#ifndef QT_NO_IM + // input methods need discrete key events + // TODO: describe design decision + && ! qic +#endif// QT_NO_IM + ) { XEvent dummy; for (;;) { diff -urN qt-x11-free-3.3.2/src/kernel/qinputcontext.cpp qt-x11-immodule-bc/src/kernel/qinputcontext.cpp --- qt-x11-free-3.3.2/src/kernel/qinputcontext.cpp 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qinputcontext.cpp 2004-06-23 03:00:46.715782752 +0900 @@ -0,0 +1,382 @@ +/**************************************************************************** +** $Id: qinputcontext.cpp,v 1.6 2004/06/22 06:47:30 daisuke Exp $ +** +** Implementation of QInputContext class +** +** Copyright (C) 2000-2003 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses for Unix/X11 may use this file in accordance with the Qt Commercial +** License Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +//#define QT_NO_IM_PREEDIT_RELOCATION + +#include "qinputcontext.h" + +#ifndef QT_NO_IM + +#include "qplatformdefs.h" + +#include "qapplication.h" +#include "qwidget.h" + +#include +#include + +class QInputContextPrivate +{ +public: + QInputContextPrivate() + : holderWidget( 0 ), composingWidget( 0 ), hasFocus( FALSE ), + isComposing( FALSE ) +#if !defined(QT_NO_IM_PREEDIT_RELOCATION) + , preeditString( QString::null ), + cursorPosition( -1 ), selLength ( 0 ) +#endif + {} + + QWidget *holderWidget; // widget to which QInputContext instance belongs. + QWidget *composingWidget; + bool hasFocus; + bool isComposing; + + void updateComposingState( const QString &text, + int newCursorPosition, int newSelLength ) { +#if !defined(QT_NO_IM_PREEDIT_RELOCATION) + preeditString = text; + cursorPosition = newCursorPosition; + selLength = newSelLength; +#endif + } + + void resetComposingState() { + isComposing = FALSE; +#if !defined(QT_NO_IM_PREEDIT_RELOCATION) + preeditString = QString::null; + cursorPosition = -1; + selLength = 0; +#endif + } + +#if !defined(QT_NO_IM_PREEDIT_RELOCATION) + QString preeditString; + int cursorPosition; + int selLength; +#endif +}; + + +/*! + This constructor sets widget of the first argument to the + holderWidget, state of the second argument to the imState, + respectively. holderWidget is widget holding QInputContext + (itself). imState is state of InputMethod. +*/ +QInputContext::QInputContext() +{ + d = new QInputContextPrivate; +} + + +QInputContext::~QInputContext() +{ + delete d; +} + +QWidget* QInputContext::holderWidget() const +{ + return d->holderWidget; +} + +void QInputContext::setHolderWidget( QWidget *w ) +{ + d->holderWidget = w; +} + +QWidget* QInputContext::focusWidget() const +{ + return d->hasFocus ? d->composingWidget : 0; +} + + +void QInputContext::setFocusWidget( QWidget *w ) +{ + if ( w ) { + bool isFocusingBack = ( w == d->composingWidget ); + bool isPreeditRelocation = ( ! isFocusingBack && isComposing() && + d->composingWidget ); + // invoke sendIMEventInternal() rather than sendIMEvent() to + // avoid altering the composing state + if ( isPreeditRelocation == TRUE ) { + // clear preedit of previously focused text + // widget. preserved preedit may be exist even if + // isPreeditRelocationEnabled() == FALSE. + sendIMEventInternal( QEvent::IMEnd ); + } + d->composingWidget = w; // changes recipient of QIMEvent + if ( isPreeditRelocation == TRUE ) { +#if !defined(QT_NO_IM_PREEDIT_RELOCATION) + if ( isPreeditRelocationEnabled() ) { + // copy preedit state to the widget that gaining focus + sendIMEventInternal( QEvent::IMStart ); + sendIMEventInternal( QEvent::IMCompose, d->preeditString, + d->cursorPosition, d->selLength ); + } else +#endif + { + // reset input context when the shared context has + // focused on another text widget + reset(); + } + } + } + d->hasFocus = w ? TRUE : FALSE; +} + + +void QInputContext::releaseComposingWidget( QWidget *w ) +{ + if ( d->composingWidget == w ) { + d->composingWidget = 0; + d->hasFocus = FALSE; + } +} + +bool QInputContext::isPreeditRelocationEnabled() +{ + return FALSE; +} + +bool QInputContext::isComposing() const +{ + return d->isComposing; +} + + +/*! + This function must be implemented in subclasses to handle key + input (except for input method used only on a specific + platform). If an event is filtered, it must return TRUE. + + keywidget is client widget into which a text is inputted. type + shows whether inputted event is KeyPress or KeyRelease. code show + inputted key code. state show keyboard modifiers (OR-ed together). + + In principle, only when composition is finished, the input is + committed by QIMEvent (IMEnd). Otherwise, the input set to + arguments, such as text, ascii, count, etc. The purpose of this + specification is to take adjustment with key compression. +*/ +bool QInputContext::filterEvent( QEvent *event ) +{ + return FALSE; +} + + +void QInputContext::sendIMEventInternal( QEvent::Type type, + const QString &text, + int cursorPosition, int selLength ) +{ +#if defined(Q_WS_X11) + if ( ! d->composingWidget ) + return; +#endif + + if ( type == QEvent::IMStart ) { + qDebug( "sending IMStart with %d chars to %p", + text.length(), d->composingWidget ); +#if defined(Q_WS_X11) + QIMEvent event( type, text, cursorPosition ); + QApplication::sendEvent( d->composingWidget, &event ); +#elif defined(Q_WS_QWS) + // just a placeholder for now + //qwsServer->sendIMEvent( QWSServer::IMCompose, + // text, cursorPosition, selLength ); +#endif + } else if ( type == QEvent::IMEnd ) { + qDebug( "sending IMEnd with %d chars to %p, text=%s", + text.length(), d->composingWidget, + (const char*)text.local8Bit() ); +#if defined(Q_WS_X11) + QIMEvent event( type, text, cursorPosition ); + QApplication::sendEvent( d->composingWidget, &event ); +#elif defined(Q_WS_QWS) + //qwsServer->sendIMEvent( QWSServer::IMEnd, + // text, cursorPosition, selLength ); +#endif + } else if ( type == QEvent::IMCompose ) { + qDebug( "sending IMCompose to %p with %d chars, cpos=%d, sellen=%d, text=%s", + d->composingWidget, + text.length(), cursorPosition, selLength, + (const char*)text.local8Bit() ); +#if defined(Q_WS_X11) + QIMComposeEvent event( type, text, cursorPosition, selLength ); + QApplication::sendEvent( d->composingWidget, &event ); +#elif defined(Q_WS_QWS) + //qwsServer->sendIMEvent( QWSServer::IMCompose, + // text, cursorPosition, selLength ); +#endif + } +} + + +void QInputContext::sendIMEvent( QEvent::Type type, const QString &text, + int cursorPosition, int selLength ) +{ +#if defined(Q_WS_X11) + if ( !focusWidget() ) + return; +#endif + + if ( type == QEvent::IMStart ) { + sendIMEventInternal( type, text, cursorPosition, selLength ); + d->isComposing = TRUE; + } else if ( type == QEvent::IMEnd ) { + d->resetComposingState(); + sendIMEventInternal( type, text, cursorPosition, selLength ); + } else if ( type == QEvent::IMCompose ) { + d->updateComposingState( text, cursorPosition, selLength ); + sendIMEventInternal( type, text, cursorPosition, selLength ); + } +} + + + +/*! + \fn void QInputContext::setFocus() + + This function must be implemented in subclasses to set focus. + + \sa unsetFocus() +*/ +void QInputContext::setFocus() +{ + // focusWidget is already set by QWidget::focusInputContext() +} + + +/*! + \fn void QInputContext::unsetFocus() + + This function must be implemented in subclasses to unset focus. + + \sa setFocus() +*/ +void QInputContext::unsetFocus() +{ + reset(); + // focusWidget is set to 0 after return by QWidget::unfocusInputContext() +} + + +/*! + \fn void QInputContext::setMicroFocus( int x, int y, int w, int h, QFont *f ) + + This function must be implemented in subclasses to handle + focusHint changes. + + x and y are cursor's positions. w is cursor's width, and h is + cursor's height. widget is client widget. f is a font on the + cursor's position. +*/ +void QInputContext::setMicroFocus( int x, int y, int w, int h, QFont *f ) +{ +} + + +/*! + \fn void QWSInputMethod::mouseHandler( int x, int state ) + + Implemented in subclasses to handle mouse + presses/releases/doubleclicks/moves within the on-the-spot text. The + parameter \a x is the offset within the string that was sent with + the IMCompose event. \a state is either \c QEvent::MouseButtonPress + or \c QEvent::MouseButtonRelease or \c QEvent::MouseButtonDblClick + or \c QEvent::MouseButtonMove. The method interface is imported from + QWSInputMethod::mouseHandler() of Qt/Embedded 2.3.7 and extended for + desktop system. + */ +void QInputContext::mouseHandler( int x, QEvent::Type type, + Qt::ButtonState button, + Qt::ButtonState state ) +{ + // Default behavior for simple ephemeral input contexts. Some + // complex input contexts should not be reset here. + if ( type == QEvent::MouseButtonPress || + type == QEvent::MouseButtonDblClick ) + reset(); +} + + +/*! + Returns the font of the current input widget + */ +QFont QInputContext::font() const +{ + if ( !focusWidget() ) + return QApplication::font(); //### absolutely last resort + + return focusWidget()->font(); +} + + +/*! + \fn void QInputContext::reset() + + This function must be implemented in subclasses to reset the state + of the input method. +*/ +void QInputContext::reset() +{ + if ( isComposing() ) + sendIMEvent( QEvent::IMEnd ); +} + + +/*! + \fn QString QInputContext::name() + + This function must be implemented in subclasses to return the name + of the input method. +*/ +QString QInputContext::name() +{ +} + + +/*! + \fn QCString QInputContext::language() + + This function must be implemented in subclasses to return a + language code (e.g. "zh_CN", "zh_TW", "zh_HK", "ja", "ko", ...) of + the input method. +*/ +QCString QInputContext::language() +{ +} + +#endif //Q_NO_IM diff -urN qt-x11-free-3.3.2/src/kernel/qinputcontext.h qt-x11-immodule-bc/src/kernel/qinputcontext.h --- qt-x11-free-3.3.2/src/kernel/qinputcontext.h 1970-01-01 09:00:00.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qinputcontext.h 2004-06-23 03:07:02.936588496 +0900 @@ -0,0 +1,116 @@ +/**************************************************************************** +** $Id: qinputcontext.h,v 1.8 2004/06/22 06:47:30 daisuke Exp $ +** +** Definition of QInputContext +** +** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. +** +** This file is part of the kernel module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QINPUTCONTEXT_H +#define QINPUTCONTEXT_H + +#ifndef QT_NO_IM + +#ifndef QT_H +#include "qglobal.h" +#include "qevent.h" +#endif + +class QWidget; +class QFont; +class QString; +class QInputContextPrivate; + + +/* + QInputContext holds the data which input method uses and offers + the function which input method has. And, in order to enable + useing each input method, it is necessary to inherit QInputContext + and to implement InputContext corresponding to it. +*/ +class QInputContext +{ +public: + QInputContext(); + virtual ~QInputContext(); + + virtual QString name(); + virtual QCString language(); + +#if defined(Q_WS_X11) + virtual bool x11FilterEvent( QWidget *keywidget, XEvent *event ); +#endif // Q_WS_X11 + virtual bool filterEvent( QEvent *event ); + virtual void reset(); + + virtual void setFocus(); + virtual void unsetFocus(); + virtual void setMicroFocus( int x, int y, int w, int h, QFont *f = 0 ); + virtual void mouseHandler( int x, QEvent::Type type, + Qt::ButtonState button, Qt::ButtonState state ); + virtual QFont font() const; + +protected: +#if defined(Q_WS_X11) + // not recommended to use these functions + QWidget *focusWidget() const; + QWidget *holderWidget() const; +#endif + + bool isComposing() const; + virtual bool isPreeditRelocationEnabled(); + virtual void sendIMEvent( QEvent::Type type, + const QString &text = QString::null, + int cursorPosition = -1, int selLength = 0 ); + +private: + void sendIMEventInternal( QEvent::Type type, + const QString &text = QString::null, + int cursorPosition = -1, int selLength = 0 ); +#if defined(Q_WS_X11) + void setFocusWidget( QWidget *w ); + void setHolderWidget( QWidget *w ); + void releaseComposingWidget( QWidget *w ); +#endif + + QInputContextPrivate *d; + + friend class QWidget; + friend class QInputContextFactory; + +private: // Disabled copy constructor and operator= + QInputContext( const QInputContext & ); + QInputContext &operator=( const QInputContext & ); + +}; + +#endif //Q_NO_IM + +#endif // QINPUTCONTEXT_H diff -urN qt-x11-free-3.3.2/src/kernel/qinputcontext_x11.cpp qt-x11-immodule-bc/src/kernel/qinputcontext_x11.cpp --- qt-x11-free-3.3.2/src/kernel/qinputcontext_x11.cpp 2004-04-19 18:36:08.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qinputcontext_x11.cpp 2004-06-23 01:10:27.072121552 +0900 @@ -33,500 +33,37 @@ ** **********************************************************************/ +#include "qinputcontext.h" + +#ifndef QT_NO_IM + #include "qplatformdefs.h" #include "qapplication.h" #include "qwidget.h" -#include "qinputcontext_p.h" - -#include -#include +#include "qt_x11_p.h" -bool qt_compose_emptied = FALSE; +/*! + This function, only if input method is depending on X11, must be overridden + in subclasses. Otherwise, this function must not. -#if !defined(QT_NO_XIM) + By default, this function translates key code and keyboard modifiers from + the event which is the 2nd argument, and pass the value to QInputContext::filterEvent(). -#define XK_MISCELLANY -#define XK_LATIN1 -#include + keywidget is client widget into which a text is inputted. event is inputted XEvent. -// #define QT_XIM_DEBUG + In principle, only when composition is finished, the input is committed + by QIMEvent (IMEnd). Otherwise, the input set to arguments, such as code, + state, text, ascii, count, etc. The purpose of this specification is to + take adjustment with key compression. -// 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 + type shows whether inputted event is KeyPress or KeyRelease. code show + inputted key code. state show keyboard modifiers (OR-ed together). */ -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 QFont &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 = QPaintDevice::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 Q_C_CALLBACKS -extern "C" { -#endif // Q_C_CALLBACKS - - static int xic_start_callback(XIC, XPointer client_data, XPointer) { - QInputContext *qic = (QInputContext *) client_data; - if (! qic) { -#ifdef QT_XIM_DEBUG - qDebug("compose start: no qic"); -#endif // QT_XIM_DEBUG - - return 0; - } - - qic->composing = TRUE; - qic->text = QString::null; - qic->focusWidget = 0; - - if ( qic->selectedChars.size() < 128 ) - qic->selectedChars.resize( 128 ); - qic->selectedChars.fill( 0 ); - -#ifdef QT_XIM_DEBUG - qDebug("compose start"); -#endif // QT_XIM_DEBUG - - return 0; - } - - static int xic_draw_callback(XIC, XPointer client_data, XPointer call_data) { - QInputContext *qic = (QInputContext *) client_data; - if (! qic) { -#ifdef QT_XIM_DEBUG - qDebug("compose event: invalid compose event %p", qic); -#endif // QT_XIM_DEBUG - - return 0; - } - - bool send_imstart = FALSE; - if (qApp->focusWidget() != qic->focusWidget && qic->text.isEmpty()) { - if (qic->focusWidget) { -#ifdef QT_XIM_DEBUG - qDebug( "sending IMEnd (empty) to %p", qic->focusWidget ); -#endif // QT_XIM_DEBUG - - QIMEvent endevent(QEvent::IMEnd, QString::null, -1); - QApplication::sendEvent(qic->focusWidget, &endevent); - } - - qic->text = QString::null; - qic->focusWidget = qApp->focusWidget(); - qic->composing = FALSE; - - if ( qic->selectedChars.size() < 128 ) - qic->selectedChars.resize( 128 ); - qic->selectedChars.fill( 0 ); - - if (qic->focusWidget) { - qic->composing = TRUE; - send_imstart = TRUE; - } - } - - if (! qic->composing || ! qic->focusWidget) { -#ifdef QT_XIM_DEBUG - qDebug("compose event: invalid compose event %d %p", - qic->composing, qic->focusWidget); -#endif // QT_XIM_DEBUG - - return 0; - } - - if ( send_imstart ) { -#ifdef QT_XIM_DEBUG - qDebug( "sending IMStart to %p", qic->focusWidget ); -#endif // QT_XIM_DEBUG - - qt_compose_emptied = FALSE; - QIMEvent startevent(QEvent::IMStart, QString::null, -1); - QApplication::sendEvent(qic->focusWidget, &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; - - QString s = QString::fromLocal8Bit(str); - - if (text->encoding_is_wchar) - delete [] str; - - if (drawstruct->chg_length < 0) - qic->text.replace(drawstruct->chg_first, UINT_MAX, s); - else - qic->text.replace(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 < 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 QT_XIM_DEBUG - qDebug( "compose emptied" ); -#endif // QT_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 QT_XIM_DEBUG - qDebug( "sending IMCompose to %p with %d chars", - qic->focusWidget, qic->text.length() ); -#endif // QT_XIM_DEBUG - - QIMComposeEvent event( QEvent::IMCompose, qic->text, cursor, sellen ); - QApplication::sendEvent(qic->focusWidget, &event); - return 0; - } - - static int xic_done_callback(XIC, XPointer client_data, XPointer) { - QInputContext *qic = (QInputContext *) client_data; - if (! qic) - return 0; - - if (qic->composing && qic->focusWidget) { -#ifdef QT_XIM_DEBUG - qDebug( "sending IMEnd (empty) to %p", qic->focusWidget ); -#endif // QT_XIM_DEBUG - - QIMEvent event(QEvent::IMEnd, QString::null, -1); - QApplication::sendEvent(qic->focusWidget, &event); - } - - qic->composing = FALSE; - qic->focusWidget = 0; - - if ( qic->selectedChars.size() < 128 ) - qic->selectedChars.resize( 128 ); - qic->selectedChars.fill( 0 ); - - return 0; - } - -#ifdef Q_C_CALLBACKS -} -#endif // Q_C_CALLBACKS - -#endif // !QT_NO_XIM - - - -QInputContext::QInputContext(QWidget *widget) - : ic(0), focusWidget(0), composing(FALSE), fontset(0) -{ -#if !defined(QT_NO_XIM) - fontsetRefCount++; - if (! qt_xim) { - qWarning("QInputContext: no input method context available"); - return; - } - - if (! widget->isTopLevel()) { - qWarning("QInputContext: 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 // !QT_NO_XIM -} - - -QInputContext::~QInputContext() -{ - -#if !defined(QT_NO_XIM) - if (ic) - XDestroyIC((XIC) ic); - - if ( --fontsetRefCount == 0 ) { - Display *dpy = QPaintDevice::x11AppDisplay(); - for ( int i = 0; i < 8; i++ ) { - if ( fontsetCache[i] && fontsetCache[i] != (XFontSet)-1 ) { - XFreeFontSet(dpy, fontsetCache[i]); - fontsetCache[i] = 0; - } - } - } - -#endif // !QT_NO_XIM - - ic = 0; - focusWidget = 0; - composing = FALSE; -} - - -void QInputContext::reset() -{ -#if !defined(QT_NO_XIM) - if (focusWidget && composing && ! text.isNull()) { -#ifdef QT_XIM_DEBUG - qDebug("QInputContext::reset: composing - sending IMEnd (empty) to %p", - focusWidget); -#endif // QT_XIM_DEBUG - - QIMEvent endevent(QEvent::IMEnd, QString::null, -1); - QApplication::sendEvent(focusWidget, &endevent); - focusWidget = 0; - text = QString::null; - if ( selectedChars.size() < 128 ) - selectedChars.resize( 128 ); - selectedChars.fill( 0 ); - - char *mb = XmbResetIC((XIC) ic); - if (mb) - XFree(mb); - } -#endif // !QT_NO_XIM -} - - -void QInputContext::setComposePosition(int x, int y) -{ -#if !defined(QT_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 // !QT_NO_XIM -} - - -void QInputContext::setComposeArea(int x, int y, int w, int h) +bool QInputContext::x11FilterEvent( QWidget *keywidget, XEvent *event ) { -#if !defined(QT_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 + return FALSE; } - -int QInputContext::lookupString(XKeyEvent *event, QCString &chars, - KeySym *key, Status *status) const -{ - int count = 0; - -#if !defined(QT_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 // QT_NO_XIM - - return count; -} - -void QInputContext::setFocus() -{ -#if !defined(QT_NO_XIM) - if (qt_xim && ic) - XSetICFocus((XIC) ic); -#endif // !QT_NO_XIM -} - -void QInputContext::setXFontSet(const QFont &f) -{ -#if !defined(QT_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 - Q_UNUSED( f ); -#endif -} +#endif //Q_NO_IM diff -urN qt-x11-free-3.3.2/src/kernel/qnamespace.h qt-x11-immodule-bc/src/kernel/qnamespace.h --- qt-x11-free-3.3.2/src/kernel/qnamespace.h 2004-04-19 18:36:09.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qnamespace.h 2004-06-23 01:10:27.073121400 +0900 @@ -397,6 +397,61 @@ Key_Help = 0x1058, Key_Direction_L = 0x1059, Key_Direction_R = 0x1060, + + // International input method support (X keycode - 0xEE00) + // Only interesting if you are writing your own input method + + // International & multi-key character composition + Key_Multi_key = 0x1120, // Multi-key character compose + Key_Codeinput = 0x1137, + Key_SingleCandidate = 0x113c, + Key_MultipleCandidate = 0x113d, + Key_PreviousCandidate = 0x113e, + + // Misc Functions + Key_Mode_switch = 0x117e, // Character set switch + //Key_script_switch = 0x117e, // Alias for mode_switch + + // Japanese keyboard support + Key_Kanji = 0x1121, // Kanji, Kanji convert + Key_Muhenkan = 0x1122, // Cancel Conversion + //Key_Henkan_Mode = 0x1123, // Start/Stop Conversion + Key_Henkan = 0x1123, // Alias for Henkan_Mode + Key_Romaji = 0x1124, // to Romaji + Key_Hiragana = 0x1125, // to Hiragana + Key_Katakana = 0x1126, // to Katakana + Key_Hiragana_Katakana = 0x1127, // Hiragana/Katakana toggle + Key_Zenkaku = 0x1128, // to Zenkaku + Key_Hankaku = 0x1129, // to Hankaku + Key_Zenkaku_Hankaku = 0x112a, // Zenkaku/Hankaku toggle + Key_Touroku = 0x112b, // Add to Dictionary + Key_Massyo = 0x112c, // Delete from Dictionary + Key_Kana_Lock = 0x112d, // Kana Lock + Key_Kana_Shift = 0x112e, // Kana Shift + Key_Eisu_Shift = 0x112f, // Alphanumeric Shift + Key_Eisu_toggle = 0x1130, // Alphanumeric toggle + //Key_Kanji_Bangou = 0x1137, // Codeinput + //Key_Zen_Koho = 0x113d, // Multiple/All Candidate(s) + //Key_Mae_Koho = 0x113e, // Previous Candidate + + // Korean support + Key_Hangul = 0x1131, // Hangul start/stop(toggle) + Key_Hangul_Start = 0x1132, // Hangul start + Key_Hangul_End = 0x1133, // Hangul end, English start + Key_Hangul_Hanja = 0x1134, // Start Hangul->Hanja Conversion + Key_Hangul_Jamo = 0x1135, // Hangul Jamo mode + Key_Hangul_Romaja = 0x1136, // Hangul Romaja mode + Key_Hangul_Codeinput = 0x1137, // Hangul code input mode + Key_Hangul_Jeonja = 0x1138, // Jeonja mode + Key_Hangul_Banja = 0x1139, // Banja mode + Key_Hangul_PreHanja = 0x113a, // Pre Hanja conversion + Key_Hangul_PostHanja = 0x113b, // Post Hanja conversion + Key_Hangul_SingleCandidate = 0x113c, // Single candidate + Key_Hangul_MultipleCandidate = 0x113d, // Multiple candidate + Key_Hangul_PreviousCandidate = 0x113e, // Previous candidate + Key_Hangul_Special = 0x113f, // Special symbols + //Key_Hangul_switch = 0x117e, // Alias for mode_switch + Key_Space = 0x20, // 7 bit printable ASCII Key_Any = Key_Space, Key_Exclam = 0x21, diff -urN qt-x11-free-3.3.2/src/kernel/qrichtext.cpp qt-x11-immodule-bc/src/kernel/qrichtext.cpp --- qt-x11-free-3.3.2/src/kernel/qrichtext.cpp 2004-04-19 18:36:05.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qrichtext.cpp 2004-06-23 01:10:27.077120792 +0900 @@ -657,7 +657,7 @@ pop(); } -bool QTextCursor::place( const QPoint &p, QTextParagraph *s, bool link ) +bool QTextCursor::place( const QPoint &p, QTextParagraph *s, bool link, bool loosePlacing, bool matchBetweenCharacters ) { QPoint pos( p ); QRect r; @@ -675,7 +675,7 @@ str = s; if ( pos.y() >= r.y() && pos.y() <= r.y() + r.height() ) break; - if ( !s->next() ) { + if ( loosePlacing == TRUE && !s->next() ) { #ifdef Q_WS_MACX pos.setX( s->rect().x() + s->rect().width() ); #endif @@ -716,7 +716,7 @@ if ( pos.x() < x ) pos.setX( x + 1 ); int cw; - int curpos = s->length()-1; + int curpos = -1; int dist = 10000000; bool inCustom = FALSE; while ( i < nextLine ) { @@ -738,14 +738,21 @@ cpos += cw; int d = cpos - pos.x(); bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft; - if ( (QABS( d ) < dist || (dist == d && dm == TRUE )) && para->string()->validCursorPosition( i ) ) { + if ( ( matchBetweenCharacters == TRUE && (QABS( d ) < dist || (dist == d && dm == TRUE )) && para->string()->validCursorPosition( i ) ) || + ( matchBetweenCharacters == FALSE && ( d == 0 || dm == TRUE ) ) ) { dist = QABS( d ); - if ( !link || pos.x() >= x + chr->x ) + if ( !link || ( pos.x() >= x + chr->x && ( loosePlacing == TRUE || pos.x() < cpos ) ) ) curpos = i; } } i++; } + if ( curpos == -1 ) { + if ( loosePlacing == TRUE ) + curpos = s->length()-1; + else + return FALSE; + } setIndex( curpos ); #ifndef QT_NO_TEXTCUSTOMITEM @@ -4828,6 +4835,9 @@ tmpw = fullSelectionWidth - xleft; painter.fillRect( xleft, y, tmpw, h, color ); painter.drawText( xstart, y + baseLine, str, start, len, dir ); + // draw preedit's underline + if (selection == QTextDocument::IMCompositionText) + painter.drawLine(xstart, y + baseLine + 1, xstart + w, y + baseLine + 1); if (selStart != start || selEnd != start + len || selWrap) painter.restore(); } diff -urN qt-x11-free-3.3.2/src/kernel/qrichtext_p.h qt-x11-immodule-bc/src/kernel/qrichtext_p.h --- qt-x11-free-3.3.2/src/kernel/qrichtext_p.h 2004-04-19 18:36:05.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qrichtext_p.h 2004-06-23 01:10:27.078120640 +0900 @@ -358,7 +358,8 @@ int totalOffsetY() const; // total document offset bool place( const QPoint &pos, QTextParagraph *s ) { return place( pos, s, FALSE ); } - bool place( const QPoint &pos, QTextParagraph *s, bool link ); + bool place( const QPoint &pos, QTextParagraph *s, bool link ) { return place( pos, s, link, TRUE, TRUE ); } + bool place( const QPoint &pos, QTextParagraph *s, bool link, bool loosePlacing, bool matchBetweenCharacters ); void restoreState(); diff -urN qt-x11-free-3.3.2/src/kernel/qt_kernel.pri qt-x11-immodule-bc/src/kernel/qt_kernel.pri --- qt-x11-free-3.3.2/src/kernel/qt_kernel.pri 2003-12-08 23:44:24.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qt_kernel.pri 2004-06-23 01:10:27.079120488 +0900 @@ -34,7 +34,6 @@ $$KERNEL_H/qimage.h \ $$KERNEL_P/qimageformatinterface_p.h \ $$KERNEL_H/qimageformatplugin.h \ - $$KERNEL_P/qinputcontext_p.h \ $$KERNEL_H/qkeycode.h \ $$KERNEL_H/qkeysequence.h \ $$KERNEL_H/qlayout.h \ @@ -99,6 +98,12 @@ $$KERNEL_CPP/qfontengine_p.h \ $$KERNEL_CPP/qtextlayout_p.h + unix:x11 { + HEADERS += $$KERNEL_H/qinputcontext.h + } else { + HEADERS += $$KERNEL_P/qinputcontext_p.h + } + win32:SOURCES += $$KERNEL_CPP/qapplication_win.cpp \ $$KERNEL_CPP/qclipboard_win.cpp \ $$KERNEL_CPP/qcolor_win.cpp \ @@ -130,6 +135,7 @@ $$KERNEL_CPP/qdesktopwidget_x11.cpp \ $$KERNEL_CPP/qeventloop_x11.cpp \ $$KERNEL_CPP/qfont_x11.cpp \ + $$KERNEL_CPP/qinputcontext.cpp \ $$KERNEL_CPP/qinputcontext_x11.cpp \ $$KERNEL_CPP/qmotifdnd_x11.cpp \ $$KERNEL_CPP/qpixmap_x11.cpp \ diff -urN qt-x11-free-3.3.2/src/kernel/qwidget.cpp qt-x11-immodule-bc/src/kernel/qwidget.cpp --- qt-x11-free-3.3.2/src/kernel/qwidget.cpp 2004-04-19 18:36:08.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qwidget.cpp 2004-06-23 01:10:27.081120184 +0900 @@ -3282,8 +3282,24 @@ if ( isActiveWindow() ) { QWidget * prev = qApp->focus_widget; if ( prev ) { - if ( prev != this ) + // This part is never executed when Q_WS_X11? Preceding XFocusOut + // had already reset focus_widget when received XFocusIn + + // Don't reset input context explicitly here. Whether reset or not + // when focusing out is a responsibility of input methods. For + // example, Japanese input context should not be reset here. The + // context sometimes contains a whole paragraph and has minutes of + // lifetime different to ephemeral one in other languages. The + // input context should be survived until focused again. So we + // delegate the responsibility to input context via + // unfocusInputContext(). + if ( prev != this ) { +#if 0 prev->resetInputContext(); +#else + prev->unfocusInputContext(); +#endif + } } #if defined(Q_WS_WIN) else { @@ -3291,9 +3307,7 @@ } #endif qApp->focus_widget = this; -#if defined(Q_WS_X11) focusInputContext(); -#endif #if defined(Q_WS_WIN) if ( !topLevelWidget()->isPopup() ) @@ -3341,6 +3355,7 @@ focusProxy()->clearFocus(); return; } else if ( hasFocus() ) { + unfocusInputContext(); QWidget* w = qApp->focusWidget(); // clear active focus qApp->focus_widget = 0; @@ -4661,7 +4676,13 @@ break; case QEvent::MouseButtonPress: + // Don't reset input context here. Whether reset or not is + // a responsibility of input method. reset() will be + // called by mouseHandler() of input method if necessary + // via mousePressEvent() of text widgets. +#if 0 resetInputContext(); +#endif mousePressEvent( (QMouseEvent*)e ); if ( ! ((QMouseEvent*)e)->isAccepted() ) return FALSE; diff -urN qt-x11-free-3.3.2/src/kernel/qwidget.h qt-x11-immodule-bc/src/kernel/qwidget.h --- qt-x11-free-3.3.2/src/kernel/qwidget.h 2004-04-19 18:36:06.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qwidget.h 2004-06-23 01:10:27.082120032 +0900 @@ -536,6 +536,9 @@ int metric( int ) const; void resetInputContext(); + void sendMouseEventToInputContext( int x, QEvent::Type type, + Qt::ButtonState button, + Qt::ButtonState state ); virtual void create( WId = 0, bool initializeWindow = TRUE, bool destroyOldWindow = TRUE ); @@ -573,6 +576,7 @@ void createInputContext(); void destroyInputContext(); void focusInputContext(); + void unfocusInputContext(); void checkChildrenDnd(); #elif defined(Q_WS_MAC) uint own_id : 1, macDropEnabled : 1; diff -urN qt-x11-free-3.3.2/src/kernel/qwidget_x11.cpp qt-x11-immodule-bc/src/kernel/qwidget_x11.cpp --- qt-x11-free-3.3.2/src/kernel/qwidget_x11.cpp 2004-04-19 18:36:07.000000000 +0900 +++ qt-x11-immodule-bc/src/kernel/qwidget_x11.cpp 2004-06-23 01:10:27.084119728 +0900 @@ -61,11 +61,11 @@ bool qt_wstate_iconified( WId ); void qt_updated_rootinfo(); -#ifndef QT_NO_XIM -#include "qinputcontext_p.h" +#ifndef QT_NO_IM +#include "qinputcontext.h" +#include "qinputcontextfactory.h" -extern XIM qt_xim; -extern XIMStyle qt_xim_style; +extern QString defaultIM; #endif // Paint event clipping magic @@ -940,22 +940,18 @@ void QWidget::setMicroFocusHint(int x, int y, int width, int height, bool text, QFont *f ) { -#ifndef QT_NO_XIM +#ifndef QT_NO_IM if ( text ) { QWidget* tlw = topLevelWidget(); QTLWExtra *topdata = tlw->topData(); // trigger input context creation if it hasn't happened already createInputContext(); - QInputContext *qic = (QInputContext *) topdata->xic; - if ( qt_xim && qic ) { - QPoint p( x, y ); - QPoint p2 = mapTo( topLevelWidget(), QPoint( 0, 0 ) ); - p = mapTo( topLevelWidget(), p); - qic->setXFontSet( f ? *f : fnt ); - qic->setComposePosition(p.x(), p.y() + height); - qic->setComposeArea(p2.x(), p2.y(), this->width(), this->height()); + QInputContext *qic = (QInputContext *) topdata->xic; + if(qic) { + QPoint gp = mapToGlobal( QPoint( x, y ) ); + qic->setMicroFocus(gp.x(), gp.y(), width, height, f); } } #endif @@ -1424,16 +1420,8 @@ QWidget *tlw = topLevelWidget(); if ( tlw->isVisible() && !tlw->topData()->embedded && !qt_deferred_map_contains(tlw) ) { XSetInputFocus( x11Display(), tlw->winId(), RevertToNone, qt_x_time); - -#ifndef QT_NO_XIM - // trigger input context creation if it hasn't happened already - createInputContext(); - - if (tlw->topData()->xic) { - QInputContext *qic = (QInputContext *) tlw->topData()->xic; - qic->setFocus(); - } -#endif + // tlw->focusInputContext(); + focusInputContext(); } } @@ -2625,62 +2613,81 @@ } +/*! + \internal + This is an internal function, you should never call this. + + This function is called when generating InputContext + according to a configuration of default InputMethod. + (e.g. when setMicroFocusHint() and so on is executed.) + + InputContext is generated only when isInputMethodEnabled() + return TRUE. +*/ void QWidget::createInputContext() { + if( !isInputMethodEnabled() ) + return; + QWidget *tlw = topLevelWidget(); QTLWExtra *topdata = tlw->topData(); -#ifndef QT_NO_XIM - if (qt_xim) { - if (! topdata->xic) { - QInputContext *qic = new QInputContext(tlw); - topdata->xic = (void *) qic; - } - } else -#endif // QT_NO_XIM - { - // qDebug("QWidget::createInputContext: no xim"); - topdata->xic = 0; - } +#ifndef QT_NO_IM + if (! topdata->xic) { + // InputContext corresponding to the name of default + // InputMethod of the Application in present is generated. + QInputContext *qic = QInputContextFactory::create(defaultIM, tlw); + + topdata->xic = (void *) qic; + } +#endif // QT_NO_IM } void QWidget::destroyInputContext() { -#ifndef QT_NO_XIM +#ifndef QT_NO_IM QInputContext *qic = (QInputContext *) extra->topextra->xic; - delete qic; -#endif // QT_NO_XIM + if(qic) + delete qic; + +#endif // QT_NO_IM extra->topextra->xic = 0; } /*! - This function is called when the user finishes input composition, - e.g. changes focus to another widget, moves the cursor, etc. + This function is called when text widgets need to be neutral state to + execute text operations properly. See qlineedit.cpp and qtextedit.cpp as + example. Ordinary reset such as along with changes focus to another + widget, moves the cursor, etc, is implicitly handled via + unfocusInputContext() because whether reset or not when such situation is + a responsibility of input methods. For example, Japanese input context + should not be reset when focus out. The context sometimes contains a whole + paragraph and has minutes of lifetime different to ephemeral one in other + languages. The input context should be survived until focused again. So we + delegate the responsibility to the input context via unfocusInputContext(). */ void QWidget::resetInputContext() { -#ifndef QT_NO_XIM - if (qt_xim_style & XIMPreeditCallbacks) { - QWidget *tlw = topLevelWidget(); - QTLWExtra *topdata = tlw->topData(); +#ifndef QT_NO_IM + QWidget *tlw = topLevelWidget(); + QTLWExtra *topdata = tlw->topData(); - // trigger input context creation if it hasn't happened already - createInputContext(); + // trigger input context creation if it hasn't happened already + createInputContext(); - if (topdata->xic) { - QInputContext *qic = (QInputContext *) topdata->xic; - qic->reset(); - } + if (topdata->xic) { + QInputContext *qic = (QInputContext *) topdata->xic; + qic->reset(); } -#endif // QT_NO_XIM +#endif // QT_NO_IM } void QWidget::focusInputContext() { -#ifndef QT_NO_XIM +#ifndef QT_NO_IM QWidget *tlw = topLevelWidget(); QTLWExtra *topdata = tlw->topData(); @@ -2689,11 +2696,56 @@ if (topdata->xic) { QInputContext *qic = (QInputContext *) topdata->xic; + + if( qic->focusWidget() != this) + qic->setFocusWidget( this ); qic->setFocus(); } -#endif // QT_NO_XIM +#endif // QT_NO_IM } + +void QWidget::unfocusInputContext() +{ +#ifndef QT_NO_IM + QWidget *tlw = topLevelWidget(); + QTLWExtra *topdata = tlw->topData(); + + // trigger input context creation if it hasn't happened already + createInputContext(); + + if (topdata->xic) { + QInputContext *qic = (QInputContext *) topdata->xic; + + // may be caused reset() in some input methods + qic->unsetFocus(); + qic->setFocusWidget( 0 ); + } +#endif // QT_NO_IM +} + + +void QWidget::sendMouseEventToInputContext( int x, QEvent::Type type, + Qt::ButtonState button, + Qt::ButtonState state ) +{ +#ifndef QT_NO_IM + QWidget *tlw = topLevelWidget(); + QTLWExtra *topdata = tlw->topData(); + + // trigger input context creation if it hasn't happened already + createInputContext(); + + if (topdata->xic) { + QInputContext *qic = (QInputContext *) topdata->xic; + + // may be causing reset() in some input methods + qic->mouseHandler( x, type, button, state ); + } +#endif // QT_NO_IM +} + + void QWidget::setWindowOpacity(double) { } diff -urN qt-x11-free-3.3.2/src/qt.pro qt-x11-immodule-bc/src/qt.pro --- qt-x11-free-3.3.2/src/qt.pro 2004-03-01 19:20:58.000000000 +0900 +++ qt-x11-immodule-bc/src/qt.pro 2004-06-23 01:10:27.085119576 +0900 @@ -37,6 +37,7 @@ TOOLS_CPP = tools CODECS_CPP = codecs WORKSPACE_CPP = workspace +INPUT_CPP = input XML_CPP = xml STYLES_CPP = styles EMBEDDED_CPP = embedded @@ -54,6 +55,7 @@ TOOLS_H = $$TOOLS_CPP CODECS_H = $$CODECS_CPP WORKSPACE_H = $$WORKSPACE_CPP + #INPUT_H = $$INPUT_CPP XML_H = $$XML_CPP CANVAS_H = $$CANVAS_CPP STYLES_H = $$STYLES_CPP @@ -70,6 +72,7 @@ TOOLS_H = $$WIN_ALL_H CODECS_H = $$WIN_ALL_H WORKSPACE_H = $$WIN_ALL_H + #INPUT_H = $$WIN_ALL_H XML_H = $$WIN_ALL_H CANVAS_H = $$WIN_ALL_H STYLES_H = $$WIN_ALL_H @@ -98,6 +101,7 @@ TOOLS_H = $$TOOLS_CPP CODECS_H = $$CODECS_CPP WORKSPACE_H = $$WORKSPACE_CPP + INPUT_H = $$INPUT_CPP XML_H = $$XML_CPP STYLES_H = $$STYLES_CPP !embedded:!mac:CONFIG += x11 x11inc @@ -112,7 +116,7 @@ EMBEDDED_H = $$EMBEDDED_CPP } -DEPENDPATH += ;$$NETWORK_H;$$KERNEL_H;$$WIDGETS_H;$$SQL_H;$$TABLE_H;$$DIALOGS_H; +DEPENDPATH += ;$$NETWORK_H;$$KERNEL_H;$$WIDGETS_H;$$INPUT_H;$$SQL_H;$$TABLE_H;$$DIALOGS_H; DEPENDPATH += $$ICONVIEW_H;$$OPENGL_H;$$TOOLS_H;$$CODECS_H;$$WORKSPACE_H;$$XML_H; DEPENDPATH += $$CANVAS_H;$$STYLES_H embedded:DEPENDPATH += ;$$EMBEDDED_H @@ -147,6 +151,7 @@ include($$DIALOGS_CPP/qt_dialogs.pri) include($$ICONVIEW_CPP/qt_iconview.pri) include($$WORKSPACE_CPP/qt_workspace.pri) +include($$INPUT_CPP/qt_input.pri) include($$NETWORK_CPP/qt_network.pri) include($$CANVAS_CPP/qt_canvas.pri) include($$TABLE_CPP/qt_table.pri) diff -urN qt-x11-free-3.3.2/src/widgets/qlineedit.cpp qt-x11-immodule-bc/src/widgets/qlineedit.cpp --- qt-x11-free-3.3.2/src/widgets/qlineedit.cpp 2004-04-19 18:36:19.000000000 +0900 +++ qt-x11-immodule-bc/src/widgets/qlineedit.cpp 2004-06-23 01:10:27.087119272 +0900 @@ -245,12 +245,17 @@ // input methods int imstart, imend, imselstart, imselend; + bool composeMode() const { return preeditLength(); } + bool hasIMSelection() const { return imSelectionLength(); } + int preeditLength() const { return ( imend - imstart ); } + int imSelectionLength() const { return ( imselend - imselstart ); } // complex text layout QTextLayout textLayout; void updateTextLayout(); void moveCursor( int pos, bool mark = FALSE ); void setText( const QString& txt ); + int xToPosInternal( int x, QTextItem::CursorPosition ) const; int xToPos( int x, QTextItem::CursorPosition = QTextItem::BetweenCharacters ) const; inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); } QRect cursorRect() const; @@ -1411,6 +1416,8 @@ */ void QLineEdit::mousePressEvent( QMouseEvent* e ) { + if ( sendMouseEventToInputContext( e ) ) + return; if ( e->button() == RightButton ) return; if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() < @@ -1440,7 +1447,8 @@ */ void QLineEdit::mouseMoveEvent( QMouseEvent * e ) { - + if ( sendMouseEventToInputContext( e ) ) + return; #ifndef QT_NO_CURSOR if ( ( e->state() & MouseButtonMask ) == 0 ) { if ( !d->readOnly && d->dragEnabled @@ -1469,6 +1477,8 @@ */ void QLineEdit::mouseReleaseEvent( QMouseEvent* e ) { + if ( sendMouseEventToInputContext( e ) ) + return; #ifndef QT_NO_DRAGANDDROP if ( e->button() == LeftButton ) { if ( d->dndTimer ) { @@ -1495,6 +1505,8 @@ */ void QLineEdit::mouseDoubleClickEvent( QMouseEvent* e ) { + if ( sendMouseEventToInputContext( e ) ) + return; if ( e->button() == Qt::LeftButton ) { deselect(); d->cursor = d->xToPos( e->pos().x() ); @@ -1763,6 +1775,33 @@ e->ignore(); } + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls QWidget::sendMouseEventToInputContext() as easy for this + class. + */ +bool QLineEdit::sendMouseEventToInputContext( QMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + int cursor = d->xToPosInternal( e->pos().x(), QTextItem::OnCharacters ); + int mousePos = cursor - d->imstart; + if ( mousePos >= 0 && mousePos < d->preeditLength() ) { + QWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } else if ( e->type() != QEvent::MouseMove ) { + // send button events on out of preedit + QWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + /*! \reimp */ void QLineEdit::imStartEvent( QIMEvent *e ) @@ -1788,7 +1827,16 @@ d->imend = d->imstart + e->text().length(); d->imselstart = d->imstart + e->cursorPos(); d->imselend = d->imselstart + e->selectionLength(); +#if 0 d->cursor = e->selectionLength() ? d->imend : d->imselend; +#else + // Cursor placement code is changed for Asian input method that + // shows candidate window. This behavior is same as Qt/E 2.3.7 + // which supports Asian input methods. Asian input methods need + // start point of IM selection text to place candidate window as + // adjacent to the selection text. + d->cursor = d->imselstart; +#endif d->updateTextLayout(); update(); } @@ -1822,6 +1870,8 @@ } if( !hasSelectedText() || style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) ) d->setCursorVisible( TRUE ); + if ( d->hasIMSelection() ) + d->cursor = d->imselstart; d->updateMicroFocusHint(); } @@ -1898,6 +1948,14 @@ } else if ( widthUsed - d->hscroll < lineRect.width() ) { d->hscroll = widthUsed - lineRect.width() + 1; } + // This updateMicroFocusHint() is corresponding to update() at + // IMCompose event. Although the function is invoked from various + // other points, some situations such as "candidate selection on + // AlignHCenter'ed text" need this invocation because + // updateMicroFocusHint() requires updated contentsRect(), and + // there are no other chances in such situation that invoke the + // function. + d->updateMicroFocusHint(); // the y offset is there to keep the baseline constant in case we have script changes in the text. QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent-fm.ascent()); @@ -1938,7 +1996,7 @@ } // input method edit area - if ( d->imstart < d->imend && (last >= d->imstart && first < d->imend ) ) { + if ( d->composeMode() && (last >= d->imstart && first < d->imend ) ) { QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imstart - first, 0 ) ), lineRect.top() ), QPoint( tix + ti.cursorToX( QMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); p->save(); @@ -1951,11 +2009,16 @@ imCol.setHsv( h1, s1, ( v1 + v2 ) / 2 ); p->fillRect( highlight, imCol ); p->drawTextItem( topLeft, ti, textflags ); + // draw preedit's underline + if (d->imend - d->imstart > 0) { + p->setPen( cg.text() ); + p->drawLine( highlight.bottomLeft(), highlight.bottomRight() ); + } p->restore(); } // input method selection - if ( d->imselstart < d->imselend && (last >= d->imselstart && first < d->imselend ) ) { + if ( d->hasIMSelection() && (last >= d->imselstart && first < d->imselend ) ) { QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imselstart - first, 0 ) ), lineRect.top() ), QPoint( tix + ti.cursorToX( QMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); p->save(); @@ -1982,7 +2045,11 @@ } // draw cursor - if ( d->cursorVisible && !supressCursor ) { + // + // Asian users regard IM selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + if ( d->cursorVisible && !supressCursor && !d->hasIMSelection() ) { QPoint from( topLeft.x() + cix, lineRect.top() ); QPoint to = from + QPoint( 0, lineRect.height() ); p->drawLine( from, to ); @@ -2090,6 +2157,10 @@ void QLineEdit::contextMenuEvent( QContextMenuEvent * e ) { #ifndef QT_NO_POPUPMENU +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif d->separate(); QGuardedPtr popup = createPopupMenu(); @@ -2274,7 +2345,7 @@ textLayout.endLine(0, 0, Qt::AlignLeft|Qt::SingleLine, &ascent); } -int QLineEditPrivate::xToPos( int x, QTextItem::CursorPosition betweenOrOn ) const +int QLineEditPrivate::xToPosInternal( int x, QTextItem::CursorPosition betweenOrOn ) const { x-= q->contentsRect().x() - hscroll + innerMargin; for ( int i = 0; i < textLayout.numItems(); ++i ) { @@ -2283,7 +2354,13 @@ if ( x >= tir.left() && x <= tir.right() ) return ti.xToCursor( x - tir.x(), betweenOrOn ) + ti.from(); } - return x < 0 ? 0 : text.length(); + return x < 0 ? -1 : text.length(); +} + +int QLineEditPrivate::xToPos( int x, QTextItem::CursorPosition betweenOrOn ) const +{ + int pos = xToPosInternal( x, betweenOrOn ); + return ( pos < 0 ) ? 0 : pos; } diff -urN qt-x11-free-3.3.2/src/widgets/qlineedit.h qt-x11-immodule-bc/src/widgets/qlineedit.h --- qt-x11-free-3.3.2/src/widgets/qlineedit.h 2004-04-19 18:36:18.000000000 +0900 +++ qt-x11-immodule-bc/src/widgets/qlineedit.h 2004-06-23 01:10:27.087119272 +0900 @@ -196,6 +196,7 @@ void dropEvent( QDropEvent * ); #endif void contextMenuEvent( QContextMenuEvent * ); + bool sendMouseEventToInputContext( QMouseEvent *e ); virtual QPopupMenu *createPopupMenu(); void windowActivationChange( bool ); #ifndef QT_NO_COMPAT diff -urN qt-x11-free-3.3.2/src/widgets/qtextedit.cpp qt-x11-immodule-bc/src/widgets/qtextedit.cpp --- qt-x11-free-3.3.2/src/widgets/qtextedit.cpp 2004-04-19 18:36:18.000000000 +0900 +++ qt-x11-immodule-bc/src/widgets/qtextedit.cpp 2004-06-23 01:10:27.091118664 +0900 @@ -107,6 +107,8 @@ int id[ 7 ]; int preeditStart; int preeditLength; + bool composeMode() const { return ( preeditLength > 0 ); } + uint ensureCursorVisibleInShowEvent : 1; uint tabChangesFocus : 1; QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized @@ -1080,6 +1082,10 @@ l += v; } } + + // This invocation is required to follow dragging of active window + // by the showed candidate window. + updateMicroFocusHint(); } /*! @@ -1554,6 +1560,35 @@ } /*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls QWidget::sendMouseEventToInputContext() as easy for this + class. + */ +bool QTextEdit::sendMouseEventToInputContext( QMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + QTextCursor c( doc ); + if ( c.place( e->pos(), doc->firstParagraph(), FALSE, FALSE, FALSE ) ) { + int mousePos = c.index() - d->preeditStart; + if ( cursor->globalY() == c.globalY() && + mousePos >= 0 && mousePos < d->preeditLength ) { + QWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } + } else if ( e->type() != QEvent::MouseMove ) { + // send button events on out of preedit + QWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! \reimp */ void QTextEdit::imStartEvent( QIMEvent *e ) @@ -1581,10 +1616,16 @@ doc->removeSelection( QTextDocument::IMCompositionText ); doc->removeSelection( QTextDocument::IMSelectionText ); - if ( d->preeditLength > 0 && cursor->paragraph() ) + if ( d->composeMode() && cursor->paragraph() ) cursor->paragraph()->remove( d->preeditStart, d->preeditLength ); cursor->setIndex( d->preeditStart ); - insert( e->text() ); + + int sellen = e->selectionLength(); + uint insertionFlags = CheckNewLines | RemoveSelected | AsIMCompositionText; + if ( sellen > 0 ) { + insertionFlags |= WithIMSelection; + } + insert( e->text(), insertionFlags ); d->preeditLength = e->text().length(); cursor->setIndex( d->preeditStart + d->preeditLength ); @@ -1595,17 +1636,28 @@ cursor->setIndex( d->preeditStart + e->cursorPos() ); - int sellen = e->selectionLength(); if ( sellen > 0 ) { cursor->setIndex( d->preeditStart + e->cursorPos() + sellen ); c = *cursor; cursor->setIndex( d->preeditStart + e->cursorPos() ); doc->setSelectionStart( QTextDocument::IMSelectionText, *cursor ); doc->setSelectionEnd( QTextDocument::IMSelectionText, c ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as Qt/E 2.3.7 which supports + // Asian input methods. Asian input methods need start point + // of IM selection text to place candidate window as adjacent + // to the selection text. cursor->setIndex( d->preeditStart + d->preeditLength ); +#endif } repaintChanged(); + + // microFocusHint is required to place candidate window for Asian + // input methods. Cursor position and IM selection text are + // updated by the event. + updateMicroFocusHint(); } /*! @@ -1618,10 +1670,12 @@ return; } + qWarning( "receiving IMEnd with %d chars", e->text().length() ); + doc->removeSelection( QTextDocument::IMCompositionText ); doc->removeSelection( QTextDocument::IMSelectionText ); - if ( d->preeditLength > 0 && cursor->paragraph() ) + if ( d->composeMode() && cursor->paragraph() ) cursor->paragraph()->remove( d->preeditStart, d->preeditLength ); if ( d->preeditStart >= 0 ) { cursor->setIndex( d->preeditStart ); @@ -2113,6 +2167,13 @@ isReadOnly() ) return; + // Asian users regard selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + if ( doc->hasSelection( QTextDocument::IMSelectionText ) ) { + visible = FALSE; + } + QPainter p( viewport() ); QRect r( cursor->topParagraph()->rect() ); cursor->paragraph()->setChanged( TRUE ); @@ -2187,6 +2248,9 @@ } #endif + if ( sendMouseEventToInputContext( e ) ) + return; + if ( d->trippleClickTimer->isActive() && ( e->globalPos() - d->trippleClickPoint ).manhattanLength() < QApplication::startDragDistance() ) { @@ -2292,7 +2356,9 @@ return; } #endif - if ( mousePressed ) { + if ( sendMouseEventToInputContext( e ) ) { + // running through intended to avoid cursor vanishing + } else if ( mousePressed ) { #ifndef QT_NO_DRAGANDDROP if ( mightStartDrag ) { dragStartTimer->stop(); @@ -2345,7 +2411,7 @@ void QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e ) { - if ( !inDoubleClick ) { // could be the release of a dblclick + if ( !inDoubleClick && !d->composeMode() ) { // could be the release of a dblclick int para = 0; int index = charAt( e->pos(), ¶ ); emit clicked( para, index ); @@ -2356,6 +2422,8 @@ return; } #endif + if ( sendMouseEventToInputContext( e ) ) + return; QTextCursor oldCursor = *cursor; if ( scrollTimer->isActive() ) scrollTimer->stop(); @@ -2448,7 +2516,7 @@ void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * e ) { - if ( e->button() != Qt::LeftButton ) { + if ( e->button() != Qt::LeftButton && !d->composeMode() ) { e->ignore(); return; } @@ -2479,6 +2547,9 @@ } else #endif { + if ( sendMouseEventToInputContext( e ) ) + return; + QTextCursor c1 = *cursor; QTextCursor c2 = *cursor; #if defined(Q_OS_MAC) @@ -2653,10 +2724,15 @@ */ void QTextEdit::contentsContextMenuEvent( QContextMenuEvent *e ) { + e->accept(); +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif + clearUndoRedo(); mousePressed = FALSE; - e->accept(); #ifndef QT_NO_POPUPMENU QPopupMenu *popup = createPopupMenu( e->pos() ); if ( !popup ) @@ -2802,8 +2878,15 @@ void QTextEdit::updateMicroFocusHint() { QTextCursor c( *cursor ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as Qt/E 2.3.7 which supports + // Asian input methods. Asian input methods need start point of IM + // selection text to place candidate window as adjacent to the + // selection text. if ( d->preeditStart != -1 ) c.setIndex( d->preeditStart ); +#endif if ( hasFocus() || viewport()->hasFocus() ) { int h = c.paragraph()->lineHeightOfChar( cursor->index() ); @@ -2968,6 +3051,8 @@ bool indent = insertionFlags & RedoIndentation; bool checkNewLine = insertionFlags & CheckNewLines; bool removeSelected = insertionFlags & RemoveSelected; + bool imComposition = insertionFlags & AsIMCompositionText; + bool imSelection = insertionFlags & WithIMSelection; QString txt( text ); drawCursor( FALSE ); if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected ) @@ -3001,7 +3086,10 @@ formatMore(); repaintChanged(); ensureCursorVisible(); - drawCursor( TRUE ); + // Asian users regard selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + drawCursor( !imSelection ); if ( undoEnabled && !isReadOnly() ) { undoRedoInfo.d->text += txt; @@ -3023,7 +3111,13 @@ doc->setSelectionEnd( QTextDocument::Standard, *cursor ); repaintChanged(); } - updateMicroFocusHint(); + // updateMicroFocusHint() should not be invoked here when this + // function is invoked from imComposeEvent() because cursor + // postion is incorrect yet. imComposeEvent() invokes + // updateMicroFocusHint() later. + if ( !imComposition ) { + updateMicroFocusHint(); + } setModified(); emit textChanged(); } diff -urN qt-x11-free-3.3.2/src/widgets/qtextedit.h qt-x11-immodule-bc/src/widgets/qtextedit.h --- qt-x11-free-3.3.2/src/widgets/qtextedit.h 2004-04-19 18:36:18.000000000 +0900 +++ qt-x11-immodule-bc/src/widgets/qtextedit.h 2004-06-23 01:10:27.092118512 +0900 @@ -211,7 +211,9 @@ enum TextInsertionFlags { RedoIndentation = 0x0001, CheckNewLines = 0x0002, - RemoveSelected = 0x0004 + RemoveSelected = 0x0004, + AsIMCompositionText = 0x0008, // internal use + WithIMSelection = 0x0010 // internal use }; QTextEdit( const QString& text, const QString& context = QString::null, @@ -439,6 +441,7 @@ void contentsDropEvent( QDropEvent *e ); #endif void contentsContextMenuEvent( QContextMenuEvent *e ); + bool sendMouseEventToInputContext( QMouseEvent *e ); bool focusNextPrevChild( bool next ); QTextDocument *document() const; QTextCursor *textCursor() const; diff -urN qt-x11-free-3.3.2/tools/qtconfig/mainwindow.cpp qt-x11-immodule-bc/tools/qtconfig/mainwindow.cpp --- qt-x11-free-3.3.2/tools/qtconfig/mainwindow.cpp 2003-12-08 18:49:15.000000000 +0900 +++ qt-x11-immodule-bc/tools/qtconfig/mainwindow.cpp 2004-06-23 01:10:27.093118360 +0900 @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -362,9 +363,50 @@ #ifdef Q_WS_X11 inputStyle->setCurrentText( settings.readEntry( "/qt/XIMInputStyle", trUtf8( "On The Spot" ) ) ); + + + /* + This code makes it possible to set up default InputMethod. + + The list of names of InputMethod which can be used is acquired + using QInputContextFactory::keys(). And it is set to inputMethodCombo + which displays the list of InputMethod. + */ + QStringList inputmethods = QInputContextFactory::keys(); + inputmethods.sort(); + inputMethodCombo->insertStringList(inputmethods); + + /* + InputMethod set up as a default in the past is chosen. + If nothing is set up, default InputMethod in the platform is chosen. + */ + QString currentIM = settings.readEntry("/qt/DefaultInputMethod"); + if (currentIM.isNull()) + currentIM = "XIM"; // default InputMethod is XIM in X11. + { + int s = 0; + QStringList::Iterator imIt = inputmethods.begin(); + while (imIt != inputmethods.end()) { + if (*imIt == currentIM) + break; + s++; + imIt++; + } + + // set up Selected InputMethod. + if (s < inputMethodCombo->count()) { + inputMethodCombo->setCurrentItem(s); + } else { + // we give up. But, probably this code is not executed. + inputMethodCombo->insertItem("Unknown"); + inputMethodCombo->setCurrentItem(inputMethodCombo->count() - 1); + } + } #else inputStyle->hide(); inputStyleLabel->hide(); + inputMethodCombo->hide(); + inputMethodLabel->hide(); #endif fontembeddingcheckbox->setChecked( settings.readBoolEntry("/qt/embedFonts", TRUE) ); @@ -443,6 +485,8 @@ else if ( style == trUtf8( "Root" ) ) str = "Root"; settings.writeEntry( "/qt/XIMInputStyle", inputStyle->currentText() ); + + settings.writeEntry("/qt/DefaultInputMethod", inputMethodCombo->currentText()); #endif QStringList effects; diff -urN qt-x11-free-3.3.2/tools/qtconfig/mainwindowbase.ui qt-x11-immodule-bc/tools/qtconfig/mainwindowbase.ui --- qt-x11-free-3.3.2/tools/qtconfig/mainwindowbase.ui 2003-11-12 05:01:02.000000000 +0900 +++ qt-x11-immodule-bc/tools/qtconfig/mainwindowbase.ui 2004-06-23 01:10:27.094118208 +0900 @@ -1016,6 +1016,19 @@ 0 + + + inputMethodLabel + + + Default Input Method: + + + + + inputMethodCombo + + spacer5 @@ -1029,7 +1042,7 @@ 20 - 40 + 30 @@ -1705,6 +1718,12 @@ somethingModified() + inputMethodCombo + activated(int) + MainWindowBase + somethingModified() + + gstylecombo activated(const QString&) MainWindowBase