1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
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>
32 #include <comphelper/lok.hxx>
33 #include <comphelper/processfactory.hxx>
34 #include <cppuhelper/implbase.hxx>
35 #include <i18nlangtag/languagetag.hxx>
36 #include <unotools/lingucfg.hxx>
38 #include <vcl/svapp.hxx>
39 #include <vcl/weld.hxx>
40 #include <linguistic/misc.hxx>
41 #include <editeng/eerdll.hxx>
42 #include <editeng/editrids.hrc>
43 #include <svtools/strings.hrc>
44 #include <unotools/resmgr.hxx>
45 #include <sal/log.hxx>
46 #include <osl/diagnose.h>
48 using namespace ::comphelper
;
49 using namespace ::linguistic
;
50 using namespace ::com::sun::star
;
51 using namespace ::com::sun::star::util
;
52 using namespace ::com::sun::star::uno
;
53 using namespace ::com::sun::star::lang
;
54 using namespace ::com::sun::star::beans
;
55 using namespace ::com::sun::star::frame
;
56 using namespace ::com::sun::star::linguistic2
;
58 static uno::Reference
< XLinguServiceManager2
> GetLngSvcMgr_Impl()
60 uno::Reference
< XComponentContext
> xContext
= comphelper::getProcessComponentContext();
61 uno::Reference
< XLinguServiceManager2
> xRes
= LinguServiceManager::create(xContext
);
67 //! Dummy implementation in order to avoid loading of lingu DLL
68 //! when only the XSupportedLocales interface is used.
69 //! The dummy accesses the real implementation (and thus loading the DLL)
70 //! when "real" work needs to be done only.
71 class ThesDummy_Impl
:
72 public cppu::WeakImplHelper
< XThesaurus
>
74 uno::Reference
< XThesaurus
> xThes
; // the real one...
75 std::unique_ptr
<Sequence
< lang::Locale
>> pLocaleSeq
;
85 virtual css::uno::Sequence
< css::lang::Locale
> SAL_CALL
86 getLocales() override
;
87 virtual sal_Bool SAL_CALL
88 hasLocale( const css::lang::Locale
& rLocale
) override
;
91 virtual css::uno::Sequence
<
92 css::uno::Reference
< css::linguistic2::XMeaning
> > SAL_CALL
93 queryMeanings( const OUString
& rTerm
,
94 const css::lang::Locale
& rLocale
,
95 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
100 void ThesDummy_Impl::GetCfgLocales()
106 Sequence
< OUString
> aNodeNames( aCfg
.GetNodeNames( "ServiceManager/ThesaurusList" ) );
107 const OUString
*pNodeNames
= aNodeNames
.getConstArray();
108 sal_Int32 nLen
= aNodeNames
.getLength();
109 pLocaleSeq
.reset( new Sequence
< lang::Locale
>( nLen
) );
110 lang::Locale
*pLocale
= pLocaleSeq
->getArray();
111 for (sal_Int32 i
= 0; i
< nLen
; ++i
)
113 pLocale
[i
] = LanguageTag::convertToLocaleWithFallback( pNodeNames
[i
] );
118 void ThesDummy_Impl::GetThes_Impl()
122 uno::Reference
< XLinguServiceManager2
> xLngSvcMgr( GetLngSvcMgr_Impl() );
123 xThes
= xLngSvcMgr
->getThesaurus();
127 // no longer needed...
134 uno::Sequence
< lang::Locale
> SAL_CALL
135 ThesDummy_Impl::getLocales()
139 return xThes
->getLocales();
140 else if (!pLocaleSeq
) // if not already loaded save startup time by avoiding loading them now
147 ThesDummy_Impl::hasLocale( const lang::Locale
& rLocale
)
151 return xThes
->hasLocale( rLocale
);
152 else if (!pLocaleSeq
) // if not already loaded save startup time by avoiding loading them now
155 sal_Int32 nLen
= pLocaleSeq
->getLength();
156 const lang::Locale
*pLocale
= pLocaleSeq
->getConstArray();
157 const lang::Locale
*pEnd
= pLocale
+ nLen
;
158 for ( ; pLocale
< pEnd
&& !bFound
; ++pLocale
)
160 bFound
= pLocale
->Language
== rLocale
.Language
&&
161 pLocale
->Country
== rLocale
.Country
&&
162 pLocale
->Variant
== rLocale
.Variant
;
168 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > SAL_CALL
169 ThesDummy_Impl::queryMeanings(
170 const OUString
& rTerm
,
171 const lang::Locale
& rLocale
,
172 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
175 uno::Sequence
< uno::Reference
< linguistic2::XMeaning
> > aRes
;
176 OSL_ENSURE( xThes
.is(), "Thesaurus missing" );
178 aRes
= xThes
->queryMeanings( rTerm
, rLocale
, rProperties
);
184 //! Dummy implementation in order to avoid loading of lingu DLL.
185 //! The dummy accesses the real implementation (and thus loading the DLL)
186 //! when it needs to be done only.
187 class SpellDummy_Impl
:
188 public cppu::WeakImplHelper
< XSpellChecker1
>
190 uno::Reference
< XSpellChecker1
> xSpell
; // the real one...
192 void GetSpell_Impl();
196 // XSupportedLanguages (for XSpellChecker1)
197 virtual css::uno::Sequence
< sal_Int16
> SAL_CALL
198 getLanguages() override
;
199 virtual sal_Bool SAL_CALL
200 hasLanguage( sal_Int16 nLanguage
) override
;
202 // XSpellChecker1 (same as XSpellChecker but sal_Int16 for language)
203 virtual sal_Bool SAL_CALL
204 isValid( const OUString
& rWord
, sal_Int16 nLanguage
,
205 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
206 virtual css::uno::Reference
< css::linguistic2::XSpellAlternatives
> SAL_CALL
207 spell( const OUString
& rWord
, sal_Int16 nLanguage
,
208 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
213 void SpellDummy_Impl::GetSpell_Impl()
217 uno::Reference
< XLinguServiceManager2
> xLngSvcMgr( GetLngSvcMgr_Impl() );
218 xSpell
.set( xLngSvcMgr
->getSpellChecker(), UNO_QUERY
);
223 uno::Sequence
< sal_Int16
> SAL_CALL
224 SpellDummy_Impl::getLanguages()
228 return xSpell
->getLanguages();
230 return uno::Sequence
< sal_Int16
>();
235 SpellDummy_Impl::hasLanguage( sal_Int16 nLanguage
)
240 bRes
= xSpell
->hasLanguage( nLanguage
);
246 SpellDummy_Impl::isValid( const OUString
& rWord
, sal_Int16 nLanguage
,
247 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
252 bRes
= xSpell
->isValid( rWord
, nLanguage
, rProperties
);
257 uno::Reference
< linguistic2::XSpellAlternatives
> SAL_CALL
258 SpellDummy_Impl::spell( const OUString
& rWord
, sal_Int16 nLanguage
,
259 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
262 uno::Reference
< linguistic2::XSpellAlternatives
> xRes
;
264 xRes
= xSpell
->spell( rWord
, nLanguage
, rProperties
);
270 //! Dummy implementation in order to avoid loading of lingu DLL.
271 //! The dummy accesses the real implementation (and thus loading the DLL)
272 //! when it needs to be done only.
273 class HyphDummy_Impl
:
274 public cppu::WeakImplHelper
< XHyphenator
>
276 uno::Reference
< XHyphenator
> xHyph
; // the real one...
283 virtual css::uno::Sequence
<
284 css::lang::Locale
> SAL_CALL
285 getLocales() override
;
286 virtual sal_Bool SAL_CALL
287 hasLocale( const css::lang::Locale
& rLocale
) override
;
290 virtual css::uno::Reference
<
291 css::linguistic2::XHyphenatedWord
> SAL_CALL
292 hyphenate( const OUString
& rWord
,
293 const css::lang::Locale
& rLocale
,
294 sal_Int16 nMaxLeading
,
295 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
296 virtual css::uno::Reference
<
297 css::linguistic2::XHyphenatedWord
> SAL_CALL
298 queryAlternativeSpelling( const OUString
& rWord
,
299 const css::lang::Locale
& rLocale
,
301 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
302 virtual css::uno::Reference
<
303 css::linguistic2::XPossibleHyphens
> SAL_CALL
304 createPossibleHyphens(
305 const OUString
& rWord
,
306 const css::lang::Locale
& rLocale
,
307 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
) override
;
312 void HyphDummy_Impl::GetHyph_Impl()
316 uno::Reference
< XLinguServiceManager2
> xLngSvcMgr( GetLngSvcMgr_Impl() );
317 xHyph
= xLngSvcMgr
->getHyphenator();
322 uno::Sequence
< lang::Locale
> SAL_CALL
323 HyphDummy_Impl::getLocales()
327 return xHyph
->getLocales();
329 return uno::Sequence
< lang::Locale
>();
334 HyphDummy_Impl::hasLocale( const lang::Locale
& rLocale
)
339 bRes
= xHyph
->hasLocale( rLocale
);
344 uno::Reference
< linguistic2::XHyphenatedWord
> SAL_CALL
345 HyphDummy_Impl::hyphenate(
346 const OUString
& rWord
,
347 const lang::Locale
& rLocale
,
348 sal_Int16 nMaxLeading
,
349 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
352 uno::Reference
< linguistic2::XHyphenatedWord
> xRes
;
354 xRes
= xHyph
->hyphenate( rWord
, rLocale
, nMaxLeading
, rProperties
);
359 uno::Reference
< linguistic2::XHyphenatedWord
> SAL_CALL
360 HyphDummy_Impl::queryAlternativeSpelling(
361 const OUString
& rWord
,
362 const lang::Locale
& rLocale
,
364 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
367 uno::Reference
< linguistic2::XHyphenatedWord
> xRes
;
369 xRes
= xHyph
->queryAlternativeSpelling( rWord
, rLocale
, nIndex
, rProperties
);
374 uno::Reference
< linguistic2::XPossibleHyphens
> SAL_CALL
375 HyphDummy_Impl::createPossibleHyphens(
376 const OUString
& rWord
,
377 const lang::Locale
& rLocale
,
378 const css::uno::Sequence
< css::beans::PropertyValue
>& rProperties
)
381 uno::Reference
< linguistic2::XPossibleHyphens
> xRes
;
383 xRes
= xHyph
->createPossibleHyphens( rWord
, rLocale
, rProperties
);
387 class LinguMgrExitLstnr
: public cppu::WeakImplHelper
<XEventListener
>
389 uno::Reference
< XDesktop2
> xDesktop
;
391 static void AtExit();
395 virtual ~LinguMgrExitLstnr() override
;
397 // lang::XEventListener
398 virtual void SAL_CALL
disposing(const EventObject
& rSource
) override
;
401 LinguMgrExitLstnr::LinguMgrExitLstnr()
403 // add object to frame::Desktop EventListeners in order to properly call
404 // the AtExit function at application exit.
406 uno::Reference
< XComponentContext
> xContext
= getProcessComponentContext();
407 xDesktop
= Desktop::create( xContext
);
408 xDesktop
->addEventListener( this );
411 LinguMgrExitLstnr::~LinguMgrExitLstnr()
415 xDesktop
->removeEventListener( this );
416 xDesktop
= nullptr; //! release reference to desktop
418 OSL_ENSURE(!xDesktop
.is(), "reference to desktop should be released");
421 void LinguMgrExitLstnr::disposing(const EventObject
& rSource
)
423 if (xDesktop
.is() && rSource
.Source
== xDesktop
)
425 xDesktop
->removeEventListener( this );
426 xDesktop
= nullptr; //! release reference to desktop
432 void LinguMgrExitLstnr::AtExit()
436 // release references
437 LinguMgr::xLngSvcMgr
= nullptr;
438 LinguMgr::xSpell
= nullptr;
439 LinguMgr::xHyph
= nullptr;
440 LinguMgr::xThes
= nullptr;
441 LinguMgr::xDicList
= nullptr;
442 LinguMgr::xProp
= nullptr;
443 LinguMgr::xIgnoreAll
= nullptr;
444 LinguMgr::xChangeAll
= nullptr;
446 LinguMgr::bExiting
= true;
448 LinguMgr::pExitLstnr
= nullptr;
452 rtl::Reference
<LinguMgrExitLstnr
> LinguMgr::pExitLstnr
;
453 bool LinguMgr::bExiting
= false;
454 uno::Reference
< XLinguServiceManager2
> LinguMgr::xLngSvcMgr
;
455 uno::Reference
< XSpellChecker1
> LinguMgr::xSpell
;
456 uno::Reference
< XHyphenator
> LinguMgr::xHyph
;
457 uno::Reference
< XThesaurus
> LinguMgr::xThes
;
458 uno::Reference
< XSearchableDictionaryList
> LinguMgr::xDicList
;
459 uno::Reference
< XLinguProperties
> LinguMgr::xProp
;
460 uno::Reference
< XDictionary
> LinguMgr::xIgnoreAll
;
461 uno::Reference
< XDictionary
> LinguMgr::xChangeAll
;
464 uno::Reference
< XLinguServiceManager2
> LinguMgr::GetLngSvcMgr()
470 pExitLstnr
= new LinguMgrExitLstnr
;
472 if (!xLngSvcMgr
.is())
473 xLngSvcMgr
= GetLngSvcMgr_Impl();
479 uno::Reference
< XSpellChecker1
> LinguMgr::GetSpellChecker()
481 return xSpell
.is() ? xSpell
: GetSpell();
484 uno::Reference
< XHyphenator
> LinguMgr::GetHyphenator()
486 return xHyph
.is() ? xHyph
: GetHyph();
489 uno::Reference
< XThesaurus
> LinguMgr::GetThesaurus()
491 return xThes
.is() ? xThes
: GetThes();
494 uno::Reference
< XSearchableDictionaryList
> LinguMgr::GetDictionaryList()
496 return xDicList
.is() ? xDicList
: GetDicList();
499 uno::Reference
< linguistic2::XLinguProperties
> LinguMgr::GetLinguPropertySet()
501 return xProp
.is() ? xProp
: GetProp();
504 uno::Reference
< XDictionary
> LinguMgr::GetStandardDic()
506 //! don't hold reference to this
507 //! (it may be removed from dictionary list and needs to be
508 //! created empty if accessed again)
509 return GetStandard();
512 uno::Reference
< XDictionary
> LinguMgr::GetIgnoreAllList()
514 return xIgnoreAll
.is() ? xIgnoreAll
: GetIgnoreAll();
517 uno::Reference
< XDictionary
> LinguMgr::GetChangeAllList()
519 return xChangeAll
.is() ? xChangeAll
: GetChangeAll();
522 uno::Reference
< XSpellChecker1
> LinguMgr::GetSpell()
528 pExitLstnr
= new LinguMgrExitLstnr
;
530 //! use dummy implementation in order to avoid loading of lingu DLL
531 xSpell
= new SpellDummy_Impl
;
535 uno::Reference
< XHyphenator
> LinguMgr::GetHyph()
541 pExitLstnr
= new LinguMgrExitLstnr
;
543 //! use dummy implementation in order to avoid loading of lingu DLL
544 xHyph
= new HyphDummy_Impl
;
548 uno::Reference
< XThesaurus
> LinguMgr::GetThes()
554 pExitLstnr
= new LinguMgrExitLstnr
;
556 //! use dummy implementation in order to avoid loading of lingu DLL
557 //! when only the XSupportedLocales interface is used.
558 //! The dummy accesses the real implementation (and thus loading the DLL)
559 //! when "real" work needs to be done only.
560 xThes
= new ThesDummy_Impl
;
564 uno::Reference
< XSearchableDictionaryList
> LinguMgr::GetDicList()
570 pExitLstnr
= new LinguMgrExitLstnr
;
572 xDicList
= linguistic2::DictionaryList::create( getProcessComponentContext() );
576 uno::Reference
< linguistic2::XLinguProperties
> LinguMgr::GetProp()
582 pExitLstnr
= new LinguMgrExitLstnr
;
584 xProp
= linguistic2::LinguProperties::create( getProcessComponentContext() );
588 uno::Reference
< XDictionary
> LinguMgr::GetIgnoreAll()
594 pExitLstnr
= new LinguMgrExitLstnr
;
596 uno::Reference
< XSearchableDictionaryList
> xTmpDicList( GetDictionaryList() );
597 if (xTmpDicList
.is())
599 const LanguageTag tag
= comphelper::LibreOfficeKit::isActive()
600 ? LanguageTag("en-US")
601 : SvtSysLocale().GetUILanguageTag();
602 std::locale
loc(Translate::Create("svt", tag
));
603 xIgnoreAll
= xTmpDicList
->getDictionaryByName(
604 Translate::get(STR_DESCRIPTION_IGNOREALLLIST
, loc
) );
609 uno::Reference
< XDictionary
> LinguMgr::GetChangeAll()
615 pExitLstnr
= new LinguMgrExitLstnr
;
617 uno::Reference
< XSearchableDictionaryList
> _xDicList
= GetDictionaryList();
620 xChangeAll
= _xDicList
->createDictionary(
622 LanguageTag::convertToLocale( LANGUAGE_NONE
),
623 DictionaryType_NEGATIVE
, OUString() );
628 uno::Reference
< XDictionary
> LinguMgr::GetStandard()
630 // Tries to return a dictionary which may hold positive entries is
631 // persistent and not read-only.
636 uno::Reference
< XSearchableDictionaryList
> xTmpDicList( GetDictionaryList() );
637 if (!xTmpDicList
.is())
640 static constexpr OUString
aDicName( u
"standard.dic"_ustr
);
641 uno::Reference
< XDictionary
> xDic
= xTmpDicList
->getDictionaryByName( aDicName
);
644 // try to create standard dictionary
645 uno::Reference
< XDictionary
> xTmp
;
648 xTmp
= xTmpDicList
->createDictionary( aDicName
,
649 LanguageTag::convertToLocale( LANGUAGE_NONE
),
650 DictionaryType_POSITIVE
,
651 linguistic::GetWritableDictionaryURL( aDicName
) );
653 catch(const css::uno::Exception
&)
657 // add new dictionary to list
660 xTmpDicList
->addDictionary( xTmp
);
661 xTmp
->setActive( true );
665 #if OSL_DEBUG_LEVEL > 1
666 uno::Reference
< XStorable
> xStor( xDic
, UNO_QUERY
);
667 OSL_ENSURE( xDic
.is() && xDic
->getDictionaryType() == DictionaryType_POSITIVE
,
668 "wrong dictionary type");
669 OSL_ENSURE( xDic
.is() && LanguageTag( xDic
->getLocale() ).getLanguageType() == LANGUAGE_NONE
,
670 "wrong dictionary language");
671 OSL_ENSURE( !xStor
.is() || (xStor
->hasLocation() && !xStor
->isReadonly()),
672 "dictionary not editable" );
678 SvxAlternativeSpelling
SvxGetAltSpelling(
679 const css::uno::Reference
< css::linguistic2::XHyphenatedWord
> & rHyphWord
)
681 SvxAlternativeSpelling aRes
;
682 if (rHyphWord
.is() && rHyphWord
->isAlternativeSpelling())
684 OUString
aWord( rHyphWord
->getWord() ),
685 aAltWord( rHyphWord
->getHyphenatedWord() );
686 sal_Int16 nHyphenationPos
= rHyphWord
->getHyphenationPos(),
687 nHyphenPos
= rHyphWord
->getHyphenPos();
688 sal_Int16 nLen
= static_cast<sal_Int16
>(aWord
.getLength());
689 sal_Int16 nAltLen
= static_cast<sal_Int16
>(aAltWord
.getLength());
690 const sal_Unicode
*pWord
= aWord
.getStr(),
691 *pAltWord
= aAltWord
.getStr();
693 // count number of chars from the left to the
694 // hyphenation pos / hyphen pos that are equal
696 while (nL
<= nHyphenationPos
&& nL
<= nHyphenPos
697 && pWord
[ nL
] == pAltWord
[ nL
])
699 // count number of chars from the right to the
700 // hyphenation pos / hyphen pos that are equal
702 sal_Int32 nIdx
= nLen
- 1;
703 sal_Int32 nAltIdx
= nAltLen
- 1;
704 while (nIdx
> nHyphenationPos
&& nAltIdx
> nHyphenPos
705 && pWord
[ nIdx
-- ] == pAltWord
[ nAltIdx
-- ])
708 aRes
.aReplacement
= aAltWord
.copy( nL
, nAltLen
- nL
- nR
);
709 aRes
.nChangedPos
= nL
;
710 aRes
.nChangedLength
= nLen
- nL
- nR
;
711 aRes
.bIsAltSpelling
= true;
717 SvxDicListChgClamp::SvxDicListChgClamp( uno::Reference
< XSearchableDictionaryList
> _xDicList
) :
718 xDicList (std::move( _xDicList
))
722 xDicList
->beginCollectEvents();
726 SvxDicListChgClamp::~SvxDicListChgClamp()
730 xDicList
->endCollectEvents();
734 short SvxDicError(weld::Window
*pParent
, linguistic::DictionaryError nError
)
737 if (linguistic::DictionaryError::NONE
!= nError
)
742 case linguistic::DictionaryError::FULL
: pRid
= RID_SVXSTR_DIC_ERR_FULL
; break;
743 case linguistic::DictionaryError::READONLY
: pRid
= RID_SVXSTR_DIC_ERR_READONLY
; break;
745 pRid
= RID_SVXSTR_DIC_ERR_UNKNOWN
;
746 SAL_WARN("editeng", "unexpected case");
748 std::unique_ptr
<weld::MessageDialog
> xInfoBox(Application::CreateMessageDialog(pParent
,
749 VclMessageType::Info
, VclButtonsType::Ok
,
751 nRes
= xInfoBox
->run();
758 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */