Revert "Revert "Revert "stronger typing for SwClient::GetRegisteredIn"" and fix SwIte...
[LibreOffice.git] / vcl / unx / generic / app / i18n_ic.cxx
blob1fd3454aeaa708ab878b05d3381e0efff7b5659e
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 <X11/Xlib.h>
22 #include <unx/i18n_ic.hxx>
23 #include <unx/i18n_im.hxx>
25 #include <unx/salframe.h>
26 #include <unx/saldisp.hxx>
28 #include <sal/log.hxx>
30 using namespace vcl;
32 static void sendEmptyCommit( SalFrame* pFrame )
34 vcl::DeletionListener aDel( pFrame );
36 SalExtTextInputEvent aEmptyEv;
37 aEmptyEv.mpTextAttr = nullptr;
38 aEmptyEv.maText.clear();
39 aEmptyEv.mnCursorPos = 0;
40 aEmptyEv.mnCursorFlags = 0;
41 pFrame->CallCallback( SalEvent::ExtTextInput, static_cast<void*>(&aEmptyEv) );
42 if( ! aDel.isDeleted() )
43 pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
46 // Constructor / Destructor, the InputContext is bound to the SalFrame, as it
47 // needs the shell window as a focus window
49 SalI18N_InputContext::~SalI18N_InputContext()
51 if ( maContext != nullptr )
52 XDestroyIC( maContext );
53 if ( mpAttributes != nullptr )
54 XFree( mpAttributes );
55 if ( mpStatusAttributes != nullptr )
56 XFree( mpStatusAttributes );
57 if ( mpPreeditAttributes != nullptr )
58 XFree( mpPreeditAttributes );
60 if (maClientData.aText.pUnicodeBuffer != nullptr)
61 free(maClientData.aText.pUnicodeBuffer);
62 if (maClientData.aText.pCharStyle != nullptr)
63 free(maClientData.aText.pCharStyle);
66 // convenience routine to add items to a XVaNestedList
68 static XVaNestedList
69 XVaAddToNestedList( XVaNestedList a_srclist, char* name, XPointer value )
71 XVaNestedList a_dstlist;
73 // if ( value == NULL )
74 // return a_srclist;
76 if ( a_srclist == nullptr )
78 a_dstlist = XVaCreateNestedList(
80 name, value,
81 nullptr );
83 else
85 a_dstlist = XVaCreateNestedList(
87 XNVaNestedList, a_srclist,
88 name, value,
89 nullptr );
92 return a_dstlist != nullptr ? a_dstlist : a_srclist ;
95 // convenience routine to create a fontset
97 static XFontSet
98 get_font_set( Display *p_display )
100 static XFontSet p_font_set = nullptr;
102 if (p_font_set == nullptr)
104 char **pp_missing_list;
105 int n_missing_count;
106 char *p_default_string;
108 p_font_set = XCreateFontSet(p_display, "-*",
109 &pp_missing_list, &n_missing_count, &p_default_string);
112 return p_font_set;
115 const XIMStyle g_nSupportedStatusStyle(
116 XIMStatusCallbacks |
117 XIMStatusNothing |
118 XIMStatusNone
121 // Constructor for an InputContext (IC)
123 SalI18N_InputContext::SalI18N_InputContext ( SalFrame *pFrame ) :
124 mbUseable( True ),
125 maContext( nullptr ),
126 mnSupportedPreeditStyle(
127 XIMPreeditCallbacks |
128 XIMPreeditNothing |
129 XIMPreeditNone
131 mnStatusStyle( 0 ),
132 mnPreeditStyle( 0 ),
133 mpAttributes( nullptr ),
134 mpStatusAttributes( nullptr ),
135 mpPreeditAttributes( nullptr )
137 #ifdef __sun
138 static const char* pIIIMPEnable = getenv( "SAL_DISABLE_OWN_IM_STATUS" );
139 if( pIIIMPEnable && *pIIIMPEnable )
140 mnSupportedStatusStyle &= ~XIMStatusCallbacks;
141 #endif
143 memset(&maPreeditStartCallback, 0, sizeof(maPreeditStartCallback));
144 memset(&maPreeditDoneCallback, 0, sizeof(maPreeditDoneCallback));
145 memset(&maPreeditDrawCallback, 0, sizeof(maPreeditDrawCallback));
146 memset(&maPreeditCaretCallback, 0, sizeof(maPreeditCaretCallback));
147 memset(&maCommitStringCallback, 0, sizeof(maCommitStringCallback));
148 memset(&maSwitchIMCallback, 0, sizeof(maSwitchIMCallback));
149 memset(&maDestroyCallback, 0, sizeof(maDestroyCallback));
151 maClientData.aText.pUnicodeBuffer = nullptr;
152 maClientData.aText.pCharStyle = nullptr;
153 maClientData.aInputEv.mpTextAttr = nullptr;
154 maClientData.aInputEv.mnCursorPos = 0;
155 maClientData.aInputEv.mnCursorFlags = 0;
157 SalI18N_InputMethod *pInputMethod;
158 pInputMethod = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetInputMethod();
160 mnSupportedPreeditStyle = XIMPreeditCallbacks | XIMPreeditPosition
161 | XIMPreeditNothing | XIMPreeditNone;
162 if (pInputMethod->UseMethod()
163 && SupportInputMethodStyle( pInputMethod->GetSupportedStyles() ) )
165 const SystemEnvData& rEnv = pFrame->GetSystemData();
166 ::Window aClientWindow = rEnv.aShellWindow;
167 ::Window aFocusWindow = rEnv.GetWindowHandle(pFrame);
169 // for status callbacks and commit string callbacks
170 #define PREEDIT_BUFSZ 16
171 maClientData.eState = PreeditStatus::StartPending;
172 maClientData.pFrame = pFrame;
173 maClientData.aText.pUnicodeBuffer =
174 static_cast<sal_Unicode*>(malloc(PREEDIT_BUFSZ * sizeof(sal_Unicode)));
175 maClientData.aText.pCharStyle =
176 static_cast<XIMFeedback*>(malloc(PREEDIT_BUFSZ * sizeof(XIMFeedback)));
177 maClientData.aText.nSize = PREEDIT_BUFSZ;
178 maClientData.aText.nLength = 0;
180 // Status attributes
182 switch ( mnStatusStyle )
184 case XIMStatusCallbacks:
186 static XIMCallback aStatusStartCallback;
187 static XIMCallback aStatusDoneCallback;
188 static XIMCallback aStatusDrawCallback;
190 aStatusStartCallback.callback = reinterpret_cast<XIMProc>(StatusStartCallback);
191 aStatusStartCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
192 aStatusDoneCallback.callback = reinterpret_cast<XIMProc>(StatusDoneCallback);
193 aStatusDoneCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
194 aStatusDrawCallback.callback = reinterpret_cast<XIMProc>(StatusDrawCallback);
195 aStatusDrawCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
197 mpStatusAttributes = XVaCreateNestedList (
199 XNStatusStartCallback, &aStatusStartCallback,
200 XNStatusDoneCallback, &aStatusDoneCallback,
201 XNStatusDrawCallback, &aStatusDrawCallback,
202 nullptr );
204 break;
207 case XIMStatusArea:
208 /* not supported */
209 break;
211 case XIMStatusNone:
212 case XIMStatusNothing:
213 default:
214 /* no arguments needed */
215 break;
218 // set preedit attributes
220 switch ( mnPreeditStyle )
222 case XIMPreeditCallbacks:
224 maPreeditCaretCallback.callback = reinterpret_cast<XIMProc>(PreeditCaretCallback);
225 maPreeditStartCallback.callback = reinterpret_cast<XIMProc>(PreeditStartCallback);
226 maPreeditDoneCallback.callback = reinterpret_cast<XIMProc>(PreeditDoneCallback);
227 maPreeditDrawCallback.callback = reinterpret_cast<XIMProc>(PreeditDrawCallback);
228 maPreeditCaretCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
229 maPreeditStartCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
230 maPreeditDoneCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
231 maPreeditDrawCallback.client_data = reinterpret_cast<XPointer>(&maClientData);
233 mpPreeditAttributes = XVaCreateNestedList (
235 XNPreeditStartCallback, &maPreeditStartCallback,
236 XNPreeditDoneCallback, &maPreeditDoneCallback,
237 XNPreeditDrawCallback, &maPreeditDrawCallback,
238 XNPreeditCaretCallback, &maPreeditCaretCallback,
239 nullptr );
241 break;
243 case XIMPreeditArea:
244 /* not supported */
245 break;
247 case XIMPreeditPosition:
249 // spot location
250 SalExtTextInputPosEvent aPosEvent;
251 pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
253 static XPoint aSpot;
254 aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth;
255 aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight;
257 // create attributes for preedit position style
258 mpPreeditAttributes = XVaCreateNestedList (
260 XNSpotLocation, &aSpot,
261 nullptr );
263 // XCreateIC() fails on Redflag Linux 2.0 if there is no
264 // fontset though the data itself is not evaluated nor is
265 // it required according to the X specs.
266 Display* pDisplay = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetDisplay();
267 XFontSet pFontSet = get_font_set(pDisplay);
269 if (pFontSet != nullptr)
271 mpPreeditAttributes = XVaAddToNestedList( mpPreeditAttributes,
272 const_cast<char*>(XNFontSet), reinterpret_cast<XPointer>(pFontSet));
275 break;
278 case XIMPreeditNone:
279 case XIMPreeditNothing:
280 default:
281 /* no arguments needed */
282 break;
285 // Create the InputContext by giving it exactly the information it
286 // deserves, because inappropriate attributes
287 // let XCreateIC fail on Solaris (eg. for C locale)
289 mpAttributes = XVaCreateNestedList(
291 XNFocusWindow, aFocusWindow,
292 XNClientWindow, aClientWindow,
293 XNInputStyle, mnPreeditStyle | mnStatusStyle,
294 nullptr );
296 if ( mnPreeditStyle != XIMPreeditNone )
298 #if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
299 if ( mpPreeditAttributes != nullptr )
300 #endif
301 mpAttributes = XVaAddToNestedList( mpAttributes,
302 const_cast<char*>(XNPreeditAttributes), static_cast<XPointer>(mpPreeditAttributes) );
304 if ( mnStatusStyle != XIMStatusNone )
306 #if defined LINUX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined DRAGONFLY
307 if ( mpStatusAttributes != nullptr )
308 #endif
309 mpAttributes = XVaAddToNestedList( mpAttributes,
310 const_cast<char*>(XNStatusAttributes), static_cast<XPointer>(mpStatusAttributes) );
312 maContext = XCreateIC( pInputMethod->GetMethod(),
313 XNVaNestedList, mpAttributes,
314 nullptr );
317 if ( maContext == nullptr )
319 #if OSL_DEBUG_LEVEL > 1
320 SAL_WARN("vcl.app", "input context creation failed.");
321 #endif
323 mbUseable = False;
325 if ( mpAttributes != nullptr )
326 XFree( mpAttributes );
327 if ( mpStatusAttributes != nullptr )
328 XFree( mpStatusAttributes );
329 if ( mpPreeditAttributes != nullptr )
330 XFree( mpPreeditAttributes );
331 if ( maClientData.aText.pUnicodeBuffer != nullptr )
332 free ( maClientData.aText.pUnicodeBuffer );
333 if ( maClientData.aText.pCharStyle != nullptr )
334 free ( maClientData.aText.pCharStyle );
336 mpAttributes = nullptr;
337 mpStatusAttributes = nullptr;
338 mpPreeditAttributes = nullptr;
339 maClientData.aText.pUnicodeBuffer = nullptr;
340 maClientData.aText.pCharStyle = nullptr;
343 if ( maContext != nullptr)
345 maDestroyCallback.callback = IC_IMDestroyCallback;
346 maDestroyCallback.client_data = reinterpret_cast<XPointer>(this);
347 XSetICValues( maContext,
348 XNDestroyCallback, &maDestroyCallback,
349 nullptr );
353 // In Solaris 8 the status window does not unmap if the frame unmapps, so
354 // unmap it the hard way
356 void
357 SalI18N_InputContext::Unmap()
359 UnsetICFocus();
360 maClientData.pFrame = nullptr;
363 void
364 SalI18N_InputContext::Map( SalFrame *pFrame )
366 if( !mbUseable )
367 return;
369 if( !pFrame )
370 return;
372 if ( maContext == nullptr )
374 SalI18N_InputMethod *pInputMethod;
375 pInputMethod = vcl_sal::getSalDisplay(GetGenericUnixSalData())->GetInputMethod();
377 maContext = XCreateIC( pInputMethod->GetMethod(),
378 XNVaNestedList, mpAttributes,
379 nullptr );
381 if( maClientData.pFrame != pFrame )
382 SetICFocus( pFrame );
385 // Handle DestroyCallbacks
386 // in fact this is a callback called from the XNDestroyCallback
388 void
389 SalI18N_InputContext::HandleDestroyIM()
391 maContext = nullptr; // don't change
392 mbUseable = False;
395 // make sure, the input method gets all the X-Events it needs, this is only
396 // called once on each frame, it relies on a valid maContext
398 void
399 SalI18N_InputContext::ExtendEventMask( ::Window aFocusWindow )
401 unsigned long nIMEventMask;
402 XWindowAttributes aWindowAttributes;
404 if ( mbUseable )
406 Display *pDisplay = XDisplayOfIM( XIMOfIC(maContext) );
408 XGetWindowAttributes( pDisplay, aFocusWindow,
409 &aWindowAttributes );
410 XGetICValues ( maContext,
411 XNFilterEvents, &nIMEventMask,
412 nullptr);
413 nIMEventMask |= aWindowAttributes.your_event_mask;
414 XSelectInput ( pDisplay, aFocusWindow, nIMEventMask );
418 // tune the styles provided by the input method with the supported one
420 unsigned int
421 SalI18N_InputContext::GetWeightingOfIMStyle( XIMStyle nStyle )
423 struct StyleWeightingT {
424 const XIMStyle nStyle;
425 const unsigned int nWeight;
428 StyleWeightingT const *pWeightPtr;
429 static const StyleWeightingT pWeight[] = {
430 { XIMPreeditCallbacks, 0x10000000 },
431 { XIMPreeditPosition, 0x02000000 },
432 { XIMPreeditArea, 0x01000000 },
433 { XIMPreeditNothing, 0x00100000 },
434 { XIMPreeditNone, 0x00010000 },
435 { XIMStatusCallbacks, 0x1000 },
436 { XIMStatusArea, 0x0100 },
437 { XIMStatusNothing, 0x0010 },
438 { XIMStatusNone, 0x0001 },
439 { 0, 0x0 }
442 int nWeight = 0;
443 for ( pWeightPtr = pWeight; pWeightPtr->nStyle != 0; pWeightPtr++ )
445 if ( (pWeightPtr->nStyle & nStyle) != 0 )
446 nWeight += pWeightPtr->nWeight;
448 return nWeight;
451 bool
452 SalI18N_InputContext::IsSupportedIMStyle( XIMStyle nStyle ) const
454 return (nStyle & mnSupportedPreeditStyle)
455 && (nStyle & g_nSupportedStatusStyle);
458 bool
459 SalI18N_InputContext::SupportInputMethodStyle( XIMStyles const *pIMStyles )
461 mnPreeditStyle = 0;
462 mnStatusStyle = 0;
464 if ( pIMStyles != nullptr )
466 int nBestScore = 0;
467 int nActualScore = 0;
469 // check whether the XIM supports one of the desired styles
470 // only a single preedit and a single status style must occur
471 // in an input method style. Hideki said so, so i trust him
472 for ( int nStyle = 0; nStyle < pIMStyles->count_styles; nStyle++ )
474 XIMStyle nProvidedStyle = pIMStyles->supported_styles[ nStyle ];
475 if ( IsSupportedIMStyle(nProvidedStyle) )
477 nActualScore = GetWeightingOfIMStyle( nProvidedStyle );
478 if ( nActualScore >= nBestScore )
480 nBestScore = nActualScore;
481 mnPreeditStyle = nProvidedStyle & mnSupportedPreeditStyle;
482 mnStatusStyle = nProvidedStyle & g_nSupportedStatusStyle;
488 return (mnPreeditStyle != 0) && (mnStatusStyle != 0) ;
491 // handle extended and normal key input
493 void
494 SalI18N_InputContext::CommitKeyEvent(sal_Unicode const * pText, std::size_t nLength)
496 if (nLength == 1 && IsControlCode(pText[0]))
497 return;
499 if( maClientData.pFrame )
501 SalExtTextInputEvent aTextEvent;
503 aTextEvent.mpTextAttr = nullptr;
504 aTextEvent.mnCursorPos = nLength;
505 aTextEvent.maText = OUString(pText, nLength);
506 aTextEvent.mnCursorFlags = 0;
508 maClientData.pFrame->CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&aTextEvent));
509 maClientData.pFrame->CallCallback(SalEvent::EndExtTextInput, nullptr);
511 #if OSL_DEBUG_LEVEL > 1
512 else
513 SAL_WARN("vcl.app", "CommitKeyEvent without frame.");
514 #endif
518 SalI18N_InputContext::UpdateSpotLocation()
520 if (maContext == nullptr || maClientData.pFrame == nullptr)
521 return -1;
523 SalExtTextInputPosEvent aPosEvent;
524 maClientData.pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
526 XPoint aSpot;
527 aSpot.x = aPosEvent.mnX + aPosEvent.mnWidth;
528 aSpot.y = aPosEvent.mnY + aPosEvent.mnHeight;
530 XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &aSpot, nullptr);
531 XSetICValues(maContext, XNPreeditAttributes, preedit_attr, nullptr);
532 XFree(preedit_attr);
534 return 0;
537 // set and unset the focus for the Input Context
538 // the context may be NULL despite it is usable if the framewindow is
539 // in unmapped state
541 void
542 SalI18N_InputContext::SetICFocus( SalFrame* pFocusFrame )
544 if ( !(mbUseable && (maContext != nullptr)) )
545 return;
547 maClientData.pFrame = pFocusFrame;
549 const SystemEnvData& rEnv = pFocusFrame->GetSystemData();
550 ::Window aClientWindow = rEnv.aShellWindow;
551 ::Window aFocusWindow = rEnv.GetWindowHandle(pFocusFrame);
553 XSetICValues( maContext,
554 XNFocusWindow, aFocusWindow,
555 XNClientWindow, aClientWindow,
556 nullptr );
558 if( maClientData.aInputEv.mpTextAttr )
560 sendEmptyCommit(pFocusFrame);
561 // begin preedit again
562 vcl_sal::getSalDisplay(GetGenericUnixSalData())->SendInternalEvent( pFocusFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
565 XSetICFocus( maContext );
568 void
569 SalI18N_InputContext::UnsetICFocus()
572 if ( mbUseable && (maContext != nullptr) )
574 // cancel an eventual event posted to begin preedit again
575 vcl_sal::getSalDisplay(GetGenericUnixSalData())->CancelInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
576 maClientData.pFrame = nullptr;
577 XUnsetICFocus( maContext );
581 // multi byte input method only
583 void
584 SalI18N_InputContext::EndExtTextInput()
586 if ( !mbUseable || (maContext == nullptr) || !maClientData.pFrame )
587 return;
589 vcl::DeletionListener aDel( maClientData.pFrame );
590 // delete preedit in sal (commit an empty string)
591 sendEmptyCommit( maClientData.pFrame );
592 if( ! aDel.isDeleted() )
594 // mark previous preedit state again (will e.g. be sent at focus gain)
595 maClientData.aInputEv.mpTextAttr = maClientData.aInputFlags.data();
596 if( static_cast<X11SalFrame*>(maClientData.pFrame)->hasFocus() )
598 // begin preedit again
599 vcl_sal::getSalDisplay(GetGenericUnixSalData())->SendInternalEvent( maClientData.pFrame, &maClientData.aInputEv, SalEvent::ExtTextInput );
604 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */