xref: /core/editeng/source/misc/unolingu.cxx (revision 91fef4a4)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 #include <memory>
22 #include <editeng/unolingu.hxx>
23 #include <com/sun/star/frame/Desktop.hpp>
24 #include <com/sun/star/frame/XStorable.hpp>
25 #include <com/sun/star/lang/XEventListener.hpp>
26 #include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
27 #include <com/sun/star/linguistic2/DictionaryList.hpp>
28 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
29 #include <com/sun/star/linguistic2/LinguProperties.hpp>
30 #include <com/sun/star/linguistic2/XSpellChecker1.hpp>
31 
32 #include <comphelper/processfactory.hxx>
33 #include <cppuhelper/implbase.hxx>
34 #include <i18nlangtag/languagetag.hxx>
35 #include <unotools/lingucfg.hxx>
36 #include <utility>
37 #include <vcl/svapp.hxx>
38 #include <vcl/weld.hxx>
39 #include <linguistic/misc.hxx>
40 #include <editeng/eerdll.hxx>
41 #include <editeng/editrids.hrc>
42 #include <svtools/strings.hrc>
43 #include <unotools/resmgr.hxx>
44 #include <sal/log.hxx>
45 #include <osl/diagnose.h>
46 
47 using namespace ::comphelper;
48 using namespace ::linguistic;
49 using namespace ::com::sun::star;
50 using namespace ::com::sun::star::util;
51 using namespace ::com::sun::star::uno;
52 using namespace ::com::sun::star::lang;
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::frame;
55 using namespace ::com::sun::star::linguistic2;
56 
57 static uno::Reference< XLinguServiceManager2 > GetLngSvcMgr_Impl()
58 {
59     uno::Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
60     uno::Reference< XLinguServiceManager2 > xRes = LinguServiceManager::create(xContext);
61     return xRes;
62 }
63 
64 namespace {
65 
66 //! Dummy implementation in order to avoid loading of lingu DLL
67 //! when only the XSupportedLocales interface is used.
68 //! The dummy accesses the real implementation (and thus loading the DLL)
69 //! when "real" work needs to be done only.
70 class ThesDummy_Impl :
71     public cppu::WeakImplHelper< XThesaurus >
72 {
73     uno::Reference< XThesaurus >              xThes;      // the real one...
74     std::unique_ptr<Sequence< lang::Locale >> pLocaleSeq;
75 
76     void GetCfgLocales();
77 
78     void GetThes_Impl();
79 
80 public:
81     ThesDummy_Impl() {}
82 
83     // XSupportedLocales
84     virtual css::uno::Sequence< css::lang::Locale > SAL_CALL
85         getLocales() override;
86     virtual sal_Bool SAL_CALL
87         hasLocale( const css::lang::Locale& rLocale ) override;
88 
89     // XThesaurus
90     virtual css::uno::Sequence<
91             css::uno::Reference< css::linguistic2::XMeaning > > SAL_CALL
92         queryMeanings( const OUString& rTerm,
93                 const css::lang::Locale& rLocale,
94                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
95 };
96 
97 }
98 
99 void ThesDummy_Impl::GetCfgLocales()
100 {
101     if (pLocaleSeq)
102         return;
103 
104     SvtLinguConfig aCfg;
105     Sequence < OUString > aNodeNames( aCfg.GetNodeNames( "ServiceManager/ThesaurusList" ) );
106     const OUString *pNodeNames = aNodeNames.getConstArray();
107     sal_Int32 nLen = aNodeNames.getLength();
108     pLocaleSeq.reset( new Sequence< lang::Locale >( nLen ) );
109     lang::Locale *pLocale = pLocaleSeq->getArray();
110     for (sal_Int32 i = 0;  i < nLen;  ++i)
111     {
112         pLocale[i] = LanguageTag::convertToLocaleWithFallback( pNodeNames[i] );
113     }
114 }
115 
116 
117 void ThesDummy_Impl::GetThes_Impl()
118 {
119     if (!xThes.is())
120     {
121         uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
122         xThes = xLngSvcMgr->getThesaurus();
123 
124         if (xThes.is())
125         {
126             // no longer needed...
127             pLocaleSeq.reset();
128         }
129     }
130 }
131 
132 
133 uno::Sequence< lang::Locale > SAL_CALL
134         ThesDummy_Impl::getLocales()
135 {
136     GetThes_Impl();
137     if (xThes.is())
138         return xThes->getLocales();
139     else if (!pLocaleSeq)       // if not already loaded save startup time by avoiding loading them now
140         GetCfgLocales();
141     return *pLocaleSeq;
142 }
143 
144 
145 sal_Bool SAL_CALL
146         ThesDummy_Impl::hasLocale( const lang::Locale& rLocale )
147 {
148     GetThes_Impl();
149     if (xThes.is())
150         return xThes->hasLocale( rLocale );
151     else if (!pLocaleSeq)       // if not already loaded save startup time by avoiding loading them now
152         GetCfgLocales();
153     bool bFound = false;
154     sal_Int32 nLen = pLocaleSeq->getLength();
155     const lang::Locale *pLocale = pLocaleSeq->getConstArray();
156     const lang::Locale *pEnd = pLocale + nLen;
157     for ( ;  pLocale < pEnd  &&  !bFound;  ++pLocale)
158     {
159         bFound = pLocale->Language == rLocale.Language  &&
160                  pLocale->Country  == rLocale.Country   &&
161                  pLocale->Variant  == rLocale.Variant;
162     }
163     return bFound;
164 }
165 
166 
167 uno::Sequence< uno::Reference< linguistic2::XMeaning > > SAL_CALL
168         ThesDummy_Impl::queryMeanings(
169                 const OUString& rTerm,
170                 const lang::Locale& rLocale,
171                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
172 {
173     GetThes_Impl();
174     uno::Sequence< uno::Reference< linguistic2::XMeaning > > aRes;
175     OSL_ENSURE( xThes.is(), "Thesaurus missing" );
176     if (xThes.is())
177         aRes = xThes->queryMeanings( rTerm, rLocale, rProperties );
178     return aRes;
179 }
180 
181 namespace {
182 
183 //! Dummy implementation in order to avoid loading of lingu DLL.
184 //! The dummy accesses the real implementation (and thus loading the DLL)
185 //! when it needs to be done only.
186 class SpellDummy_Impl :
187     public cppu::WeakImplHelper< XSpellChecker1 >
188 {
189     uno::Reference< XSpellChecker1 >     xSpell;      // the real one...
190 
191     void    GetSpell_Impl();
192 
193 public:
194 
195     // XSupportedLanguages (for XSpellChecker1)
196     virtual css::uno::Sequence< sal_Int16 > SAL_CALL
197         getLanguages() override;
198     virtual sal_Bool SAL_CALL
199         hasLanguage( sal_Int16 nLanguage ) override;
200 
201     // XSpellChecker1 (same as XSpellChecker but sal_Int16 for language)
202     virtual sal_Bool SAL_CALL
203         isValid( const OUString& rWord, sal_Int16 nLanguage,
204                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
205     virtual css::uno::Reference< css::linguistic2::XSpellAlternatives > SAL_CALL
206         spell( const OUString& rWord, sal_Int16 nLanguage,
207                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
208 };
209 
210 }
211 
212 void SpellDummy_Impl::GetSpell_Impl()
213 {
214     if (!xSpell.is())
215     {
216         uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
217         xSpell.set( xLngSvcMgr->getSpellChecker(), UNO_QUERY );
218     }
219 }
220 
221 
222 uno::Sequence< sal_Int16 > SAL_CALL
223     SpellDummy_Impl::getLanguages()
224 {
225     GetSpell_Impl();
226     if (xSpell.is())
227         return xSpell->getLanguages();
228     else
229         return uno::Sequence< sal_Int16 >();
230 }
231 
232 
233 sal_Bool SAL_CALL
234     SpellDummy_Impl::hasLanguage( sal_Int16 nLanguage )
235 {
236     GetSpell_Impl();
237     bool bRes = false;
238     if (xSpell.is())
239         bRes = xSpell->hasLanguage( nLanguage );
240     return bRes;
241 }
242 
243 
244 sal_Bool SAL_CALL
245     SpellDummy_Impl::isValid( const OUString& rWord, sal_Int16 nLanguage,
246             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
247 {
248     GetSpell_Impl();
249     bool bRes = true;
250     if (xSpell.is())
251         bRes = xSpell->isValid( rWord, nLanguage, rProperties );
252     return bRes;
253 }
254 
255 
256 uno::Reference< linguistic2::XSpellAlternatives > SAL_CALL
257     SpellDummy_Impl::spell( const OUString& rWord, sal_Int16 nLanguage,
258             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
259 {
260     GetSpell_Impl();
261     uno::Reference< linguistic2::XSpellAlternatives > xRes;
262     if (xSpell.is())
263         xRes = xSpell->spell( rWord, nLanguage, rProperties );
264     return xRes;
265 }
266 
267 namespace {
268 
269 //! Dummy implementation in order to avoid loading of lingu DLL.
270 //! The dummy accesses the real implementation (and thus loading the DLL)
271 //! when it needs to be done only.
272 class HyphDummy_Impl :
273     public cppu::WeakImplHelper< XHyphenator >
274 {
275     uno::Reference< XHyphenator >     xHyph;      // the real one...
276 
277     void    GetHyph_Impl();
278 
279 public:
280 
281     // XSupportedLocales
282     virtual css::uno::Sequence<
283             css::lang::Locale > SAL_CALL
284         getLocales() override;
285     virtual sal_Bool SAL_CALL
286         hasLocale( const css::lang::Locale& rLocale ) override;
287 
288     // XHyphenator
289     virtual css::uno::Reference<
290             css::linguistic2::XHyphenatedWord > SAL_CALL
291         hyphenate( const OUString& rWord,
292                 const css::lang::Locale& rLocale,
293                 sal_Int16 nMaxLeading,
294                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
295     virtual css::uno::Reference<
296             css::linguistic2::XHyphenatedWord > SAL_CALL
297         queryAlternativeSpelling( const OUString& rWord,
298                 const css::lang::Locale& rLocale,
299                 sal_Int16 nIndex,
300                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
301     virtual css::uno::Reference<
302             css::linguistic2::XPossibleHyphens > SAL_CALL
303         createPossibleHyphens(
304                 const OUString& rWord,
305                 const css::lang::Locale& rLocale,
306                 const css::uno::Sequence< css::beans::PropertyValue >& rProperties ) override;
307 };
308 
309 }
310 
311 void HyphDummy_Impl::GetHyph_Impl()
312 {
313     if (!xHyph.is())
314     {
315         uno::Reference< XLinguServiceManager2 > xLngSvcMgr( GetLngSvcMgr_Impl() );
316         xHyph = xLngSvcMgr->getHyphenator();
317     }
318 }
319 
320 
321 uno::Sequence< lang::Locale > SAL_CALL
322     HyphDummy_Impl::getLocales()
323 {
324     GetHyph_Impl();
325     if (xHyph.is())
326         return xHyph->getLocales();
327     else
328         return uno::Sequence< lang::Locale >();
329 }
330 
331 
332 sal_Bool SAL_CALL
333     HyphDummy_Impl::hasLocale( const lang::Locale& rLocale )
334 {
335     GetHyph_Impl();
336     bool bRes = false;
337     if (xHyph.is())
338         bRes = xHyph->hasLocale( rLocale );
339     return bRes;
340 }
341 
342 
343 uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL
344     HyphDummy_Impl::hyphenate(
345             const OUString& rWord,
346             const lang::Locale& rLocale,
347             sal_Int16 nMaxLeading,
348             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
349 {
350     GetHyph_Impl();
351     uno::Reference< linguistic2::XHyphenatedWord > xRes;
352     if (xHyph.is())
353         xRes = xHyph->hyphenate( rWord, rLocale, nMaxLeading, rProperties );
354     return xRes;
355 }
356 
357 
358 uno::Reference< linguistic2::XHyphenatedWord > SAL_CALL
359     HyphDummy_Impl::queryAlternativeSpelling(
360             const OUString& rWord,
361             const lang::Locale& rLocale,
362             sal_Int16 nIndex,
363             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
364 {
365     GetHyph_Impl();
366     uno::Reference< linguistic2::XHyphenatedWord > xRes;
367     if (xHyph.is())
368         xRes = xHyph->queryAlternativeSpelling( rWord, rLocale, nIndex, rProperties );
369     return xRes;
370 }
371 
372 
373 uno::Reference< linguistic2::XPossibleHyphens > SAL_CALL
374     HyphDummy_Impl::createPossibleHyphens(
375             const OUString& rWord,
376             const lang::Locale& rLocale,
377             const css::uno::Sequence< css::beans::PropertyValue >& rProperties )
378 {
379     GetHyph_Impl();
380     uno::Reference< linguistic2::XPossibleHyphens > xRes;
381     if (xHyph.is())
382         xRes = xHyph->createPossibleHyphens( rWord, rLocale, rProperties );
383     return xRes;
384 }
385 
386 class LinguMgrExitLstnr : public cppu::WeakImplHelper<XEventListener>
387 {
388     uno::Reference< XDesktop2 >        xDesktop;
389 
390     static void AtExit();
391 
392 public:
393     LinguMgrExitLstnr();
394     virtual ~LinguMgrExitLstnr() override;
395 
396     // lang::XEventListener
397     virtual void    SAL_CALL disposing(const EventObject& rSource) override;
398 };
399 
400 LinguMgrExitLstnr::LinguMgrExitLstnr()
401 {
402     // add object to frame::Desktop EventListeners in order to properly call
403     // the AtExit function at application exit.
404 
405     uno::Reference< XComponentContext >  xContext = getProcessComponentContext();
406     xDesktop = Desktop::create( xContext );
407     xDesktop->addEventListener( this );
408 }
409 
410 LinguMgrExitLstnr::~LinguMgrExitLstnr()
411 {
412     if (xDesktop.is())
413     {
414         xDesktop->removeEventListener( this );
415         xDesktop = nullptr;    //! release reference to desktop
416     }
417     OSL_ENSURE(!xDesktop.is(), "reference to desktop should be released");
418 }
419 
420 void LinguMgrExitLstnr::disposing(const EventObject& rSource)
421 {
422     if (xDesktop.is()  &&  rSource.Source == xDesktop)
423     {
424         xDesktop->removeEventListener( this );
425         xDesktop = nullptr;    //! release reference to desktop
426 
427         AtExit();
428     }
429 }
430 
431 void LinguMgrExitLstnr::AtExit()
432 {
433     SolarMutexGuard g;
434 
435     // release references
436     LinguMgr::xLngSvcMgr    = nullptr;
437     LinguMgr::xSpell        = nullptr;
438     LinguMgr::xHyph         = nullptr;
439     LinguMgr::xThes         = nullptr;
440     LinguMgr::xDicList      = nullptr;
441     LinguMgr::xProp         = nullptr;
442     LinguMgr::xIgnoreAll    = nullptr;
443     LinguMgr::xChangeAll    = nullptr;
444 
445     LinguMgr::bExiting      = true;
446 
447     LinguMgr::pExitLstnr    = nullptr;
448 }
449 
450 
451 rtl::Reference<LinguMgrExitLstnr> LinguMgr::pExitLstnr;
452 bool                            LinguMgr::bExiting      = false;
453 uno::Reference< XLinguServiceManager2 >  LinguMgr::xLngSvcMgr;
454 uno::Reference< XSpellChecker1 >    LinguMgr::xSpell;
455 uno::Reference< XHyphenator >       LinguMgr::xHyph;
456 uno::Reference< XThesaurus >        LinguMgr::xThes;
457 uno::Reference< XSearchableDictionaryList >   LinguMgr::xDicList;
458 uno::Reference< XLinguProperties >  LinguMgr::xProp;
459 uno::Reference< XDictionary >       LinguMgr::xIgnoreAll;
460 uno::Reference< XDictionary >       LinguMgr::xChangeAll;
461 
462 
463 uno::Reference< XLinguServiceManager2 > LinguMgr::GetLngSvcMgr()
464 {
465     if (bExiting)
466         return nullptr;
467 
468     if (!pExitLstnr)
469         pExitLstnr = new LinguMgrExitLstnr;
470 
471     if (!xLngSvcMgr.is())
472         xLngSvcMgr = GetLngSvcMgr_Impl();
473 
474     return xLngSvcMgr;
475 }
476 
477 
478 uno::Reference< XSpellChecker1 > LinguMgr::GetSpellChecker()
479 {
480     return xSpell.is() ? xSpell : GetSpell();
481 }
482 
483 uno::Reference< XHyphenator > LinguMgr::GetHyphenator()
484 {
485     return xHyph.is() ? xHyph : GetHyph();
486 }
487 
488 uno::Reference< XThesaurus > LinguMgr::GetThesaurus()
489 {
490     return xThes.is() ? xThes : GetThes();
491 }
492 
493 uno::Reference< XSearchableDictionaryList > LinguMgr::GetDictionaryList()
494 {
495     return xDicList.is() ? xDicList : GetDicList();
496 }
497 
498 uno::Reference< linguistic2::XLinguProperties > LinguMgr::GetLinguPropertySet()
499 {
500     return xProp.is() ? xProp : GetProp();
501 }
502 
503 uno::Reference< XDictionary > LinguMgr::GetStandardDic()
504 {
505     //! don't hold reference to this
506     //! (it may be removed from dictionary list and needs to be
507     //! created empty if accessed again)
508     return GetStandard();
509 }
510 
511 uno::Reference< XDictionary > LinguMgr::GetIgnoreAllList()
512 {
513     return xIgnoreAll.is() ? xIgnoreAll : GetIgnoreAll();
514 }
515 
516 uno::Reference< XDictionary > LinguMgr::GetChangeAllList()
517 {
518     return xChangeAll.is() ? xChangeAll : GetChangeAll();
519 }
520 
521 uno::Reference< XSpellChecker1 > LinguMgr::GetSpell()
522 {
523     if (bExiting)
524         return nullptr;
525 
526     if (!pExitLstnr)
527         pExitLstnr = new LinguMgrExitLstnr;
528 
529     //! use dummy implementation in order to avoid loading of lingu DLL
530     xSpell = new SpellDummy_Impl;
531     return xSpell;
532 }
533 
534 uno::Reference< XHyphenator > LinguMgr::GetHyph()
535 {
536     if (bExiting)
537         return nullptr;
538 
539     if (!pExitLstnr)
540         pExitLstnr = new LinguMgrExitLstnr;
541 
542     //! use dummy implementation in order to avoid loading of lingu DLL
543     xHyph = new HyphDummy_Impl;
544     return xHyph;
545 }
546 
547 uno::Reference< XThesaurus > LinguMgr::GetThes()
548 {
549     if (bExiting)
550         return nullptr;
551 
552     if (!pExitLstnr)
553         pExitLstnr = new LinguMgrExitLstnr;
554 
555     //! use dummy implementation in order to avoid loading of lingu DLL
556     //! when only the XSupportedLocales interface is used.
557     //! The dummy accesses the real implementation (and thus loading the DLL)
558     //! when "real" work needs to be done only.
559     xThes = new ThesDummy_Impl;
560     return xThes;
561 }
562 
563 uno::Reference< XSearchableDictionaryList > LinguMgr::GetDicList()
564 {
565     if (bExiting)
566         return nullptr;
567 
568     if (!pExitLstnr)
569         pExitLstnr = new LinguMgrExitLstnr;
570 
571     xDicList = linguistic2::DictionaryList::create( getProcessComponentContext() );
572     return xDicList;
573 }
574 
575 uno::Reference< linguistic2::XLinguProperties > LinguMgr::GetProp()
576 {
577     if (bExiting)
578         return nullptr;
579 
580     if (!pExitLstnr)
581         pExitLstnr = new LinguMgrExitLstnr;
582 
583     xProp = linguistic2::LinguProperties::create( getProcessComponentContext()  );
584     return xProp;
585 }
586 
587 uno::Reference< XDictionary > LinguMgr::GetIgnoreAll()
588 {
589     if (bExiting)
590         return nullptr;
591 
592     if (!pExitLstnr)
593         pExitLstnr = new LinguMgrExitLstnr;
594 
595     uno::Reference< XSearchableDictionaryList >  xTmpDicList( GetDictionaryList() );
596     if (xTmpDicList.is())
597     {
598         std::locale loc(Translate::Create("svt"));
599         xIgnoreAll = xTmpDicList->getDictionaryByName(
600                                     Translate::get(STR_DESCRIPTION_IGNOREALLLIST, loc) );
601     }
602     return xIgnoreAll;
603 }
604 
605 uno::Reference< XDictionary > LinguMgr::GetChangeAll()
606 {
607     if (bExiting)
608         return nullptr;
609 
610     if (!pExitLstnr)
611         pExitLstnr = new LinguMgrExitLstnr;
612 
613     uno::Reference< XSearchableDictionaryList > _xDicList = GetDictionaryList();
614     if (_xDicList.is())
615     {
616         xChangeAll = _xDicList->createDictionary(
617                             "ChangeAllList",
618                             LanguageTag::convertToLocale( LANGUAGE_NONE ),
619                             DictionaryType_NEGATIVE, OUString() );
620     }
621     return xChangeAll;
622 }
623 
624 uno::Reference< XDictionary > LinguMgr::GetStandard()
625 {
626     // Tries to return a dictionary which may hold positive entries is
627     // persistent and not read-only.
628 
629     if (bExiting)
630         return nullptr;
631 
632     uno::Reference< XSearchableDictionaryList >  xTmpDicList( GetDictionaryList() );
633     if (!xTmpDicList.is())
634         return nullptr;
635 
636     static const OUStringLiteral aDicName( u"standard.dic" );
637     uno::Reference< XDictionary > xDic = xTmpDicList->getDictionaryByName( aDicName );
638     if (!xDic.is())
639     {
640         // try to create standard dictionary
641         uno::Reference< XDictionary >    xTmp;
642         try
643         {
644             xTmp =  xTmpDicList->createDictionary( aDicName,
645                         LanguageTag::convertToLocale( LANGUAGE_NONE ),
646                         DictionaryType_POSITIVE,
647                         linguistic::GetWritableDictionaryURL( aDicName ) );
648         }
649         catch(const css::uno::Exception &)
650         {
651         }
652 
653         // add new dictionary to list
654         if (xTmp.is())
655         {
656             xTmpDicList->addDictionary( xTmp );
657             xTmp->setActive( true );
658         }
659         xDic = xTmp;
660     }
661 #if OSL_DEBUG_LEVEL > 1
662     uno::Reference< XStorable >      xStor( xDic, UNO_QUERY );
663     OSL_ENSURE( xDic.is() && xDic->getDictionaryType() == DictionaryType_POSITIVE,
664             "wrong dictionary type");
665     OSL_ENSURE( xDic.is() && LanguageTag( xDic->getLocale() ).getLanguageType() == LANGUAGE_NONE,
666             "wrong dictionary language");
667     OSL_ENSURE( !xStor.is() || (xStor->hasLocation() && !xStor->isReadonly()),
668             "dictionary not editable" );
669 #endif
670 
671     return xDic;
672 }
673 
674 SvxAlternativeSpelling SvxGetAltSpelling(
675         const css::uno::Reference< css::linguistic2::XHyphenatedWord > & rHyphWord )
676 {
677     SvxAlternativeSpelling aRes;
678     if (rHyphWord.is() && rHyphWord->isAlternativeSpelling())
679     {
680         OUString aWord( rHyphWord->getWord() ),
681                  aAltWord( rHyphWord->getHyphenatedWord() );
682         sal_Int16   nHyphenationPos     = rHyphWord->getHyphenationPos(),
683                 nHyphenPos          = rHyphWord->getHyphenPos();
684         sal_Int16   nLen    = static_cast<sal_Int16>(aWord.getLength());
685         sal_Int16   nAltLen = static_cast<sal_Int16>(aAltWord.getLength());
686         const sal_Unicode *pWord    = aWord.getStr(),
687                           *pAltWord = aAltWord.getStr();
688 
689         // count number of chars from the left to the
690         // hyphenation pos / hyphen pos that are equal
691         sal_Int16 nL = 0;
692         while (nL <= nHyphenationPos && nL <= nHyphenPos
693                && pWord[ nL ] == pAltWord[ nL ])
694             ++nL;
695         // count number of chars from the right to the
696         // hyphenation pos / hyphen pos that are equal
697         sal_Int16 nR = 0;
698         sal_Int32 nIdx    = nLen - 1;
699         sal_Int32 nAltIdx = nAltLen - 1;
700         while (nIdx > nHyphenationPos && nAltIdx > nHyphenPos
701                && pWord[ nIdx-- ] == pAltWord[ nAltIdx-- ])
702             ++nR;
703 
704         aRes.aReplacement       = aAltWord.copy( nL, nAltLen - nL - nR );
705         aRes.nChangedPos        = nL;
706         aRes.nChangedLength     = nLen - nL - nR;
707         aRes.bIsAltSpelling     = true;
708     }
709     return aRes;
710 }
711 
712 
713 SvxDicListChgClamp::SvxDicListChgClamp( uno::Reference< XSearchableDictionaryList > _xDicList ) :
714     xDicList    (std::move( _xDicList ))
715 {
716     if (xDicList.is())
717     {
718         xDicList->beginCollectEvents();
719     }
720 }
721 
722 SvxDicListChgClamp::~SvxDicListChgClamp()
723 {
724     if (xDicList.is())
725     {
726         xDicList->endCollectEvents();
727     }
728 }
729 
730 short SvxDicError(weld::Window *pParent, linguistic::DictionaryError nError)
731 {
732     short nRes = 0;
733     if (linguistic::DictionaryError::NONE != nError)
734     {
735         TranslateId pRid;
736         switch (nError)
737         {
738             case linguistic::DictionaryError::FULL     : pRid = RID_SVXSTR_DIC_ERR_FULL;  break;
739             case linguistic::DictionaryError::READONLY : pRid = RID_SVXSTR_DIC_ERR_READONLY;  break;
740             default:
741                 pRid = RID_SVXSTR_DIC_ERR_UNKNOWN;
742                 SAL_WARN("editeng", "unexpected case");
743         }
744         std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent,
745                                                       VclMessageType::Info, VclButtonsType::Ok,
746                                                       EditResId(pRid)));
747         nRes = xInfoBox->run();
748 
749     }
750     return nRes;
751 }
752 
753 
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
755