Update ooo320-m1
[ooovba.git] / vcl / unx / source / app / i18n_im.cxx
blob43f4d5d4debef2e2950b4ab0f7ecc6a31eda4364
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: i18n_im.cxx,v $
10 * $Revision: 1.39 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include <stdio.h>
35 #include <string.h>
36 #ifdef LINUX
37 # ifndef __USE_XOPEN
38 # define __USE_XOPEN
39 # endif
40 #endif
41 #include <poll.h>
42 #ifdef SOLARIS
43 // for SetSystemEnvironment()
44 #include <sal/alloca.h>
45 #endif
47 #include <tools/prex.h>
48 #include <X11/Xlocale.h>
49 #include <X11/Xlib.h>
50 #include <XIM.h>
51 #include <tools/postx.h>
53 #include <salunx.h>
54 #include <saldisp.hxx>
55 #include "i18n_im.hxx"
56 #include <i18n_status.hxx>
58 #include <osl/thread.h>
60 using namespace vcl;
61 #include "i18n_cb.hxx"
62 #if defined(SOLARIS) || defined(LINUX) || defined(IRIX)
63 extern "C" char * XSetIMValues(XIM im, ...);
64 #endif
66 // ------------------------------------------------------------------------------------
68 // kinput2 IME needs special key handling since key release events are filtered in
69 // preeditmode and XmbResetIC does not work
71 // ------------------------------------------------------------------------------------
73 Bool
74 IMServerKinput2 ()
76 const static char* p_xmodifiers = getenv ("XMODIFIERS");
77 const static Bool b_kinput2 = (p_xmodifiers != NULL)
78 && (strcmp(p_xmodifiers, "@im=kinput2") == 0);
80 return b_kinput2;
83 class XKeyEventOp : XKeyEvent
85 private:
86 void init();
88 public:
89 XKeyEventOp();
90 ~XKeyEventOp();
92 XKeyEventOp& operator= (const XKeyEvent &rEvent);
93 void erase ();
94 Bool match (const XKeyEvent &rEvent) const;
97 void
98 XKeyEventOp::init()
100 type = 0; /* serial = 0; */
101 send_event = 0; display = 0;
102 window = 0; root = 0;
103 subwindow = 0; /* time = 0; */
104 /* x = 0; y = 0; */
105 /* x_root = 0; y_root = 0; */
106 state = 0; keycode = 0;
107 same_screen = 0;
110 XKeyEventOp::XKeyEventOp()
112 init();
115 XKeyEventOp::~XKeyEventOp()
119 XKeyEventOp&
120 XKeyEventOp::operator= (const XKeyEvent &rEvent)
122 type = rEvent.type; /* serial = rEvent.serial; */
123 send_event = rEvent.send_event; display = rEvent.display;
124 window = rEvent.window; root = rEvent.root;
125 subwindow = rEvent.subwindow;/* time = rEvent.time; */
126 /* x = rEvent.x, y = rEvent.y; */
127 /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
128 state = rEvent.state; keycode = rEvent.keycode;
129 same_screen = rEvent.same_screen;
131 return *this;
134 void
135 XKeyEventOp::erase ()
137 init();
140 Bool
141 XKeyEventOp::match (const XKeyEvent &rEvent) const
143 return ( (type == XLIB_KeyPress && rEvent.type == KeyRelease)
144 || (type == KeyRelease && rEvent.type == XLIB_KeyPress ))
145 /* && serial == rEvent.serial */
146 && send_event == rEvent.send_event
147 && display == rEvent.display
148 && window == rEvent.window
149 && root == rEvent.root
150 && subwindow == rEvent.subwindow
151 /* && time == rEvent.time
152 && x == rEvent.x
153 && y == rEvent.y
154 && x_root == rEvent.x_root
155 && y_root == rEvent.y_root */
156 && state == rEvent.state
157 && keycode == rEvent.keycode
158 && same_screen == rEvent.same_screen;
161 // -------------------------------------------------------------------------
163 // locale handling
165 // -------------------------------------------------------------------------
167 // Locale handling of the operating system layer
169 static char*
170 SetSystemLocale( const char* p_inlocale )
172 char *p_outlocale;
174 if ( (p_outlocale = setlocale(LC_ALL, p_inlocale)) == NULL )
176 fprintf( stderr, "I18N: Operating system doesn't support locale \"%s\"\n",
177 p_inlocale );
180 return p_outlocale;
183 #ifdef SOLARIS
184 static void
185 SetSystemEnvironment( const char* p_locale )
187 const char *lc_all = "LC_ALL=%s";
188 const char *lang = "LANG=%s";
190 char *p_buffer;
192 if (p_locale != NULL)
194 p_buffer = (char*)alloca(10 + strlen(p_locale));
195 sprintf(p_buffer, lc_all, p_locale);
196 putenv(strdup(p_buffer));
197 sprintf(p_buffer, lang, p_locale);
198 putenv(strdup(p_buffer));
201 #endif
203 static Bool
204 IsPosixLocale( const char* p_locale )
206 if ( p_locale == NULL )
207 return False;
208 if ( (p_locale[ 0 ] == 'C') && (p_locale[ 1 ] == '\0') )
209 return True;
210 if ( strncmp(p_locale, "POSIX", sizeof("POSIX")) == 0 )
211 return True;
213 return False;
216 // Locale handling of the X Window System layer
218 static Bool
219 IsXWindowCompatibleLocale( const char* p_locale )
221 if ( p_locale == NULL )
222 return False;
224 if ( !XSupportsLocale() )
226 fprintf (stderr, "I18N: X Window System doesn't support locale \"%s\"\n",
227 p_locale );
228 return False;
230 return True;
233 // Set the operating system locale prior to trying to open an
234 // XIM InputMethod.
235 // Handle the cases where the current locale is either not supported by the
236 // operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
237 // by providing a fallback.
238 // Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
239 // see i8988, i9188, i8930, i16318
240 // on Solaris the environment needs to be set equivalent to the locale (#i37047#)
242 Bool
243 SalI18N_InputMethod::SetLocale( const char* pLocale )
245 // check whether we want an Input Method engine, if we don't we
246 // do not need to set the locale
247 if ( mbUseable )
249 char *locale = SetSystemLocale( pLocale );
250 if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) )
252 osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1);
253 locale = SetSystemLocale( "en_US" );
254 #ifdef SOLARIS
255 SetSystemEnvironment( "en_US" );
256 #endif
257 if (! IsXWindowCompatibleLocale(locale))
259 locale = SetSystemLocale( "C" );
260 #ifdef SOLARIS
261 SetSystemEnvironment( "C" );
262 #endif
263 if (! IsXWindowCompatibleLocale(locale))
264 mbUseable = False;
268 // must not fail if mbUseable since XSupportsLocale() asserts success
269 if ( mbUseable && XSetLocaleModifiers("") == NULL )
271 fprintf (stderr, "I18N: Can't set X modifiers for locale \"%s\"\n",
272 locale);
273 mbUseable = False;
277 return mbUseable;
280 Bool
281 SalI18N_InputMethod::PosixLocale()
283 if (mbMultiLingual)
284 return False;
285 if (maMethod)
286 return IsPosixLocale (XLocaleOfIM (maMethod));
287 return False;
290 // ------------------------------------------------------------------------
292 // Constructor / Destructor / Initialisation
294 // ------------------------------------------------------------------------
296 SalI18N_InputMethod::SalI18N_InputMethod( ) : mbUseable( bUseInputMethodDefault ),
297 mbMultiLingual( False ),
298 maMethod( (XIM)NULL ),
299 mpStyles( (XIMStyles*)NULL )
301 const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" );
302 if ( pUseInputMethod != NULL )
303 mbUseable = pUseInputMethod[0] != '\0' ;
306 SalI18N_InputMethod::~SalI18N_InputMethod()
308 ::vcl::I18NStatus::free();
309 if ( mpStyles != NULL )
310 XFree( mpStyles );
311 if ( maMethod != NULL )
312 XCloseIM ( maMethod );
316 // XXX
317 // debug routine: lets have a look at the provided method styles
320 #if OSL_DEBUG_LEVEL > 1
322 extern "C" char*
323 GetMethodName( XIMStyle nStyle, char *pBuf, int nBufSize)
325 struct StyleName {
326 const XIMStyle nStyle;
327 const char *pName;
328 const int nNameLen;
331 StyleName *pDescPtr;
332 static const StyleName pDescription[] = {
333 { XIMPreeditArea, "PreeditArea ", sizeof("PreeditArea ") },
334 { XIMPreeditCallbacks, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
335 { XIMPreeditPosition, "PreeditPosition ", sizeof("PreeditPosition ") },
336 { XIMPreeditNothing, "PreeditNothing ", sizeof("PreeditNothing ") },
337 { XIMPreeditNone, "PreeditNone ", sizeof("PreeditNone ") },
338 { XIMStatusArea, "StatusArea ", sizeof("StatusArea ") },
339 { XIMStatusCallbacks, "StatusCallbacks ", sizeof("StatusCallbacks ") },
340 { XIMStatusNothing, "StatusNothing ", sizeof("StatusNothing ") },
341 { XIMStatusNone, "StatusNone ", sizeof("StatusNone ") },
342 { 0, "NULL", 0 }
345 if ( nBufSize > 0 )
346 pBuf[0] = '\0';
348 char *pBufPtr = pBuf;
349 for ( pDescPtr = const_cast<StyleName*>(pDescription); pDescPtr->nStyle != 0; pDescPtr++ )
351 int nSize = pDescPtr->nNameLen - 1;
352 if ( (nStyle & pDescPtr->nStyle) && (nBufSize > nSize) )
354 strncpy( pBufPtr, pDescPtr->pName, nSize + 1);
355 pBufPtr += nSize;
356 nBufSize -= nSize;
360 return pBuf;
363 extern "C" void
364 PrintInputStyle( XIMStyles *pStyle )
366 char pBuf[ 128 ];
367 int nBuf = sizeof( pBuf );
369 if ( pStyle == NULL )
370 fprintf( stderr, "no input method styles\n");
371 else
372 for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ )
374 fprintf( stderr, "style #%i = %s\n", nStyle,
375 GetMethodName(pStyle->supported_styles[nStyle], pBuf, nBuf) );
379 #endif
382 // this is the real constructing routine, since locale setting has to be done
383 // prior to xopendisplay, the xopenim call has to be delayed
386 Bool
387 SalI18N_InputMethod::CreateMethod ( Display *pDisplay )
389 if ( mbUseable )
391 const bool bTryMultiLingual =
392 #ifdef LINUX
393 false;
394 #else
395 true;
396 #endif
397 if ( bTryMultiLingual && getenv("USE_XOPENIM") == NULL )
399 mbMultiLingual = True; // set ml-input flag to create input-method
400 maMethod = XvaOpenIM(pDisplay, NULL, NULL, NULL,
401 XNMultiLingualInput, mbMultiLingual, /* dummy */
402 (void *)0);
403 // get ml-input flag from input-method
404 if ( maMethod == (XIM)NULL )
405 mbMultiLingual = False;
406 else
407 if ( XGetIMValues(maMethod,
408 XNMultiLingualInput, &mbMultiLingual, NULL ) != NULL )
409 mbMultiLingual = False;
410 if( mbMultiLingual )
412 XIMUnicodeCharacterSubsets* subsets;
413 if( XGetIMValues( maMethod,
414 XNQueryUnicodeCharacterSubset, &subsets, NULL ) == NULL )
416 #if OSL_DEBUG_LEVEL > 1
417 fprintf( stderr, "IM reports %d subsets: ", subsets->count_subsets );
418 #endif
419 I18NStatus& rStatus( I18NStatus::get() );
420 rStatus.clearChoices();
421 for( int i = 0; i < subsets->count_subsets; i++ )
423 #if OSL_DEBUG_LEVEL > 1
424 fprintf( stderr,"\"%s\" ", subsets->supported_subsets[i].name );
425 #endif
426 rStatus.addChoice( String( subsets->supported_subsets[i].name, RTL_TEXTENCODING_UTF8 ), &subsets->supported_subsets[i] );
428 #if OSL_DEBUG_LEVEL > 1
429 fprintf( stderr, "\n" );
430 #endif
432 #if OSL_DEBUG_LEVEL > 1
433 else
434 fprintf( stderr, "query subsets failed\n" );
435 #endif
438 else
440 maMethod = XOpenIM(pDisplay, NULL, NULL, NULL);
441 mbMultiLingual = False;
444 if ((maMethod == (XIM)NULL) && (getenv("XMODIFIERS") != NULL))
446 putenv (strdup("XMODIFIERS"));
447 XSetLocaleModifiers("");
448 maMethod = XOpenIM(pDisplay, NULL, NULL, NULL);
449 mbMultiLingual = False;
452 if ( maMethod != (XIM)NULL )
454 if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, NULL)
455 != NULL)
456 mbUseable = False;
457 #if OSL_DEBUG_LEVEL > 1
458 fprintf(stderr, "Creating %s-Lingual InputMethod\n",
459 mbMultiLingual ? "Multi" : "Mono" );
460 PrintInputStyle( mpStyles );
461 #endif
463 else
465 mbUseable = False;
469 #if OSL_DEBUG_LEVEL > 1
470 if ( !mbUseable )
471 fprintf(stderr, "input method creation failed\n");
472 #endif
474 maDestroyCallback.callback = (XIMProc)IM_IMDestroyCallback;
475 maDestroyCallback.client_data = (XPointer)this;
476 if (mbUseable && maMethod != NULL)
477 XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, NULL);
479 return mbUseable;
483 // give IM the opportunity to look at the event, and possibly hide it
486 Bool
487 SalI18N_InputMethod::FilterEvent( XEvent *pEvent, XLIB_Window window )
489 if (!mbUseable)
490 return False;
492 Bool bFilterEvent = XFilterEvent (pEvent, window);
494 if (pEvent->type != XLIB_KeyPress && pEvent->type != KeyRelease)
495 return bFilterEvent;
498 * fix broken key release handling of some IMs
500 XKeyEvent* pKeyEvent = &(pEvent->xkey);
501 static XKeyEventOp maLastKeyPress;
503 if (bFilterEvent)
505 if (pKeyEvent->type == KeyRelease)
506 bFilterEvent = !maLastKeyPress.match (*pKeyEvent);
507 maLastKeyPress.erase();
509 else /* (!bFilterEvent) */
511 if (pKeyEvent->type == XLIB_KeyPress)
512 maLastKeyPress = *pKeyEvent;
513 else
514 maLastKeyPress.erase();
517 return bFilterEvent;
520 void
521 SalI18N_InputMethod::HandleDestroyIM()
523 mbUseable = False;
524 mbMultiLingual = False;
525 maMethod = NULL;
528 // ------------------------------------------------------------------------
530 // add a connection watch into the SalXLib yieldTable to allow iiimp
531 // connection processing: soffice waits in select() not in XNextEvent(), so
532 // there may be requests pending on the iiimp internal connection that will
533 // not be processed until XNextEvent is called the next time. If we do not
534 // have the focus because the atok12 lookup choice aux window has it we stay
535 // deaf and dump otherwise.
537 // ------------------------------------------------------------------------
540 InputMethod_HasPendingEvent(int nFileDescriptor, void *pData)
542 if (pData == NULL)
543 return 0;
545 struct pollfd aFileDescriptor;
546 #ifdef SOLARIS
547 nfds_t nNumDescriptor = 1;
548 #else
549 unsigned int nNumDescriptor = 1;
550 #endif
551 aFileDescriptor.fd = nFileDescriptor;
552 aFileDescriptor.events = POLLRDNORM;
553 aFileDescriptor.revents = 0;
555 int nPoll = poll (&aFileDescriptor, nNumDescriptor, 0 /* timeout */ );
557 if (nPoll > 0)
559 /* at least some conditions in revent are set */
560 if ( (aFileDescriptor.revents & POLLHUP)
561 || (aFileDescriptor.revents & POLLERR)
562 || (aFileDescriptor.revents & POLLNVAL))
563 return 0; /* oops error condition set */
565 if (aFileDescriptor.revents & POLLRDNORM)
566 return 1; /* success */
569 /* nPoll == 0 means timeout, nPoll < 0 means error */
570 return 0;
574 InputMethod_IsEventQueued(int nFileDescriptor, void *pData)
576 return InputMethod_HasPendingEvent (nFileDescriptor, pData);
580 InputMethod_HandleNextEvent(int nFileDescriptor, void *pData)
582 if (pData != NULL)
583 XProcessInternalConnection((Display*)pData, nFileDescriptor);
585 return 0;
588 extern "C" void
589 InputMethod_ConnectionWatchProc (Display *pDisplay, XPointer pClientData,
590 int nFileDescriptor, Bool bOpening, XPointer*)
592 SalXLib *pConnectionHandler = (SalXLib*)pClientData;
594 if (pConnectionHandler == NULL)
595 return;
597 if (bOpening)
599 pConnectionHandler->Insert (nFileDescriptor, pDisplay,
600 InputMethod_HasPendingEvent,
601 InputMethod_IsEventQueued,
602 InputMethod_HandleNextEvent);
604 else
606 pConnectionHandler->Remove (nFileDescriptor);
610 Bool
611 SalI18N_InputMethod::AddConnectionWatch(Display *pDisplay, void *pConnectionHandler)
613 // sanity check
614 if (pDisplay == NULL || pConnectionHandler == NULL)
615 return False;
617 // if we are not ml all the extended text input comes on the stock X queue,
618 // so there is no need to monitor additional file descriptors.
619 #ifndef SOLARIS
620 if (!mbMultiLingual || !mbUseable)
621 return False;
622 #endif
624 // pConnectionHandler must be really a pointer to a SalXLib
625 Status nStatus = XAddConnectionWatch (pDisplay, InputMethod_ConnectionWatchProc,
626 (XPointer)pConnectionHandler);
627 return (Bool)nStatus;