nss: upgrade to release 3.73
[LibreOffice.git] / vcl / unx / generic / app / i18n_im.cxx
blob6a655ca39ea10e19760a482feaa070c9c7860ab9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <string.h>
22 #include <iostream>
24 #ifdef LINUX
25 # ifndef __USE_XOPEN
26 # define __USE_XOPEN
27 # endif
28 #endif
30 #include <X11/Xlib.h>
32 #include <unx/i18n_im.hxx>
34 #include <osl/thread.h>
35 #include <osl/process.h>
36 #include <sal/log.hxx>
38 #include <unx/i18n_cb.hxx>
40 using namespace vcl;
42 // kinput2 IME needs special key handling since key release events are filtered in
43 // preeditmode and XmbResetIC does not work
45 namespace {
47 class XKeyEventOp : public XKeyEvent
49 private:
50 void init();
52 public:
53 XKeyEventOp();
55 XKeyEventOp& operator= (const XKeyEvent &rEvent);
56 void erase ();
57 bool match (const XKeyEvent &rEvent) const;
62 void
63 XKeyEventOp::init()
65 type = 0; /* serial = 0; */
66 send_event = 0; display = nullptr;
67 window = 0; root = 0;
68 subwindow = 0; /* time = 0; */
69 /* x = 0; y = 0; */
70 /* x_root = 0; y_root = 0; */
71 state = 0; keycode = 0;
72 same_screen = 0;
75 XKeyEventOp::XKeyEventOp()
77 init();
80 XKeyEventOp&
81 XKeyEventOp::operator= (const XKeyEvent &rEvent)
83 type = rEvent.type; /* serial = rEvent.serial; */
84 send_event = rEvent.send_event; display = rEvent.display;
85 window = rEvent.window; root = rEvent.root;
86 subwindow = rEvent.subwindow;/* time = rEvent.time; */
87 /* x = rEvent.x, y = rEvent.y; */
88 /* x_root = rEvent.x_root, y_root = rEvent.y_root; */
89 state = rEvent.state; keycode = rEvent.keycode;
90 same_screen = rEvent.same_screen;
92 return *this;
95 void
96 XKeyEventOp::erase ()
98 init();
101 bool
102 XKeyEventOp::match (const XKeyEvent &rEvent) const
104 return ( (type == KeyPress && rEvent.type == KeyRelease)
105 || (type == KeyRelease && rEvent.type == KeyPress ))
106 /* && serial == rEvent.serial */
107 && send_event == rEvent.send_event
108 && display == rEvent.display
109 && window == rEvent.window
110 && root == rEvent.root
111 && subwindow == rEvent.subwindow
112 /* && time == rEvent.time
113 && x == rEvent.x
114 && y == rEvent.y
115 && x_root == rEvent.x_root
116 && y_root == rEvent.y_root */
117 && state == rEvent.state
118 && keycode == rEvent.keycode
119 && same_screen == rEvent.same_screen;
122 // locale handling
124 // Locale handling of the operating system layer
126 static char*
127 SetSystemLocale( const char* p_inlocale )
129 char *p_outlocale = setlocale(LC_ALL, p_inlocale);
131 SAL_WARN_IF(p_outlocale == nullptr, "vcl.app",
132 "I18N: Operating system doesn't support locale \""
133 << p_inlocale << "\".");
135 return p_outlocale;
138 #ifdef __sun
139 static void
140 SetSystemEnvironment( const OUString& rLocale )
142 OUString LC_ALL_Var("LC_ALL");
143 osl_setEnvironment(LC_ALL_Var.pData, rLocale.pData);
145 OUString LANG_Var("LANG");
146 osl_setEnvironment(LANG_Var.pData, rLocale.pData);
148 #endif
150 static Bool
151 IsPosixLocale( const char* p_locale )
153 if ( p_locale == nullptr )
154 return False;
155 if ( (p_locale[ 0 ] == 'C') && (p_locale[ 1 ] == '\0') )
156 return True;
157 if ( strncmp(p_locale, "POSIX", sizeof("POSIX")) == 0 )
158 return True;
160 return False;
163 // Locale handling of the X Window System layer
165 static Bool
166 IsXWindowCompatibleLocale( const char* p_locale )
168 if ( p_locale == nullptr )
169 return False;
171 if ( !XSupportsLocale() )
173 SAL_WARN("vcl.app",
174 "I18N: X Window System doesn't support locale \""
175 << p_locale << "\".");
176 return False;
178 return True;
181 // Set the operating system locale prior to trying to open an
182 // XIM InputMethod.
183 // Handle the cases where the current locale is either not supported by the
184 // operating system (LANG=gaga) or by the XWindow system (LANG=aa_ER@saaho)
185 // by providing a fallback.
186 // Upgrade "C" or "POSIX" to "en_US" locale to allow umlauts and accents
187 // see i8988, i9188, i8930, i16318
188 // on Solaris the environment needs to be set equivalent to the locale (#i37047#)
190 void
191 SalI18N_InputMethod::SetLocale()
193 // check whether we want an Input Method engine, if we don't we
194 // do not need to set the locale
195 if ( !mbUseable )
196 return;
198 char *locale = SetSystemLocale( "" );
199 if ( (!IsXWindowCompatibleLocale(locale)) || IsPosixLocale(locale) )
201 osl_setThreadTextEncoding (RTL_TEXTENCODING_ISO_8859_1);
202 locale = SetSystemLocale( "en_US" );
203 #ifdef __sun
204 SetSystemEnvironment( "en_US" );
205 #endif
206 if (! IsXWindowCompatibleLocale(locale))
208 locale = SetSystemLocale( "C" );
209 #ifdef __sun
210 SetSystemEnvironment( "C" );
211 #endif
212 if (! IsXWindowCompatibleLocale(locale))
213 mbUseable = False;
217 // must not fail if mbUseable since XSupportsLocale() asserts success
218 if ( mbUseable && XSetLocaleModifiers("") == nullptr )
220 SAL_WARN("vcl.app",
221 "I18N: Can't set X modifiers for locale \""
222 << locale << "\".");
223 mbUseable = False;
227 Bool
228 SalI18N_InputMethod::PosixLocale()
230 if (maMethod)
231 return IsPosixLocale (XLocaleOfIM (maMethod));
232 return False;
235 // Constructor / Destructor / Initialisation
237 SalI18N_InputMethod::SalI18N_InputMethod( )
238 : mbUseable( bUseInputMethodDefault )
239 , maMethod( nullptr )
240 , mpStyles( nullptr )
242 maDestroyCallback.callback = nullptr;
243 maDestroyCallback.client_data = nullptr;
244 const char *pUseInputMethod = getenv( "SAL_USEINPUTMETHOD" );
245 if ( pUseInputMethod != nullptr )
246 mbUseable = pUseInputMethod[0] != '\0' ;
249 SalI18N_InputMethod::~SalI18N_InputMethod()
251 if ( mpStyles != nullptr )
252 XFree( mpStyles );
253 if ( maMethod != nullptr )
254 XCloseIM ( maMethod );
257 // XXX
258 // debug routine: lets have a look at the provided method styles
260 #if OSL_DEBUG_LEVEL > 1
262 extern "C" char*
263 GetMethodName( XIMStyle nStyle, char *pBuf, int nBufSize)
265 struct StyleName {
266 const XIMStyle nStyle;
267 const char *pName;
268 const int nNameLen;
271 StyleName *pDescPtr;
272 static const StyleName pDescription[] = {
273 { XIMPreeditArea, "PreeditArea ", sizeof("PreeditArea ") },
274 { XIMPreeditCallbacks, "PreeditCallbacks ",sizeof("PreeditCallbacks ")},
275 { XIMPreeditPosition, "PreeditPosition ", sizeof("PreeditPosition ") },
276 { XIMPreeditNothing, "PreeditNothing ", sizeof("PreeditNothing ") },
277 { XIMPreeditNone, "PreeditNone ", sizeof("PreeditNone ") },
278 { XIMStatusArea, "StatusArea ", sizeof("StatusArea ") },
279 { XIMStatusCallbacks, "StatusCallbacks ", sizeof("StatusCallbacks ") },
280 { XIMStatusNothing, "StatusNothing ", sizeof("StatusNothing ") },
281 { XIMStatusNone, "StatusNone ", sizeof("StatusNone ") },
282 { 0, "NULL", 0 }
285 if ( nBufSize > 0 )
286 pBuf[0] = '\0';
288 char *pBufPtr = pBuf;
289 for ( pDescPtr = const_cast<StyleName*>(pDescription); pDescPtr->nStyle != 0; pDescPtr++ )
291 int nSize = pDescPtr->nNameLen - 1;
292 if ( (nStyle & pDescPtr->nStyle) && (nBufSize > nSize) )
294 strncpy( pBufPtr, pDescPtr->pName, nSize + 1);
295 pBufPtr += nSize;
296 nBufSize -= nSize;
300 return pBuf;
303 extern "C" void
304 PrintInputStyle( XIMStyles *pStyle )
306 char pBuf[ 128 ];
307 int nBuf = sizeof( pBuf );
309 if ( pStyle == NULL )
310 SAL_INFO("vcl.app", "no input method styles.");
311 else
312 for ( int nStyle = 0; nStyle < pStyle->count_styles; nStyle++ )
314 SAL_INFO("vcl.app", "style #"
315 << nStyle
316 << " = "
317 << GetMethodName(pStyle->supported_styles[nStyle], pBuf, nBuf));
321 #endif
323 // this is the real constructing routine, since locale setting has to be done
324 // prior to xopendisplay, the xopenim call has to be delayed
326 void
327 SalI18N_InputMethod::CreateMethod ( Display *pDisplay )
329 if ( mbUseable )
331 maMethod = XOpenIM(pDisplay, nullptr, nullptr, nullptr);
333 if ((maMethod == nullptr) && (getenv("XMODIFIERS") != nullptr))
335 OUString envVar("XMODIFIERS");
336 osl_clearEnvironment(envVar.pData);
337 XSetLocaleModifiers("");
338 maMethod = XOpenIM(pDisplay, nullptr, nullptr, nullptr);
341 if ( maMethod != nullptr )
343 if ( XGetIMValues(maMethod, XNQueryInputStyle, &mpStyles, nullptr)
344 != nullptr)
345 mbUseable = False;
346 #if OSL_DEBUG_LEVEL > 1
347 SAL_INFO("vcl.app", "Creating Mono-Lingual InputMethod.");
348 PrintInputStyle( mpStyles );
349 #endif
351 else
353 mbUseable = False;
357 #if OSL_DEBUG_LEVEL > 1
358 SAL_WARN_IF(!mbUseable, "vcl.app", "input method creation failed.");
359 #endif
361 maDestroyCallback.callback = IM_IMDestroyCallback;
362 maDestroyCallback.client_data = reinterpret_cast<XPointer>(this);
363 if (mbUseable && maMethod != nullptr)
364 XSetIMValues(maMethod, XNDestroyCallback, &maDestroyCallback, nullptr);
367 // give IM the opportunity to look at the event, and possibly hide it
369 bool
370 SalI18N_InputMethod::FilterEvent( XEvent *pEvent, ::Window window )
372 if (!mbUseable)
373 return False;
375 bool bFilterEvent = XFilterEvent (pEvent, window);
377 if (pEvent->type != KeyPress && pEvent->type != KeyRelease)
378 return bFilterEvent;
381 * fix broken key release handling of some IMs
383 XKeyEvent* pKeyEvent = &(pEvent->xkey);
384 static XKeyEventOp s_aLastKeyPress;
386 if (bFilterEvent)
388 if (pKeyEvent->type == KeyRelease)
389 bFilterEvent = !s_aLastKeyPress.match (*pKeyEvent);
390 s_aLastKeyPress.erase();
392 else /* (!bFilterEvent) */
394 if (pKeyEvent->type == KeyPress)
395 s_aLastKeyPress = *pKeyEvent;
396 else
397 s_aLastKeyPress.erase();
400 return bFilterEvent;
403 void
404 SalI18N_InputMethod::HandleDestroyIM()
406 mbUseable = False;
407 maMethod = nullptr;
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */