1 /*
2 
3   Copyright (c) 2004-2005 Kazuki Ohta <mover@hct.zaq.ne.jp>
4   Copyright (c) 2005-2013 uim Project https://github.com/uim/uim
5 
6   All rights reserved.
7 
8   Redistribution and use in source and binary forms, with or without
9   modification, are permitted provided that the following conditions
10   are met:
11 
12   1. Redistributions of source code must retain the above copyright
13      notice, this list of conditions and the following disclaimer.
14   2. Redistributions in binary form must reproduce the above copyright
15      notice, this list of conditions and the following disclaimer in the
16      documentation and/or other materials provided with the distribution.
17   3. Neither the name of authors nor the names of its contributors
18      may be used to endorse or promote products derived from this software
19      without specific prior written permission.
20 
21   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
22   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24   ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
25   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31   SUCH DAMAGE.
32 
33 */
34 #include "quiminputcontext.h"
35 
36 #include <cctype>
37 #include <cstring>
38 
39 #include <QtCore/QPoint>
40 #include <QtGui/QApplication>
41 #include <QtGui/QInputMethodEvent>
42 #include <QtGui/QLabel>
43 #include <QtGui/QTextCharFormat>
44 
45 #include <uim/uim-helper.h>
46 #include <uim/uim-im-switcher.h>
47 #include <uim/uim-scm.h>
48 
49 #include "candidatewindowproxy.h"
50 #include "caretstateindicator.h"
51 #include "plugin.h"
52 #include "qhelpermanager.h"
53 #include "qtextutil.h"
54 #include "quiminfomanager.h"
55 #include "quiminputcontext_compose.h"
56 
57 #if UIM_QT_USE_JAPANESE_KANA_KEYBOARD_HACK
58 #include <X11/Xlib.h>
59 
60 #include "uim/uim-x-util.h"
61 #endif
62 
63 static const char DEFAULT_SEPARATOR_STR[] = "|";
64 
65 QUimInputContext *focusedInputContext = 0;
66 bool disableFocusedContext = false;
67 
68 QList<QUimInputContext*> contextList;
69 
70 QUimHelperManager * QUimInputContext::m_HelperManager = 0;
71 #ifdef Q_WS_X11
72 DefTree *QUimInputContext::mTreeTop = 0;
73 #endif
74 
75 static int unicodeToUKey(ushort c);
76 
77 // I think that current index-based query API of uim for language and
78 // input method name is useless and should be redesigned. I will
79 // suggest the change in future. -- YamaKen 2004-07-28
80 
QUimInputContext(const char * imname)81 QUimInputContext::QUimInputContext( const char *imname )
82         : candwinIsActive( false ), m_isComposing( false ), m_uc( 0 )
83 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
84         , focusedWidget( 0 )
85 #endif
86 {
87 #ifdef ENABLE_DEBUG
88     qDebug( "QUimInputContext()" );
89 #endif
90 
91     contextList.append( this );
92 
93     // must be initialized before createUimContext() call
94     if ( !m_HelperManager )
95         m_HelperManager = new QUimHelperManager;
96 
97     if ( imname )
98         m_uc = createUimContext( imname );
99 
100     createCandidateWindow();
101 
102 #ifdef Q_WS_X11
103     if ( !mTreeTop )
104         create_compose_tree();
105     mCompose = new Compose( mTreeTop, this );
106 #endif
107     mTextUtil = new QUimTextUtil( this );
108 
109     // read configuration
110     updatePosition();
111 
112     m_indicator = new CaretStateIndicator;
113 }
114 
~QUimInputContext()115 QUimInputContext::~QUimInputContext()
116 {
117 #ifdef ENABLE_DEBUG
118     qDebug( "~QUimInputContext()" );
119 #endif
120 
121     contextList.removeAll( this );
122 
123     if ( m_uc )
124         uim_release_context( m_uc );
125     delete proxy;
126 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
127     foreach ( const uim_context uc, m_ucHash )
128         if ( uc )
129             uim_release_context( uc );
130     foreach ( const CandidateWindowProxy* window, proxyHash )
131         delete window;
132 #endif
133 
134     if ( this == focusedInputContext )
135     {
136         focusedInputContext = 0;
137         disableFocusedContext = true;
138     }
139 
140 #ifdef Q_WS_X11
141     delete mCompose;
142 #endif
143 }
144 
createUimContext(const char * imname)145 uim_context QUimInputContext::createUimContext( const char *imname )
146 {
147     uim_context uc = uim_create_context( this, "UTF-8",
148                                          0, imname,
149                                          0,
150                                          QUimInputContext::commit_cb );
151 
152     m_HelperManager->checkHelperConnection(uc);
153     /**/
154 
155     uim_set_preedit_cb( uc, QUimInputContext::clear_cb,
156                         QUimInputContext::pushback_cb,
157                         QUimInputContext::update_cb );
158 
159     uim_set_candidate_selector_cb( uc,
160                                    QUimInputContext::cand_activate_cb,
161                                    QUimInputContext::cand_select_cb,
162                                    QUimInputContext::cand_shift_page_cb,
163                                    QUimInputContext::cand_deactivate_cb );
164 
165 
166     uim_set_prop_list_update_cb( uc, QUimHelperManager::update_prop_list_cb );
167     uim_set_prop_label_update_cb( uc, QUimHelperManager::update_prop_label_cb );
168 
169     uim_set_im_switch_request_cb( uc,
170                                   QUimInputContext::switch_app_global_im_cb,
171                                   QUimInputContext::switch_system_global_im_cb);
172 
173     uim_set_text_acquisition_cb( uc,
174                                  QUimTextUtil::acquire_text_cb,
175                                  QUimTextUtil::delete_text_cb);
176 
177 #if UIM_QT_USE_DELAY
178     uim_set_delay_candidate_selector_cb( uc,
179         QUimInputContext::cand_activate_with_delay_cb );
180 #endif /* !UIM_QT_USE_DELAY */
181 
182     uim_prop_list_update( uc );
183 
184     return uc;
185 }
186 
createCandidateWindow()187 void QUimInputContext::createCandidateWindow()
188 {
189     proxy = new CandidateWindowProxy;
190     proxy->setQUimInputContext( this );
191     proxy->hide();
192 }
193 
194 #ifdef Q_WS_X11
x11FilterEvent(QWidget * keywidget,XEvent * event)195 bool QUimInputContext::x11FilterEvent( QWidget *keywidget, XEvent *event )
196 {
197     Q_UNUSED( keywidget )
198 
199 #if UIM_QT_USE_JAPANESE_KANA_KEYBOARD_HACK
200     return uim_x_kana_input_hack_filter_event( m_uc, event );
201 #else
202     Q_UNUSED( event )
203     return false;
204 #endif
205 }
206 #endif // Q_WS_X11
207 
filterEvent(const QEvent * event)208 bool QUimInputContext::filterEvent( const QEvent *event )
209 {
210 #ifdef ENABLE_DEBUG
211     qDebug( "filterEvent" );
212 #endif
213 
214     int type = event->type();
215 
216     if ( type != QEvent::KeyPress &&
217             type != QEvent::KeyRelease )
218         return false;
219 
220     const QKeyEvent *keyevent = static_cast<const QKeyEvent *>( event );
221     int qkey = keyevent->key();
222 
223     int modifier = 0;
224     if ( keyevent->modifiers() & Qt::ShiftModifier )
225         modifier |= UMod_Shift;
226     if ( keyevent->modifiers() & Qt::ControlModifier )
227         modifier |= UMod_Control;
228     if ( keyevent->modifiers() & Qt::AltModifier )
229         modifier |= UMod_Alt;
230 #if defined(Q_WS_X11)
231     if ( keyevent->modifiers() & Qt::MetaModifier )
232         modifier |= UMod_Meta;
233 #endif
234 
235     int key = 0;
236     if ( isascii( qkey ) && isprint( qkey ) )
237     {
238         int ascii = keyevent->text()[ 0 ].toAscii();
239         if ( isalpha( ascii ) )
240         {
241             key = ascii;  // uim needs lower/upper encoded key
242         }
243         else
244         {
245             if ( keyevent->modifiers() & Qt::ControlModifier &&
246                  ( ascii >= 0x01 && ascii <= 0x1a ) )
247                 if ( keyevent->modifiers() & Qt::ShiftModifier )
248                     key = ascii + 0x40;
249                 else
250                     key = ascii + 0x60;
251             else
252                 key = qkey;
253         }
254     }
255     else if ( qkey >= Qt::Key_nobreakspace && qkey <= Qt::Key_ydiaeresis )
256 	key = qkey;
257     else if ( qkey == Qt::Key_unknown )
258     {
259         QString text = keyevent->text();
260         if ( !text.isNull() )
261         {
262             QChar s = text.at(0);
263             key = unicodeToUKey ( s.unicode() );
264         }
265         else
266         {
267             key = UKey_Other;
268         }
269     }
270     else
271     {
272         if ( qkey >= Qt::Key_F1 && qkey <= Qt::Key_F35 )
273         {
274             key = qkey - Qt::Key_F1 + UKey_F1;
275         }
276         else if ( qkey >= Qt::Key_Dead_Grave && qkey <= Qt::Key_Dead_Horn )
277         {
278             key = qkey - Qt::Key_Dead_Grave + UKey_Dead_Grave;
279         }
280         else if ( qkey >= Qt::Key_Kanji && qkey <= Qt::Key_Eisu_toggle )
281         {
282             key = qkey - Qt::Key_Kanji + UKey_Kanji;
283         }
284         else if ( qkey >= Qt::Key_Hangul && qkey <= Qt::Key_Hangul_Special )
285         {
286             key = qkey - Qt::Key_Hangul + UKey_Hangul;
287         }
288         else
289         {
290             switch ( qkey )
291             {
292             case Qt::Key_Tab: key = UKey_Tab; break;
293             case Qt::Key_Backspace: key = UKey_Backspace; break;
294             case Qt::Key_Escape: key = UKey_Escape; break;
295             case Qt::Key_Delete: key = UKey_Delete; break;
296             case Qt::Key_Return: key = UKey_Return; break;
297             case Qt::Key_Left: key = UKey_Left; break;
298             case Qt::Key_Up: key = UKey_Up; break;
299             case Qt::Key_Right: key = UKey_Right; break;
300             case Qt::Key_Down: key = UKey_Down; break;
301             case Qt::Key_PageUp: key = UKey_Prior; break;
302             case Qt::Key_PageDown: key = UKey_Next; break;
303             case Qt::Key_Home: key = UKey_Home; break;
304             case Qt::Key_End: key = UKey_End; break;
305             case Qt::Key_Multi_key: key = UKey_Multi_key; break;
306             case Qt::Key_Mode_switch: key = UKey_Mode_switch; break;
307             case Qt::Key_Codeinput: key = UKey_Codeinput; break;
308             case Qt::Key_SingleCandidate: key = UKey_SingleCandidate; break;
309             case Qt::Key_MultipleCandidate: key = UKey_MultipleCandidate; break;
310             case Qt::Key_PreviousCandidate: key = UKey_PreviousCandidate; break;
311             // Qt4 seems to add its own modifier even the event is
312             // KeyPress, which differs from ordinary model.  So remove
313             // them for uim.
314             case Qt::Key_Shift: key = UKey_Shift_key;
315                 if ( type == QEvent::KeyPress )
316                     modifier &= ~UMod_Shift;
317                 break;
318             case Qt::Key_Control: key = UKey_Control_key;
319                 if ( type == QEvent::KeyPress )
320                     modifier &= ~UMod_Control;
321                 break;
322             case Qt::Key_Alt: key = UKey_Alt_key;
323                 if ( type == QEvent::KeyPress )
324                     modifier &= ~UMod_Alt;
325                 break;
326             case Qt::Key_Meta: key = UKey_Meta_key;
327 #ifdef Q_WS_X11
328                 if ( type == QEvent::KeyPress )
329                     modifier &= ~UMod_Meta;
330 #endif
331                 break;
332             case Qt::Key_CapsLock: key = UKey_Caps_Lock; break;
333             case Qt::Key_NumLock: key = UKey_Num_Lock; break;
334             case Qt::Key_ScrollLock: key = UKey_Scroll_Lock; break;
335             default: key = UKey_Other;
336             }
337         }
338     }
339 
340     int notFiltered;
341     if ( type == QEvent::KeyPress )
342     {
343         notFiltered = uim_press_key( m_uc, key, modifier );
344 #ifdef Q_WS_X11
345         if ( notFiltered )
346             return mCompose->handle_qkey( keyevent );
347 #else
348         if ( notFiltered )
349             return false;
350 #endif
351     }
352     else if ( type == QEvent::KeyRelease )
353     {
354         notFiltered = uim_release_key( m_uc, key, modifier );
355 #ifdef Q_WS_X11
356         if ( notFiltered )
357             return mCompose->handle_qkey( keyevent );
358 #else
359         if ( notFiltered )
360             return false;
361 #endif
362     }
363 
364     return true;
365 }
366 
setFocusWidget(QWidget * w)367 void QUimInputContext::setFocusWidget( QWidget *w )
368 {
369 #ifdef ENABLE_DEBUG
370     qDebug( "QUimInputContext::setFocusWidget() w = %p", w );
371 #endif
372 
373     QInputContext::setFocusWidget( w );
374 
375     if ( w )
376         setFocus();
377     else
378         unsetFocus();
379 }
380 
381 // Qt4 does not have QInputContext::setFocus()
setFocus()382 void QUimInputContext::setFocus()
383 {
384 #ifdef ENABLE_DEBUG
385     qDebug( "QUimInputContext: %p->setFocus(), focusWidget()=%p",
386             this, QApplication::focusWidget() );
387 #endif
388 
389     focusedInputContext = this;
390     disableFocusedContext = false;
391 
392 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
393     focusedWidget = QApplication::focusWidget();
394     if ( isPreeditPreservationEnabled()
395             && m_ucHash.contains( focusedWidget ) )
396         restorePreedit();
397     else
398 #endif
399     if ( candwinIsActive )
400         proxy->popup();
401 
402     m_HelperManager->checkHelperConnection(m_uc);
403 
404     uim_helper_client_focus_in( m_uc );
405     uim_prop_list_update( m_uc );
406 
407     uim_focus_in_context( m_uc );
408 }
409 
410 // Qt4 does not have QInputContext::unsetFocus()
unsetFocus()411 void QUimInputContext::unsetFocus()
412 {
413 #ifdef ENABLE_DEBUG
414     qDebug( "QUimInputContext: %p->unsetFocus(), focusWidget()=%p",
415             this, QApplication::focusWidget() );
416 #endif
417 
418     uim_focus_out_context( m_uc );
419 
420     proxy->hide();
421     m_indicator->hide();
422 
423     m_HelperManager->checkHelperConnection(m_uc);
424 
425     uim_helper_client_focus_out( m_uc );
426 }
427 
focusedIC()428 QUimInputContext * QUimInputContext::focusedIC()
429 {
430     return focusedInputContext;
431 }
432 
mouseHandler(int x,QMouseEvent * e)433 void QUimInputContext::mouseHandler( int x, QMouseEvent *e )
434 {
435     switch ( e->type() )
436     {
437     case QEvent::MouseButtonPress:
438     case QEvent::MouseButtonRelease:
439     case QEvent::MouseButtonDblClick:
440     case QEvent::MouseMove:
441 #ifdef ENABLE_DEBUG
442         qDebug( "QUimInputContext::mouseHandler: "
443                 "x=%d, type=%d, button=%d ", x, e->type(), e->button() );
444 #else
445         Q_UNUSED( x )
446 #endif
447         break;
448     default:
449         break;
450     }
451 }
452 
reset()453 void QUimInputContext::reset()
454 {
455 #ifdef ENABLE_DEBUG
456     qDebug( "QUimInputContext::reset()" );
457 #endif
458 
459     candwinIsActive = false;
460 
461 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
462     // Japanese input context sometimes contains a whole paragraph
463     // and has minutes of lifetime different to ephemeral one
464     // in other languages. The input context should be survived
465     // until focused again.
466     if ( isPreeditPreservationEnabled()
467             && !m_ucHash.contains( focusedWidget ) ) {
468         psegs.isEmpty() ? proxy->hide() : savePreedit();
469         return;
470     }
471 #endif
472     proxy->hide();
473     uim_reset_context( m_uc );
474 #ifdef Q_WS_X11
475     mCompose->reset();
476 #endif
477     clearPreedit();
478     updatePreedit();
479 }
480 
update()481 void QUimInputContext::update()
482 {
483     QWidget *w = QApplication::focusWidget();
484 
485 #ifdef ENABLE_DEBUG
486     qDebug( "QUimInputContext::update() w = %p", w );
487 #endif
488 
489     if ( w ) {
490         QRect mf = w->inputMethodQuery( Qt::ImMicroFocus ).toRect();
491         QPoint p = w->mapToGlobal( mf.topLeft() );
492         proxy->layoutWindow( p.x(), p.y(), mf.height() );
493         m_indicator->move( w->mapToGlobal( mf.bottomLeft() )
494             + QPoint( 0, CaretStateIndicator::SPACING ) );
495     }
496 }
497 
identifierName()498 QString QUimInputContext::identifierName()
499 {
500     return QString( "uim" );
501 }
502 
language()503 QString QUimInputContext::language()
504 {
505     const QUimInfoManager *manager
506         = UimInputContextPlugin::getQUimInfoManager();
507     const QString name = QString::fromUtf8( uim_get_current_im_name( m_uc ) );
508     return manager->imLang( name );
509 }
510 
511 // callbacks for uim
commit_cb(void * ptr,const char * str)512 void QUimInputContext::commit_cb( void *ptr, const char *str )
513 {
514     QString qs = QString::fromUtf8( str );
515 #ifdef ENABLE_DEBUG
516     qDebug( "commit_cb : str = |%s|", qs.toLocal8Bit().data() );
517 #endif
518 
519     QUimInputContext *ic = static_cast<QUimInputContext *>( ptr );
520     ic->commitString( qs );
521 }
522 
clear_cb(void * ptr)523 void QUimInputContext::clear_cb( void *ptr )
524 {
525 #ifdef ENABLE_DEBUG
526     qDebug( "clear_cb" );
527 #endif
528 
529     QUimInputContext* ic = static_cast<QUimInputContext*>( ptr );
530     ic->clearPreedit();
531 }
532 
pushback_cb(void * ptr,int attr,const char * str)533 void QUimInputContext::pushback_cb( void *ptr, int attr, const char *str )
534 {
535     QString qs = QString::fromUtf8( str );
536 #ifdef ENABLE_DEBUG
537     qDebug( "pushback_cb :  str = |%s|", qs.toLocal8Bit().data() );
538 #endif
539 
540     if ( !str )
541         return ;
542     // Reject invalid empty string. UPreeditAttr_Cursor or
543     // UPreeditAttr_Separator with empty string is *valid* and
544     // required to work properly.
545     if ( !strcmp( str, "" ) && !( attr & ( UPreeditAttr_Cursor | UPreeditAttr_Separator ) ) )
546         return ;
547 
548     QUimInputContext* ic = static_cast<QUimInputContext*>( ptr );
549     ic->pushbackPreeditString( attr, qs );
550 }
551 
update_cb(void * ptr)552 void QUimInputContext::update_cb( void *ptr )
553 {
554 #ifdef ENABLE_DEBUG
555     qDebug( "update_cb" );
556 #endif
557 
558     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
559     ic->updatePreedit();
560 }
561 
cand_activate_cb(void * ptr,int nr,int displayLimit)562 void QUimInputContext::cand_activate_cb( void *ptr, int nr, int displayLimit )
563 {
564 #ifdef ENABLE_DEBUG
565     qDebug( "cand_activate_cb" );
566 #endif
567 
568     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
569     ic->proxy->candidateActivate( nr, displayLimit );
570 }
571 
cand_select_cb(void * ptr,int index)572 void QUimInputContext::cand_select_cb( void *ptr, int index )
573 {
574 #ifdef ENABLE_DEBUG
575     qDebug( "cand_select_cb" );
576 #endif
577 
578     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
579     ic->proxy->candidateSelect( index );
580 }
581 
cand_shift_page_cb(void * ptr,int forward)582 void QUimInputContext::cand_shift_page_cb( void *ptr, int forward )
583 {
584 #ifdef ENABLE_DEBUG
585     qDebug( "cand_shift_page_cb" );
586 #endif
587 
588     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
589     ic->proxy->candidateShiftPage( forward );
590 }
591 
cand_deactivate_cb(void * ptr)592 void QUimInputContext::cand_deactivate_cb( void *ptr )
593 {
594 #ifdef ENABLE_DEBUG
595     qDebug( "cand_deactivate_cb" );
596 #endif
597 
598     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
599     ic->proxy->deactivateCandwin();
600     ic->candwinIsActive = false;
601 }
602 
switch_app_global_im_cb(void * ptr,const char * name)603 void QUimInputContext::switch_app_global_im_cb( void *ptr, const char *name )
604 {
605     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
606     ic->switch_app_global_im( name );
607 }
608 
switch_system_global_im_cb(void * ptr,const char * name)609 void QUimInputContext::switch_system_global_im_cb( void *ptr, const char *name )
610 {
611     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
612     ic->switch_system_global_im( name );
613 }
614 
615 #if UIM_QT_USE_DELAY
cand_activate_with_delay_cb(void * ptr,int delay)616 void QUimInputContext::cand_activate_with_delay_cb( void *ptr, int delay )
617 {
618     QUimInputContext *ic = static_cast<QUimInputContext*>( ptr );
619     ic->proxy->candidateActivateWithDelay( delay );
620 }
621 #endif /* !UIM_QT_USE_DELAY */
622 
commitString(const QString & str)623 void QUimInputContext::commitString( const QString& str )
624 {
625     QInputMethodEvent e;
626     e.setCommitString( str );
627     sendEvent( e );
628 
629     m_isComposing = false;
630 }
631 
clearPreedit()632 void QUimInputContext::clearPreedit()
633 {
634     psegs.clear();
635 }
636 
pushbackPreeditString(int attr,const QString & str)637 void QUimInputContext::pushbackPreeditString( int attr, const QString& str )
638 {
639     PreeditSegment ps( attr, str );
640     psegs.append( ps );
641 }
642 
updatePreedit()643 void QUimInputContext::updatePreedit()
644 {
645     QString newString = getPreeditString();
646 
647     if ( !isComposing() ) {
648         if ( newString.isEmpty() )
649             return;
650 
651         // Start conversion
652         m_isComposing = true;
653     }
654 
655     if ( !newString.isEmpty() ) {
656         QInputMethodEvent e( newString, getPreeditAttrs() );
657         sendEvent( e );
658         // Qt4.3.1 does not call back update() here
659         update();
660     } else {
661         // Complete conversion implicitly since the preedit is empty
662         commitString( "" );
663     }
664 }
665 
666 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
savePreedit()667 void QUimInputContext::savePreedit()
668 {
669     m_ucHash.insert( focusedWidget, m_uc );
670     psegsHash.insert( focusedWidget, psegs );
671     proxyHash.insert( focusedWidget, proxy );
672     visibleHash.insert( focusedWidget, proxy->isVisible() );
673     proxy->hide();
674 
675     const char *imname = uim_get_current_im_name( m_uc );
676     if ( imname )
677         m_uc = createUimContext( imname );
678     psegs.clear();
679     createCandidateWindow();
680 }
681 
restorePreedit()682 void QUimInputContext::restorePreedit()
683 {
684     CandidateWindowProxy *window = proxyHash.take( focusedWidget );
685     // if window is 0, updateStyle() was called.
686     if ( !window ) {
687         psegs = psegsHash.take( focusedWidget );
688         QString preedit;
689         while ( !psegs.isEmpty() ) {
690             preedit += psegs.takeFirst().str;
691         }
692         commitString( preedit );
693 
694         uim_context uc = m_ucHash.take( focusedWidget );
695         if ( uc )
696             uim_release_context( uc );
697         visibleHash.remove( focusedWidget );
698         return;
699     }
700     if ( m_uc )
701         uim_release_context( m_uc );
702     delete proxy;
703     m_uc = m_ucHash.take( focusedWidget );
704     psegs = psegsHash.take( focusedWidget );
705     proxy = window;
706     if ( visibleHash.take( focusedWidget ) )
707         proxy->popup();
708 }
709 #endif
710 
saveContext()711 void QUimInputContext::saveContext()
712 {
713     // just send QInputMethodEvent and keep preedit string
714     if ( isComposing() )
715         commitString( "" );
716 }
717 
restoreContext()718 void QUimInputContext::restoreContext()
719 {
720     updatePreedit();
721 }
722 
isPreeditPreservationEnabled()723 bool QUimInputContext::isPreeditPreservationEnabled()
724 {
725     return ( language() == "ja" );
726 }
727 
getPreeditString()728 QString QUimInputContext::getPreeditString()
729 {
730     QString pstr;
731 
732     QList<PreeditSegment>::ConstIterator seg = psegs.begin();
733     const QList<PreeditSegment>::ConstIterator end = psegs.end();
734     for ( ; seg != end; ++seg )
735     {
736         if ( ( *seg ).attr & UPreeditAttr_Separator && ( *seg ).str.isEmpty() )
737         {
738             pstr += DEFAULT_SEPARATOR_STR;
739         }
740         else
741         {
742             pstr += ( *seg ).str;
743         }
744     }
745 
746     return pstr;
747 }
748 
getPreeditCursorPosition()749 int QUimInputContext::getPreeditCursorPosition()
750 {
751     if ( proxy->isAlwaysLeftPosition() )
752         return 0;
753 
754     int cursorPos = 0;
755     QList<PreeditSegment>::ConstIterator seg = psegs.begin();
756     const QList<PreeditSegment>::ConstIterator end = psegs.end();
757     for ( ; seg != end; ++seg )
758     {
759         if ( ( *seg ).attr & UPreeditAttr_Cursor )
760         {
761             return cursorPos;
762         }
763         else if ( ( *seg ).attr & UPreeditAttr_Separator
764                   && ( *seg ).str.isEmpty() )
765         {
766             cursorPos += QString( DEFAULT_SEPARATOR_STR ).length();
767         }
768         else
769         {
770             cursorPos += ( *seg ).str.length();
771         }
772     }
773 
774     return cursorPos;
775 }
776 
getPreeditSelectionLength()777 int QUimInputContext::getPreeditSelectionLength()
778 {
779     int selectionLength = 0;
780 
781     QList<PreeditSegment>::ConstIterator seg = psegs.begin();
782     const QList<PreeditSegment>::ConstIterator end = psegs.end();
783     for ( ; seg != end; ++seg )
784     {
785         // In converting state, uim encodes UPreeditAttr_Cursor into
786         // selected segment rather than separated empty cursor
787         // segment. So we can get selection length by length of this
788         // 'selected segment'. Don't use visual attributes such as
789         // UPreeditAttr_Underline or UPreeditAttr_Reverse to detect
790         // logical selection length. They are sometimes disabled by
791         // user preference.
792         if ( ( *seg ).attr & UPreeditAttr_Cursor )
793         {
794             selectionLength = ( *seg ).str.length();
795             return selectionLength;
796         }
797     }
798 
799     return 0;
800 }
801 
getUserDefinedColor(const char * symbol)802 static QColor getUserDefinedColor( const char *symbol )
803 {
804     char *literal = uim_scm_symbol_value_str( symbol );
805     QColor color( QString::fromAscii( literal ) );
806     free( literal );
807     return color;
808 }
809 
getPreeditAttrs()810 QList<QInputMethodEvent::Attribute> QUimInputContext::getPreeditAttrs()
811 {
812     const int HIDE_CARET = 0;
813     const int SHOW_CARET = 1;
814     const int DUMMY = 0;
815     QList<QInputMethodEvent::Attribute> attrs;
816 
817     QList<PreeditSegment>::ConstIterator seg = psegs.begin();
818     const QList<PreeditSegment>::ConstIterator end = psegs.end();
819     int segPos = 0;
820     for ( ; seg != end; ++seg ) {
821         int uimAttr = ( *seg ).attr;
822         int segStrLen = ( *seg ).str.length();
823         QTextCharFormat segFmt;
824 
825         if ( uimAttr & UPreeditAttr_Cursor ) {
826             // Transparent cursor is required to set microfocus even
827             // if the caret is invisible to user.
828             //
829             // FIXME: Segment string may be outframed if the cursor is
830             // located near the right frame.
831             int visibility = ( segStrLen ) ? HIDE_CARET : SHOW_CARET;
832             QInputMethodEvent::Attribute cursor( QInputMethodEvent::Cursor,
833                                                  segPos, visibility, DUMMY );
834             attrs << cursor;
835         } else if ( uimAttr & UPreeditAttr_Separator ) {
836             if ( !segStrLen )
837                 segStrLen = QString( DEFAULT_SEPARATOR_STR ).length();
838             if ( !( uimAttr & UPreeditAttr_Reverse ) ) {
839                 QColor color = getUserDefinedColor( "separator-foreground" );
840                 if ( color.isValid() )
841                     segFmt.setForeground( color );
842                 color = getUserDefinedColor( "separator-background" );
843                 if ( color.isValid() )
844                     segFmt.setBackground( color );
845 
846             }
847         }
848 
849         if ( segStrLen ) {
850             if ( uimAttr & UPreeditAttr_Reverse ) {
851 #if 0
852                 // FIXME: fmt.foreground() is white (expecting black)
853                 QTextFormat fmt = standardFormat( PreeditFormat );
854                 segFmt.setForeground( fmt.background() );
855                 segFmt.setBackground( fmt.foreground() );
856 #else
857                 // FIXME: Retrieve customized colors from the text widget
858                 // foreground symbol
859                 const char *fgSymbol;
860                 // background symbol
861                 const char *bgSymbol;
862                 if ( uimAttr & UPreeditAttr_Separator ) {
863                     fgSymbol = "reversed-separator-foreground";
864                     bgSymbol = "reversed-separator-background";
865                 } else {
866                     fgSymbol = "reversed-preedit-foreground";
867                     bgSymbol = "reversed-preedit-background";
868                 }
869                 QColor color = getUserDefinedColor( fgSymbol );
870                 segFmt.setForeground( color.isValid() ? color : Qt::white );
871                 color = getUserDefinedColor( bgSymbol );
872                 segFmt.setBackground( color.isValid() ? color : Qt::black );
873 #endif
874             }
875             if ( uimAttr & UPreeditAttr_UnderLine ) {
876                 segFmt.setFontUnderline( true );
877             }
878             QInputMethodEvent::Attribute segAttr( QInputMethodEvent::TextFormat,
879                                                   segPos, segStrLen, segFmt );
880             attrs << segAttr;
881             segPos += segStrLen;
882         }
883     }
884 
885     return attrs;
886 }
887 
switch_app_global_im(const char * name)888 void QUimInputContext::switch_app_global_im( const char *name )
889 {
890     QList<QUimInputContext*>::iterator it;
891     QString im_name_sym( "'" );
892 
893     im_name_sym += name;
894 
895     for ( it = contextList.begin(); it != contextList.end(); ++it )
896     {
897         if ( ( *it ) != this) {
898             uim_switch_im( ( *it )->uimContext(), name );
899             ( *it )->updatePosition();
900         }
901     }
902     uim_prop_update_custom(this->uimContext(), "custom-preserved-default-im-name", im_name_sym.toUtf8().data() );
903 }
904 
switch_system_global_im(const char * name)905 void QUimInputContext::switch_system_global_im( const char *name )
906 {
907     switch_app_global_im( name );
908     QUimHelperManager::send_im_change_whole_desktop( name );
909 }
910 
updatePosition()911 void QUimInputContext::updatePosition()
912 {
913     char * leftp = uim_scm_symbol_value_str( "candidate-window-position" );
914     proxy->setAlwaysLeftPosition( leftp && !strcmp( leftp, "left" ) );
915     free( leftp );
916 }
917 
updateStyle()918 void QUimInputContext::updateStyle()
919 {
920     // don't update window style if deprecated uim-candwin-prog is set
921     char *candwinprog = uim_scm_symbol_value_str( "uim-candwin-prog" );
922     if ( candwinprog ) {
923         free( candwinprog );
924         return;
925     }
926     delete proxy;
927     createCandidateWindow();
928 #ifdef WORKAROUND_BROKEN_RESET_IN_QT4
929     // invalidate all the candidate windows stored in proxyHash
930     QHashIterator<QWidget*, CandidateWindowProxy*> i( proxyHash );
931     while ( i.hasNext() ) {
932         i.next();
933         QWidget *widget = i.key();
934         delete proxyHash[ widget ];
935         proxyHash[ widget ] = 0;
936     }
937 #endif
938 }
939 
updateIndicator(const QString & str)940 void QUimInputContext::updateIndicator( const QString &str )
941 {
942     m_indicator->update( str );
943 }
944 
unicodeToUKey(ushort c)945 static int unicodeToUKey (ushort c) {
946     int sym;
947 
948     switch (c) {
949     case 0x00A5: sym = UKey_Yen; break;
950     case 0x3002: sym = UKey_Kana_Fullstop; break;
951     case 0x300C: sym = UKey_Kana_OpeningBracket; break;
952     case 0x300D: sym = UKey_Kana_ClosingBracket; break;
953     case 0x3001: sym = UKey_Kana_Comma; break;
954     case 0x30FB: sym = UKey_Kana_Conjunctive; break;
955     case 0x30F2: sym = UKey_Kana_WO; break;
956     case 0x30A1: sym = UKey_Kana_a; break;
957     case 0x30A3: sym = UKey_Kana_i; break;
958     case 0x30A5: sym = UKey_Kana_u; break;
959     case 0x30A7: sym = UKey_Kana_e; break;
960     case 0x30A9: sym = UKey_Kana_o; break;
961     case 0x30E3: sym = UKey_Kana_ya; break;
962     case 0x30E5: sym = UKey_Kana_yu; break;
963     case 0x30E7: sym = UKey_Kana_yo; break;
964     case 0x30C3: sym = UKey_Kana_tsu; break;
965     case 0x30FC: sym = UKey_Kana_ProlongedSound; break;
966     case 0x30A2: sym = UKey_Kana_A; break;
967     case 0x30A4: sym = UKey_Kana_I; break;
968     case 0x30A6: sym = UKey_Kana_U; break;
969     case 0x30A8: sym = UKey_Kana_E; break;
970     case 0x30AA: sym = UKey_Kana_O; break;
971     case 0x30AB: sym = UKey_Kana_KA; break;
972     case 0x30AD: sym = UKey_Kana_KI; break;
973     case 0x30AF: sym = UKey_Kana_KU; break;
974     case 0x30B1: sym = UKey_Kana_KE; break;
975     case 0x30B3: sym = UKey_Kana_KO; break;
976     case 0x30B5: sym = UKey_Kana_SA; break;
977     case 0x30B7: sym = UKey_Kana_SHI; break;
978     case 0x30B9: sym = UKey_Kana_SU; break;
979     case 0x30BB: sym = UKey_Kana_SE; break;
980     case 0x30BD: sym = UKey_Kana_SO; break;
981     case 0x30BF: sym = UKey_Kana_TA; break;
982     case 0x30C1: sym = UKey_Kana_CHI; break;
983     case 0x30C4: sym = UKey_Kana_TSU; break;
984     case 0x30C6: sym = UKey_Kana_TE; break;
985     case 0x30C8: sym = UKey_Kana_TO; break;
986     case 0x30CA: sym = UKey_Kana_NA; break;
987     case 0x30CB: sym = UKey_Kana_NI; break;
988     case 0x30CC: sym = UKey_Kana_NU; break;
989     case 0x30CD: sym = UKey_Kana_NE; break;
990     case 0x30CE: sym = UKey_Kana_NO; break;
991     case 0x30CF: sym = UKey_Kana_HA; break;
992     case 0x30D2: sym = UKey_Kana_HI; break;
993     case 0x30D5: sym = UKey_Kana_FU; break;
994     case 0x30D8: sym = UKey_Kana_HE; break;
995     case 0x30DB: sym = UKey_Kana_HO; break;
996     case 0x30DE: sym = UKey_Kana_MA; break;
997     case 0x30DF: sym = UKey_Kana_MI; break;
998     case 0x30E0: sym = UKey_Kana_MU; break;
999     case 0x30E1: sym = UKey_Kana_ME; break;
1000     case 0x30E2: sym = UKey_Kana_MO; break;
1001     case 0x30E4: sym = UKey_Kana_YA; break;
1002     case 0x30E6: sym = UKey_Kana_YU; break;
1003     case 0x30E8: sym = UKey_Kana_YO; break;
1004     case 0x30E9: sym = UKey_Kana_RA; break;
1005     case 0x30EA: sym = UKey_Kana_RI; break;
1006     case 0x30EB: sym = UKey_Kana_RU; break;
1007     case 0x30EC: sym = UKey_Kana_RE; break;
1008     case 0x30ED: sym = UKey_Kana_RO; break;
1009     case 0x30EF: sym = UKey_Kana_WA; break;
1010     case 0x30F3: sym = UKey_Kana_N; break;
1011     case 0x309B: sym = UKey_Kana_VoicedSound; break;
1012     case 0x309C: sym = UKey_Kana_SemivoicedSound; break;
1013     default:
1014         sym = UKey_Other;
1015         break;
1016     }
1017 
1018     return sym;
1019 }
1020