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_linguistic.hxx" 26 #include <tools/string.hxx> 27 #include <tools/fsys.hxx> 28 #include <tools/debug.hxx> 29 #include <unotools/pathoptions.hxx> 30 #include <svl/lngmisc.hxx> 31 #include <ucbhelper/content.hxx> 32 #include <i18npool/mslangid.hxx> 33 #include <com/sun/star/ucb/XCommandEnvironment.hpp> 34 #include <com/sun/star/beans/XPropertySet.hpp> 35 #include <com/sun/star/beans/XFastPropertySet.hpp> 36 #include <com/sun/star/beans/XPropertyChangeListener.hpp> 37 #include <com/sun/star/frame/XTerminateListener.hpp> 38 #include <com/sun/star/frame/XDesktop.hpp> 39 #include <com/sun/star/frame/XStorable.hpp> 40 41 #include <com/sun/star/beans/PropertyValues.hpp> 42 #include <com/sun/star/uno/Sequence.hxx> 43 #include <com/sun/star/uno/Reference.h> 44 #include <com/sun/star/linguistic2/DictionaryType.hpp> 45 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> 46 #include <unotools/processfactory.hxx> 47 #include <unotools/localedatawrapper.hxx> 48 #include <unotools/syslocale.hxx> 49 50 #include <rtl/instance.hxx> 51 52 #include "linguistic/misc.hxx" 53 #include "defs.hxx" 54 #include "linguistic/lngprops.hxx" 55 #include "linguistic/hyphdta.hxx" 56 #include <i18npool/mslangid.hxx> 57 58 using namespace utl; 59 using namespace osl; 60 using namespace rtl; 61 using namespace com::sun::star; 62 using namespace com::sun::star::beans; 63 using namespace com::sun::star::lang; 64 using namespace com::sun::star::uno; 65 using namespace com::sun::star::i18n; 66 using namespace com::sun::star::linguistic2; 67 68 namespace linguistic 69 { 70 71 /////////////////////////////////////////////////////////////////////////// 72 73 //!! multi-thread safe mutex for all platforms !! 74 struct LinguMutex : public rtl::Static< osl::Mutex, LinguMutex > 75 { 76 }; 77 78 osl::Mutex & GetLinguMutex() 79 { 80 return LinguMutex::get(); 81 } 82 83 /////////////////////////////////////////////////////////////////////////// 84 85 LocaleDataWrapper & GetLocaleDataWrapper( sal_Int16 nLang ) 86 { 87 static LocaleDataWrapper aLclDtaWrp( 88 getProcessServiceFactory(), 89 CreateLocale( SvtSysLocale().GetUILanguage() ) ); 90 91 const Locale &rLcl = aLclDtaWrp.getLoadedLocale(); 92 Locale aLcl( CreateLocale( nLang ) ); 93 if (aLcl.Language != rLcl.Language || 94 aLcl.Country != rLcl.Country || 95 aLcl.Variant != rLcl.Variant) 96 aLclDtaWrp.setLocale( aLcl ); 97 return aLclDtaWrp; 98 } 99 100 /////////////////////////////////////////////////////////////////////////// 101 102 /** 103 returns text-encoding used for ByteString unicode String conversion 104 */ 105 rtl_TextEncoding GetTextEncoding( sal_Int16 nLanguage ) 106 { 107 DBG_ASSERT( nLanguage != LANGUAGE_NONE, "invalid language argument" ); 108 static sal_Int16 nLastLanguage = LANGUAGE_NONE; 109 110 // set default value for unknown languages 111 static rtl_TextEncoding nEncoding = RTL_TEXTENCODING_DONTKNOW; 112 113 if (nLastLanguage != nLanguage) 114 { 115 //!! IPR uses textencodings Latin-1, Latin-2, Latin-5 and Latin-7 !! 116 117 nLastLanguage = nLanguage; 118 switch (nLanguage) 119 { 120 case LANGUAGE_GERMAN : 121 case LANGUAGE_GERMAN_SWISS : 122 case LANGUAGE_ENGLISH_US : 123 case LANGUAGE_ENGLISH_UK : 124 case LANGUAGE_FRENCH : 125 case LANGUAGE_ITALIAN : 126 case LANGUAGE_SPANISH : 127 case LANGUAGE_CATALAN : 128 case LANGUAGE_PORTUGUESE : 129 case LANGUAGE_PORTUGUESE_BRAZILIAN : 130 case LANGUAGE_DANISH : 131 case LANGUAGE_DUTCH : 132 case LANGUAGE_SWEDISH : 133 case LANGUAGE_FINNISH : 134 case LANGUAGE_NORWEGIAN_BOKMAL : 135 case LANGUAGE_NORWEGIAN_NYNORSK : 136 case LANGUAGE_AFRIKAANS : 137 case LANGUAGE_ENGLISH_EIRE : 138 case LANGUAGE_ENGLISH_AUS : 139 #ifdef WNT 140 nEncoding = RTL_TEXTENCODING_MS_1252; break; 141 #else 142 nEncoding = RTL_TEXTENCODING_ISO_8859_1; break; 143 #endif 144 case LANGUAGE_CZECH : 145 case LANGUAGE_HUNGARIAN : 146 case LANGUAGE_POLISH : 147 #ifdef WNT 148 nEncoding = RTL_TEXTENCODING_MS_1250; break; 149 #else 150 nEncoding = RTL_TEXTENCODING_ISO_8859_2; break; 151 #endif 152 case LANGUAGE_RUSSIAN : 153 #ifdef WNT 154 nEncoding = RTL_TEXTENCODING_MS_1251; break; 155 #else 156 nEncoding = RTL_TEXTENCODING_ISO_8859_5; break; 157 #endif 158 case LANGUAGE_GREEK : 159 #ifdef WNT 160 nEncoding = RTL_TEXTENCODING_MS_1253; break; 161 #else 162 nEncoding = RTL_TEXTENCODING_ISO_8859_7; break; 163 #endif 164 default: 165 DBG_ASSERT( 0, "unexpected language" ); 166 } 167 } 168 169 return nEncoding; 170 } 171 172 /////////////////////////////////////////////////////////////////////////// 173 174 static inline sal_Int32 Minimum( sal_Int32 n1, sal_Int32 n2, sal_Int32 n3 ) 175 { 176 sal_Int32 nMin = n1 < n2 ? n1 : n2; 177 return nMin < n3 ? nMin : n3; 178 } 179 180 /////////////////////////////////////////////////////////////////////////// 181 182 class IntArray2D 183 { 184 private: 185 sal_Int32 *pData; 186 int n1, n2; 187 188 public: 189 IntArray2D( int nDim1, int nDim2 ); 190 ~IntArray2D(); 191 192 sal_Int32 & Value( int i, int k ); 193 }; 194 195 IntArray2D::IntArray2D( int nDim1, int nDim2 ) 196 { 197 n1 = nDim1; 198 n2 = nDim2; 199 pData = new sal_Int32[n1 * n2]; 200 } 201 202 IntArray2D::~IntArray2D() 203 { 204 delete[] pData; 205 } 206 207 sal_Int32 & IntArray2D::Value( int i, int k ) 208 { 209 DBG_ASSERT( 0 <= i && i < n1, "first index out of range" ); 210 DBG_ASSERT( 0 <= k && k < n2, "first index out of range" ); 211 DBG_ASSERT( i * n2 + k < n1 * n2, "index out of range" ); 212 return pData[ i * n2 + k ]; 213 } 214 215 216 sal_Int32 LevDistance( const OUString &rTxt1, const OUString &rTxt2 ) 217 { 218 sal_Int32 nLen1 = rTxt1.getLength(); 219 sal_Int32 nLen2 = rTxt2.getLength(); 220 221 if (nLen1 == 0) 222 return nLen2; 223 if (nLen2 == 0) 224 return nLen1; 225 226 IntArray2D aData( nLen1 + 1, nLen2 + 1 ); 227 228 sal_Int32 i, k; 229 for (i = 0; i <= nLen1; ++i) 230 aData.Value(i, 0) = i; 231 for (k = 0; k <= nLen2; ++k) 232 aData.Value(0, k) = k; 233 for (i = 1; i <= nLen1; ++i) 234 { 235 for (k = 1; k <= nLen2; ++k) 236 { 237 sal_Unicode c1i = rTxt1.getStr()[i - 1]; 238 sal_Unicode c2k = rTxt2.getStr()[k - 1]; 239 sal_Int32 nCost = c1i == c2k ? 0 : 1; 240 sal_Int32 nNew = Minimum( aData.Value(i-1, k ) + 1, 241 aData.Value(i , k-1) + 1, 242 aData.Value(i-1, k-1) + nCost ); 243 // take transposition (exchange with left or right char) in account 244 if (2 < i && 2 < k) 245 { 246 int nT = aData.Value(i-2, k-2) + 1; 247 if (rTxt1.getStr()[i - 2] != c1i) 248 ++nT; 249 if (rTxt2.getStr()[k - 2] != c2k) 250 ++nT; 251 if (nT < nNew) 252 nNew = nT; 253 } 254 255 aData.Value(i, k) = nNew; 256 } 257 } 258 sal_Int32 nDist = aData.Value(nLen1, nLen2); 259 return nDist; 260 } 261 262 /////////////////////////////////////////////////////////////////////////// 263 264 sal_Bool IsUseDicList( const PropertyValues &rProperties, 265 const uno::Reference< XPropertySet > &rxProp ) 266 { 267 sal_Bool bRes = sal_True; 268 269 sal_Int32 nLen = rProperties.getLength(); 270 const PropertyValue *pVal = rProperties.getConstArray(); 271 sal_Int32 i; 272 273 for ( i = 0; i < nLen; ++i) 274 { 275 if (UPH_IS_USE_DICTIONARY_LIST == pVal[i].Handle) 276 { 277 pVal[i].Value >>= bRes; 278 break; 279 } 280 } 281 if (i >= nLen) // no temporary value found in 'rProperties' 282 { 283 uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY ); 284 if (xFast.is()) 285 xFast->getFastPropertyValue( UPH_IS_USE_DICTIONARY_LIST ) >>= bRes; 286 } 287 288 return bRes; 289 } 290 291 292 sal_Bool IsIgnoreControlChars( const PropertyValues &rProperties, 293 const uno::Reference< XPropertySet > &rxProp ) 294 { 295 sal_Bool bRes = sal_True; 296 297 sal_Int32 nLen = rProperties.getLength(); 298 const PropertyValue *pVal = rProperties.getConstArray(); 299 sal_Int32 i; 300 301 for ( i = 0; i < nLen; ++i) 302 { 303 if (UPH_IS_IGNORE_CONTROL_CHARACTERS == pVal[i].Handle) 304 { 305 pVal[i].Value >>= bRes; 306 break; 307 } 308 } 309 if (i >= nLen) // no temporary value found in 'rProperties' 310 { 311 uno::Reference< XFastPropertySet > xFast( rxProp, UNO_QUERY ); 312 if (xFast.is()) 313 xFast->getFastPropertyValue( UPH_IS_IGNORE_CONTROL_CHARACTERS ) >>= bRes; 314 } 315 316 return bRes; 317 } 318 319 320 static sal_Bool lcl_HasHyphInfo( const uno::Reference<XDictionaryEntry> &xEntry ) 321 { 322 sal_Bool bRes = sal_False; 323 if (xEntry.is()) 324 { 325 // there has to be (at least one) '=' denoting a hyphenation position 326 // and it must not be before any character of the word 327 sal_Int32 nIdx = xEntry->getDictionaryWord().indexOf( '=' ); 328 bRes = nIdx != -1 && nIdx != 0; 329 } 330 return bRes; 331 } 332 333 334 uno::Reference< XDictionaryEntry > SearchDicList( 335 const uno::Reference< XDictionaryList > &xDicList, 336 const OUString &rWord, sal_Int16 nLanguage, 337 sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry ) 338 { 339 MutexGuard aGuard( GetLinguMutex() ); 340 341 uno::Reference< XDictionaryEntry > xEntry; 342 343 if (!xDicList.is()) 344 return xEntry; 345 346 const uno::Sequence< uno::Reference< XDictionary > > 347 aDics( xDicList->getDictionaries() ); 348 const uno::Reference< XDictionary > 349 *pDic = aDics.getConstArray(); 350 sal_Int32 nDics = xDicList->getCount(); 351 352 sal_Int32 i; 353 for (i = 0; i < nDics; i++) 354 { 355 uno::Reference< XDictionary > axDic( pDic[i], UNO_QUERY ); 356 357 DictionaryType eType = axDic->getDictionaryType(); 358 sal_Int16 nLang = LocaleToLanguage( axDic->getLocale() ); 359 360 if ( axDic.is() && axDic->isActive() 361 && (nLang == nLanguage || nLang == LANGUAGE_NONE) ) 362 { 363 DBG_ASSERT( eType != DictionaryType_MIXED, 364 "lng : unexpected dictionary type" ); 365 366 if ( (!bSearchPosDics && eType == DictionaryType_NEGATIVE) 367 || ( bSearchPosDics && eType == DictionaryType_POSITIVE)) 368 { 369 if ( (xEntry = axDic->getEntry( rWord )).is() ) 370 { 371 if (bSearchSpellEntry || lcl_HasHyphInfo( xEntry )) 372 break; 373 } 374 xEntry = 0; 375 } 376 } 377 } 378 379 return xEntry; 380 } 381 382 383 sal_Bool SaveDictionaries( const uno::Reference< XDictionaryList > &xDicList ) 384 { 385 if (!xDicList.is()) 386 return sal_True; 387 388 sal_Bool bRet = sal_True; 389 390 Sequence< uno::Reference< XDictionary > > aDics( xDicList->getDictionaries() ); 391 const uno::Reference< XDictionary > *pDic = aDics.getConstArray(); 392 sal_Int32 nCount = aDics.getLength(); 393 for (sal_Int32 i = 0; i < nCount; i++) 394 { 395 try 396 { 397 uno::Reference< frame::XStorable > xStor( pDic[i], UNO_QUERY ); 398 if (xStor.is()) 399 { 400 if (!xStor->isReadonly() && xStor->hasLocation()) 401 xStor->store(); 402 } 403 } 404 catch(uno::Exception &) 405 { 406 bRet = sal_False; 407 } 408 } 409 410 return bRet; 411 } 412 413 414 sal_uInt8 AddEntryToDic( 415 uno::Reference< XDictionary > &rxDic, 416 const OUString &rWord, sal_Bool bIsNeg, 417 const OUString &rRplcTxt, sal_Int16 /* nRplcLang */, 418 sal_Bool bStripDot ) 419 { 420 if (!rxDic.is()) 421 return DIC_ERR_NOT_EXISTS; 422 423 OUString aTmp( rWord ); 424 if (bStripDot) 425 { 426 sal_Int32 nLen = rWord.getLength(); 427 if (nLen > 0 && '.' == rWord[ nLen - 1]) 428 { 429 // remove trailing '.' 430 // (this is the official way to do this :-( ) 431 aTmp = aTmp.copy( 0, nLen - 1 ); 432 } 433 } 434 sal_Bool bAddOk = rxDic->add( aTmp, bIsNeg, rRplcTxt ); 435 436 sal_uInt8 nRes = DIC_ERR_NONE; 437 if (!bAddOk) 438 { 439 if (rxDic->isFull()) 440 nRes = DIC_ERR_FULL; 441 else 442 { 443 uno::Reference< frame::XStorable > xStor( rxDic, UNO_QUERY ); 444 if (xStor.is() && xStor->isReadonly()) 445 nRes = DIC_ERR_READONLY; 446 else 447 nRes = DIC_ERR_UNKNOWN; 448 } 449 } 450 451 return nRes; 452 } 453 454 455 /////////////////////////////////////////////////////////////////////////// 456 457 LanguageType LocaleToLanguage( const Locale& rLocale ) 458 { 459 // empty Locale -> LANGUAGE_NONE 460 if ( rLocale.Language.getLength() == 0 ) 461 return LANGUAGE_NONE; 462 463 return MsLangId::convertLocaleToLanguage( rLocale ); 464 } 465 466 467 Locale& LanguageToLocale( Locale& rLocale, LanguageType eLang ) 468 { 469 if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) 470 MsLangId::convertLanguageToLocale( eLang, rLocale ); 471 472 return rLocale; 473 } 474 475 Locale CreateLocale( LanguageType eLang ) 476 { 477 Locale aLocale; 478 if ( eLang != LANGUAGE_NONE /* && eLang != LANGUAGE_SYSTEM */) 479 return MsLangId::convertLanguageToLocale( eLang ); 480 481 return aLocale; 482 } 483 484 uno::Sequence< Locale > LangSeqToLocaleSeq( const uno::Sequence< sal_Int16 > &rLangSeq ) 485 { 486 const sal_Int16 *pLang = rLangSeq.getConstArray(); 487 sal_Int32 nCount = rLangSeq.getLength(); 488 489 uno::Sequence< Locale > aLocales( nCount ); 490 Locale *pLocale = aLocales.getArray(); 491 for (sal_Int32 i = 0; i < nCount; ++i) 492 { 493 LanguageToLocale( pLocale[i], pLang[ i ] ); 494 } 495 496 return aLocales; 497 } 498 499 uno::Sequence< sal_Int16 > 500 LocaleSeqToLangSeq( uno::Sequence< Locale > &rLocaleSeq ) 501 { 502 const Locale *pLocale = rLocaleSeq.getConstArray(); 503 sal_Int32 nCount = rLocaleSeq.getLength(); 504 505 uno::Sequence< sal_Int16 > aLangs( nCount ); 506 sal_Int16 *pLang = aLangs.getArray(); 507 for (sal_Int32 i = 0; i < nCount; ++i) 508 { 509 pLang[i] = LocaleToLanguage( pLocale[i] ); 510 } 511 512 return aLangs; 513 } 514 515 /////////////////////////////////////////////////////////////////////////// 516 517 sal_Bool IsReadOnly( const String &rURL, sal_Bool *pbExist ) 518 { 519 sal_Bool bRes = sal_False; 520 sal_Bool bExists = sal_False; 521 522 if (rURL.Len() > 0) 523 { 524 try 525 { 526 uno::Reference< ::com::sun::star::ucb::XCommandEnvironment > xCmdEnv; 527 ::ucbhelper::Content aContent( rURL, xCmdEnv ); 528 529 bExists = aContent.isDocument(); 530 if (bExists) 531 { 532 Any aAny( aContent.getPropertyValue( A2OU( "IsReadOnly" ) ) ); 533 aAny >>= bRes; 534 } 535 } 536 catch (Exception &) 537 { 538 bRes = sal_True; 539 } 540 } 541 542 if (pbExist) 543 *pbExist = bExists; 544 return bRes; 545 } 546 547 /////////////////////////////////////////////////////////////////////////// 548 549 550 static sal_Bool GetAltSpelling( sal_Int16 &rnChgPos, sal_Int16 &rnChgLen, OUString &rRplc, 551 uno::Reference< XHyphenatedWord > &rxHyphWord ) 552 { 553 sal_Bool bRes = rxHyphWord->isAlternativeSpelling(); 554 if (bRes) 555 { 556 OUString aWord( rxHyphWord->getWord() ), 557 aHyphenatedWord( rxHyphWord->getHyphenatedWord() ); 558 sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos(); 559 /*sal_Int16 nHyphenPos = rxHyphWord->getHyphenPos()*/; 560 const sal_Unicode *pWord = aWord.getStr(), 561 *pAltWord = aHyphenatedWord.getStr(); 562 563 // at least char changes directly left or right to the hyphen 564 // should(!) be handled properly... 565 //! nHyphenationPos and nHyphenPos differ at most by 1 (see above) 566 //! Beware: eg "Schiffahrt" in German (pre spelling reform) 567 //! proves to be a bit nasty (nChgPosLeft and nChgPosRight overlap 568 //! to an extend.) 569 570 // find first different char from left 571 sal_Int32 nPosL = 0, 572 nAltPosL = 0; 573 for (sal_Int16 i = 0 ; pWord[ nPosL ] == pAltWord[ nAltPosL ]; nPosL++, nAltPosL++, i++) 574 { 575 // restrict changes area beginning to the right to 576 // the char immediately following the hyphen. 577 //! serves to insert the additional "f" in "Schiffahrt" at 578 //! position 5 rather than position 6. 579 if (i >= nHyphenationPos + 1) 580 break; 581 } 582 583 // find first different char from right 584 sal_Int32 nPosR = aWord.getLength() - 1, 585 nAltPosR = aHyphenatedWord.getLength() - 1; 586 for ( ; nPosR >= nPosL && nAltPosR >= nAltPosL 587 && pWord[ nPosR ] == pAltWord[ nAltPosR ]; 588 nPosR--, nAltPosR--) 589 ; 590 591 rnChgPos = sal::static_int_cast< sal_Int16 >(nPosL); 592 rnChgLen = sal::static_int_cast< sal_Int16 >(nPosR - nPosL + 1); 593 DBG_ASSERT( rnChgLen >= 0, "nChgLen < 0"); 594 595 sal_Int32 nTxtStart = nPosL; 596 sal_Int32 nTxtLen = nAltPosL - nPosL + 1; 597 rRplc = aHyphenatedWord.copy( nTxtStart, nTxtLen ); 598 } 599 return bRes; 600 } 601 602 603 static sal_Int16 GetOrigWordPos( const OUString &rOrigWord, sal_Int16 nPos ) 604 { 605 sal_Int32 nLen = rOrigWord.getLength(); 606 sal_Int32 i = -1; 607 while (nPos >= 0 && i++ < nLen) 608 { 609 sal_Unicode cChar = rOrigWord[i]; 610 sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar ); 611 if (!bSkip) 612 --nPos; 613 } 614 return sal::static_int_cast< sal_Int16 >((0 <= i && i < nLen) ? i : -1); 615 } 616 617 618 sal_Int32 GetPosInWordToCheck( const OUString &rTxt, sal_Int32 nPos ) 619 { 620 sal_Int32 nRes = -1; 621 sal_Int32 nLen = rTxt.getLength(); 622 if (0 <= nPos && nPos < nLen) 623 { 624 nRes = 0; 625 for (sal_Int32 i = 0; i < nPos; ++i) 626 { 627 sal_Unicode cChar = rTxt[i]; 628 sal_Bool bSkip = IsHyphen( cChar ) || IsControlChar( cChar ); 629 if (!bSkip) 630 ++nRes; 631 } 632 } 633 return nRes; 634 } 635 636 637 uno::Reference< XHyphenatedWord > RebuildHyphensAndControlChars( 638 const OUString &rOrigWord, 639 uno::Reference< XHyphenatedWord > &rxHyphWord ) 640 { 641 uno::Reference< XHyphenatedWord > xRes; 642 if (rOrigWord.getLength() && rxHyphWord.is()) 643 { 644 sal_Int16 nChgPos = 0, 645 nChgLen = 0; 646 OUString aRplc; 647 sal_Bool bAltSpelling = GetAltSpelling( nChgPos, nChgLen, aRplc, rxHyphWord ); 648 #if OSL_DEBUG_LEVEL > 1 649 OUString aWord( rxHyphWord->getWord() ); 650 #endif 651 652 OUString aOrigHyphenatedWord; 653 sal_Int16 nOrigHyphenPos = -1; 654 sal_Int16 nOrigHyphenationPos = -1; 655 if (!bAltSpelling) 656 { 657 aOrigHyphenatedWord = rOrigWord; 658 nOrigHyphenPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenPos() ); 659 nOrigHyphenationPos = GetOrigWordPos( rOrigWord, rxHyphWord->getHyphenationPos() ); 660 } 661 else 662 { 663 //! should at least work with the German words 664 //! B�-c-k-er and Sc-hif-fah-rt 665 666 OUString aLeft, aRight; 667 sal_Int16 nPos = GetOrigWordPos( rOrigWord, nChgPos ); 668 669 // get words like Sc-hif-fah-rt to work correct 670 sal_Int16 nHyphenationPos = rxHyphWord->getHyphenationPos(); 671 if (nChgPos > nHyphenationPos) 672 --nPos; 673 674 aLeft = rOrigWord.copy( 0, nPos ); 675 aRight = rOrigWord.copy( nPos + nChgLen ); 676 677 aOrigHyphenatedWord = aLeft; 678 aOrigHyphenatedWord += aRplc; 679 aOrigHyphenatedWord += aRight; 680 681 nOrigHyphenPos = sal::static_int_cast< sal_Int16 >(aLeft.getLength() + 682 rxHyphWord->getHyphenPos() - nChgPos); 683 nOrigHyphenationPos = GetOrigWordPos( rOrigWord, nHyphenationPos ); 684 } 685 686 if (nOrigHyphenPos == -1 || nOrigHyphenationPos == -1) 687 { 688 DBG_ASSERT( 0, "failed to get nOrigHyphenPos or nOrigHyphenationPos" ); 689 } 690 else 691 { 692 sal_Int16 nLang = LocaleToLanguage( rxHyphWord->getLocale() ); 693 xRes = new HyphenatedWord( 694 rOrigWord, nLang, nOrigHyphenationPos, 695 aOrigHyphenatedWord, nOrigHyphenPos ); 696 } 697 698 } 699 return xRes; 700 } 701 702 703 /////////////////////////////////////////////////////////////////////////// 704 705 706 static CharClass & lcl_GetCharClass() 707 { 708 static CharClass aCC( CreateLocale( LANGUAGE_ENGLISH_US ) ); 709 return aCC; 710 } 711 712 713 osl::Mutex & lcl_GetCharClassMutex() 714 { 715 static osl::Mutex aMutex; 716 return aMutex; 717 } 718 719 720 sal_Bool IsUpper( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage ) 721 { 722 MutexGuard aGuard( lcl_GetCharClassMutex() ); 723 724 CharClass &rCC = lcl_GetCharClass(); 725 rCC.setLocale( CreateLocale( nLanguage ) ); 726 sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen ); 727 return (nFlags & KCharacterType::UPPER) 728 && !(nFlags & KCharacterType::LOWER); 729 } 730 731 732 sal_Bool IsLower( const String &rText, xub_StrLen nPos, xub_StrLen nLen, sal_Int16 nLanguage ) 733 { 734 MutexGuard aGuard( lcl_GetCharClassMutex() ); 735 736 CharClass &rCC = lcl_GetCharClass(); 737 rCC.setLocale( CreateLocale( nLanguage ) ); 738 sal_Int32 nFlags = rCC.getStringType( rText, nPos, nLen ); 739 return (nFlags & KCharacterType::LOWER) 740 && !(nFlags & KCharacterType::UPPER); 741 } 742 743 744 String ToLower( const String &rText, sal_Int16 nLanguage ) 745 { 746 MutexGuard aGuard( lcl_GetCharClassMutex() ); 747 748 CharClass &rCC = lcl_GetCharClass(); 749 rCC.setLocale( CreateLocale( nLanguage ) ); 750 return rCC.lower( rText ); 751 } 752 753 754 String ToUpper( const String &rText, sal_Int16 nLanguage ) 755 { 756 MutexGuard aGuard( lcl_GetCharClassMutex() ); 757 758 CharClass &rCC = lcl_GetCharClass(); 759 rCC.setLocale( CreateLocale( nLanguage ) ); 760 return rCC.upper( rText ); 761 } 762 763 764 String ToTitle( const String &rText, sal_Int16 nLanguage ) 765 { 766 MutexGuard aGuard( lcl_GetCharClassMutex() ); 767 768 CharClass &rCC = lcl_GetCharClass(); 769 rCC.setLocale( CreateLocale( nLanguage ) ); 770 return rCC.toTitle( rText, 0, rText.Len() ); 771 } 772 773 774 sal_Unicode ToLower( const sal_Unicode cChar, sal_Int16 nLanguage ) 775 { 776 MutexGuard aGuard( lcl_GetCharClassMutex() ); 777 778 CharClass &rCC = lcl_GetCharClass(); 779 rCC.setLocale( CreateLocale( nLanguage ) ); 780 return rCC.lower( cChar ).GetChar(0); 781 } 782 783 784 sal_Unicode ToUpper( const sal_Unicode cChar, sal_Int16 nLanguage ) 785 { 786 MutexGuard aGuard( lcl_GetCharClassMutex() ); 787 788 CharClass &rCC = lcl_GetCharClass(); 789 rCC.setLocale( CreateLocale( nLanguage ) ); 790 return rCC.upper( cChar ).GetChar(0); 791 } 792 793 // sorted(!) array of unicode ranges for code points that are exclusively(!) used as numbers 794 // and thus may NOT not be part of names or words like the Chinese/Japanese number characters 795 static const sal_uInt32 the_aDigitZeroes [] = 796 { 797 0x00000030, //0039 ; Decimal # Nd [10] DIGIT ZERO..DIGIT NINE 798 0x00000660, //0669 ; Decimal # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE 799 0x000006F0, //06F9 ; Decimal # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE 800 0x000007C0, //07C9 ; Decimal # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE 801 0x00000966, //096F ; Decimal # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE 802 0x000009E6, //09EF ; Decimal # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE 803 0x00000A66, //0A6F ; Decimal # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE 804 0x00000AE6, //0AEF ; Decimal # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE 805 0x00000B66, //0B6F ; Decimal # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE 806 0x00000BE6, //0BEF ; Decimal # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE 807 0x00000C66, //0C6F ; Decimal # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE 808 0x00000CE6, //0CEF ; Decimal # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE 809 0x00000D66, //0D6F ; Decimal # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE 810 0x00000E50, //0E59 ; Decimal # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE 811 0x00000ED0, //0ED9 ; Decimal # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE 812 0x00000F20, //0F29 ; Decimal # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE 813 0x00001040, //1049 ; Decimal # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE 814 0x00001090, //1099 ; Decimal # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE 815 0x000017E0, //17E9 ; Decimal # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE 816 0x00001810, //1819 ; Decimal # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE 817 0x00001946, //194F ; Decimal # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE 818 0x000019D0, //19D9 ; Decimal # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE 819 0x00001B50, //1B59 ; Decimal # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE 820 0x00001BB0, //1BB9 ; Decimal # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE 821 0x00001C40, //1C49 ; Decimal # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE 822 0x00001C50, //1C59 ; Decimal # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE 823 0x0000A620, //A629 ; Decimal # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE 824 0x0000A8D0, //A8D9 ; Decimal # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE 825 0x0000A900, //A909 ; Decimal # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE 826 0x0000AA50, //AA59 ; Decimal # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE 827 0x0000FF10, //FF19 ; Decimal # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE 828 0x000104A0, //104A9 ; Decimal # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE 829 0x0001D7CE //1D7FF ; Decimal # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE 830 }; 831 832 sal_Bool HasDigits( const OUString &rText ) 833 { 834 static const int nNumDigitZeroes = sizeof(the_aDigitZeroes) / sizeof(the_aDigitZeroes[0]); 835 const sal_Int32 nLen = rText.getLength(); 836 837 sal_Int32 i = 0; 838 while (i < nLen) // for all characters ... 839 { 840 const sal_uInt32 nCodePoint = rText.iterateCodePoints( &i ); // handle unicode surrogates correctly... 841 for (int j = 0; j < nNumDigitZeroes; ++j) // ... check in all 0..9 ranges 842 { 843 sal_uInt32 nDigitZero = the_aDigitZeroes[ j ]; 844 if (nDigitZero > nCodePoint) 845 break; 846 if (/*nDigitZero <= nCodePoint &&*/ nCodePoint <= nDigitZero + 9) 847 return sal_True; 848 } 849 } 850 return sal_False; 851 } 852 853 854 sal_Bool IsNumeric( const String &rText ) 855 { 856 sal_Bool bRes = sal_False; 857 xub_StrLen nLen = rText.Len(); 858 if (nLen) 859 { 860 bRes = sal_True; 861 xub_StrLen i = 0; 862 while (i < nLen) 863 { 864 sal_Unicode cChar = rText.GetChar( i++ ); 865 if ( !((sal_Unicode)'0' <= cChar && cChar <= (sal_Unicode)'9') ) 866 { 867 bRes = sal_False; 868 break; 869 } 870 } 871 } 872 return bRes; 873 } 874 875 876 /////////////////////////////////////////////////////////////////////////// 877 878 uno::Reference< XInterface > GetOneInstanceService( const char *pServiceName ) 879 { 880 uno::Reference< XInterface > xRef; 881 882 if (pServiceName) 883 { 884 uno::Reference< XMultiServiceFactory > xMgr( getProcessServiceFactory() ); 885 if (xMgr.is()) 886 { 887 try 888 { 889 xRef = xMgr->createInstance( A2OU( pServiceName ) ); 890 } 891 catch (uno::Exception &) 892 { 893 DBG_ASSERT( 0, "createInstance failed" ); 894 } 895 } 896 } 897 898 return xRef; 899 } 900 901 uno::Reference< XPropertySet > GetLinguProperties() 902 { 903 return uno::Reference< XPropertySet > ( 904 GetOneInstanceService( SN_LINGU_PROPERTIES ), UNO_QUERY ); 905 } 906 907 uno::Reference< XSearchableDictionaryList > GetSearchableDictionaryList() 908 { 909 return uno::Reference< XSearchableDictionaryList > ( 910 GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY ); 911 } 912 913 uno::Reference< XDictionaryList > GetDictionaryList() 914 { 915 return uno::Reference< XDictionaryList > ( 916 GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY ); 917 } 918 919 uno::Reference< XDictionary > GetIgnoreAllList() 920 { 921 uno::Reference< XDictionary > xRes; 922 uno::Reference< XDictionaryList > xDL( GetDictionaryList() ); 923 if (xDL.is()) 924 xRes = xDL->getDictionaryByName( A2OU("IgnoreAllList") ); 925 return xRes; 926 } 927 928 /////////////////////////////////////////////////////////////////////////// 929 930 AppExitListener::AppExitListener() 931 { 932 // add object to Desktop EventListeners in order to properly call 933 // the AtExit function at appliction exit. 934 uno::Reference< XMultiServiceFactory > xMgr = getProcessServiceFactory(); 935 936 if (xMgr.is()) 937 { 938 try 939 { 940 xDesktop = uno::Reference< frame::XDesktop >( 941 xMgr->createInstance( A2OU( SN_DESKTOP ) ), UNO_QUERY ); 942 } 943 catch (uno::Exception &) 944 { 945 DBG_ASSERT( 0, "createInstance failed" ); 946 } 947 } 948 } 949 950 AppExitListener::~AppExitListener() 951 { 952 } 953 954 955 void AppExitListener::Activate() 956 { 957 if (xDesktop.is()) 958 xDesktop->addTerminateListener( this ); 959 } 960 961 962 void AppExitListener::Deactivate() 963 { 964 if (xDesktop.is()) 965 xDesktop->removeTerminateListener( this ); 966 } 967 968 969 void SAL_CALL 970 AppExitListener::disposing( const EventObject& rEvtSource ) 971 throw(RuntimeException) 972 { 973 MutexGuard aGuard( GetLinguMutex() ); 974 975 if (xDesktop.is() && rEvtSource.Source == xDesktop) 976 { 977 xDesktop = NULL; //! release reference to desktop 978 } 979 } 980 981 982 void SAL_CALL 983 AppExitListener::queryTermination( const EventObject& /*rEvtSource*/ ) 984 throw(frame::TerminationVetoException, RuntimeException) 985 { 986 //MutexGuard aGuard( GetLinguMutex() ); 987 } 988 989 990 void SAL_CALL 991 AppExitListener::notifyTermination( const EventObject& rEvtSource ) 992 throw(RuntimeException) 993 { 994 MutexGuard aGuard( GetLinguMutex() ); 995 996 if (xDesktop.is() && rEvtSource.Source == xDesktop) 997 { 998 AtExit(); 999 } 1000 } 1001 1002 /////////////////////////////////////////////////////////////////////////// 1003 1004 } // namespace linguistic 1005 1006