1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_vcl.hxx" 26 27 #include <stdio.h> 28 #include <string.h> 29 30 #include <sal/alloca.h> 31 #include <osl/thread.h> 32 33 #include <tools/prex.h> 34 #include <X11/Xlocale.h> 35 #include <X11/Xlib.h> 36 #include <tools/postx.h> 37 38 #include "unx/salunx.h" 39 #include "unx/XIM.h" 40 #include "unx/i18n_cb.hxx" 41 #include "unx/i18n_status.hxx" 42 #include "unx/i18n_ic.hxx" 43 #include "unx/i18n_im.hxx" 44 #include "salframe.hxx" 45 46 // ------------------------------------------------------------------------- 47 // 48 // i. preedit start callback 49 // 50 // ------------------------------------------------------------------------- 51 52 int 53 PreeditStartCallback ( XIC, XPointer client_data, XPointer ) 54 { 55 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 56 if ( pPreeditData->eState == ePreeditStatusActivationRequired ) 57 { 58 pPreeditData->eState = ePreeditStatusActive; 59 pPreeditData->aText.nCursorPos = 0; 60 pPreeditData->aText.nLength = 0; 61 } 62 63 return -1; 64 } 65 66 // ------------------------------------------------------------------------- 67 // 68 // ii. preedit done callback 69 // 70 // ------------------------------------------------------------------------- 71 72 void 73 PreeditDoneCallback ( XIC, XPointer client_data, XPointer ) 74 { 75 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 76 if (pPreeditData->eState == ePreeditStatusActive ) 77 { 78 if( pPreeditData->pFrame ) 79 pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); 80 } 81 pPreeditData->eState = ePreeditStatusStartPending; 82 } 83 84 // ------------------------------------------------------------------------- 85 // 86 // iii. preedit draw callback 87 // 88 // ------------------------------------------------------------------------- 89 90 // 91 // Handle deletion of text in a preedit_draw_callback 92 // from and howmuch are guaranteed to be nonnegative 93 // 94 95 void 96 Preedit_DeleteText(preedit_text_t *ptext, int from, int howmuch) 97 { 98 // If we've been asked to delete no text then just set 99 // nLength correctly and return 100 if (ptext->nLength == 0) 101 { 102 ptext->nLength = from; 103 return; 104 } 105 106 int to = from + howmuch; 107 108 if (to == (int)ptext->nLength) 109 { 110 // delete from the end of the text 111 ptext->nLength = from; 112 } 113 else 114 if (to < (int)ptext->nLength) 115 { 116 // cut out of the middle of the text 117 memmove( (void*)(ptext->pUnicodeBuffer + from), 118 (void*)(ptext->pUnicodeBuffer + to), 119 (ptext->nLength - to) * sizeof(sal_Unicode)); 120 memmove( (void*)(ptext->pCharStyle + from), 121 (void*)(ptext->pCharStyle + to), 122 (ptext->nLength - to) * sizeof(XIMFeedback)); 123 ptext->nLength -= howmuch; 124 } 125 else 126 // if ( to > pText->nLength ) 127 { 128 // XXX this indicates an error, are we out of sync ? 129 fprintf(stderr, "Preedit_DeleteText( from=%i to=%i length=%i )\n", 130 from, to, ptext->nLength ); 131 fprintf (stderr, "\t XXX internal error, out of sync XXX\n"); 132 133 ptext->nLength = from; 134 } 135 136 // NULL-terminate the string 137 ptext->pUnicodeBuffer[ptext->nLength] = (sal_Unicode)0; 138 } 139 140 // reallocate the textbuffer with sufficiently large size 2^x 141 // nnewlimit is presupposed to be larger than ptext->size 142 void 143 enlarge_buffer ( preedit_text_t *ptext, int nnewlimit ) 144 { 145 size_t nnewsize = ptext->nSize; 146 147 while ( nnewsize <= (size_t)nnewlimit ) 148 nnewsize *= 2; 149 150 ptext->nSize = nnewsize; 151 ptext->pUnicodeBuffer = (sal_Unicode*)realloc((void*)ptext->pUnicodeBuffer, 152 nnewsize * sizeof(sal_Unicode)); 153 ptext->pCharStyle = (XIMFeedback*)realloc((void*)ptext->pCharStyle, 154 nnewsize * sizeof(XIMFeedback)); 155 } 156 157 // 158 // Handle insertion of text in a preedit_draw_callback 159 // string field of XIMText struct is guaranteed to be != NULL 160 // 161 162 void 163 Preedit_InsertText(preedit_text_t *pText, XIMText *pInsertText, int where, 164 Bool isMultilingual) 165 { 166 sal_Unicode *pInsertTextString; 167 int nInsertTextLength = 0; 168 XIMFeedback *pInsertTextCharStyle = pInsertText->feedback; 169 170 nInsertTextLength = pInsertText->length; 171 172 if (isMultilingual) 173 { 174 XIMUnicodeText *pUniText = (XIMUnicodeText*)pInsertText; 175 pInsertTextString = pUniText->string.utf16_char; 176 } 177 else 178 { 179 // can't handle wchar_t strings, so convert to multibyte chars first 180 char *pMBString; 181 size_t nMBLength; 182 if (pInsertText->encoding_is_wchar) 183 { 184 wchar_t *pWCString = pInsertText->string.wide_char; 185 size_t nBytes = wcstombs ( NULL, pWCString, 1024 /* dont care */); 186 pMBString = (char*)alloca( nBytes + 1 ); 187 nMBLength = wcstombs ( pMBString, pWCString, nBytes + 1); 188 } 189 else 190 { 191 pMBString = pInsertText->string.multi_byte; 192 nMBLength = strlen(pMBString); // xxx 193 } 194 195 // convert multibyte chars to unicode 196 rtl_TextEncoding nEncoding = osl_getThreadTextEncoding(); 197 198 if (nEncoding != RTL_TEXTENCODING_UNICODE) 199 { 200 rtl_TextToUnicodeConverter aConverter = 201 rtl_createTextToUnicodeConverter( nEncoding ); 202 rtl_TextToUnicodeContext aContext = 203 rtl_createTextToUnicodeContext(aConverter); 204 205 sal_Size nBufferSize = nInsertTextLength * 2; 206 207 pInsertTextString = (sal_Unicode*)alloca(nBufferSize); 208 209 sal_uInt32 nConversionInfo; 210 sal_Size nConvertedChars; 211 212 rtl_convertTextToUnicode( aConverter, aContext, 213 pMBString, nMBLength, 214 pInsertTextString, nBufferSize, 215 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_IGNORE 216 | RTL_TEXTTOUNICODE_FLAGS_INVALID_IGNORE, 217 &nConversionInfo, &nConvertedChars ); 218 219 rtl_destroyTextToUnicodeContext(aConverter, aContext); 220 rtl_destroyTextToUnicodeConverter(aConverter); 221 222 } 223 else 224 { 225 pInsertTextString = (sal_Unicode*)pMBString; 226 } 227 } 228 229 // enlarge target text-buffer if necessary 230 if (pText->nSize <= (pText->nLength + nInsertTextLength)) 231 enlarge_buffer(pText, pText->nLength + nInsertTextLength); 232 233 // insert text: displace old mem and put new bytes in 234 int from = where; 235 int to = where + nInsertTextLength; 236 int howmany = pText->nLength - where; 237 238 memmove((void*)(pText->pUnicodeBuffer + to), 239 (void*)(pText->pUnicodeBuffer + from), 240 howmany * sizeof(sal_Unicode)); 241 memmove((void*)(pText->pCharStyle + to), 242 (void*)(pText->pCharStyle + from), 243 howmany * sizeof(XIMFeedback)); 244 245 to = from; 246 howmany = nInsertTextLength; 247 248 memcpy((void*)(pText->pUnicodeBuffer + to), (void*)pInsertTextString, 249 howmany * sizeof(sal_Unicode)); 250 memcpy((void*)(pText->pCharStyle + to), (void*)pInsertTextCharStyle, 251 howmany * sizeof(XIMFeedback)); 252 253 pText->nLength += howmany; 254 255 // NULL-terminate the string 256 pText->pUnicodeBuffer[pText->nLength] = (sal_Unicode)0; 257 } 258 259 // 260 // Handle the change of attributes in a preedit_draw_callback 261 // 262 void 263 Preedit_UpdateAttributes ( preedit_text_t* ptext, XIMFeedback* feedback, 264 int from, int amount ) 265 { 266 if ( (from + amount) > (int)ptext->nLength ) 267 { 268 // XXX this indicates an error, are we out of sync ? 269 fprintf (stderr, "Preedit_UpdateAttributes( %i + %i > %i )\n", 270 from, amount, ptext->nLength ); 271 fprintf (stderr, "\t XXX internal error, out of sync XXX\n"); 272 273 return; 274 } 275 276 memcpy ( ptext->pCharStyle + from, 277 feedback, amount * sizeof(XIMFeedback) ); 278 } 279 280 // Convert the XIM feedback values into appropriate VCL 281 // SAL_EXTTEXTINPUT_ATTR values 282 // returns an allocate list of attributes, which must be freed by caller 283 sal_uInt16* 284 Preedit_FeedbackToSAL ( XIMFeedback* pfeedback, int nlength, std::vector<sal_uInt16>& rSalAttr ) 285 { 286 sal_uInt16 *psalattr; 287 sal_uInt16 nval; 288 sal_uInt16 noldval = 0; 289 XIMFeedback nfeedback; 290 291 // only work with reasonable length 292 if (nlength > 0 && nlength > sal::static_int_cast<int>(rSalAttr.size()) ) 293 { 294 rSalAttr.reserve( nlength ); 295 psalattr = &rSalAttr[0]; 296 } 297 else 298 return (sal_uInt16*)NULL; 299 300 for (int npos = 0; npos < nlength; npos++) 301 { 302 nval = 0; 303 nfeedback = pfeedback[npos]; 304 305 // means to use the feedback of the previous char 306 if (nfeedback == 0) 307 { 308 nval = noldval; 309 } 310 // convert feedback to attributes 311 else 312 { 313 if (nfeedback & XIMReverse) 314 nval |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; 315 if (nfeedback & XIMUnderline) 316 nval |= SAL_EXTTEXTINPUT_ATTR_UNDERLINE; 317 if (nfeedback & XIMHighlight) 318 nval |= SAL_EXTTEXTINPUT_ATTR_HIGHLIGHT; 319 if (nfeedback & XIMPrimary) 320 nval |= SAL_EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE; 321 if (nfeedback & XIMSecondary) 322 nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; 323 if (nfeedback & XIMTertiary) // same as 2ery 324 nval |= SAL_EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE; 325 326 /* 327 // visibility feedback not supported now 328 if ( (nfeedback & XIMVisibleToForward) 329 || (nfeedback & XIMVisibleToBackward) 330 || (nfeedback & XIMVisibleCenter) ) 331 { } 332 */ 333 } 334 // copy in list 335 psalattr[npos] = nval; 336 noldval = nval; 337 } 338 // return list of sal attributes 339 return psalattr; 340 } 341 342 void 343 PreeditDrawCallback(XIC ic, XPointer client_data, 344 XIMPreeditDrawCallbackStruct *call_data) 345 { 346 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 347 348 // if there's nothing to change then change nothing 349 if ( ( (call_data->text == NULL) && (call_data->chg_length == 0) ) 350 || pPreeditData->pFrame == NULL ) 351 return; 352 353 // #88564# Solaris 7 deletes the preedit buffer after commit 354 // since the next call to preeditstart will have the same effect just skip this. 355 // if (pPreeditData->eState == ePreeditStatusStartPending && call_data->text == NULL) 356 // return; 357 358 if ( pPreeditData->eState == ePreeditStatusStartPending ) 359 pPreeditData->eState = ePreeditStatusActivationRequired; 360 PreeditStartCallback( ic, client_data, NULL ); 361 362 // Edit the internal textbuffer as indicated by the call_data, 363 // chg_first and chg_length are guaranteed to be nonnegative 364 365 // handle text deletion 366 if (call_data->text == NULL) 367 { 368 Preedit_DeleteText(&(pPreeditData->aText), 369 call_data->chg_first, call_data->chg_length ); 370 } 371 else 372 { 373 // handle text insertion 374 if ( (call_data->chg_length == 0) 375 && (call_data->text->string.wide_char != NULL)) 376 { 377 Preedit_InsertText(&(pPreeditData->aText), call_data->text, 378 call_data->chg_first, pPreeditData->bIsMultilingual); 379 } 380 else 381 // handle text replacement by deletion and insertion of text, 382 // not smart, just good enough 383 if ( (call_data->chg_length != 0) 384 && (call_data->text->string.wide_char != NULL)) 385 { 386 Preedit_DeleteText(&(pPreeditData->aText), 387 call_data->chg_first, call_data->chg_length); 388 Preedit_InsertText(&(pPreeditData->aText), call_data->text, 389 call_data->chg_first, pPreeditData->bIsMultilingual); 390 } 391 else 392 // not really a text update, only attributes are concerned 393 if ( (call_data->chg_length != 0) 394 && (call_data->text->string.wide_char == NULL)) 395 { 396 Preedit_UpdateAttributes(&(pPreeditData->aText), 397 call_data->text->feedback, 398 call_data->chg_first, call_data->chg_length); 399 } 400 } 401 402 // 403 // build the SalExtTextInputEvent and send it up 404 // 405 pPreeditData->aInputEv.mnTime = 0; 406 pPreeditData->aInputEv.mpTextAttr = Preedit_FeedbackToSAL( 407 pPreeditData->aText.pCharStyle, pPreeditData->aText.nLength, pPreeditData->aInputFlags); 408 pPreeditData->aInputEv.mnCursorPos = call_data->caret; 409 pPreeditData->aInputEv.maText = String (pPreeditData->aText.pUnicodeBuffer, 410 pPreeditData->aText.nLength); 411 pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible 412 pPreeditData->aInputEv.mnDeltaStart = 0; // call_data->chg_first; 413 pPreeditData->aInputEv.mbOnlyCursor = False; 414 415 if ( pPreeditData->eState == ePreeditStatusActive && pPreeditData->pFrame ) 416 pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); 417 if (pPreeditData->aText.nLength == 0 && pPreeditData->pFrame ) 418 pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); 419 420 if (pPreeditData->aText.nLength == 0) 421 pPreeditData->eState = ePreeditStatusStartPending; 422 423 GetPreeditSpotLocation(ic, (XPointer)pPreeditData); 424 } 425 426 void 427 GetPreeditSpotLocation(XIC ic, XPointer client_data) 428 { 429 // 430 // Send SalEventExtTextInputPos event to get spotlocation 431 // 432 SalExtTextInputPosEvent mPosEvent; 433 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 434 435 if( pPreeditData->pFrame ) 436 pPreeditData->pFrame->CallCallback(SALEVENT_EXTTEXTINPUTPOS, (void*)&mPosEvent); 437 438 XPoint point; 439 point.x = mPosEvent.mnX + mPosEvent.mnWidth; 440 point.y = mPosEvent.mnY + mPosEvent.mnHeight; 441 442 XVaNestedList preedit_attr; 443 preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &point, NULL); 444 XSetICValues(ic, XNPreeditAttributes, preedit_attr, NULL); 445 XFree(preedit_attr); 446 447 return; 448 } 449 450 // ------------------------------------------------------------------------- 451 // 452 // iv. preedit caret callback 453 // 454 // ------------------------------------------------------------------------- 455 456 #if OSL_DEBUG_LEVEL > 1 457 void 458 PreeditCaretCallback ( XIC ic, XPointer client_data, 459 XIMPreeditCaretCallbackStruct *call_data ) 460 #else 461 void 462 PreeditCaretCallback ( XIC, XPointer,XIMPreeditCaretCallbackStruct* ) 463 #endif 464 { 465 #if OSL_DEBUG_LEVEL > 1 466 // XXX PreeditCaretCallback is pure debug code for now 467 const char *direction = "?"; 468 const char *style = "?"; 469 470 switch ( call_data->style ) 471 { 472 case XIMIsInvisible: style = "Invisible"; break; 473 case XIMIsPrimary: style = "Primary"; break; 474 case XIMIsSecondary: style = "Secondary"; break; 475 } 476 switch ( call_data->direction ) 477 { 478 case XIMForwardChar: direction = "Forward char"; break; 479 case XIMBackwardChar: direction = "Backward char"; break; 480 case XIMForwardWord: direction = "Forward word"; break; 481 case XIMBackwardWord: direction = "Backward word"; break; 482 case XIMCaretUp: direction = "Caret up"; break; 483 case XIMCaretDown: direction = "Caret down"; break; 484 case XIMNextLine: direction = "Next line"; break; 485 case XIMPreviousLine: direction = "Previous line"; break; 486 case XIMLineStart: direction = "Line start"; break; 487 case XIMLineEnd: direction = "Line end"; break; 488 case XIMAbsolutePosition: direction = "Absolute"; break; 489 case XIMDontChange: direction = "Dont change"; break; 490 } 491 492 fprintf (stderr, "PreeditCaretCallback( ic=%p, client=%p,\n", 493 ic, client_data ); 494 fprintf (stderr, "\t position=%i, direction=\"%s\", style=\"%s\" )\n", 495 call_data->position, direction, style ); 496 #endif 497 } 498 499 // ----------------------------------------------------------------------- 500 // 501 // v. commit string callback: convert an extended text input (iiimp ... ) 502 // into an ordinary key-event 503 // 504 // ----------------------------------------------------------------------- 505 506 Bool 507 IsControlCode(sal_Unicode nChar) 508 { 509 if ( nChar <= 0x1F // C0 controls 510 /* || (0x80 <= nChar && nChar <= 0x9F) C1 controls */ ) 511 return True; 512 else 513 return False; 514 } 515 516 int 517 CommitStringCallback( XIC ic, XPointer client_data, XPointer call_data ) 518 { 519 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 520 521 XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data; 522 sal_Unicode *p_unicode_data = (sal_Unicode*)cbtext->string.utf16_char; 523 524 // #86964# filter unexpected pure control events 525 if (cbtext->length == 1 && IsControlCode(p_unicode_data[0]) ) 526 { 527 if( pPreeditData->pFrame ) 528 { 529 pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); 530 } 531 } 532 else 533 { 534 if( pPreeditData->pFrame ) 535 { 536 pPreeditData->aInputEv.mnTime = 0; 537 pPreeditData->aInputEv.mpTextAttr = 0; 538 pPreeditData->aInputEv.mnCursorPos = cbtext->length; 539 pPreeditData->aInputEv.maText = UniString(p_unicode_data, cbtext->length); 540 pPreeditData->aInputEv.mnCursorFlags = 0; // default: make cursor visible 541 pPreeditData->aInputEv.mnDeltaStart = 0; 542 pPreeditData->aInputEv.mbOnlyCursor = False; 543 544 pPreeditData->pFrame->CallCallback( SALEVENT_EXTTEXTINPUT, (void*)&pPreeditData->aInputEv); 545 pPreeditData->pFrame->CallCallback( SALEVENT_ENDEXTTEXTINPUT, (void*)NULL ); 546 } 547 } 548 pPreeditData->eState = ePreeditStatusStartPending; 549 550 GetPreeditSpotLocation(ic, (XPointer)pPreeditData); 551 552 return 0; 553 } 554 555 // ---------------------------------------------------------------------------------- 556 // 557 // vi. status callbacks: for now these are empty, they are just needed for turbo linux 558 // 559 // ---------------------------------------------------------------------------------- 560 561 void 562 StatusStartCallback (XIC, XPointer, XPointer) 563 { 564 return; 565 } 566 567 void 568 StatusDoneCallback (XIC, XPointer, XPointer) 569 { 570 return; 571 } 572 573 void 574 StatusDrawCallback (XIC ic, XPointer client_data, XIMStatusDrawCallbackStruct *call_data) 575 { 576 preedit_data_t* pPreeditData = (preedit_data_t*)client_data; 577 if( pPreeditData->bIsMultilingual ) 578 { 579 // IIIMP 580 XIMUnicodeText *cbtext = (XIMUnicodeText *)call_data->data.text; 581 ::vcl::I18NStatus::get().setStatusText( String( cbtext->string.utf16_char, call_data->data.text->length ) ); 582 XIMUnicodeCharacterSubset* pSubset = NULL; 583 if( ! XGetICValues( ic, 584 XNUnicodeCharacterSubset, & pSubset, 585 NULL ) 586 && pSubset ) 587 { 588 ::vcl::I18NStatus::get().changeIM( String( ByteString( pSubset->name ), RTL_TEXTENCODING_UTF8 ) ); 589 #if OSL_DEBUG_LEVEL > 1 590 fprintf( stderr, "got XNUnicodeCharacterSubset\n %d\n %d\n %s\n %d\n", pSubset->index, pSubset->subset_id, pSubset->name, pSubset->is_active ); 591 #endif 592 } 593 } 594 else if( call_data->type == XIMTextType ) 595 { 596 String aText; 597 if( call_data->data.text ) 598 { 599 // XIM with text 600 sal_Char* pMBString = NULL; 601 size_t nLength = 0; 602 if( call_data->data.text->encoding_is_wchar ) 603 { 604 if( call_data->data.text->string.wide_char ) 605 { 606 wchar_t* pWString = call_data->data.text->string.wide_char; 607 size_t nBytes = wcstombs( NULL, pWString, 1024 ); 608 pMBString = (sal_Char*)alloca( nBytes+1 ); 609 nLength = wcstombs( pMBString, pWString, nBytes+1 ); 610 } 611 } 612 else 613 { 614 if( call_data->data.text->string.multi_byte ) 615 { 616 pMBString = call_data->data.text->string.multi_byte; 617 nLength = strlen( pMBString ); 618 } 619 } 620 if( nLength ) 621 aText = String( pMBString, nLength, gsl_getSystemTextEncoding() ); 622 } 623 ::vcl::I18NStatus::get().setStatusText( aText ); 624 } 625 #if OSL_DEBUG_LEVEL > 1 626 else 627 fprintf( stderr, "XIMStatusDataType %s not supported\n", 628 call_data->type == XIMBitmapType ? "XIMBitmapType" : ByteString::CreateFromInt32( call_data->type ).GetBuffer() ); 629 #endif 630 return; 631 } 632 633 void 634 SwitchIMCallback (XIC, XPointer, XPointer call_data) 635 { 636 XIMSwitchIMNotifyCallbackStruct* pCallData = (XIMSwitchIMNotifyCallbackStruct*)call_data; 637 ::vcl::I18NStatus::get().changeIM( String( ByteString( pCallData->to->name ), RTL_TEXTENCODING_UTF8 ) ); 638 } 639 640 // ---------------------------------------------------------------------------------- 641 // 642 // vii. destroy callbacks: internally disable all IC/IM calls 643 // 644 // ---------------------------------------------------------------------------------- 645 646 void 647 IC_IMDestroyCallback (XIM, XPointer client_data, XPointer) 648 { 649 SalI18N_InputContext *pContext = (SalI18N_InputContext*)client_data; 650 if (pContext != NULL) 651 pContext->HandleDestroyIM(); 652 } 653 654 void 655 IM_IMDestroyCallback (XIM, XPointer client_data, XPointer) 656 { 657 SalI18N_InputMethod *pMethod = (SalI18N_InputMethod*)client_data; 658 if (pMethod != NULL) 659 pMethod->HandleDestroyIM(); 660 } 661