calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / unx / generic / app / i18n_cb.cxx
blobc17c01a4d225fcd547e92f9f3bdb42e0844890ab
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>
23 #include <o3tl/safeint.hxx>
24 #include <osl/thread.h>
25 #include <sal/log.hxx>
27 #include <X11/Xlib.h>
29 #include <vcl/commandevent.hxx>
30 #include <unx/i18n_cb.hxx>
31 #include <unx/i18n_ic.hxx>
32 #include <unx/i18n_im.hxx>
33 #include <salframe.hxx>
35 // i. preedit start callback
37 int
38 PreeditStartCallback ( XIC, XPointer client_data, XPointer )
40 preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
41 if ( pPreeditData->eState == PreeditStatus::ActivationRequired )
43 pPreeditData->eState = PreeditStatus::Active;
44 pPreeditData->aText.nLength = 0;
47 return -1;
50 // ii. preedit done callback
52 void
53 PreeditDoneCallback ( XIC, XPointer client_data, XPointer )
55 preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
56 if (pPreeditData->eState == PreeditStatus::Active )
58 if( pPreeditData->pFrame )
59 pPreeditData->pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
61 pPreeditData->eState = PreeditStatus::StartPending;
64 // iii. preedit draw callback
66 // Handle deletion of text in a preedit_draw_callback
67 // from and howmuch are guaranteed to be nonnegative
69 static void
70 Preedit_DeleteText(preedit_text_t *ptext, int from, int howmuch)
72 // If we've been asked to delete no text then just set
73 // nLength correctly and return
74 if (ptext->nLength == 0)
76 ptext->nLength = from;
77 return;
80 int to = from + howmuch;
82 if (to == static_cast<int>(ptext->nLength))
84 // delete from the end of the text
85 ptext->nLength = from;
87 else if (to < static_cast<int>(ptext->nLength))
89 // cut out of the middle of the text
90 memmove( static_cast<void*>(ptext->pUnicodeBuffer + from),
91 static_cast<void*>(ptext->pUnicodeBuffer + to),
92 (ptext->nLength - to) * sizeof(sal_Unicode));
93 memmove( static_cast<void*>(ptext->pCharStyle + from),
94 static_cast<void*>(ptext->pCharStyle + to),
95 (ptext->nLength - to) * sizeof(XIMFeedback));
96 ptext->nLength -= howmuch;
98 else
100 // XXX this indicates an error, are we out of sync ?
101 SAL_INFO("vcl.app", "Preedit_DeleteText( from=" << from
102 << " to=" << to
103 << " length=" << ptext->nLength
104 << " ).");
105 fprintf (stderr, "\t XXX internal error, out of sync XXX\n");
107 ptext->nLength = from;
110 // NULL-terminate the string
111 ptext->pUnicodeBuffer[ptext->nLength] = u'\0';
114 // reallocate the textbuffer with sufficiently large size 2^x
115 // nnewlimit is presupposed to be larger than ptext->size
116 static void
117 enlarge_buffer ( preedit_text_t *ptext, int nnewlimit )
119 size_t nnewsize = ptext->nSize;
121 while ( nnewsize <= o3tl::make_unsigned(nnewlimit) )
122 nnewsize *= 2;
124 ptext->nSize = nnewsize;
125 ptext->pUnicodeBuffer = static_cast<sal_Unicode*>(realloc(static_cast<void*>(ptext->pUnicodeBuffer),
126 nnewsize * sizeof(sal_Unicode)));
127 ptext->pCharStyle = static_cast<XIMFeedback*>(realloc(static_cast<void*>(ptext->pCharStyle),
128 nnewsize * sizeof(XIMFeedback)));
131 // Handle insertion of text in a preedit_draw_callback
132 // string field of XIMText struct is guaranteed to be != NULL
134 static void
135 Preedit_InsertText(preedit_text_t *pText, XIMText *pInsertText, int where)
137 sal_Unicode *pInsertTextString;
138 int nInsertTextLength = 0;
139 XIMFeedback *pInsertTextCharStyle = pInsertText->feedback;
141 nInsertTextLength = pInsertText->length;
143 // can't handle wchar_t strings, so convert to multibyte chars first
144 char *pMBString;
145 size_t nMBLength;
146 if (pInsertText->encoding_is_wchar)
148 wchar_t *pWCString = pInsertText->string.wide_char;
149 size_t nBytes = wcstombs ( nullptr, pWCString, 0 /* don't care */);
150 pMBString = static_cast<char*>(alloca( nBytes + 1 ));
151 nMBLength = wcstombs ( pMBString, pWCString, nBytes + 1);
153 else
155 pMBString = pInsertText->string.multi_byte;
156 nMBLength = strlen(pMBString); // xxx
159 // convert multibyte chars to unicode
160 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding();
162 if (nEncoding != RTL_TEXTENCODING_UNICODE)
164 rtl_TextToUnicodeConverter aConverter =
165 rtl_createTextToUnicodeConverter( nEncoding );
166 rtl_TextToUnicodeContext aContext =
167 rtl_createTextToUnicodeContext(aConverter);
169 sal_Size nBufferSize = nInsertTextLength * 2;
171 pInsertTextString = static_cast<sal_Unicode*>(alloca(nBufferSize));
173 sal_uInt32 nConversionInfo;
174 sal_Size nConvertedChars;
176 rtl_convertTextToUnicode( aConverter, aContext,
177 pMBString, nMBLength,
178 pInsertTextString, nBufferSize,
179 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE
180 | RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE,
181 &nConversionInfo, &nConvertedChars );
183 rtl_destroyTextToUnicodeContext(aConverter, aContext);
184 rtl_destroyTextToUnicodeConverter(aConverter);
187 else
189 pInsertTextString = reinterpret_cast<sal_Unicode*>(pMBString);
192 // enlarge target text-buffer if necessary
193 if (pText->nSize <= (pText->nLength + nInsertTextLength))
194 enlarge_buffer(pText, pText->nLength + nInsertTextLength);
196 // insert text: displace old mem and put new bytes in
197 int from = where;
198 int to = where + nInsertTextLength;
199 int howmany = pText->nLength - where;
201 memmove(static_cast<void*>(pText->pUnicodeBuffer + to),
202 static_cast<void*>(pText->pUnicodeBuffer + from),
203 howmany * sizeof(sal_Unicode));
204 memmove(static_cast<void*>(pText->pCharStyle + to),
205 static_cast<void*>(pText->pCharStyle + from),
206 howmany * sizeof(XIMFeedback));
208 to = from;
209 howmany = nInsertTextLength;
211 memcpy(static_cast<void*>(pText->pUnicodeBuffer + to), static_cast<void*>(pInsertTextString),
212 howmany * sizeof(sal_Unicode));
213 memcpy(static_cast<void*>(pText->pCharStyle + to), static_cast<void*>(pInsertTextCharStyle),
214 howmany * sizeof(XIMFeedback));
216 pText->nLength += howmany;
218 // NULL-terminate the string
219 pText->pUnicodeBuffer[pText->nLength] = u'\0';
222 // Handle the change of attributes in a preedit_draw_callback
224 static void
225 Preedit_UpdateAttributes ( preedit_text_t* ptext, XIMFeedback const * feedback,
226 int from, int amount )
228 if ( (from + amount) > static_cast<int>(ptext->nLength) )
230 // XXX this indicates an error, are we out of sync ?
231 SAL_INFO("vcl.app", "Preedit_UpdateAttributes( "
232 << from << " + " << amount << " > " << ptext->nLength
233 << " ).");
234 fprintf (stderr, "\t XXX internal error, out of sync XXX\n");
236 return;
239 memcpy ( ptext->pCharStyle + from,
240 feedback, amount * sizeof(XIMFeedback) );
243 // Convert the XIM feedback values into appropriate VCL
244 // EXTTEXTINPUT_ATTR values
245 // returns an allocate list of attributes, which must be freed by caller
246 static ExtTextInputAttr*
247 Preedit_FeedbackToSAL ( const XIMFeedback* pfeedback, int nlength, std::vector<ExtTextInputAttr>& rSalAttr )
249 ExtTextInputAttr *psalattr;
250 ExtTextInputAttr nval;
251 ExtTextInputAttr noldval = ExtTextInputAttr::NONE;
252 XIMFeedback nfeedback;
254 // only work with reasonable length
255 if (nlength > 0 && nlength > sal::static_int_cast<int>(rSalAttr.size()) )
257 rSalAttr.reserve( nlength );
258 psalattr = rSalAttr.data();
260 else
261 return nullptr;
263 for (int npos = 0; npos < nlength; npos++)
265 nval = ExtTextInputAttr::NONE;
266 nfeedback = pfeedback[npos];
268 // means to use the feedback of the previous char
269 if (nfeedback == 0)
271 nval = noldval;
273 // convert feedback to attributes
274 else
276 if (nfeedback & XIMReverse)
277 nval |= ExtTextInputAttr::Highlight;
278 if (nfeedback & XIMUnderline)
279 nval |= ExtTextInputAttr::Underline;
280 if (nfeedback & XIMHighlight)
281 nval |= ExtTextInputAttr::Highlight;
282 if (nfeedback & XIMPrimary)
283 nval |= ExtTextInputAttr::DottedUnderline;
284 if (nfeedback & XIMSecondary)
285 nval |= ExtTextInputAttr::DashDotUnderline;
286 if (nfeedback & XIMTertiary) // same as 2ery
287 nval |= ExtTextInputAttr::DashDotUnderline;
290 // copy in list
291 psalattr[npos] = nval;
292 noldval = nval;
294 // return list of sal attributes
295 return psalattr;
298 void
299 PreeditDrawCallback(XIC ic, XPointer client_data,
300 XIMPreeditDrawCallbackStruct *call_data)
302 preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
304 // if there's nothing to change then change nothing
305 if ( ( (call_data->text == nullptr) && (call_data->chg_length == 0) )
306 || pPreeditData->pFrame == nullptr )
307 return;
309 // Solaris 7 deletes the preedit buffer after commit
310 // since the next call to preeditstart will have the same effect just skip this.
311 // if (pPreeditData->eState == ePreeditStatusStartPending && call_data->text == NULL)
312 // return;
314 if ( pPreeditData->eState == PreeditStatus::StartPending )
315 pPreeditData->eState = PreeditStatus::ActivationRequired;
316 PreeditStartCallback( ic, client_data, nullptr );
318 // Edit the internal textbuffer as indicated by the call_data,
319 // chg_first and chg_length are guaranteed to be nonnegative
321 // handle text deletion
322 if (call_data->text == nullptr)
324 Preedit_DeleteText(&(pPreeditData->aText),
325 call_data->chg_first, call_data->chg_length );
327 else
329 // handle text insertion
330 if ( (call_data->chg_length == 0)
331 && (call_data->text->string.wide_char != nullptr))
333 Preedit_InsertText(&(pPreeditData->aText), call_data->text,
334 call_data->chg_first);
336 else if ( (call_data->chg_length != 0)
337 && (call_data->text->string.wide_char != nullptr))
339 // handle text replacement by deletion and insertion of text,
340 // not smart, just good enough
342 Preedit_DeleteText(&(pPreeditData->aText),
343 call_data->chg_first, call_data->chg_length);
344 Preedit_InsertText(&(pPreeditData->aText), call_data->text,
345 call_data->chg_first);
347 else if ( (call_data->chg_length != 0)
348 && (call_data->text->string.wide_char == nullptr))
350 // not really a text update, only attributes are concerned
351 Preedit_UpdateAttributes(&(pPreeditData->aText),
352 call_data->text->feedback,
353 call_data->chg_first, call_data->chg_length);
357 // build the SalExtTextInputEvent and send it up
359 pPreeditData->aInputEv.mpTextAttr = Preedit_FeedbackToSAL(
360 pPreeditData->aText.pCharStyle, pPreeditData->aText.nLength, pPreeditData->aInputFlags);
361 pPreeditData->aInputEv.mnCursorPos = call_data->caret;
362 pPreeditData->aInputEv.maText = OUString(pPreeditData->aText.pUnicodeBuffer,
363 pPreeditData->aText.nLength);
364 pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible
366 if ( pPreeditData->eState == PreeditStatus::Active && pPreeditData->pFrame )
367 pPreeditData->pFrame->CallCallback(SalEvent::ExtTextInput, static_cast<void*>(&pPreeditData->aInputEv));
368 if (pPreeditData->aText.nLength == 0 && pPreeditData->pFrame )
369 pPreeditData->pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
371 if (pPreeditData->aText.nLength == 0)
372 pPreeditData->eState = PreeditStatus::StartPending;
374 GetPreeditSpotLocation(ic, reinterpret_cast<XPointer>(pPreeditData));
377 void
378 GetPreeditSpotLocation(XIC ic, XPointer client_data)
381 // Send SalEventExtTextInputPos event to get spotlocation
383 SalExtTextInputPosEvent aPosEvent;
384 preedit_data_t* pPreeditData = reinterpret_cast<preedit_data_t*>(client_data);
386 if( pPreeditData->pFrame )
387 pPreeditData->pFrame->CallCallback(SalEvent::ExtTextInputPos, static_cast<void*>(&aPosEvent));
389 XPoint point;
390 point.x = aPosEvent.mnX + aPosEvent.mnWidth;
391 point.y = aPosEvent.mnY + aPosEvent.mnHeight;
393 XVaNestedList preedit_attr;
394 preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, nullptr);
395 XSetICValues(ic, XNPreeditAttributes, preedit_attr, nullptr);
396 XFree(preedit_attr);
399 // iv. preedit caret callback
401 #if OSL_DEBUG_LEVEL > 1
402 void
403 PreeditCaretCallback ( XIC ic, XPointer client_data,
404 XIMPreeditCaretCallbackStruct *call_data )
406 // XXX PreeditCaretCallback is pure debug code for now
407 const char *direction = "?";
408 const char *style = "?";
410 switch ( call_data->style )
412 case XIMIsInvisible: style = "Invisible"; break;
413 case XIMIsPrimary: style = "Primary"; break;
414 case XIMIsSecondary: style = "Secondary"; break;
416 switch ( call_data->direction )
418 case XIMForwardChar: direction = "Forward char"; break;
419 case XIMBackwardChar: direction = "Backward char"; break;
420 case XIMForwardWord: direction = "Forward word"; break;
421 case XIMBackwardWord: direction = "Backward word"; break;
422 case XIMCaretUp: direction = "Caret up"; break;
423 case XIMCaretDown: direction = "Caret down"; break;
424 case XIMNextLine: direction = "Next line"; break;
425 case XIMPreviousLine: direction = "Previous line"; break;
426 case XIMLineStart: direction = "Line start"; break;
427 case XIMLineEnd: direction = "Line end"; break;
428 case XIMAbsolutePosition: direction = "Absolute"; break;
429 case XIMDontChange: direction = "Don't change"; break;
432 SAL_INFO("vcl.app", "PreeditCaretCallback( ic=" << ic
433 << ", client=" << client_data
434 << ",");
435 SAL_INFO("vcl.app", "\t position=" << call_data->position
436 << ", direction=\"" << direction
437 << "\", style=\"" << style
438 << "\" ).");
440 #else
441 void
442 PreeditCaretCallback ( XIC, XPointer, XIMPreeditCaretCallbackStruct* )
445 #endif
447 // v. commit string callback: convert an extended text input (iiimp ... )
448 // into an ordinary key-event
450 Bool
451 IsControlCode(sal_Unicode nChar)
453 if ( nChar <= 0x1F /* C0 controls */ )
454 return True;
455 else
456 return False;
459 // vi. status callbacks: for now these are empty, they are just needed for turbo linux
461 void
462 StatusStartCallback (XIC, XPointer, XPointer)
466 void
467 StatusDoneCallback (XIC, XPointer, XPointer)
471 void
472 StatusDrawCallback (XIC, XPointer, XIMStatusDrawCallbackStruct *)
476 // vii. destroy callbacks: internally disable all IC/IM calls
478 void
479 IC_IMDestroyCallback (XIM, XPointer client_data, XPointer)
481 SalI18N_InputContext *pContext = reinterpret_cast<SalI18N_InputContext*>(client_data);
482 if (pContext != nullptr)
483 pContext->HandleDestroyIM();
486 void
487 IM_IMDestroyCallback (XIM, XPointer client_data, XPointer)
489 SalI18N_InputMethod *pMethod = reinterpret_cast<SalI18N_InputMethod*>(client_data);
490 if (pMethod != nullptr)
491 pMethod->HandleDestroyIM();
494 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */