1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
23 #include <sal/alloca.h>
24 #include <osl/thread.h>
27 #include <X11/Xlocale.h>
31 #include "vcl/cmdevt.hxx"
32 #include "unx/salunx.h"
34 #include "unx/i18n_cb.hxx"
35 #include "unx/i18n_status.hxx"
36 #include "unx/i18n_ic.hxx"
37 #include "unx/i18n_im.hxx"
38 #include "salframe.hxx"
40 // i. preedit start callback
43 PreeditStartCallback ( XIC
, XPointer client_data
, XPointer
)
45 preedit_data_t
* pPreeditData
= reinterpret_cast<preedit_data_t
*>(client_data
);
46 if ( pPreeditData
->eState
== ePreeditStatusActivationRequired
)
48 pPreeditData
->eState
= ePreeditStatusActive
;
49 pPreeditData
->aText
.nCursorPos
= 0;
50 pPreeditData
->aText
.nLength
= 0;
56 // ii. preedit done callback
59 PreeditDoneCallback ( XIC
, XPointer client_data
, XPointer
)
61 preedit_data_t
* pPreeditData
= reinterpret_cast<preedit_data_t
*>(client_data
);
62 if (pPreeditData
->eState
== ePreeditStatusActive
)
64 if( pPreeditData
->pFrame
)
65 pPreeditData
->pFrame
->CallCallback( SALEVENT_ENDEXTTEXTINPUT
, (void*)NULL
);
67 pPreeditData
->eState
= ePreeditStatusStartPending
;
70 // iii. preedit draw callback
72 // Handle deletion of text in a preedit_draw_callback
73 // from and howmuch are guaranteed to be nonnegative
76 Preedit_DeleteText(preedit_text_t
*ptext
, int from
, int howmuch
)
78 // If we've been asked to delete no text then just set
79 // nLength correctly and return
80 if (ptext
->nLength
== 0)
82 ptext
->nLength
= from
;
86 int to
= from
+ howmuch
;
88 if (to
== (int)ptext
->nLength
)
90 // delete from the end of the text
91 ptext
->nLength
= from
;
94 if (to
< (int)ptext
->nLength
)
96 // cut out of the middle of the text
97 memmove( (void*)(ptext
->pUnicodeBuffer
+ from
),
98 (void*)(ptext
->pUnicodeBuffer
+ to
),
99 (ptext
->nLength
- to
) * sizeof(sal_Unicode
));
100 memmove( (void*)(ptext
->pCharStyle
+ from
),
101 (void*)(ptext
->pCharStyle
+ to
),
102 (ptext
->nLength
- to
) * sizeof(XIMFeedback
));
103 ptext
->nLength
-= howmuch
;
107 // XXX this indicates an error, are we out of sync ?
108 fprintf(stderr
, "Preedit_DeleteText( from=%i to=%i length=%i )\n",
109 from
, to
, ptext
->nLength
);
110 fprintf (stderr
, "\t XXX internal error, out of sync XXX\n");
112 ptext
->nLength
= from
;
115 // NULL-terminate the string
116 ptext
->pUnicodeBuffer
[ptext
->nLength
] = (sal_Unicode
)0;
119 // reallocate the textbuffer with sufficiently large size 2^x
120 // nnewlimit is presupposed to be larger than ptext->size
122 enlarge_buffer ( preedit_text_t
*ptext
, int nnewlimit
)
124 size_t nnewsize
= ptext
->nSize
;
126 while ( nnewsize
<= (size_t)nnewlimit
)
129 ptext
->nSize
= nnewsize
;
130 ptext
->pUnicodeBuffer
= static_cast<sal_Unicode
*>(realloc((void*)ptext
->pUnicodeBuffer
,
131 nnewsize
* sizeof(sal_Unicode
)));
132 ptext
->pCharStyle
= static_cast<XIMFeedback
*>(realloc((void*)ptext
->pCharStyle
,
133 nnewsize
* sizeof(XIMFeedback
)));
136 // Handle insertion of text in a preedit_draw_callback
137 // string field of XIMText struct is guaranteed to be != NULL
140 Preedit_InsertText(preedit_text_t
*pText
, XIMText
*pInsertText
, int where
)
142 sal_Unicode
*pInsertTextString
;
143 int nInsertTextLength
= 0;
144 XIMFeedback
*pInsertTextCharStyle
= pInsertText
->feedback
;
146 nInsertTextLength
= pInsertText
->length
;
148 // can't handle wchar_t strings, so convert to multibyte chars first
151 if (pInsertText
->encoding_is_wchar
)
153 wchar_t *pWCString
= pInsertText
->string
.wide_char
;
154 size_t nBytes
= wcstombs ( NULL
, pWCString
, 1024 /* dont care */);
155 pMBString
= static_cast<char*>(alloca( nBytes
+ 1 ));
156 nMBLength
= wcstombs ( pMBString
, pWCString
, nBytes
+ 1);
160 pMBString
= pInsertText
->string
.multi_byte
;
161 nMBLength
= strlen(pMBString
); // xxx
164 // convert multibyte chars to unicode
165 rtl_TextEncoding nEncoding
= osl_getThreadTextEncoding();
167 if (nEncoding
!= RTL_TEXTENCODING_UNICODE
)
169 rtl_TextToUnicodeConverter aConverter
=
170 rtl_createTextToUnicodeConverter( nEncoding
);
171 rtl_TextToUnicodeContext aContext
=
172 rtl_createTextToUnicodeContext(aConverter
);
174 sal_Size nBufferSize
= nInsertTextLength
* 2;
176 pInsertTextString
= static_cast<sal_Unicode
*>(alloca(nBufferSize
));
178 sal_uInt32 nConversionInfo
;
179 sal_Size nConvertedChars
;
181 rtl_convertTextToUnicode( aConverter
, aContext
,
182 pMBString
, nMBLength
,
183 pInsertTextString
, nBufferSize
,
184 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE
185 | RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE
,
186 &nConversionInfo
, &nConvertedChars
);
188 rtl_destroyTextToUnicodeContext(aConverter
, aContext
);
189 rtl_destroyTextToUnicodeConverter(aConverter
);
194 pInsertTextString
= reinterpret_cast<sal_Unicode
*>(pMBString
);
197 // enlarge target text-buffer if necessary
198 if (pText
->nSize
<= (pText
->nLength
+ nInsertTextLength
))
199 enlarge_buffer(pText
, pText
->nLength
+ nInsertTextLength
);
201 // insert text: displace old mem and put new bytes in
203 int to
= where
+ nInsertTextLength
;
204 int howmany
= pText
->nLength
- where
;
206 memmove((void*)(pText
->pUnicodeBuffer
+ to
),
207 (void*)(pText
->pUnicodeBuffer
+ from
),
208 howmany
* sizeof(sal_Unicode
));
209 memmove((void*)(pText
->pCharStyle
+ to
),
210 (void*)(pText
->pCharStyle
+ from
),
211 howmany
* sizeof(XIMFeedback
));
214 howmany
= nInsertTextLength
;
216 memcpy((void*)(pText
->pUnicodeBuffer
+ to
), (void*)pInsertTextString
,
217 howmany
* sizeof(sal_Unicode
));
218 memcpy((void*)(pText
->pCharStyle
+ to
), (void*)pInsertTextCharStyle
,
219 howmany
* sizeof(XIMFeedback
));
221 pText
->nLength
+= howmany
;
223 // NULL-terminate the string
224 pText
->pUnicodeBuffer
[pText
->nLength
] = (sal_Unicode
)0;
227 // Handle the change of attributes in a preedit_draw_callback
230 Preedit_UpdateAttributes ( preedit_text_t
* ptext
, XIMFeedback
* feedback
,
231 int from
, int amount
)
233 if ( (from
+ amount
) > (int)ptext
->nLength
)
235 // XXX this indicates an error, are we out of sync ?
236 fprintf (stderr
, "Preedit_UpdateAttributes( %i + %i > %i )\n",
237 from
, amount
, ptext
->nLength
);
238 fprintf (stderr
, "\t XXX internal error, out of sync XXX\n");
243 memcpy ( ptext
->pCharStyle
+ from
,
244 feedback
, amount
* sizeof(XIMFeedback
) );
247 // Convert the XIM feedback values into appropriate VCL
248 // EXTTEXTINPUT_ATTR values
249 // returns an allocate list of attributes, which must be freed by caller
251 Preedit_FeedbackToSAL ( XIMFeedback
* pfeedback
, int nlength
, std::vector
<sal_uInt16
>& rSalAttr
)
253 sal_uInt16
*psalattr
;
255 sal_uInt16 noldval
= 0;
256 XIMFeedback nfeedback
;
258 // only work with reasonable length
259 if (nlength
> 0 && nlength
> sal::static_int_cast
<int>(rSalAttr
.size()) )
261 rSalAttr
.reserve( nlength
);
262 psalattr
= &rSalAttr
[0];
265 return (sal_uInt16
*)NULL
;
267 for (int npos
= 0; npos
< nlength
; npos
++)
270 nfeedback
= pfeedback
[npos
];
272 // means to use the feedback of the previous char
277 // convert feedback to attributes
280 if (nfeedback
& XIMReverse
)
281 nval
|= EXTTEXTINPUT_ATTR_HIGHLIGHT
;
282 if (nfeedback
& XIMUnderline
)
283 nval
|= EXTTEXTINPUT_ATTR_UNDERLINE
;
284 if (nfeedback
& XIMHighlight
)
285 nval
|= EXTTEXTINPUT_ATTR_HIGHLIGHT
;
286 if (nfeedback
& XIMPrimary
)
287 nval
|= EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE
;
288 if (nfeedback
& XIMSecondary
)
289 nval
|= EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE
;
290 if (nfeedback
& XIMTertiary
) // same as 2ery
291 nval
|= EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE
;
295 psalattr
[npos
] = nval
;
298 // return list of sal attributes
303 PreeditDrawCallback(XIC ic
, XPointer client_data
,
304 XIMPreeditDrawCallbackStruct
*call_data
)
306 preedit_data_t
* pPreeditData
= reinterpret_cast<preedit_data_t
*>(client_data
);
308 // if there's nothing to change then change nothing
309 if ( ( (call_data
->text
== NULL
) && (call_data
->chg_length
== 0) )
310 || pPreeditData
->pFrame
== NULL
)
313 // Solaris 7 deletes the preedit buffer after commit
314 // since the next call to preeditstart will have the same effect just skip this.
315 // if (pPreeditData->eState == ePreeditStatusStartPending && call_data->text == NULL)
318 if ( pPreeditData
->eState
== ePreeditStatusStartPending
)
319 pPreeditData
->eState
= ePreeditStatusActivationRequired
;
320 PreeditStartCallback( ic
, client_data
, NULL
);
322 // Edit the internal textbuffer as indicated by the call_data,
323 // chg_first and chg_length are guaranteed to be nonnegative
325 // handle text deletion
326 if (call_data
->text
== NULL
)
328 Preedit_DeleteText(&(pPreeditData
->aText
),
329 call_data
->chg_first
, call_data
->chg_length
);
333 // handle text insertion
334 if ( (call_data
->chg_length
== 0)
335 && (call_data
->text
->string
.wide_char
!= NULL
))
337 Preedit_InsertText(&(pPreeditData
->aText
), call_data
->text
,
338 call_data
->chg_first
);
340 else if ( (call_data
->chg_length
!= 0)
341 && (call_data
->text
->string
.wide_char
!= NULL
))
343 // handle text replacement by deletion and insertion of text,
344 // not smart, just good enough
346 Preedit_DeleteText(&(pPreeditData
->aText
),
347 call_data
->chg_first
, call_data
->chg_length
);
348 Preedit_InsertText(&(pPreeditData
->aText
), call_data
->text
,
349 call_data
->chg_first
);
351 else if ( (call_data
->chg_length
!= 0)
352 && (call_data
->text
->string
.wide_char
== NULL
))
354 // not really a text update, only attributes are concerned
355 Preedit_UpdateAttributes(&(pPreeditData
->aText
),
356 call_data
->text
->feedback
,
357 call_data
->chg_first
, call_data
->chg_length
);
361 // build the SalExtTextInputEvent and send it up
363 pPreeditData
->aInputEv
.mnTime
= 0;
364 pPreeditData
->aInputEv
.mpTextAttr
= Preedit_FeedbackToSAL(
365 pPreeditData
->aText
.pCharStyle
, pPreeditData
->aText
.nLength
, pPreeditData
->aInputFlags
);
366 pPreeditData
->aInputEv
.mnCursorPos
= call_data
->caret
;
367 pPreeditData
->aInputEv
.maText
= OUString(pPreeditData
->aText
.pUnicodeBuffer
,
368 pPreeditData
->aText
.nLength
);
369 pPreeditData
->aInputEv
.mnCursorFlags
= 0; // default: make cursor visible
370 pPreeditData
->aInputEv
.mbOnlyCursor
= False
;
372 if ( pPreeditData
->eState
== ePreeditStatusActive
&& pPreeditData
->pFrame
)
373 pPreeditData
->pFrame
->CallCallback(SALEVENT_EXTTEXTINPUT
, (void*)&pPreeditData
->aInputEv
);
374 if (pPreeditData
->aText
.nLength
== 0 && pPreeditData
->pFrame
)
375 pPreeditData
->pFrame
->CallCallback( SALEVENT_ENDEXTTEXTINPUT
, (void*)NULL
);
377 if (pPreeditData
->aText
.nLength
== 0)
378 pPreeditData
->eState
= ePreeditStatusStartPending
;
380 GetPreeditSpotLocation(ic
, reinterpret_cast<XPointer
>(pPreeditData
));
384 GetPreeditSpotLocation(XIC ic
, XPointer client_data
)
387 // Send SalEventExtTextInputPos event to get spotlocation
389 SalExtTextInputPosEvent mPosEvent
;
390 preedit_data_t
* pPreeditData
= reinterpret_cast<preedit_data_t
*>(client_data
);
392 if( pPreeditData
->pFrame
)
393 pPreeditData
->pFrame
->CallCallback(SALEVENT_EXTTEXTINPUTPOS
, (void*)&mPosEvent
);
396 point
.x
= mPosEvent
.mnX
+ mPosEvent
.mnWidth
;
397 point
.y
= mPosEvent
.mnY
+ mPosEvent
.mnHeight
;
399 XVaNestedList preedit_attr
;
400 preedit_attr
= XVaCreateNestedList(0, XNSpotLocation
, &point
, NULL
);
401 XSetICValues(ic
, XNPreeditAttributes
, preedit_attr
, NULL
);
407 // iv. preedit caret callback
409 #if OSL_DEBUG_LEVEL > 1
411 PreeditCaretCallback ( XIC ic
, XPointer client_data
,
412 XIMPreeditCaretCallbackStruct
*call_data
)
415 PreeditCaretCallback ( XIC
, XPointer
,XIMPreeditCaretCallbackStruct
* )
418 #if OSL_DEBUG_LEVEL > 1
419 // XXX PreeditCaretCallback is pure debug code for now
420 const char *direction
= "?";
421 const char *style
= "?";
423 switch ( call_data
->style
)
425 case XIMIsInvisible
: style
= "Invisible"; break;
426 case XIMIsPrimary
: style
= "Primary"; break;
427 case XIMIsSecondary
: style
= "Secondary"; break;
429 switch ( call_data
->direction
)
431 case XIMForwardChar
: direction
= "Forward char"; break;
432 case XIMBackwardChar
: direction
= "Backward char"; break;
433 case XIMForwardWord
: direction
= "Forward word"; break;
434 case XIMBackwardWord
: direction
= "Backward word"; break;
435 case XIMCaretUp
: direction
= "Caret up"; break;
436 case XIMCaretDown
: direction
= "Caret down"; break;
437 case XIMNextLine
: direction
= "Next line"; break;
438 case XIMPreviousLine
: direction
= "Previous line"; break;
439 case XIMLineStart
: direction
= "Line start"; break;
440 case XIMLineEnd
: direction
= "Line end"; break;
441 case XIMAbsolutePosition
: direction
= "Absolute"; break;
442 case XIMDontChange
: direction
= "Dont change"; break;
445 fprintf (stderr
, "PreeditCaretCallback( ic=%p, client=%p,\n",
447 fprintf (stderr
, "\t position=%i, direction=\"%s\", style=\"%s\" )\n",
448 call_data
->position
, direction
, style
);
452 // v. commit string callback: convert an extended text input (iiimp ... )
453 // into an ordinary key-event
456 IsControlCode(sal_Unicode nChar
)
458 if ( nChar
<= 0x1F /* C0 controls */ )
464 // vi. status callbacks: for now these are empty, they are just needed for turbo linux
467 StatusStartCallback (XIC
, XPointer
, XPointer
)
473 StatusDoneCallback (XIC
, XPointer
, XPointer
)
479 StatusDrawCallback (XIC
, XPointer
, XIMStatusDrawCallbackStruct
*call_data
)
481 if( call_data
->type
== XIMTextType
)
484 if( call_data
->data
.text
)
487 sal_Char
* pMBString
= NULL
;
489 if( call_data
->data
.text
->encoding_is_wchar
)
491 if( call_data
->data
.text
->string
.wide_char
)
493 wchar_t* pWString
= call_data
->data
.text
->string
.wide_char
;
494 size_t nBytes
= wcstombs( NULL
, pWString
, 1024 );
495 pMBString
= static_cast<sal_Char
*>(alloca( nBytes
+1 ));
496 nLength
= wcstombs( pMBString
, pWString
, nBytes
+1 );
501 if( call_data
->data
.text
->string
.multi_byte
)
503 pMBString
= call_data
->data
.text
->string
.multi_byte
;
504 nLength
= strlen( pMBString
);
508 aText
= OUString( pMBString
, nLength
, osl_getThreadTextEncoding() );
510 vcl::I18NStatus::get().setStatusText( aText
);
512 #if OSL_DEBUG_LEVEL > 1
515 fprintf( stderr
, "XIMStatusDataType %s not supported\n",
516 call_data
->type
== XIMBitmapType
? "XIMBitmapType" : OString::number(call_data
->type
).getStr() );
523 SwitchIMCallback (XIC
, XPointer
, XPointer call_data
)
525 XIMSwitchIMNotifyCallbackStruct
* pCallData
= reinterpret_cast<XIMSwitchIMNotifyCallbackStruct
*>(call_data
);
526 vcl::I18NStatus::get().changeIM( OStringToOUString(pCallData
->to
->name
, RTL_TEXTENCODING_UTF8
) );
529 // vii. destroy callbacks: internally disable all IC/IM calls
532 IC_IMDestroyCallback (XIM
, XPointer client_data
, XPointer
)
534 SalI18N_InputContext
*pContext
= reinterpret_cast<SalI18N_InputContext
*>(client_data
);
535 if (pContext
!= NULL
)
536 pContext
->HandleDestroyIM();
540 IM_IMDestroyCallback (XIM
, XPointer client_data
, XPointer
)
542 SalI18N_InputMethod
*pMethod
= reinterpret_cast<SalI18N_InputMethod
*>(client_data
);
544 pMethod
->HandleDestroyIM();
547 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */