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
GetLinguMutex()78 osl::Mutex & GetLinguMutex()
79 {
80 return LinguMutex::get();
81 }
82
83 ///////////////////////////////////////////////////////////////////////////
84
GetLocaleDataWrapper(sal_Int16 nLang)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 */
GetTextEncoding(sal_Int16 nLanguage)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
Minimum(sal_Int32 n1,sal_Int32 n2,sal_Int32 n3)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
IntArray2D(int nDim1,int nDim2)195 IntArray2D::IntArray2D( int nDim1, int nDim2 )
196 {
197 n1 = nDim1;
198 n2 = nDim2;
199 pData = new sal_Int32[n1 * n2];
200 }
201
~IntArray2D()202 IntArray2D::~IntArray2D()
203 {
204 delete[] pData;
205 }
206
Value(int i,int k)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
LevDistance(const OUString & rTxt1,const OUString & rTxt2)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
IsUseDicList(const PropertyValues & rProperties,const uno::Reference<XPropertySet> & rxProp)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
IsIgnoreControlChars(const PropertyValues & rProperties,const uno::Reference<XPropertySet> & rxProp)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
lcl_HasHyphInfo(const uno::Reference<XDictionaryEntry> & xEntry)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
SearchDicList(const uno::Reference<XDictionaryList> & xDicList,const OUString & rWord,sal_Int16 nLanguage,sal_Bool bSearchPosDics,sal_Bool bSearchSpellEntry)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
SaveDictionaries(const uno::Reference<XDictionaryList> & xDicList)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
AddEntryToDic(uno::Reference<XDictionary> & rxDic,const OUString & rWord,sal_Bool bIsNeg,const OUString & rRplcTxt,sal_Int16,sal_Bool bStripDot)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
LocaleToLanguage(const Locale & rLocale)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
LanguageToLocale(Locale & rLocale,LanguageType eLang)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
CreateLocale(LanguageType eLang)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
LangSeqToLocaleSeq(const uno::Sequence<sal_Int16> & rLangSeq)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 >
LocaleSeqToLangSeq(uno::Sequence<Locale> & rLocaleSeq)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
IsReadOnly(const String & rURL,sal_Bool * pbExist)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
GetAltSpelling(sal_Int16 & rnChgPos,sal_Int16 & rnChgLen,OUString & rRplc,uno::Reference<XHyphenatedWord> & rxHyphWord)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
GetOrigWordPos(const OUString & rOrigWord,sal_Int16 nPos)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
GetPosInWordToCheck(const OUString & rTxt,sal_Int32 nPos)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
RebuildHyphensAndControlChars(const OUString & rOrigWord,uno::Reference<XHyphenatedWord> & rxHyphWord)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
lcl_GetCharClass()706 static CharClass & lcl_GetCharClass()
707 {
708 static CharClass aCC( CreateLocale( LANGUAGE_ENGLISH_US ) );
709 return aCC;
710 }
711
712
lcl_GetCharClassMutex()713 osl::Mutex & lcl_GetCharClassMutex()
714 {
715 static osl::Mutex aMutex;
716 return aMutex;
717 }
718
719
IsUpper(const String & rText,xub_StrLen nPos,xub_StrLen nLen,sal_Int16 nLanguage)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
IsLower(const String & rText,xub_StrLen nPos,xub_StrLen nLen,sal_Int16 nLanguage)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
ToLower(const String & rText,sal_Int16 nLanguage)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
ToUpper(const String & rText,sal_Int16 nLanguage)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
ToTitle(const String & rText,sal_Int16 nLanguage)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
ToLower(const sal_Unicode cChar,sal_Int16 nLanguage)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
ToUpper(const sal_Unicode cChar,sal_Int16 nLanguage)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
HasDigits(const OUString & rText)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
IsNumeric(const String & rText)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
GetOneInstanceService(const char * pServiceName)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
GetLinguProperties()901 uno::Reference< XPropertySet > GetLinguProperties()
902 {
903 return uno::Reference< XPropertySet > (
904 GetOneInstanceService( SN_LINGU_PROPERTIES ), UNO_QUERY );
905 }
906
GetSearchableDictionaryList()907 uno::Reference< XSearchableDictionaryList > GetSearchableDictionaryList()
908 {
909 return uno::Reference< XSearchableDictionaryList > (
910 GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY );
911 }
912
GetDictionaryList()913 uno::Reference< XDictionaryList > GetDictionaryList()
914 {
915 return uno::Reference< XDictionaryList > (
916 GetOneInstanceService( SN_DICTIONARY_LIST ), UNO_QUERY );
917 }
918
GetIgnoreAllList()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
AppExitListener()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
~AppExitListener()950 AppExitListener::~AppExitListener()
951 {
952 }
953
954
Activate()955 void AppExitListener::Activate()
956 {
957 if (xDesktop.is())
958 xDesktop->addTerminateListener( this );
959 }
960
961
Deactivate()962 void AppExitListener::Deactivate()
963 {
964 if (xDesktop.is())
965 xDesktop->removeTerminateListener( this );
966 }
967
968
969 void SAL_CALL
disposing(const EventObject & rEvtSource)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
queryTermination(const EventObject &)983 AppExitListener::queryTermination( const EventObject& /*rEvtSource*/ )
984 throw(frame::TerminationVetoException, RuntimeException)
985 {
986 //MutexGuard aGuard( GetLinguMutex() );
987 }
988
989
990 void SAL_CALL
notifyTermination(const EventObject & rEvtSource)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