diff options
Diffstat (limited to 'main/dependencies/qt3/src/kernel/qinputcontext.cpp')
-rw-r--r-- | main/dependencies/qt3/src/kernel/qinputcontext.cpp | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/main/dependencies/qt3/src/kernel/qinputcontext.cpp b/main/dependencies/qt3/src/kernel/qinputcontext.cpp new file mode 100644 index 000000000..5433ae473 --- /dev/null +++ b/main/dependencies/qt3/src/kernel/qinputcontext.cpp @@ -0,0 +1,856 @@ +/**************************************************************************** +** $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 "qpopupmenu.h" + +#include <stdlib.h> +#include <limits.h> + +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 +}; + + +// UPDATED COMMENT REQUIRED -- 2004-07-08 YamaKen +/*! + \class QInputContext qinputcontext.h + \brief The QInputContext class abstracts the input method dependent data and composing state. + + \ingroup i18n + + An input method is responsible to input complex text that cannot + be inputted via simple keymap. It converts a sequence of input + events (typically key events) into a text string through the input + method specific converting process. The class of the processes are + widely ranging from simple finite state machine to complex text + translator that pools a whole paragraph of a text with text + editing capability to perform grammar and semantic analysis. + + To abstract such different input method specific intermediate + information, Qt offers the QInputContext as base class. The + concept is well known as 'input context' in the input method + domain. an input context is created for a text widget in response + to a demand. It is ensured that an input context is prepared for + an input method before input to a text widget. + + Multiple input contexts that is belonging to a single input method + may concurrently coexist. Suppose multi-window text editor. Each + text widget of window A and B holds different QInputContext + instance which contains different state information such as + partially composed text. + + \section1 Groups of functions: + + \table + \header \i Context \i Functions + + \row \i Receiving information \i + x11FilterEvent(), + filterEvent(), + setMicroFocus(), + mouseHandler() + + \row \i Sending back composed text \i + sendIMEvent(), + + \row \i State change notification \i + setFocus(), + unsetFocus(), + reset() + + \row \i Context information \i + identifierName(), + language(), + font(), + isComposing(), + + \endtable + + + \section1 Sharing input context between text widgets + + Any input context can be shared between several text widgets to + reduce resource consumption. In ideal case, each text widgets + should be allocated dedicated input context. But some complex + input contexts require slightly heavy resource such as 100 + kilobytes of memory. It prevents quite many text widgets from + being used concurrently. + + To resolve such problem, we can share an input context. There is + one 'input context holder widget' per text widgets that shares + identical input context. In this model, the holder widget owns the + shared input context. Other text widgets access the input context + via QApplication::locateICHolderWidget(). But the access + convention is transparently hidden into QWidget, so developers are + not required to aware of it. + + What developer should know is only the mapping function + QApplication::locateICHolderWidget(). It accepts a widget as + argument and returns its holder widget. Default implementation + returns the top-level widget of the widget as reasonable + assumption. But some applications should reimplement the function + to fit application specific usability. See + QApplication::locateICHolderWidget() for further information. + + + \section1 Preedit preservation + + As described above, input contexts have wide variety of amount of + the state information in accordance with belonging input + method. It is ranging from 2-3 keystrokes of sequence in + deterministic input methods to hundreds of keystrokes with + semantic text refinement in complex input methods such as ordinary + Japanese input method. The difference requires the different reset + policies in losing input focus. + + The former simple input method case, users will prefer resetting + the context to back to the neutral state when something + happened. Suppose a web browsing. The user scroll the page by + scrollbar after he or she has typed a half of the valid key + sequence into a text widget. In the case, the input context should + be reset in losing focus when he or she has dragged the + scrollbar. He or she will be confused if the input context is + still preserved until focused back to the text widget because he + or she will restart typing with first key of the sequence as a + habitual operation. + + On the other hand, we should choose completely different policy + for the latter complex input method case. Suppose same situation + as above but he or she is using a complex input method. In the + case, he or she will be angry if the input context has been lost + when he or she has dragged the scrollbar because the input context + contained a valuably composed text made up by considerable input + cost. So we should not reset the input context in the case. And + the input context should be preserved until focused back to the + text widget. This behavior is named as 'preedit preservation'. + + The two policies can be switched by calling or not calling reset() + in unsetFocus(). Default implementation of unsetFocus() calls + reset() to fit the simple input methods. The implementation is + expressed as 'preedit preservation is disabled'. + + + \section1 Preedit relocation + + Although the most case of the preedit preservation problem for + complex input methods is resolved as described above, there is a + special case. Suppose the case that matches all of the following + conditions. + + \list + + \i a input focus has been moved from a text widget to another text + widget directly + + \i the input context is shared between the two text widgets + + \i preedit preservation is enabled for the input context + + \endlist + + In the case, there are the following two requirements that + contradicts each other. The input context sharing causes it. + + \list + + \i the input context has to be reset to prepare to input to the + newly focused text widget + + \i the input context has to be preserved until focused back to the + previous text widget + + \endlist + + A intrinsic feature named 'preedit relocation' is available to + compromise the requirements. If the feature is enabled for the + input context, it is simply moved to the new text widget with the + preedit string. The user continues the input on the new text + widget, or relocate it to another text widget. The preedit of + previous text widget is automatically cleared to back to the + neutral state of the widget. + + This strange behavior is just a compromise. As described in + previous section, complex input method user should not be exposed + to the risk losing the input context because it contains valuable + long text made up with considerable input cost. The user will + immediately focus back to the previous text widget to continue the + input in the correct text widget if the preedit relocation + occurred. The feature is mainly existing as safety. + + The feature properly works even if the focus is moved as + following. Input method developers are not required to be aware of + the relocation protocol since QInputContext transparently handles + it. + + a text widget -> a non-text widget -> another text widget + + To enable the preedit relocation feature, the input context class + have to reimplement isPreeditRelocationEnabled() as returns TRUE. + The implementation requires that the preedit preservation is also + enabled since preedit relocation is a special case of the preedit + preservation. If the preedit relocation is disabled, the input + context is simply reset in the relocation case. + + + \section1 Input context instanciation + \section1 Input method switching + + \section1 Text widget implementor's guide + + Add following code fragment into createPopupMenu() to add input + method dependent submenus. + + \code + #ifndef QT_NO_IM + QInputContext *qic = getInputContext(); + if ( qic ) + qic->addMenusTo( popup ); + #endif + \endcode + + \sa QInputContextPlugin, QInputContextFactory, QApplication::locateICHolderWidget(), QApplication::defaultInputMethod() +*/ + + +/*! + Constructs an input context. + + holderWidget is set immediately after this constructor has been + returned on the X11 platform. +*/ +QInputContext::QInputContext( QObject *parent ) + : QObject( parent ) +{ + d = new QInputContextPrivate; +} + + +/*! + Destroys the input context. +*/ +QInputContext::~QInputContext() +{ + delete d; +} + +#if defined(Q_WS_X11) +/*! + \internal + Returns the owner of this input context. Ordinary input methods + should not call this function directly to keep platform + independence and flexible configuration possibility. + + The return value may differ from focusWidget() if the input + context is shared between several text widgets. + + \sa setHolderWidget(), focusWidget() +*/ +QWidget *QInputContext::holderWidget() const +{ + return d->holderWidget; +} + +/*! + \internal + Sets the owner of this input context. Ordinary input methods + must not call this function directly. + + \sa holderWidget() +*/ +void QInputContext::setHolderWidget( QWidget *w ) +{ + d->holderWidget = w; +} + +/*! + \internal + Returns the widget that has an input focus for this input + context. Ordinary input methods should not call this function + directly to keep platform independence and flexible configuration + possibility. + + The return value may differ from holderWidget() if the input + context is shared between several text widgets. + + \sa setFocusWidget(), holderWidget() +*/ +QWidget *QInputContext::focusWidget() const +{ + return d->hasFocus ? d->composingWidget : 0; +} + + +/*! + \internal + Sets the widget that has an input focus for this input + context. Ordinary input methods must not call this function + directly. + + \sa focusWidget() +*/ +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; +} + + +/*! + \internal + This function is called from QWidget to keep input state + consistency. Ordinary input method must not call this function + directly. +*/ +void QInputContext::releaseComposingWidget( QWidget *w ) +{ + if ( d->composingWidget == w ) { + d->composingWidget = 0; + d->hasFocus = FALSE; + } +} +#endif // Q_WS_X11 + +/*! + \internal + This function can be reimplemented in a subclass as returning TRUE + if you want making your input method enable the preedit + relocation. See the description for preedit relocation of + QInputContext. + + /sa QInputContext +*/ +bool QInputContext::isPreeditRelocationEnabled() +{ + return FALSE; +} + +/*! + This function indicates whether IMStart event had been sent to the + text widget. It is ensured that an input context can send IMCompose + or IMEnd event safely if this function returned TRUE. + + The state is automatically being tracked through sendIMEvent(). + + \sa sendIMEvent() +*/ +bool QInputContext::isComposing() const +{ + return d->isComposing; +} + + +/*! + This function can be reimplemented in a subclass to filter input + events. + + Return TRUE if the \a event has been consumed. Otherwise, the + unfiltered \a event will be forwarded to widgets as ordinary + way. Although the input events have accept() and ignore() + methods, leave it untouched. + + \a event is currently restricted to QKeyEvent. But some input + method related events such as QWheelEvent or QTabletEvent may be + added in future. + + The filtering opportunity is always given to the input context as + soon as possible. It has to be taken place before any other key + event consumers such as eventfilters and accelerators because some + input methods require quite various key combination and + sequences. It often conflicts with accelerators and so on, so we + must give the input context the filtering opportunity first to + ensure all input methods work properly regardless of application + design. + + Ordinary input methods require discrete key events to work + properly, so Qt's key compression is always disabled for any input + contexts. + + \sa QKeyEvent, x11FilterEvent() +*/ +bool QInputContext::filterEvent( const QEvent *event ) +{ + return FALSE; +} + + +/*! + \fn void QInputContext::deletionRequested() + + Emit this signal when a fatal error has been caused in the input + context. The input context will be deleted by the owner which is + usually the holder widget. +*/ + +/*! + \fn void QInputContext::imEventGenerated( QObject *receiver, QIMEvent *e ) + + \internal + This signal is emitted when the user has sent a QIMEvent through + sendIMEvent(). Ordinary input methods should not emit this signal + directly. + + \a receiver is a platform dependent destination of the \a e. + + \sa QIMEvent, sendIMEvent(), sendIMEventInternal(), +*/ + +/*! + \internal + Sends a QIMEvent to the client via imEventGenerated() + signal. Ordinary input method should not call this function + directly. + + \sa QIMEvent, QIMComposeEvent, sendIMEvent(), imEventGenerated() +*/ +void QInputContext::sendIMEventInternal( QEvent::Type type, + const QString &text, + int cursorPosition, int selLength ) +{ + QObject *receiver = 0; + QIMEvent *event = 0; + +#if defined(Q_WS_X11) + receiver = d->composingWidget; +#elif defined(Q_WS_QWS) + // just a placeholder +#endif + if ( ! receiver ) + return; + + if ( type == QEvent::IMStart ) { + qDebug( "sending IMStart with %d chars to %p", + text.length(), receiver ); + event = new QIMEvent( type, text, cursorPosition ); + } else if ( type == QEvent::IMEnd ) { + qDebug( "sending IMEnd with %d chars to %p, text=%s", + text.length(), receiver, (const char*)text.local8Bit() ); + event = new QIMEvent( type, text, cursorPosition ); + } else if ( type == QEvent::IMCompose ) { + qDebug( "sending IMCompose to %p with %d chars, cpos=%d, sellen=%d, text=%s", + receiver, text.length(), cursorPosition, selLength, + (const char*)text.local8Bit() ); + event = new QIMComposeEvent( type, text, cursorPosition, selLength ); + } + + if ( event ) + emit imEventGenerated( receiver, event ); +} + + +/*! + Call this function to send QIMEvent to the text widget. This + function constructs a QIMEvent based on the arguments and send it + to the appropriate widget. Ordinary input method should not + reimplement this function. + + \a type is either \c QEvent::IMStart or \c QEvent::IMCompose or \c + QEvent::IMEnd. You have to send a \c QEvent::IMStart to start + composing, then send several \c QEvent::IMCompose to update the + preedit of the widget, and finalize the composition with sending + \c QEvent::IMEnd. + + \c QEvent::IMStart should always be sent without arguments as: + \code + sendIMEvent( QEvent::IMStart ) + \endcode + + And \c QEvent::IMCompose can be sent without cursor: + \code + sendIMEvent( QEvent::IMCompose, QString( "a text" ) ) + \endcode + + Or optionally with cursor with \a cursorPosition: + \code + sendIMEvent( QEvent::IMCompose, QString( "a text with cursor" ), 12 ) + \endcode + Note that \a cursorPosition also specifies microfocus position. + + Or optionally with selection text: + \code + sendIMEvent( QEvent::IMCompose, QString( "a text with selection" ), 12, 9 ) + \endcode + \a cursorPosition and \a selLength must be within the \a text. The + \a cursorPosition also specifies microfocus position in the case: + + \c QEvent::IMEnd can be sent without arguments to terminate the + composition with null string: + \code + sendIMEvent( QEvent::IMEnd ) + \endcode + + Or optionally accepts \a text to commit a string: + \code + sendIMEvent( QEvent::IMEnd, QString( "a text" ) ) + \endcode + + \sa QIMEvent, QIMComposeEvent, setMicroFocus() +*/ +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 ); + } +} + + +/*! + This function can be reimplemented in a subclass to detect + that the input context has been focused on. + + The input context will receive input events through + x11FilterEvent() and filterEvent() after setFocus() until + unsetFocus() has been called. + + an input context is ensured that setFocus() is called exactly once + until unsetFocus() has been called even if preedit relocation has + occurred. This means that an input focus will survive between + several widgets that sharing the input context. + + On the X11 platform, focusWidget is already set before this + function has been called. + + \sa unsetFocus() +*/ +void QInputContext::setFocus() +{ +} + + +/*! + This function can be reimplemented in a subclass to detect + that the input context has lost the focus. + + an input context is ensured that unsetFocus() is not called during + preedit relocation. This means that an input focus will survive + between several widgets that sharing the input context. + + Default implementation that calls reset() is sufficient for simple + input methods. You can override this function to alter the + behavior. For example, most Japanese input contexts should not be + reset on losing focus. The context sometimes contains a whole + paragraph and has minutes of lifetime different to ephemeral one + in other languages. The piled input context should be survived + until focused again since Japanese user naturally expects so. + + On the X11 platform, focusWidget is valid until this function has + been returned. + + \sa setFocus() +*/ +void QInputContext::unsetFocus() +{ + reset(); +} + + +/*! + This function can be implemented in a subclass to handle + microfocus changes. + + 'microfocus' stands for the input method focus point in the + preedit (XIM "spot" point) for complex language input handling. It + can be used to place auxiliary GUI widgets such as candidate + selection window. + + \a x, \a y, \a w and \a h represents the position and size of the + cursor in the preedit string. \a f is the font on the location of + the cursor. +*/ +void QInputContext::setMicroFocus( int x, int y, int w, int h, QFont *f ) +{ +} + + +/*! + This function can be reimplemented in a subclass to handle mouse + presses/releases/doubleclicks/moves within the preedit text. You + can use the function to implement mouse-oriented user interface + such as text selection or popup menu for candidate selection. + + The parameter \a x is the offset within the string that was sent + with the IMCompose event. The alteration boundary of \a x is + ensured as character boundary of preedit string accurately. + + \a type is either \c QEvent::MouseButtonPress or \c + QEvent::MouseButtonRelease or \c QEvent::MouseButtonDblClick or \c + QEvent::MouseButtonMove. Refer \a button and \a state to determine + what operation has performed. + + 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(); +} + + +/*! + This function can be reimplemented in a subclass to reset the + state of the input method. + + This function is called by several widgets to reset input + state. For example, a text widget call this function before + inserting a text to make widget ready to accept a text. + + Default implementation is sufficient for simple input method. You + can override this function to reset external input method engines + in complex input method. In the case, call QInputContext::reset() + to ensure proper termination of inputting. + + You must not send any QIMEvent except empty IMEnd event using + QInputContext::reset() at reimplemented reset(). It will break + input state consistency. +*/ +void QInputContext::reset() +{ + if ( isComposing() ) + sendIMEvent( QEvent::IMEnd ); +} + + +/*! + This function must be implemented in any subclasses to return the + identifier name of the input method. + + Return value is the name to identify and specify input methods for + the input method switching mechanism and so on. The name has to be + consistent with QInputContextPlugin::keys(). The name has to + consist of ASCII characters only. + + There are two different names with different responsibility in the + input method domain. This function returns one of them. Another + name is called 'display name' that stands for the name for + endusers appeared in a menu and so on. + + \sa QInputContextPlugin::keys(), QInputContextPlugin::displayName() +*/ +QString QInputContext::identifierName() +{ + return ""; +} + + +/*! + This function must be implemented in any subclasses to return a + language code (e.g. "zh_CN", "zh_TW", "zh_HK", "ja", "ko", ...) + of the input context. If the input context can handle multiple + languages, return the currently used one. The name has to be + consistent with QInputContextPlugin::language(). + + This information will be used by language tagging feature in + QIMEvent. It is required to distinguish unified han characters + correctly. It enables proper font and character code + handling. Suppose CJK-awared multilingual web browser + (that automatically modifies fonts in CJK-mixed text) and XML editor + (that automatically inserts lang attr). + + \sa QInputContextPlugin::language() +*/ +QString QInputContext::language() +{ + return ""; +} + + +#if (QT_VERSION-0 >= 0x040000) +/*! + This is a preliminary interface for Qt4 + */ +QList<QAction *> QInputContext::actions() +{ +} +#else +/*! + This function can be reimplemented in a subclass to provide input + method dependent popup menus. Return 0 if the menus are + unnecessary. + + Ownership of the object and children are transferred to the + caller, and the result must not be called + setAutoDelete(). QInputContextMenu::title is used for label text + of the popup menu as submenu. + + \sa addMenusTo() +*/ +QPtrList<QInputContextMenu> *QInputContext::menus() +{ + return 0; +} +#endif + +/*! + Appends input method dependent submenus into \a popup. A separator + is also inserted into \a popup if \a action is InsertSeparator. + + This is an utility function only for convenience in limited + situation. This function is used by input context owner such as + text widgets to add the submenus to its own context menu. If you + want to insert the submenus in more flexible way, use + QInputContext::menus() manually. \a popup is not restricted to + context menu of a text widget. For example, the owner may be a + input method menu of Qtopia taskbar in Qt/Embedded platform. + + \sa menus(), QInputContextMenu::Action +*/ +void QInputContext::addMenusTo( QPopupMenu *popup, QInputContextMenu::Action action ) +{ + if ( ! popup ) + return; + + QPtrList<QInputContextMenu> *imMenus = menus(); + if ( imMenus ) { + if ( action == QInputContextMenu::InsertSeparator ) + popup->insertSeparator(); + for ( QPtrList<QInputContextMenu>::Iterator it = imMenus->begin(); + it != imMenus->end(); + ++it ) { + QInputContextMenu *imMenu = *it; + popup->insertItem( imMenu->title, imMenu->popup ); + } + imMenus->clear(); + delete imMenus; + } +} + +#endif //Q_NO_IM |