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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <com/sun/star/deployment/DeploymentException.hpp>
24 #include <com/sun/star/deployment/ExtensionManager.hpp>
25 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
26 #include <com/sun/star/container/XEnumeration.hpp>
27 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
28 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
29 #include <com/sun/star/linguistic2/XSupportedLocales.hpp>
30 #include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
31 #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp>
32 #include <com/sun/star/linguistic2/ProofreadingIterator.hpp>
34 #include <tools/debug.hxx>
35 #include <unotools/lingucfg.hxx>
36 #include <vcl/svapp.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/sequence.hxx>
39 #include <i18nlangtag/lang.h>
40 #include <i18nlangtag/languagetag.hxx>
41 #include <cppuhelper/factory.hxx>
42 #include <cppuhelper/implbase.hxx>
43 #include <cppuhelper/supportsservice.hxx>
44 #include <cppuhelper/weak.hxx>
46 #include "lngsvcmgr.hxx"
47 #include <linguistic/misc.hxx>
48 #include "spelldsp.hxx"
49 #include "hyphdsp.hxx"
50 #include "thesdsp.hxx"
51 #include "gciterator.hxx"
53 using namespace com::sun::star
;
54 using namespace linguistic
;
56 uno::Sequence
< OUString
> static GetLangSvcList( const uno::Any
&rVal
);
57 uno::Sequence
< OUString
> static GetLangSvc( const uno::Any
&rVal
);
59 static bool lcl_SeqHasString( const uno::Sequence
< OUString
> &rSeq
, const OUString
&rText
)
61 return !rText
.isEmpty()
62 && comphelper::findValue(rSeq
, rText
) != -1;
66 static uno::Sequence
< lang::Locale
> GetAvailLocales(
67 const uno::Sequence
< OUString
> &rSvcImplNames
)
69 uno::Sequence
< lang::Locale
> aRes
;
71 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
72 if( rSvcImplNames
.hasElements() )
74 std::set
< LanguageType
> aLanguages
;
76 // All of these services only use one arg, but need two args for compat reasons
77 uno::Sequence
< uno::Any
> aArgs(2);
78 aArgs
.getArray()[0] <<= GetLinguProperties();
80 // check all services for the supported languages and new
81 // languages to the result
83 for (const OUString
& rImplName
: rSvcImplNames
)
85 uno::Reference
< linguistic2::XSupportedLocales
> xSuppLoc
;
88 xSuppLoc
.set( xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
89 rImplName
, aArgs
, xContext
),
92 catch (uno::Exception
&)
94 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
99 const uno::Sequence
< lang::Locale
> aLoc( xSuppLoc
->getLocales() );
100 for (const lang::Locale
& rLoc
: aLoc
)
102 LanguageType nLang
= LinguLocaleToLanguage( rLoc
);
104 // It's a set, so insertion fails if language was already added.
105 aLanguages
.insert( nLang
);
110 SAL_WARN( "linguistic", "interface not supported by service" );
114 // build return sequence
115 std::vector
<lang::Locale
> aVec
;
116 aVec
.reserve(aLanguages
.size());
118 std::transform(aLanguages
.begin(), aLanguages
.end(), std::back_inserter(aVec
),
119 [](const LanguageType
& rLang
) -> lang::Locale
{ return LanguageTag::convertToLocale(rLang
); });
121 aRes
= comphelper::containerToSequence(aVec
);
130 const OUString aSvcImplName
;
131 const std::vector
< LanguageType
> aSuppLanguages
;
133 SvcInfo( const OUString
&rSvcImplName
,
134 const std::vector
< LanguageType
> &rSuppLanguages
) :
135 aSvcImplName (rSvcImplName
),
136 aSuppLanguages (rSuppLanguages
)
140 bool HasLanguage( LanguageType nLanguage
) const;
144 bool SvcInfo::HasLanguage( LanguageType nLanguage
) const
146 for ( auto const & i
: aSuppLanguages
)
154 class LngSvcMgrListenerHelper
:
155 public cppu::WeakImplHelper
157 linguistic2::XLinguServiceEventListener
,
158 linguistic2::XDictionaryListEventListener
161 LngSvcMgr
&rMyManager
;
163 ::comphelper::OInterfaceContainerHelper2 aLngSvcMgrListeners
;
164 ::comphelper::OInterfaceContainerHelper2 aLngSvcEvtBroadcasters
;
165 uno::Reference
< linguistic2::XSearchableDictionaryList
> xDicList
;
167 sal_Int16 nCombinedLngSvcEvt
;
169 void LaunchEvent( sal_Int16 nLngSvcEvtFlags
);
174 LngSvcMgrListenerHelper( LngSvcMgr
&rLngSvcMgr
,
175 const uno::Reference
< linguistic2::XSearchableDictionaryList
> &rxDicList
);
177 LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper
&) = delete;
178 LngSvcMgrListenerHelper
& operator=(const LngSvcMgrListenerHelper
&) = delete;
180 // lang::XEventListener
181 virtual void SAL_CALL
182 disposing( const lang::EventObject
& rSource
) override
;
184 // linguistic2::XLinguServiceEventListener
185 virtual void SAL_CALL
186 processLinguServiceEvent( const linguistic2::LinguServiceEvent
& aLngSvcEvent
) override
;
188 // linguistic2::XDictionaryListEventListener
189 virtual void SAL_CALL
190 processDictionaryListEvent(
191 const linguistic2::DictionaryListEvent
& rDicListEvent
) override
;
193 inline void AddLngSvcMgrListener(
194 const uno::Reference
< lang::XEventListener
>& rxListener
);
195 inline void RemoveLngSvcMgrListener(
196 const uno::Reference
< lang::XEventListener
>& rxListener
);
197 void DisposeAndClear( const lang::EventObject
&rEvtObj
);
198 void AddLngSvcEvtBroadcaster(
199 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
);
200 void RemoveLngSvcEvtBroadcaster(
201 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
);
203 void AddLngSvcEvt( sal_Int16 nLngSvcEvt
);
207 LngSvcMgrListenerHelper::LngSvcMgrListenerHelper(
208 LngSvcMgr
&rLngSvcMgr
,
209 const uno::Reference
< linguistic2::XSearchableDictionaryList
> &rxDicList
) :
210 rMyManager ( rLngSvcMgr
),
211 aLngSvcMgrListeners ( GetLinguMutex() ),
212 aLngSvcEvtBroadcasters ( GetLinguMutex() ),
213 xDicList ( rxDicList
)
217 xDicList
->addDictionaryListEventListener(
218 static_cast<linguistic2::XDictionaryListEventListener
*>(this), false );
221 nCombinedLngSvcEvt
= 0;
225 void SAL_CALL
LngSvcMgrListenerHelper::disposing( const lang::EventObject
& rSource
)
227 osl::MutexGuard
aGuard( GetLinguMutex() );
229 uno::Reference
< uno::XInterface
> xRef( rSource
.Source
);
232 aLngSvcMgrListeners
.removeInterface( xRef
);
233 aLngSvcEvtBroadcasters
.removeInterface( xRef
);
234 if (xDicList
== xRef
)
239 void LngSvcMgrListenerHelper::Timeout()
241 osl::MutexGuard
aGuard( GetLinguMutex() );
244 // change event source to LinguServiceManager since the listeners
245 // probably do not know (and need not to know) about the specific
246 // SpellChecker's or Hyphenator's.
247 linguistic2::LinguServiceEvent
aEvtObj(
248 static_cast<css::linguistic2::XLinguServiceManager
*>(&rMyManager
), nCombinedLngSvcEvt
);
249 nCombinedLngSvcEvt
= 0;
251 if (rMyManager
.mxSpellDsp
.is())
252 rMyManager
.mxSpellDsp
->FlushSpellCache();
254 // pass event on to linguistic2::XLinguServiceEventListener's
255 aLngSvcMgrListeners
.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent
, aEvtObj
);
260 void LngSvcMgrListenerHelper::AddLngSvcEvt( sal_Int16 nLngSvcEvt
)
262 nCombinedLngSvcEvt
|= nLngSvcEvt
;
268 LngSvcMgrListenerHelper::processLinguServiceEvent(
269 const linguistic2::LinguServiceEvent
& rLngSvcEvent
)
271 osl::MutexGuard
aGuard( GetLinguMutex() );
272 AddLngSvcEvt( rLngSvcEvent
.nEvent
);
277 LngSvcMgrListenerHelper::processDictionaryListEvent(
278 const linguistic2::DictionaryListEvent
& rDicListEvent
)
280 osl::MutexGuard
aGuard( GetLinguMutex() );
282 sal_Int16 nDlEvt
= rDicListEvent
.nCondensedEvent
;
286 // we do keep the original event source here though...
288 // pass event on to linguistic2::XDictionaryListEventListener's
289 aLngSvcMgrListeners
.notifyEach( &linguistic2::XDictionaryListEventListener::processDictionaryListEvent
, rDicListEvent
);
291 // "translate" DictionaryList event into linguistic2::LinguServiceEvent
292 sal_Int16 nLngSvcEvt
= 0;
293 sal_Int16
const nSpellCorrectFlags
=
294 linguistic2::DictionaryListEventFlags::ADD_NEG_ENTRY
|
295 linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY
|
296 linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC
|
297 linguistic2::DictionaryListEventFlags::DEACTIVATE_POS_DIC
;
298 if (0 != (nDlEvt
& nSpellCorrectFlags
))
299 nLngSvcEvt
|= linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN
;
301 sal_Int16
const nSpellWrongFlags
=
302 linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY
|
303 linguistic2::DictionaryListEventFlags::DEL_NEG_ENTRY
|
304 linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC
|
305 linguistic2::DictionaryListEventFlags::DEACTIVATE_NEG_DIC
;
306 if (0 != (nDlEvt
& nSpellWrongFlags
))
307 nLngSvcEvt
|= linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN
;
309 sal_Int16
const nHyphenateFlags
=
310 linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY
|
311 linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY
|
312 linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC
|
313 linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC
;
314 if (0 != (nDlEvt
& nHyphenateFlags
))
315 nLngSvcEvt
|= linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN
;
317 if (rMyManager
.mxSpellDsp
.is())
318 rMyManager
.mxSpellDsp
->FlushSpellCache();
320 LaunchEvent( nLngSvcEvt
);
324 void LngSvcMgrListenerHelper::LaunchEvent( sal_Int16 nLngSvcEvtFlags
)
326 linguistic2::LinguServiceEvent
aEvt(
327 static_cast<css::linguistic2::XLinguServiceManager
*>(&rMyManager
), nLngSvcEvtFlags
);
329 // pass event on to linguistic2::XLinguServiceEventListener's
330 aLngSvcMgrListeners
.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent
, aEvt
);
334 inline void LngSvcMgrListenerHelper::AddLngSvcMgrListener(
335 const uno::Reference
< lang::XEventListener
>& rxListener
)
337 aLngSvcMgrListeners
.addInterface( rxListener
);
341 inline void LngSvcMgrListenerHelper::RemoveLngSvcMgrListener(
342 const uno::Reference
< lang::XEventListener
>& rxListener
)
344 aLngSvcMgrListeners
.removeInterface( rxListener
);
348 void LngSvcMgrListenerHelper::DisposeAndClear( const lang::EventObject
&rEvtObj
)
350 // call "disposing" for all listeners and clear list
351 aLngSvcMgrListeners
.disposeAndClear( rEvtObj
);
353 // remove references to this object hold by the broadcasters
354 comphelper::OInterfaceIteratorHelper2
aIt( aLngSvcEvtBroadcasters
);
355 while (aIt
.hasMoreElements())
357 uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> xRef( aIt
.next(), uno::UNO_QUERY
);
359 RemoveLngSvcEvtBroadcaster( xRef
);
362 // remove reference to this object hold by the dictionary-list
365 xDicList
->removeDictionaryListEventListener(
366 static_cast<linguistic2::XDictionaryListEventListener
*>(this) );
372 void LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster(
373 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
)
375 if (rxBroadcaster
.is())
377 aLngSvcEvtBroadcasters
.addInterface( rxBroadcaster
);
378 rxBroadcaster
->addLinguServiceEventListener(
379 static_cast<linguistic2::XLinguServiceEventListener
*>(this) );
384 void LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster(
385 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
)
387 if (rxBroadcaster
.is())
389 aLngSvcEvtBroadcasters
.removeInterface( rxBroadcaster
);
390 rxBroadcaster
->removeLinguServiceEventListener(
391 static_cast<linguistic2::XLinguServiceEventListener
*>(this) );
396 LngSvcMgr::LngSvcMgr()
397 : utl::ConfigItem("Office.Linguistic")
398 , aEvtListeners(GetLinguMutex())
402 // request notify events when properties (i.e. something in the subtree) changes
403 uno::Sequence
< OUString
> aNames
{
404 "ServiceManager/SpellCheckerList",
405 "ServiceManager/GrammarCheckerList",
406 "ServiceManager/HyphenatorList",
407 "ServiceManager/ThesaurusList"
409 EnableNotification( aNames
);
413 aUpdateIdle
.SetPriority(TaskPriority::LOWEST
);
414 aUpdateIdle
.SetInvokeHandler(LINK(this, LngSvcMgr
, updateAndBroadcast
));
416 // request to be notified if an extension has been added/removed
417 uno::Reference
<uno::XComponentContext
> xContext(comphelper::getProcessComponentContext());
419 uno::Reference
<deployment::XExtensionManager
> xExtensionManager
;
421 xExtensionManager
= deployment::ExtensionManager::get(xContext
);
422 } catch ( const uno::DeploymentException
& ) {
423 SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
424 } catch ( const deployment::DeploymentException
& ) {
425 SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
427 if (xExtensionManager
.is())
429 xMB
.set(xExtensionManager
, uno::UNO_QUERY_THROW
);
431 uno::Reference
<util::XModifyListener
> xListener(this);
432 xMB
->addModifyListener( xListener
);
436 // css::util::XModifyListener
437 void LngSvcMgr::modified(const lang::EventObject
&)
440 osl::MutexGuard
aGuard(GetLinguMutex());
441 //assume that if an extension has been added/removed that
442 //it might be a dictionary extension, so drop our cache
444 pAvailSpellSvcs
.reset();
445 pAvailGrammarSvcs
.reset();
446 pAvailHyphSvcs
.reset();
447 pAvailThesSvcs
.reset();
451 SolarMutexGuard aGuard
;
452 //schedule in an update to execute in the main thread
457 //run update, and inform everyone that dictionaries (may) have changed, this
458 //needs to be run in the main thread because
459 //utl::ConfigChangeListener_Impl::changesOccurred grabs the SolarMutex and we
460 //get notified that an extension was added from an extension manager thread
461 IMPL_LINK_NOARG(LngSvcMgr
, updateAndBroadcast
, Timer
*, void)
463 osl::MutexGuard
aGuard( GetLinguMutex() );
467 if (mxListenerHelper
.is())
469 mxListenerHelper
->AddLngSvcEvt(
470 linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN
|
471 linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN
|
472 linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN
|
473 linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN
);
477 void LngSvcMgr::stopListening()
479 osl::MutexGuard
aGuard(GetLinguMutex());
486 uno::Reference
<util::XModifyListener
> xListener(this);
487 xMB
->removeModifyListener(xListener
);
489 catch (const uno::Exception
&)
496 void LngSvcMgr::disposing(const lang::EventObject
&)
501 LngSvcMgr::~LngSvcMgr()
505 // memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
506 // will be freed in the destructor of the respective Reference's
507 // xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp
509 pAvailSpellSvcs
.reset();
510 pAvailGrammarSvcs
.reset();
511 pAvailHyphSvcs
.reset();
512 pAvailThesSvcs
.reset();
521 bool lcl_FindEntry( const OUString
&rEntry
, const Sequence
< OUString
> &rCfgSvcs
)
523 return comphelper::findValue(rCfgSvcs
, rEntry
) != -1;
526 bool lcl_FindEntry( const OUString
&rEntry
, const std::vector
< OUString
> &rCfgSvcs
)
528 return std::find(rCfgSvcs
.begin(), rCfgSvcs
.end(), rEntry
) != rCfgSvcs
.end();
531 Sequence
< OUString
> lcl_GetLastFoundSvcs(
532 SvtLinguConfig
const &rCfg
,
533 const OUString
&rLastFoundList
,
534 const OUString
& rCfgLocaleStr
)
536 Sequence
< OUString
> aRes
;
538 Sequence
< OUString
> aNodeNames( rCfg
.GetNodeNames(rLastFoundList
) );
539 bool bFound
= lcl_FindEntry( rCfgLocaleStr
, aNodeNames
);
543 Sequence
< OUString
> aNames
{ rLastFoundList
+ "/" + rCfgLocaleStr
};
544 Sequence
< Any
> aValues( rCfg
.GetProperties( aNames
) );
545 if (aValues
.hasElements())
547 SAL_WARN_IF( aValues
.getLength() != 1, "linguistic", "unexpected length of sequence" );
548 Sequence
< OUString
> aSvcImplNames
;
549 if (aValues
.getConstArray()[0] >>= aSvcImplNames
)
550 aRes
= aSvcImplNames
;
553 SAL_WARN( "linguistic", "type mismatch" );
561 Sequence
< OUString
> lcl_RemoveMissingEntries(
562 const Sequence
< OUString
> &rCfgSvcs
,
563 const Sequence
< OUString
> &rAvailSvcs
)
565 std::vector
<OUString
> aRes
;
566 aRes
.reserve(rCfgSvcs
.getLength());
568 std::copy_if(rCfgSvcs
.begin(), rCfgSvcs
.end(), std::back_inserter(aRes
),
569 [&rAvailSvcs
](const OUString
& entry
) { return lcl_SeqHasString(rAvailSvcs
, entry
); });
571 return comphelper::containerToSequence(aRes
);
574 Sequence
< OUString
> lcl_GetNewEntries(
575 const Sequence
< OUString
> &rLastFoundSvcs
,
576 const Sequence
< OUString
> &rAvailSvcs
)
578 std::vector
<OUString
> aRes
;
579 aRes
.reserve(rAvailSvcs
.getLength());
581 std::copy_if(rAvailSvcs
.begin(), rAvailSvcs
.end(), std::back_inserter(aRes
),
582 [&rLastFoundSvcs
](const OUString
& rEntry
) {
583 return !rEntry
.isEmpty() && !lcl_FindEntry( rEntry
, rLastFoundSvcs
); });
585 return comphelper::containerToSequence(aRes
);
588 Sequence
< OUString
> lcl_MergeSeq(
589 const Sequence
< OUString
> &rCfgSvcs
,
590 const Sequence
< OUString
> &rNewSvcs
)
592 std::vector
<OUString
> aRes
;
593 aRes
.reserve(rCfgSvcs
.getLength() + rNewSvcs
.getLength());
595 auto lVecNotHasString
= [&aRes
](const OUString
& rEntry
)
596 { return !rEntry
.isEmpty() && !lcl_FindEntry(rEntry
, aRes
); };
598 // add previously configured service first and append
599 // new found services at the end
600 for (const Sequence
< OUString
> &rSeq
: { rCfgSvcs
, rNewSvcs
})
602 std::copy_if(rSeq
.begin(), rSeq
.end(), std::back_inserter(aRes
), lVecNotHasString
);
605 return comphelper::containerToSequence(aRes
);
609 void LngSvcMgr::UpdateAll()
611 using beans::PropertyValue
;
615 typedef std::map
< OUString
, Sequence
< OUString
> > list_entry_map_t
;
619 const int nNumServices
= 4;
620 const char * const apServices
[nNumServices
] = { SN_SPELLCHECKER
, SN_GRAMMARCHECKER
, SN_HYPHENATOR
, SN_THESAURUS
};
621 const char * const apCurLists
[nNumServices
] = { "ServiceManager/SpellCheckerList", "ServiceManager/GrammarCheckerList", "ServiceManager/HyphenatorList", "ServiceManager/ThesaurusList" };
622 const char * const apLastFoundLists
[nNumServices
] = { "ServiceManager/LastFoundSpellCheckers", "ServiceManager/LastFoundGrammarCheckers", "ServiceManager/LastFoundHyphenators", "ServiceManager/LastFoundThesauri" };
624 // usage of indices as above: 0 = spell checker, 1 = grammar checker, 2 = hyphenator, 3 = thesaurus
625 std::vector
< list_entry_map_t
> aLastFoundSvcs(nNumServices
);
626 std::vector
< list_entry_map_t
> aCurSvcs(nNumServices
);
628 for (int k
= 0; k
< nNumServices
; ++k
)
630 OUString
aService( OUString::createFromAscii( apServices
[k
] ) );
631 OUString
aActiveList( OUString::createFromAscii( apCurLists
[k
] ) );
632 OUString
aLastFoundList( OUString::createFromAscii( apLastFoundLists
[k
] ) );
635 // remove configured but not available language/services entries
637 const Sequence
< OUString
> aNodeNames( aCfg
.GetNodeNames( aActiveList
) ); // list of configured locales
638 for (const OUString
& rNodeName
: aNodeNames
)
640 Locale
aLocale( LanguageTag::convertToLocale( rNodeName
));
641 Sequence
< OUString
> aCfgSvcs( getConfiguredServices( aService
, aLocale
));
642 Sequence
< OUString
> aAvailSvcs( getAvailableServices( aService
, aLocale
));
644 aCfgSvcs
= lcl_RemoveMissingEntries( aCfgSvcs
, aAvailSvcs
);
646 aCurSvcs
[k
][ rNodeName
] = aCfgSvcs
;
650 // add new available language/service entries
652 // set last found services to currently available ones
654 const Sequence
< Locale
> aAvailLocales( getAvailableLocales(aService
) );
655 for (const Locale
& rAvailLocale
: aAvailLocales
)
657 OUString
aCfgLocaleStr( LanguageTag::convertToBcp47( rAvailLocale
));
659 Sequence
< OUString
> aAvailSvcs( getAvailableServices( aService
, rAvailLocale
));
661 aLastFoundSvcs
[k
][ aCfgLocaleStr
] = aAvailSvcs
;
663 Sequence
< OUString
> aLastSvcs(
664 lcl_GetLastFoundSvcs( aCfg
, aLastFoundList
, aCfgLocaleStr
));
665 Sequence
< OUString
> aNewSvcs
=
666 lcl_GetNewEntries( aLastSvcs
, aAvailSvcs
);
668 Sequence
< OUString
> aCfgSvcs( aCurSvcs
[k
][ aCfgLocaleStr
] );
670 // merge services list (previously configured to be listed first).
671 aCfgSvcs
= lcl_MergeSeq( aCfgSvcs
, aNewSvcs
);
673 aCurSvcs
[k
][ aCfgLocaleStr
] = aCfgSvcs
;
678 // write new data back to configuration
680 for (int k
= 0; k
< nNumServices
; ++k
)
682 for (int i
= 0; i
< 2; ++i
)
684 const char *pSubNodeName
= (i
== 0) ? apCurLists
[k
] : apLastFoundLists
[k
];
685 OUString
aSubNodeName( OUString::createFromAscii(pSubNodeName
) );
687 list_entry_map_t
&rCurMap
= (i
== 0) ? aCurSvcs
[k
] : aLastFoundSvcs
[k
];
688 sal_Int32 nVals
= static_cast< sal_Int32
>( rCurMap
.size() );
689 Sequence
< PropertyValue
> aNewValues( nVals
);
690 PropertyValue
*pNewValue
= aNewValues
.getArray();
691 for (auto const& elem
: rCurMap
)
693 pNewValue
->Name
= aSubNodeName
+ "/" + elem
.first
;
694 pNewValue
->Value
<<= elem
.second
;
697 OSL_ENSURE( pNewValue
- aNewValues
.getConstArray() == nVals
,
698 "possible mismatch of sequence size and property number" );
701 // add new or replace existing entries.
702 bool bRes
= aCfg
.ReplaceSetProperties( aSubNodeName
, aNewValues
);
703 SAL_WARN_IF(!bRes
, "linguistic", "failed to set new configuration values");
708 //The new settings in the configuration get applied ! because we are
709 //listening to the configuration for changes of the relevant ! properties
710 //and Notify applies the new settings.
713 void LngSvcMgr::Notify( const uno::Sequence
< OUString
> &rPropertyNames
)
715 const OUString
aSpellCheckerList( "ServiceManager/SpellCheckerList" );
716 const OUString
aGrammarCheckerList( "ServiceManager/GrammarCheckerList" );
717 const OUString
aHyphenatorList( "ServiceManager/HyphenatorList" );
718 const OUString
aThesaurusList( "ServiceManager/ThesaurusList" );
720 const uno::Sequence
< OUString
> aSpellCheckerListEntries( GetNodeNames( aSpellCheckerList
) );
721 const uno::Sequence
< OUString
> aGrammarCheckerListEntries( GetNodeNames( aGrammarCheckerList
) );
722 const uno::Sequence
< OUString
> aHyphenatorListEntries( GetNodeNames( aHyphenatorList
) );
723 const uno::Sequence
< OUString
> aThesaurusListEntries( GetNodeNames( aThesaurusList
) );
725 uno::Sequence
< uno::Any
> aValues
;
726 uno::Sequence
< OUString
> aNames( 1 );
727 OUString
*pNames
= aNames
.getArray();
729 for (const OUString
& rName
: rPropertyNames
)
731 // property names look like
732 // "ServiceManager/ThesaurusList/de-CH"
735 nKeyStart
= rName
.lastIndexOf( '/' );
738 aKeyText
= rName
.copy( nKeyStart
+ 1 );
739 SAL_WARN_IF( aKeyText
.isEmpty(), "linguistic", "unexpected key (lang::Locale) string" );
740 if (rName
.startsWith( aSpellCheckerList
))
742 osl::MutexGuard
aGuard(GetLinguMutex());
744 // delete old cached data, needs to be acquired new on demand
745 pAvailSpellSvcs
.reset();
747 if (lcl_SeqHasString( aSpellCheckerListEntries
, aKeyText
))
749 pNames
[0] = aSpellCheckerList
+ "/" + aKeyText
;
750 aValues
= /*aCfg.*/GetProperties( aNames
);
751 uno::Sequence
< OUString
> aSvcImplNames
;
752 if (aValues
.hasElements())
753 aSvcImplNames
= GetLangSvcList( aValues
.getConstArray()[0] );
755 LanguageType nLang
= LANGUAGE_NONE
;
756 if (!aKeyText
.isEmpty())
757 nLang
= LanguageTag::convertToLanguageType( aKeyText
);
759 GetSpellCheckerDsp_Impl( false ); // don't set service list, it will be done below
760 mxSpellDsp
->SetServiceList( LanguageTag::convertToLocale(nLang
), aSvcImplNames
);
763 else if (rName
.startsWith( aGrammarCheckerList
))
765 osl::MutexGuard
aGuard(GetLinguMutex());
767 // delete old cached data, needs to be acquired new on demand
768 pAvailGrammarSvcs
.reset();
770 if (lcl_SeqHasString( aGrammarCheckerListEntries
, aKeyText
))
772 pNames
[0] = aGrammarCheckerList
+ "/" + aKeyText
;
773 aValues
= /*aCfg.*/GetProperties( aNames
);
774 uno::Sequence
< OUString
> aSvcImplNames
;
775 if (aValues
.hasElements())
776 aSvcImplNames
= GetLangSvc( aValues
.getConstArray()[0] );
778 LanguageType nLang
= LANGUAGE_NONE
;
779 if (!aKeyText
.isEmpty())
780 nLang
= LanguageTag::convertToLanguageType( aKeyText
);
782 if (SvtLinguConfig().HasGrammarChecker())
784 GetGrammarCheckerDsp_Impl( false ); // don't set service list, it will be done below
785 mxGrammarDsp
->SetServiceList( LanguageTag::convertToLocale(nLang
), aSvcImplNames
);
789 else if (rName
.startsWith( aHyphenatorList
))
791 osl::MutexGuard
aGuard(GetLinguMutex());
793 // delete old cached data, needs to be acquired new on demand
794 pAvailHyphSvcs
.reset();
796 if (lcl_SeqHasString( aHyphenatorListEntries
, aKeyText
))
798 pNames
[0] = aHyphenatorList
+ "/" + aKeyText
;
799 aValues
= /*aCfg.*/GetProperties( aNames
);
800 uno::Sequence
< OUString
> aSvcImplNames
;
801 if (aValues
.hasElements())
802 aSvcImplNames
= GetLangSvc( aValues
.getConstArray()[0] );
804 LanguageType nLang
= LANGUAGE_NONE
;
805 if (!aKeyText
.isEmpty())
806 nLang
= LanguageTag::convertToLanguageType( aKeyText
);
808 GetHyphenatorDsp_Impl( false ); // don't set service list, it will be done below
809 mxHyphDsp
->SetServiceList( LanguageTag::convertToLocale(nLang
), aSvcImplNames
);
812 else if (rName
.startsWith( aThesaurusList
))
814 osl::MutexGuard
aGuard(GetLinguMutex());
816 // delete old cached data, needs to be acquired new on demand
817 pAvailThesSvcs
.reset();
819 if (lcl_SeqHasString( aThesaurusListEntries
, aKeyText
))
821 pNames
[0] = aThesaurusList
+ "/" + aKeyText
;
822 aValues
= /*aCfg.*/GetProperties( aNames
);
823 uno::Sequence
< OUString
> aSvcImplNames
;
824 if (aValues
.hasElements())
825 aSvcImplNames
= GetLangSvcList( aValues
.getConstArray()[0] );
827 LanguageType nLang
= LANGUAGE_NONE
;
828 if (!aKeyText
.isEmpty())
829 nLang
= LanguageTag::convertToLanguageType( aKeyText
);
831 GetThesaurusDsp_Impl( false ); // don't set service list, it will be done below
832 mxThesDsp
->SetServiceList( LanguageTag::convertToLocale(nLang
), aSvcImplNames
);
837 SAL_WARN( "linguistic", "notified for unexpected property" );
843 void LngSvcMgr::ImplCommit()
845 // everything necessary should have already been done by 'SaveCfgSvcs'
846 // called from within 'setConfiguredServices'.
847 // Also this class usually exits only when the Office is being shutdown.
851 void LngSvcMgr::GetListenerHelper_Impl()
853 if (!mxListenerHelper
.is())
855 mxListenerHelper
= new LngSvcMgrListenerHelper( *this, linguistic::GetDictionaryList() );
860 void LngSvcMgr::GetSpellCheckerDsp_Impl( bool bSetSvcList
)
862 if (!mxSpellDsp
.is())
864 mxSpellDsp
= new SpellCheckerDispatcher( *this );
866 SetCfgServiceLists( *mxSpellDsp
);
871 void LngSvcMgr::GetGrammarCheckerDsp_Impl( bool bSetSvcList
)
873 if (mxGrammarDsp
.is() || !SvtLinguConfig().HasGrammarChecker())
876 //! since the grammar checking iterator needs to be a one instance service
877 //! we need to create it the correct way!
878 uno::Reference
< linguistic2::XProofreadingIterator
> xGCI
;
881 xGCI
= linguistic2::ProofreadingIterator::create( comphelper::getProcessComponentContext() );
883 catch (const uno::Exception
&)
886 SAL_WARN_IF( !xGCI
.is(), "linguistic", "instantiating grammar checking iterator failed" );
890 mxGrammarDsp
= dynamic_cast< GrammarCheckingIterator
* >(xGCI
.get());
891 SAL_WARN_IF( mxGrammarDsp
== nullptr, "linguistic", "failed to get implementation" );
892 if (bSetSvcList
&& mxGrammarDsp
.is())
893 SetCfgServiceLists( *mxGrammarDsp
);
898 void LngSvcMgr::GetHyphenatorDsp_Impl( bool bSetSvcList
)
902 mxHyphDsp
= new HyphenatorDispatcher( *this );
904 SetCfgServiceLists( *mxHyphDsp
);
909 void LngSvcMgr::GetThesaurusDsp_Impl( bool bSetSvcList
)
913 mxThesDsp
= new ThesaurusDispatcher
;
915 SetCfgServiceLists( *mxThesDsp
);
920 void LngSvcMgr::GetAvailableSpellSvcs_Impl()
925 pAvailSpellSvcs
.reset(new SvcInfoArray
);
927 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
929 uno::Reference
< container::XContentEnumerationAccess
> xEnumAccess( xContext
->getServiceManager(), uno::UNO_QUERY
);
930 uno::Reference
< container::XEnumeration
> xEnum
;
931 if (xEnumAccess
.is())
932 xEnum
= xEnumAccess
->createContentEnumeration( SN_SPELLCHECKER
);
937 while (xEnum
->hasMoreElements())
939 uno::Any aCurrent
= xEnum
->nextElement();
940 uno::Reference
< lang::XSingleComponentFactory
> xCompFactory
;
941 uno::Reference
< lang::XSingleServiceFactory
> xFactory
;
943 uno::Reference
< linguistic2::XSpellChecker
> xSvc
;
944 xCompFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
945 if (!xCompFactory
.is())
947 xFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
949 if ( xCompFactory
.is() || xFactory
.is() )
953 xSvc
.set( ( xCompFactory
.is() ? xCompFactory
->createInstanceWithContext( xContext
) : xFactory
->createInstance() ), uno::UNO_QUERY
);
955 catch (const uno::Exception
&)
957 SAL_WARN( "linguistic", "createInstance failed" );
964 std::vector
< LanguageType
> aLanguages
;
965 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
967 aImplName
= xInfo
->getImplementationName();
968 SAL_WARN_IF( aImplName
.isEmpty(), "linguistic", "empty implementation name" );
969 uno::Sequence
<lang::Locale
> aLocaleSequence(xSvc
->getLocales());
970 aLanguages
= LocaleSeqToLangVec( aLocaleSequence
);
972 pAvailSpellSvcs
->push_back( std::make_unique
<SvcInfo
>( aImplName
, aLanguages
) );
978 void LngSvcMgr::GetAvailableGrammarSvcs_Impl()
980 if (pAvailGrammarSvcs
)
983 pAvailGrammarSvcs
.reset(new SvcInfoArray
);
985 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
987 uno::Reference
< container::XContentEnumerationAccess
> xEnumAccess( xContext
->getServiceManager(), uno::UNO_QUERY
);
988 uno::Reference
< container::XEnumeration
> xEnum
;
989 if (xEnumAccess
.is())
990 xEnum
= xEnumAccess
->createContentEnumeration( SN_GRAMMARCHECKER
);
995 while (xEnum
->hasMoreElements())
997 uno::Any aCurrent
= xEnum
->nextElement();
998 uno::Reference
< lang::XSingleComponentFactory
> xCompFactory
;
999 uno::Reference
< lang::XSingleServiceFactory
> xFactory
;
1001 uno::Reference
< linguistic2::XProofreader
> xSvc
;
1002 xCompFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1003 if (!xCompFactory
.is())
1005 xFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1007 if ( xCompFactory
.is() || xFactory
.is() )
1011 if (xCompFactory
.is())
1013 xSvc
.set(xCompFactory
->createInstanceWithContext(xContext
), uno::UNO_QUERY
);
1017 xSvc
.set(xFactory
->createInstance(), uno::UNO_QUERY
);
1020 catch (const uno::Exception
&)
1022 SAL_WARN( "linguistic", "createInstance failed" );
1026 if (xSvc
.is() && pAvailGrammarSvcs
)
1029 std::vector
< LanguageType
> aLanguages
;
1030 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
1032 aImplName
= xInfo
->getImplementationName();
1033 SAL_WARN_IF( aImplName
.isEmpty(), "linguistic", "empty implementation name" );
1034 uno::Sequence
<lang::Locale
> aLocaleSequence(xSvc
->getLocales());
1035 aLanguages
= LocaleSeqToLangVec( aLocaleSequence
);
1037 pAvailGrammarSvcs
->push_back( std::make_unique
<SvcInfo
>( aImplName
, aLanguages
) );
1043 void LngSvcMgr::GetAvailableHyphSvcs_Impl()
1048 pAvailHyphSvcs
.reset(new SvcInfoArray
);
1049 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
1051 uno::Reference
< container::XContentEnumerationAccess
> xEnumAccess( xContext
->getServiceManager(), uno::UNO_QUERY
);
1052 uno::Reference
< container::XEnumeration
> xEnum
;
1053 if (xEnumAccess
.is())
1054 xEnum
= xEnumAccess
->createContentEnumeration( SN_HYPHENATOR
);
1059 while (xEnum
->hasMoreElements())
1061 uno::Any aCurrent
= xEnum
->nextElement();
1062 uno::Reference
< lang::XSingleComponentFactory
> xCompFactory
;
1063 uno::Reference
< lang::XSingleServiceFactory
> xFactory
;
1065 uno::Reference
< linguistic2::XHyphenator
> xSvc
;
1066 xCompFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1067 if (!xCompFactory
.is())
1069 xFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1071 if ( xCompFactory
.is() || xFactory
.is() )
1075 xSvc
.set( ( xCompFactory
.is() ? xCompFactory
->createInstanceWithContext( xContext
) : xFactory
->createInstance() ), uno::UNO_QUERY
);
1077 catch (const uno::Exception
&)
1079 SAL_WARN( "linguistic", "createInstance failed" );
1085 std::vector
< LanguageType
> aLanguages
;
1086 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
1088 aImplName
= xInfo
->getImplementationName();
1089 SAL_WARN_IF( aImplName
.isEmpty(), "linguistic", "empty implementation name" );
1090 uno::Sequence
<lang::Locale
> aLocaleSequence(xSvc
->getLocales());
1091 aLanguages
= LocaleSeqToLangVec( aLocaleSequence
);
1092 pAvailHyphSvcs
->push_back( std::make_unique
<SvcInfo
>( aImplName
, aLanguages
) );
1098 void LngSvcMgr::GetAvailableThesSvcs_Impl()
1103 pAvailThesSvcs
.reset(new SvcInfoArray
);
1105 uno::Reference
< uno::XComponentContext
> xContext( comphelper::getProcessComponentContext() );
1107 uno::Reference
< container::XContentEnumerationAccess
> xEnumAccess( xContext
->getServiceManager(), uno::UNO_QUERY
);
1108 uno::Reference
< container::XEnumeration
> xEnum
;
1109 if (xEnumAccess
.is())
1110 xEnum
= xEnumAccess
->createContentEnumeration( SN_THESAURUS
);
1115 while (xEnum
->hasMoreElements())
1117 uno::Any aCurrent
= xEnum
->nextElement();
1118 uno::Reference
< lang::XSingleComponentFactory
> xCompFactory
;
1119 uno::Reference
< lang::XSingleServiceFactory
> xFactory
;
1121 uno::Reference
< linguistic2::XThesaurus
> xSvc
;
1122 xCompFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1123 if (!xCompFactory
.is())
1125 xFactory
.set(aCurrent
, css::uno::UNO_QUERY
);
1127 if ( xCompFactory
.is() || xFactory
.is() )
1131 xSvc
.set( ( xCompFactory
.is() ? xCompFactory
->createInstanceWithContext( xContext
) : xFactory
->createInstance() ), uno::UNO_QUERY
);
1133 catch (const uno::Exception
&)
1135 SAL_WARN( "linguistic", "createInstance failed" );
1141 std::vector
< LanguageType
> aLanguages
;
1142 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
1144 aImplName
= xInfo
->getImplementationName();
1145 SAL_WARN_IF( aImplName
.isEmpty(), "linguistic", "empty implementation name" );
1146 uno::Sequence
<lang::Locale
> aLocaleSequence(xSvc
->getLocales());
1147 aLanguages
= LocaleSeqToLangVec( aLocaleSequence
);
1149 pAvailThesSvcs
->push_back( std::make_unique
<SvcInfo
>( aImplName
, aLanguages
) );
1155 void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher
&rSpellDsp
)
1157 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Spell" );
1159 OUString
aNode("ServiceManager/SpellCheckerList");
1160 uno::Sequence
< OUString
> aNames( /*aCfg.*/GetNodeNames( aNode
) );
1162 // append path prefix need for 'GetProperties' call below
1163 OUString aPrefix
= aNode
+ "/";
1164 for (OUString
& name
: aNames
)
1166 name
= aPrefix
+ name
;
1169 const uno::Sequence
< uno::Any
> aValues( /*aCfg.*/GetProperties( aNames
) );
1170 if (!(aNames
.hasElements() && aNames
.getLength() == aValues
.getLength()))
1173 const OUString
*pNames
= aNames
.getConstArray();
1174 for (const uno::Any
& rValue
: aValues
)
1176 uno::Sequence
< OUString
> aSvcImplNames
;
1177 if (rValue
>>= aSvcImplNames
)
1179 OUString
aLocaleStr( *pNames
++ );
1180 sal_Int32 nSeparatorPos
= aLocaleStr
.lastIndexOf( '/' );
1181 aLocaleStr
= aLocaleStr
.copy( nSeparatorPos
+ 1 );
1182 rSpellDsp
.SetServiceList( LanguageTag::convertToLocale(aLocaleStr
), aSvcImplNames
);
1188 void LngSvcMgr::SetCfgServiceLists( GrammarCheckingIterator
&rGrammarDsp
)
1190 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Grammar" );
1192 OUString
aNode("ServiceManager/GrammarCheckerList");
1193 uno::Sequence
< OUString
> aNames( /*aCfg.*/GetNodeNames( aNode
) );
1195 // append path prefix need for 'GetProperties' call below
1196 OUString aPrefix
= aNode
+ "/";
1197 for (OUString
& name
: aNames
)
1199 name
= aPrefix
+ name
;
1202 const uno::Sequence
< uno::Any
> aValues( /*aCfg.*/GetProperties( aNames
) );
1203 if (!(aNames
.hasElements() && aNames
.getLength() == aValues
.getLength()))
1206 const OUString
*pNames
= aNames
.getConstArray();
1207 for (const uno::Any
& rValue
: aValues
)
1209 uno::Sequence
< OUString
> aSvcImplNames
;
1210 if (rValue
>>= aSvcImplNames
)
1212 // there should only be one grammar checker in use per language...
1213 if (aSvcImplNames
.getLength() > 1)
1214 aSvcImplNames
.realloc(1);
1216 OUString
aLocaleStr( *pNames
++ );
1217 sal_Int32 nSeparatorPos
= aLocaleStr
.lastIndexOf( '/' );
1218 aLocaleStr
= aLocaleStr
.copy( nSeparatorPos
+ 1 );
1219 rGrammarDsp
.SetServiceList( LanguageTag::convertToLocale(aLocaleStr
), aSvcImplNames
);
1225 void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher
&rHyphDsp
)
1227 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Hyph" );
1229 OUString
aNode("ServiceManager/HyphenatorList");
1230 uno::Sequence
< OUString
> aNames( /*aCfg.*/GetNodeNames( aNode
) );
1232 // append path prefix need for 'GetProperties' call below
1233 OUString aPrefix
= aNode
+ "/";
1234 for (OUString
& name
: aNames
)
1236 name
= aPrefix
+ name
;
1239 const uno::Sequence
< uno::Any
> aValues( /*aCfg.*/GetProperties( aNames
) );
1240 if (!(aNames
.hasElements() && aNames
.getLength() == aValues
.getLength()))
1243 const OUString
*pNames
= aNames
.getConstArray();
1244 for (const uno::Any
& rValue
: aValues
)
1246 uno::Sequence
< OUString
> aSvcImplNames
;
1247 if (rValue
>>= aSvcImplNames
)
1249 // there should only be one hyphenator in use per language...
1250 if (aSvcImplNames
.getLength() > 1)
1251 aSvcImplNames
.realloc(1);
1253 OUString
aLocaleStr( *pNames
++ );
1254 sal_Int32 nSeparatorPos
= aLocaleStr
.lastIndexOf( '/' );
1255 aLocaleStr
= aLocaleStr
.copy( nSeparatorPos
+ 1 );
1256 rHyphDsp
.SetServiceList( LanguageTag::convertToLocale(aLocaleStr
), aSvcImplNames
);
1262 void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher
&rThesDsp
)
1264 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Thes" );
1266 OUString
aNode("ServiceManager/ThesaurusList");
1267 uno::Sequence
< OUString
> aNames( /*aCfg.*/GetNodeNames( aNode
) );
1269 // append path prefix need for 'GetProperties' call below
1270 OUString aPrefix
= aNode
+ "/";
1271 for (OUString
& name
: aNames
)
1273 name
= aPrefix
+ name
;
1276 const uno::Sequence
< uno::Any
> aValues( /*aCfg.*/GetProperties( aNames
) );
1277 if (!(aNames
.hasElements() && aNames
.getLength() == aValues
.getLength()))
1280 const OUString
*pNames
= aNames
.getConstArray();
1281 for (const uno::Any
& rValue
: aValues
)
1283 uno::Sequence
< OUString
> aSvcImplNames
;
1284 if (rValue
>>= aSvcImplNames
)
1286 OUString
aLocaleStr( *pNames
++ );
1287 sal_Int32 nSeparatorPos
= aLocaleStr
.lastIndexOf( '/' );
1288 aLocaleStr
= aLocaleStr
.copy( nSeparatorPos
+ 1 );
1289 rThesDsp
.SetServiceList( LanguageTag::convertToLocale(aLocaleStr
), aSvcImplNames
);
1295 uno::Reference
< linguistic2::XSpellChecker
> SAL_CALL
1296 LngSvcMgr::getSpellChecker()
1298 osl::MutexGuard
aGuard( GetLinguMutex() );
1299 #if OSL_DEBUG_LEVEL > 0
1300 getAvailableLocales(SN_SPELLCHECKER
);
1303 uno::Reference
< linguistic2::XSpellChecker
> xRes
;
1306 if (!mxSpellDsp
.is())
1307 GetSpellCheckerDsp_Impl();
1308 xRes
= mxSpellDsp
.get();
1314 uno::Reference
< linguistic2::XHyphenator
> SAL_CALL
1315 LngSvcMgr::getHyphenator()
1317 osl::MutexGuard
aGuard( GetLinguMutex() );
1318 #if OSL_DEBUG_LEVEL > 0
1319 getAvailableLocales(SN_HYPHENATOR
);
1321 uno::Reference
< linguistic2::XHyphenator
> xRes
;
1324 if (!mxHyphDsp
.is())
1325 GetHyphenatorDsp_Impl();
1326 xRes
= mxHyphDsp
.get();
1332 uno::Reference
< linguistic2::XThesaurus
> SAL_CALL
1333 LngSvcMgr::getThesaurus()
1335 osl::MutexGuard
aGuard( GetLinguMutex() );
1336 #if OSL_DEBUG_LEVEL > 0
1337 getAvailableLocales(SN_THESAURUS
);
1339 uno::Reference
< linguistic2::XThesaurus
> xRes
;
1342 if (!mxThesDsp
.is())
1343 GetThesaurusDsp_Impl();
1344 xRes
= mxThesDsp
.get();
1351 LngSvcMgr::addLinguServiceManagerListener(
1352 const uno::Reference
< lang::XEventListener
>& xListener
)
1354 osl::MutexGuard
aGuard( GetLinguMutex() );
1356 if (bDisposing
|| !xListener
.is())
1359 if (!mxListenerHelper
.is())
1360 GetListenerHelper_Impl();
1361 mxListenerHelper
->AddLngSvcMgrListener( xListener
);
1367 LngSvcMgr::removeLinguServiceManagerListener(
1368 const uno::Reference
< lang::XEventListener
>& xListener
)
1370 osl::MutexGuard
aGuard( GetLinguMutex() );
1372 if (bDisposing
|| !xListener
.is())
1375 DBG_ASSERT( mxListenerHelper
.is(), "listener removed without being added" );
1376 if (!mxListenerHelper
.is())
1377 GetListenerHelper_Impl();
1378 mxListenerHelper
->RemoveLngSvcMgrListener( xListener
);
1383 uno::Sequence
< OUString
> SAL_CALL
1384 LngSvcMgr::getAvailableServices(
1385 const OUString
& rServiceName
,
1386 const lang::Locale
& rLocale
)
1388 osl::MutexGuard
aGuard( GetLinguMutex() );
1390 uno::Sequence
< OUString
> aRes
;
1391 const SvcInfoArray
*pInfoArray
= nullptr;
1393 if (rServiceName
== SN_SPELLCHECKER
)
1395 GetAvailableSpellSvcs_Impl();
1396 pInfoArray
= pAvailSpellSvcs
.get();
1398 else if (rServiceName
== SN_GRAMMARCHECKER
)
1400 GetAvailableGrammarSvcs_Impl();
1401 pInfoArray
= pAvailGrammarSvcs
.get();
1403 else if (rServiceName
== SN_HYPHENATOR
)
1405 GetAvailableHyphSvcs_Impl();
1406 pInfoArray
= pAvailHyphSvcs
.get();
1408 else if (rServiceName
== SN_THESAURUS
)
1410 GetAvailableThesSvcs_Impl();
1411 pInfoArray
= pAvailThesSvcs
.get();
1416 std::vector
<OUString
> aVec
;
1417 aVec
.reserve(pInfoArray
->size());
1419 LanguageType nLanguage
= LinguLocaleToLanguage( rLocale
);
1420 for (const auto& pInfo
: *pInfoArray
)
1422 if (LinguIsUnspecified( nLanguage
)
1423 || pInfo
->HasLanguage( nLanguage
))
1425 aVec
.push_back(pInfo
->aSvcImplName
);
1429 aRes
= comphelper::containerToSequence(aVec
);
1436 uno::Sequence
< lang::Locale
> SAL_CALL
1437 LngSvcMgr::getAvailableLocales(
1438 const OUString
& rServiceName
)
1440 osl::MutexGuard
aGuard( GetLinguMutex() );
1442 uno::Sequence
< lang::Locale
> aRes
;
1444 uno::Sequence
< lang::Locale
> *pAvailLocales
= nullptr;
1445 if (rServiceName
== SN_SPELLCHECKER
)
1446 pAvailLocales
= &aAvailSpellLocales
;
1447 else if (rServiceName
== SN_GRAMMARCHECKER
)
1448 pAvailLocales
= &aAvailGrammarLocales
;
1449 else if (rServiceName
== SN_HYPHENATOR
)
1450 pAvailLocales
= &aAvailHyphLocales
;
1451 else if (rServiceName
== SN_THESAURUS
)
1452 pAvailLocales
= &aAvailThesLocales
;
1454 // Nowadays (with OOo lingu in SO) we want to know immediately about
1455 // new downloaded dictionaries and have them ready right away if the Tools/Options...
1456 // is used to activate them. Thus we can not rely anymore on buffered data.
1459 *pAvailLocales
= GetAvailLocales(getAvailableServices(rServiceName
, lang::Locale()));
1460 aRes
= *pAvailLocales
;
1466 static bool IsEqSvcList( const uno::Sequence
< OUString
> &rList1
,
1467 const uno::Sequence
< OUString
> &rList2
)
1469 // returns true if both sequences are equal
1470 return rList1
.getLength() == rList2
.getLength()
1471 && std::equal(rList1
.begin(), rList1
.end(), rList2
.begin(), rList2
.end());
1476 LngSvcMgr::setConfiguredServices(
1477 const OUString
& rServiceName
,
1478 const lang::Locale
& rLocale
,
1479 const uno::Sequence
< OUString
>& rServiceImplNames
)
1481 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::setConfiguredServices" );
1483 osl::MutexGuard
aGuard( GetLinguMutex() );
1485 LanguageType nLanguage
= LinguLocaleToLanguage( rLocale
);
1486 if (LinguIsUnspecified( nLanguage
))
1489 if (rServiceName
== SN_SPELLCHECKER
)
1491 if (!mxSpellDsp
.is())
1492 GetSpellCheckerDsp_Impl();
1493 bool bChanged
= !IsEqSvcList( rServiceImplNames
,
1494 mxSpellDsp
->GetServiceList( rLocale
) );
1497 mxSpellDsp
->SetServiceList( rLocale
, rServiceImplNames
);
1498 SaveCfgSvcs( SN_SPELLCHECKER
);
1500 if (mxListenerHelper
)
1501 mxListenerHelper
->AddLngSvcEvt(
1502 linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN
|
1503 linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN
);
1506 else if (rServiceName
== SN_GRAMMARCHECKER
)
1508 if (!mxGrammarDsp
.is())
1509 GetGrammarCheckerDsp_Impl();
1510 bool bChanged
= !IsEqSvcList( rServiceImplNames
,
1511 mxGrammarDsp
->GetServiceList( rLocale
) );
1514 mxGrammarDsp
->SetServiceList( rLocale
, rServiceImplNames
);
1515 SaveCfgSvcs( SN_GRAMMARCHECKER
);
1517 if (mxListenerHelper
)
1518 mxListenerHelper
->AddLngSvcEvt(
1519 linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN
);
1522 else if (rServiceName
== SN_HYPHENATOR
)
1524 if (!mxHyphDsp
.is())
1525 GetHyphenatorDsp_Impl();
1526 bool bChanged
= !IsEqSvcList( rServiceImplNames
,
1527 mxHyphDsp
->GetServiceList( rLocale
) );
1530 mxHyphDsp
->SetServiceList( rLocale
, rServiceImplNames
);
1531 SaveCfgSvcs( SN_HYPHENATOR
);
1533 if (mxListenerHelper
)
1534 mxListenerHelper
->AddLngSvcEvt(
1535 linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN
);
1538 else if (rServiceName
== SN_THESAURUS
)
1540 if (!mxThesDsp
.is())
1541 GetThesaurusDsp_Impl();
1542 bool bChanged
= !IsEqSvcList( rServiceImplNames
,
1543 mxThesDsp
->GetServiceList( rLocale
) );
1546 mxThesDsp
->SetServiceList( rLocale
, rServiceImplNames
);
1547 SaveCfgSvcs( SN_THESAURUS
);
1553 bool LngSvcMgr::SaveCfgSvcs( const OUString
&rServiceName
)
1555 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs" );
1559 LinguDispatcher
*pDsp
= nullptr;
1560 uno::Sequence
< lang::Locale
> aLocales
;
1562 if (rServiceName
== SN_SPELLCHECKER
)
1565 GetSpellCheckerDsp_Impl();
1566 pDsp
= mxSpellDsp
.get();
1567 aLocales
= getAvailableLocales( SN_SPELLCHECKER
);
1569 else if (rServiceName
== SN_GRAMMARCHECKER
)
1571 if (!mxGrammarDsp
.is())
1572 GetGrammarCheckerDsp_Impl();
1573 pDsp
= mxGrammarDsp
.get();
1574 aLocales
= getAvailableLocales( SN_GRAMMARCHECKER
);
1576 else if (rServiceName
== SN_HYPHENATOR
)
1578 if (!mxHyphDsp
.is())
1579 GetHyphenatorDsp_Impl();
1580 pDsp
= mxHyphDsp
.get();
1581 aLocales
= getAvailableLocales( SN_HYPHENATOR
);
1583 else if (rServiceName
== SN_THESAURUS
)
1585 if (!mxThesDsp
.is())
1586 GetThesaurusDsp_Impl();
1587 pDsp
= mxThesDsp
.get();
1588 aLocales
= getAvailableLocales( SN_THESAURUS
);
1591 if (pDsp
&& aLocales
.hasElements())
1593 uno::Sequence
< beans::PropertyValue
> aValues( aLocales
.getLength() );
1594 beans::PropertyValue
*pValue
= aValues
.getArray();
1596 // get node name to be used
1597 const char *pNodeName
= nullptr;
1598 if (pDsp
== mxSpellDsp
.get())
1599 pNodeName
= "ServiceManager/SpellCheckerList";
1600 else if (pDsp
== mxGrammarDsp
.get())
1601 pNodeName
= "ServiceManager/GrammarCheckerList";
1602 else if (pDsp
== mxHyphDsp
.get())
1603 pNodeName
= "ServiceManager/HyphenatorList";
1604 else if (pDsp
== mxThesDsp
.get())
1605 pNodeName
= "ServiceManager/ThesaurusList";
1608 SAL_WARN( "linguistic", "node name missing" );
1610 OUString
aNodeName( OUString::createFromAscii(pNodeName
) );
1612 for (const lang::Locale
& rLocale
: std::as_const(aLocales
))
1614 uno::Sequence
< OUString
> aSvcImplNames
= pDsp
->GetServiceList( rLocale
);
1616 // build value to be written back to configuration
1618 if ((pDsp
== mxHyphDsp
.get() || pDsp
== mxGrammarDsp
.get()) && aSvcImplNames
.getLength() > 1)
1619 aSvcImplNames
.realloc(1); // there should be only one entry for hyphenators or grammar checkers (because they are not chained)
1620 aCfgAny
<<= aSvcImplNames
;
1621 DBG_ASSERT( aCfgAny
.hasValue(), "missing value for 'Any' type" );
1623 OUString
aCfgLocaleStr( LanguageTag::convertToBcp47( rLocale
));
1624 pValue
->Value
= aCfgAny
;
1625 pValue
->Name
= aNodeName
+ "/" + aCfgLocaleStr
;
1629 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" );
1630 // change, add new or replace existing entries.
1631 bRes
|= /*aCfg.*/ReplaceSetProperties( aNodeName
, aValues
);
1639 static uno::Sequence
< OUString
> GetLangSvcList( const uno::Any
&rVal
)
1641 uno::Sequence
< OUString
> aRes
;
1643 if (rVal
.hasValue())
1646 #if OSL_DEBUG_LEVEL > 0
1647 for (const OUString
& rSvcName
: std::as_const(aRes
))
1649 SAL_WARN_IF( rSvcName
.isEmpty(), "linguistic", "service impl-name missing" );
1658 static uno::Sequence
< OUString
> GetLangSvc( const uno::Any
&rVal
)
1660 uno::Sequence
< OUString
> aRes
;
1661 if (!rVal
.hasValue())
1664 // allowing for a sequence here as well (even though it should only
1665 // be a string) makes coding easier in other places since one needs
1666 // not make a special case for writing a string only and not a
1667 // sequence of strings.
1670 // but only the first string should be used.
1671 if (aRes
.getLength() > 1)
1677 if ((rVal
>>= aImplName
) && !aImplName
.isEmpty())
1680 aRes
.getArray()[0] = aImplName
;
1684 SAL_WARN( "linguistic", "GetLangSvc: unexpected type encountered" );
1692 uno::Sequence
< OUString
> SAL_CALL
1693 LngSvcMgr::getConfiguredServices(
1694 const OUString
& rServiceName
,
1695 const lang::Locale
& rLocale
)
1697 osl::MutexGuard
aGuard( GetLinguMutex() );
1699 uno::Sequence
< OUString
> aSvcImplNames
;
1701 OUString
aCfgLocale( LanguageTag::convertToBcp47( rLocale
) );
1703 uno::Sequence
< uno::Any
> aValues
;
1704 uno::Sequence
< OUString
> aNames( 1 );
1705 OUString
*pNames
= aNames
.getArray();
1706 if ( rServiceName
== SN_SPELLCHECKER
)
1708 OUString
aNode( "ServiceManager/SpellCheckerList");
1709 const uno::Sequence
< OUString
> aNodeEntries( GetNodeNames( aNode
) );
1710 if (lcl_SeqHasString( aNodeEntries
, aCfgLocale
))
1712 pNames
[0] = aNode
+ "/" + aCfgLocale
;
1713 aValues
= /*aCfg.*/GetProperties( aNames
);
1714 if (aValues
.hasElements())
1715 aSvcImplNames
= GetLangSvcList( aValues
.getConstArray()[0] );
1718 else if ( rServiceName
== SN_GRAMMARCHECKER
)
1720 OUString
aNode( "ServiceManager/GrammarCheckerList");
1721 const uno::Sequence
< OUString
> aNodeEntries( GetNodeNames( aNode
) );
1722 if (lcl_SeqHasString( aNodeEntries
, aCfgLocale
))
1724 pNames
[0] = aNode
+ "/" + aCfgLocale
;
1725 aValues
= /*aCfg.*/GetProperties( aNames
);
1726 if (aValues
.hasElements())
1727 aSvcImplNames
= GetLangSvc( aValues
.getConstArray()[0] );
1730 else if ( rServiceName
== SN_HYPHENATOR
)
1732 OUString
aNode( "ServiceManager/HyphenatorList");
1733 const uno::Sequence
< OUString
> aNodeEntries( GetNodeNames( aNode
) );
1734 if (lcl_SeqHasString( aNodeEntries
, aCfgLocale
))
1736 pNames
[0] = aNode
+ "/" + aCfgLocale
;
1737 aValues
= /*aCfg.*/GetProperties( aNames
);
1738 if (aValues
.hasElements())
1739 aSvcImplNames
= GetLangSvc( aValues
.getConstArray()[0] );
1742 else if ( rServiceName
== SN_THESAURUS
)
1744 OUString
aNode( "ServiceManager/ThesaurusList");
1745 const uno::Sequence
< OUString
> aNodeEntries( GetNodeNames( aNode
) );
1746 if (lcl_SeqHasString( aNodeEntries
, aCfgLocale
))
1748 pNames
[0] = aNode
+ "/" + aCfgLocale
;
1749 aValues
= /*aCfg.*/GetProperties( aNames
);
1750 if (aValues
.hasElements())
1751 aSvcImplNames
= GetLangSvcList( aValues
.getConstArray()[0] );
1755 return aSvcImplNames
;
1760 LngSvcMgr::dispose()
1762 osl::MutexGuard
aGuard( GetLinguMutex() );
1768 // require listeners to release this object
1769 lang::EventObject
aEvtObj( static_cast<XLinguServiceManager
*>(this) );
1770 aEvtListeners
.disposeAndClear( aEvtObj
);
1772 if (mxListenerHelper
.is())
1773 mxListenerHelper
->DisposeAndClear( aEvtObj
);
1779 LngSvcMgr::addEventListener(
1780 const uno::Reference
< lang::XEventListener
>& xListener
)
1782 osl::MutexGuard
aGuard( GetLinguMutex() );
1784 if (!bDisposing
&& xListener
.is())
1786 aEvtListeners
.addInterface( xListener
);
1792 LngSvcMgr::removeEventListener(
1793 const uno::Reference
< lang::XEventListener
>& xListener
)
1795 osl::MutexGuard
aGuard( GetLinguMutex() );
1799 aEvtListeners
.removeInterface( xListener
);
1804 bool LngSvcMgr::AddLngSvcEvtBroadcaster(
1805 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
)
1807 if (!rxBroadcaster
.is())
1809 if (!mxListenerHelper
.is())
1810 GetListenerHelper_Impl();
1811 mxListenerHelper
->AddLngSvcEvtBroadcaster( rxBroadcaster
);
1817 LngSvcMgr::getImplementationName()
1819 return "com.sun.star.lingu2.LngSvcMgr";
1824 LngSvcMgr::supportsService( const OUString
& ServiceName
)
1826 return cppu::supportsService(this, ServiceName
);
1830 uno::Sequence
< OUString
> SAL_CALL
1831 LngSvcMgr::getSupportedServiceNames()
1833 return { "com.sun.star.linguistic2.LinguServiceManager" };
1836 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1837 linguistic_LngSvcMgr_get_implementation(
1838 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
1840 return cppu::acquire(static_cast<cppu::OWeakObject
*>(new LngSvcMgr()));
1844 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */