Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / linguistic / source / lngsvcmgr.cxx
blobf8d373a6c5543d5cc6549e308fc1baeef9ca9a9d
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/XMultiServiceFactory.hpp>
28 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
29 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
30 #include <com/sun/star/linguistic2/XSupportedLocales.hpp>
31 #include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp>
32 #include <com/sun/star/linguistic2/LinguServiceEventFlags.hpp>
33 #include <com/sun/star/linguistic2/ProofreadingIterator.hpp>
35 #include <tools/debug.hxx>
36 #include <unotools/lingucfg.hxx>
37 #include <vcl/svapp.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/sequence.hxx>
40 #include <i18nlangtag/lang.h>
41 #include <i18nlangtag/languagetag.hxx>
42 #include <cppuhelper/factory.hxx>
43 #include <cppuhelper/implbase.hxx>
44 #include <cppuhelper/supportsservice.hxx>
46 #include "lngsvcmgr.hxx"
47 #include "lngreg.hxx"
48 #include <linguistic/misc.hxx>
49 #include "spelldsp.hxx"
50 #include "hyphdsp.hxx"
51 #include "thesdsp.hxx"
52 #include "gciterator.hxx"
54 using namespace com::sun::star;
55 using namespace linguistic;
57 uno::Sequence< OUString > static GetLangSvcList( const uno::Any &rVal );
58 uno::Sequence< OUString > static GetLangSvc( const uno::Any &rVal );
60 static bool lcl_SeqHasString( const uno::Sequence< OUString > &rSeq, const OUString &rText )
62 return !rText.isEmpty()
63 && comphelper::findValue(rSeq, rText) != -1;
67 static uno::Sequence< lang::Locale > GetAvailLocales(
68 const uno::Sequence< OUString > &rSvcImplNames )
70 uno::Sequence< lang::Locale > aRes;
72 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
73 if( rSvcImplNames.hasElements() )
75 std::set< LanguageType > aLanguages;
77 //! since we're going to create one-instance services we have to
78 //! supply their arguments even if we would not need them here...
79 uno::Sequence< uno::Any > aArgs(2);
80 aArgs.getArray()[0] <<= GetLinguProperties();
82 // check all services for the supported languages and new
83 // languages to the result
85 for (const OUString& rImplName : rSvcImplNames)
87 uno::Reference< linguistic2::XSupportedLocales > xSuppLoc;
88 try
90 xSuppLoc.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
91 rImplName, aArgs, xContext ),
92 uno::UNO_QUERY );
94 catch (uno::Exception &)
96 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
99 if (xSuppLoc.is())
101 const uno::Sequence< lang::Locale > aLoc( xSuppLoc->getLocales() );
102 for (const lang::Locale& rLoc : aLoc)
104 LanguageType nLang = LinguLocaleToLanguage( rLoc );
106 // It's a set, so insertion fails if language was already added.
107 aLanguages.insert( nLang );
110 else
112 SAL_WARN( "linguistic", "interface not supported by service" );
116 // build return sequence
117 std::vector<lang::Locale> aVec;
118 aVec.reserve(aLanguages.size());
120 std::transform(aLanguages.begin(), aLanguages.end(), std::back_inserter(aVec),
121 [](const LanguageType& rLang) -> lang::Locale { return LanguageTag::convertToLocale(rLang); });
123 aRes = comphelper::containerToSequence(aVec);
126 return aRes;
130 struct SvcInfo
132 const OUString aSvcImplName;
133 const std::vector< LanguageType > aSuppLanguages;
135 SvcInfo( const OUString &rSvcImplName,
136 const std::vector< LanguageType > &rSuppLanguages ) :
137 aSvcImplName (rSvcImplName),
138 aSuppLanguages (rSuppLanguages)
142 bool HasLanguage( LanguageType nLanguage ) const;
146 bool SvcInfo::HasLanguage( LanguageType nLanguage ) const
148 for ( auto const & i : aSuppLanguages)
150 if (nLanguage == i)
151 return true;
153 return false;
156 class LngSvcMgrListenerHelper :
157 public cppu::WeakImplHelper
159 linguistic2::XLinguServiceEventListener,
160 linguistic2::XDictionaryListEventListener
163 LngSvcMgr &rMyManager;
165 ::comphelper::OInterfaceContainerHelper2 aLngSvcMgrListeners;
166 ::comphelper::OInterfaceContainerHelper2 aLngSvcEvtBroadcasters;
167 uno::Reference< linguistic2::XSearchableDictionaryList > xDicList;
169 sal_Int16 nCombinedLngSvcEvt;
171 void LaunchEvent( sal_Int16 nLngSvcEvtFlags );
173 void Timeout();
175 public:
176 LngSvcMgrListenerHelper( LngSvcMgr &rLngSvcMgr,
177 const uno::Reference< linguistic2::XSearchableDictionaryList > &rxDicList );
179 LngSvcMgrListenerHelper(const LngSvcMgrListenerHelper&) = delete;
180 LngSvcMgrListenerHelper& operator=(const LngSvcMgrListenerHelper&) = delete;
182 // lang::XEventListener
183 virtual void SAL_CALL
184 disposing( const lang::EventObject& rSource ) override;
186 // linguistic2::XLinguServiceEventListener
187 virtual void SAL_CALL
188 processLinguServiceEvent( const linguistic2::LinguServiceEvent& aLngSvcEvent ) override;
190 // linguistic2::XDictionaryListEventListener
191 virtual void SAL_CALL
192 processDictionaryListEvent(
193 const linguistic2::DictionaryListEvent& rDicListEvent ) override;
195 inline void AddLngSvcMgrListener(
196 const uno::Reference< lang::XEventListener >& rxListener );
197 inline void RemoveLngSvcMgrListener(
198 const uno::Reference< lang::XEventListener >& rxListener );
199 void DisposeAndClear( const lang::EventObject &rEvtObj );
200 void AddLngSvcEvtBroadcaster(
201 const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
202 void RemoveLngSvcEvtBroadcaster(
203 const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster );
205 void AddLngSvcEvt( sal_Int16 nLngSvcEvt );
209 LngSvcMgrListenerHelper::LngSvcMgrListenerHelper(
210 LngSvcMgr &rLngSvcMgr,
211 const uno::Reference< linguistic2::XSearchableDictionaryList > &rxDicList ) :
212 rMyManager ( rLngSvcMgr ),
213 aLngSvcMgrListeners ( GetLinguMutex() ),
214 aLngSvcEvtBroadcasters ( GetLinguMutex() ),
215 xDicList ( rxDicList )
217 if (xDicList.is())
219 xDicList->addDictionaryListEventListener(
220 static_cast<linguistic2::XDictionaryListEventListener *>(this), false );
223 nCombinedLngSvcEvt = 0;
227 void SAL_CALL LngSvcMgrListenerHelper::disposing( const lang::EventObject& rSource )
229 osl::MutexGuard aGuard( GetLinguMutex() );
231 uno::Reference< uno::XInterface > xRef( rSource.Source );
232 if ( xRef.is() )
234 aLngSvcMgrListeners .removeInterface( xRef );
235 aLngSvcEvtBroadcasters.removeInterface( xRef );
236 if (xDicList == xRef)
237 xDicList = nullptr;
241 void LngSvcMgrListenerHelper::Timeout()
243 osl::MutexGuard aGuard( GetLinguMutex() );
246 // change event source to LinguServiceManager since the listeners
247 // probably do not know (and need not to know) about the specific
248 // SpellChecker's or Hyphenator's.
249 linguistic2::LinguServiceEvent aEvtObj(
250 static_cast<css::linguistic2::XLinguServiceManager*>(&rMyManager), nCombinedLngSvcEvt );
251 nCombinedLngSvcEvt = 0;
253 if (rMyManager.mxSpellDsp.is())
254 rMyManager.mxSpellDsp->FlushSpellCache();
256 // pass event on to linguistic2::XLinguServiceEventListener's
257 aLngSvcMgrListeners.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, aEvtObj );
262 void LngSvcMgrListenerHelper::AddLngSvcEvt( sal_Int16 nLngSvcEvt )
264 nCombinedLngSvcEvt |= nLngSvcEvt;
265 Timeout();
269 void SAL_CALL
270 LngSvcMgrListenerHelper::processLinguServiceEvent(
271 const linguistic2::LinguServiceEvent& rLngSvcEvent )
273 osl::MutexGuard aGuard( GetLinguMutex() );
274 AddLngSvcEvt( rLngSvcEvent.nEvent );
278 void SAL_CALL
279 LngSvcMgrListenerHelper::processDictionaryListEvent(
280 const linguistic2::DictionaryListEvent& rDicListEvent )
282 osl::MutexGuard aGuard( GetLinguMutex() );
284 sal_Int16 nDlEvt = rDicListEvent.nCondensedEvent;
285 if (0 == nDlEvt)
286 return;
288 // we do keep the original event source here though...
290 // pass event on to linguistic2::XDictionaryListEventListener's
291 aLngSvcMgrListeners.notifyEach( &linguistic2::XDictionaryListEventListener::processDictionaryListEvent, rDicListEvent );
293 // "translate" DictionaryList event into linguistic2::LinguServiceEvent
294 sal_Int16 nLngSvcEvt = 0;
295 sal_Int16 const nSpellCorrectFlags =
296 linguistic2::DictionaryListEventFlags::ADD_NEG_ENTRY |
297 linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY |
298 linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC |
299 linguistic2::DictionaryListEventFlags::DEACTIVATE_POS_DIC;
300 if (0 != (nDlEvt & nSpellCorrectFlags))
301 nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN;
303 sal_Int16 const nSpellWrongFlags =
304 linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY |
305 linguistic2::DictionaryListEventFlags::DEL_NEG_ENTRY |
306 linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
307 linguistic2::DictionaryListEventFlags::DEACTIVATE_NEG_DIC;
308 if (0 != (nDlEvt & nSpellWrongFlags))
309 nLngSvcEvt |= linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN;
311 sal_Int16 const nHyphenateFlags =
312 linguistic2::DictionaryListEventFlags::ADD_POS_ENTRY |
313 linguistic2::DictionaryListEventFlags::DEL_POS_ENTRY |
314 linguistic2::DictionaryListEventFlags::ACTIVATE_POS_DIC |
315 linguistic2::DictionaryListEventFlags::ACTIVATE_NEG_DIC;
316 if (0 != (nDlEvt & nHyphenateFlags))
317 nLngSvcEvt |= linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN;
319 if (rMyManager.mxSpellDsp.is())
320 rMyManager.mxSpellDsp->FlushSpellCache();
321 if (nLngSvcEvt)
322 LaunchEvent( nLngSvcEvt );
326 void LngSvcMgrListenerHelper::LaunchEvent( sal_Int16 nLngSvcEvtFlags )
328 linguistic2::LinguServiceEvent aEvt(
329 static_cast<css::linguistic2::XLinguServiceManager*>(&rMyManager), nLngSvcEvtFlags );
331 // pass event on to linguistic2::XLinguServiceEventListener's
332 aLngSvcMgrListeners.notifyEach( &linguistic2::XLinguServiceEventListener::processLinguServiceEvent, aEvt );
336 inline void LngSvcMgrListenerHelper::AddLngSvcMgrListener(
337 const uno::Reference< lang::XEventListener >& rxListener )
339 aLngSvcMgrListeners.addInterface( rxListener );
343 inline void LngSvcMgrListenerHelper::RemoveLngSvcMgrListener(
344 const uno::Reference< lang::XEventListener >& rxListener )
346 aLngSvcMgrListeners.removeInterface( rxListener );
350 void LngSvcMgrListenerHelper::DisposeAndClear( const lang::EventObject &rEvtObj )
352 // call "disposing" for all listeners and clear list
353 aLngSvcMgrListeners .disposeAndClear( rEvtObj );
355 // remove references to this object hold by the broadcasters
356 comphelper::OInterfaceIteratorHelper2 aIt( aLngSvcEvtBroadcasters );
357 while (aIt.hasMoreElements())
359 uno::Reference< linguistic2::XLinguServiceEventBroadcaster > xRef( aIt.next(), uno::UNO_QUERY );
360 if (xRef.is())
361 RemoveLngSvcEvtBroadcaster( xRef );
364 // remove reference to this object hold by the dictionary-list
365 if (xDicList.is())
367 xDicList->removeDictionaryListEventListener(
368 static_cast<linguistic2::XDictionaryListEventListener *>(this) );
369 xDicList = nullptr;
374 void LngSvcMgrListenerHelper::AddLngSvcEvtBroadcaster(
375 const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
377 if (rxBroadcaster.is())
379 aLngSvcEvtBroadcasters.addInterface( rxBroadcaster );
380 rxBroadcaster->addLinguServiceEventListener(
381 static_cast<linguistic2::XLinguServiceEventListener *>(this) );
386 void LngSvcMgrListenerHelper::RemoveLngSvcEvtBroadcaster(
387 const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
389 if (rxBroadcaster.is())
391 aLngSvcEvtBroadcasters.removeInterface( rxBroadcaster );
392 rxBroadcaster->removeLinguServiceEventListener(
393 static_cast<linguistic2::XLinguServiceEventListener *>(this) );
398 LngSvcMgr::LngSvcMgr()
399 : utl::ConfigItem("Office.Linguistic")
400 , aEvtListeners(GetLinguMutex())
402 bDisposing = false;
404 // request notify events when properties (i.e. something in the subtree) changes
405 uno::Sequence< OUString > aNames{
406 "ServiceManager/SpellCheckerList",
407 "ServiceManager/GrammarCheckerList",
408 "ServiceManager/HyphenatorList",
409 "ServiceManager/ThesaurusList"
411 EnableNotification( aNames );
413 UpdateAll();
415 aUpdateIdle.SetPriority(TaskPriority::LOWEST);
416 aUpdateIdle.SetInvokeHandler(LINK(this, LngSvcMgr, updateAndBroadcast));
418 // request to be notified if an extension has been added/removed
419 uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
421 uno::Reference<deployment::XExtensionManager> xExtensionManager;
422 try {
423 xExtensionManager = deployment::ExtensionManager::get(xContext);
424 } catch ( const uno::DeploymentException & ) {
425 SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
426 } catch ( const deployment::DeploymentException & ) {
427 SAL_WARN( "linguistic", "no extension manager - should fire on mobile only" );
429 if (xExtensionManager.is())
431 xMB.set(xExtensionManager, uno::UNO_QUERY_THROW);
433 uno::Reference<util::XModifyListener> xListener(this);
434 xMB->addModifyListener( xListener );
438 // css::util::XModifyListener
439 void LngSvcMgr::modified(const lang::EventObject&)
442 osl::MutexGuard aGuard(GetLinguMutex());
443 //assume that if an extension has been added/removed that
444 //it might be a dictionary extension, so drop our cache
446 pAvailSpellSvcs.reset();
447 pAvailGrammarSvcs.reset();
448 pAvailHyphSvcs.reset();
449 pAvailThesSvcs.reset();
453 SolarMutexGuard aGuard;
454 //schedule in an update to execute in the main thread
455 aUpdateIdle.Start();
459 //run update, and inform everyone that dictionaries (may) have changed, this
460 //needs to be run in the main thread because
461 //utl::ConfigChangeListener_Impl::changesOccurred grabs the SolarMutex and we
462 //get notified that an extension was added from an extension manager thread
463 IMPL_LINK_NOARG(LngSvcMgr, updateAndBroadcast, Timer *, void)
465 osl::MutexGuard aGuard( GetLinguMutex() );
467 UpdateAll();
469 if (mxListenerHelper.is())
471 mxListenerHelper->AddLngSvcEvt(
472 linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
473 linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN |
474 linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN |
475 linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
479 void LngSvcMgr::stopListening()
481 osl::MutexGuard aGuard(GetLinguMutex());
483 if (xMB.is())
487 uno::Reference<util::XModifyListener> xListener(this);
488 xMB->removeModifyListener(xListener);
490 catch (const uno::Exception&)
494 xMB.clear();
498 void LngSvcMgr::disposing(const lang::EventObject&)
500 stopListening();
503 LngSvcMgr::~LngSvcMgr()
505 stopListening();
507 // memory for pSpellDsp, pHyphDsp, pThesDsp, pListenerHelper
508 // will be freed in the destructor of the respective Reference's
509 // xSpellDsp, xGrammarDsp, xHyphDsp, xThesDsp
511 pAvailSpellSvcs.reset();
512 pAvailGrammarSvcs.reset();
513 pAvailHyphSvcs.reset();
514 pAvailThesSvcs.reset();
517 namespace
519 using lang::Locale;
520 using uno::Any;
521 using uno::Sequence;
523 bool lcl_FindEntry( const OUString &rEntry, const Sequence< OUString > &rCfgSvcs )
525 return comphelper::findValue(rCfgSvcs, rEntry) != -1;
528 bool lcl_FindEntry( const OUString &rEntry, const std::vector< OUString > &rCfgSvcs )
530 return std::find(rCfgSvcs.begin(), rCfgSvcs.end(), rEntry) != rCfgSvcs.end();
533 Sequence< OUString > lcl_GetLastFoundSvcs(
534 SvtLinguConfig const &rCfg,
535 const OUString &rLastFoundList ,
536 const OUString& rCfgLocaleStr )
538 Sequence< OUString > aRes;
540 Sequence< OUString > aNodeNames( rCfg.GetNodeNames(rLastFoundList) );
541 bool bFound = lcl_FindEntry( rCfgLocaleStr, aNodeNames);
543 if (bFound)
545 Sequence< OUString > aNames { rLastFoundList + "/" + rCfgLocaleStr };
546 Sequence< Any > aValues( rCfg.GetProperties( aNames ) );
547 if (aValues.hasElements())
549 SAL_WARN_IF( aValues.getLength() != 1, "linguistic", "unexpected length of sequence" );
550 Sequence< OUString > aSvcImplNames;
551 if (aValues.getConstArray()[0] >>= aSvcImplNames)
552 aRes = aSvcImplNames;
553 else
555 SAL_WARN( "linguistic", "type mismatch" );
560 return aRes;
563 Sequence< OUString > lcl_RemoveMissingEntries(
564 const Sequence< OUString > &rCfgSvcs,
565 const Sequence< OUString > &rAvailSvcs )
567 std::vector<OUString> aRes;
568 aRes.reserve(rCfgSvcs.getLength());
570 std::copy_if(rCfgSvcs.begin(), rCfgSvcs.end(), std::back_inserter(aRes),
571 [&rAvailSvcs](const OUString& entry) { return lcl_SeqHasString(rAvailSvcs, entry); });
573 return comphelper::containerToSequence(aRes);
576 Sequence< OUString > lcl_GetNewEntries(
577 const Sequence< OUString > &rLastFoundSvcs,
578 const Sequence< OUString > &rAvailSvcs )
580 std::vector<OUString> aRes;
581 aRes.reserve(rAvailSvcs.getLength());
583 std::copy_if(rAvailSvcs.begin(), rAvailSvcs.end(), std::back_inserter(aRes),
584 [&rLastFoundSvcs](const OUString& rEntry) {
585 return !rEntry.isEmpty() && !lcl_FindEntry( rEntry, rLastFoundSvcs ); });
587 return comphelper::containerToSequence(aRes);
590 Sequence< OUString > lcl_MergeSeq(
591 const Sequence< OUString > &rCfgSvcs,
592 const Sequence< OUString > &rNewSvcs )
594 std::vector<OUString> aRes;
595 aRes.reserve(rCfgSvcs.getLength() + rNewSvcs.getLength());
597 auto lVecNotHasString = [&aRes](const OUString& rEntry)
598 { return !rEntry.isEmpty() && !lcl_FindEntry(rEntry, aRes); };
600 // add previously configured service first and append
601 // new found services at the end
602 for (const Sequence< OUString > &rSeq : { rCfgSvcs, rNewSvcs })
604 std::copy_if(rSeq.begin(), rSeq.end(), std::back_inserter(aRes), lVecNotHasString);
607 return comphelper::containerToSequence(aRes);
611 void LngSvcMgr::UpdateAll()
613 using beans::PropertyValue;
614 using lang::Locale;
615 using uno::Sequence;
617 typedef Sequence< OUString > Sequence_OUString_t;
618 typedef std::map< OUString, Sequence_OUString_t > list_entry_map_t;
620 SvtLinguConfig aCfg;
622 const int nNumServices = 4;
623 const sal_Char * const apServices[nNumServices] = { SN_SPELLCHECKER, SN_GRAMMARCHECKER, SN_HYPHENATOR, SN_THESAURUS };
624 const sal_Char * const apCurLists[nNumServices] = { "ServiceManager/SpellCheckerList", "ServiceManager/GrammarCheckerList", "ServiceManager/HyphenatorList", "ServiceManager/ThesaurusList" };
625 const sal_Char * const apLastFoundLists[nNumServices] = { "ServiceManager/LastFoundSpellCheckers", "ServiceManager/LastFoundGrammarCheckers", "ServiceManager/LastFoundHyphenators", "ServiceManager/LastFoundThesauri" };
627 // usage of indices as above: 0 = spell checker, 1 = grammar checker, 2 = hyphenator, 3 = thesaurus
628 std::vector< list_entry_map_t > aLastFoundSvcs(nNumServices);
629 std::vector< list_entry_map_t > aCurSvcs(nNumServices);
631 for (int k = 0; k < nNumServices; ++k)
633 OUString aService( OUString::createFromAscii( apServices[k] ) );
634 OUString aActiveList( OUString::createFromAscii( apCurLists[k] ) );
635 OUString aLastFoundList( OUString::createFromAscii( apLastFoundLists[k] ) );
638 // remove configured but not available language/services entries
640 const Sequence< OUString > aNodeNames( aCfg.GetNodeNames( aActiveList ) ); // list of configured locales
641 for (const OUString& rNodeName : aNodeNames)
643 Locale aLocale( LanguageTag::convertToLocale( rNodeName));
644 Sequence< OUString > aCfgSvcs( getConfiguredServices( aService, aLocale ));
645 Sequence< OUString > aAvailSvcs( getAvailableServices( aService, aLocale ));
647 aCfgSvcs = lcl_RemoveMissingEntries( aCfgSvcs, aAvailSvcs );
649 aCurSvcs[k][ rNodeName ] = aCfgSvcs;
653 // add new available language/service entries
654 // and
655 // set last found services to currently available ones
657 const Sequence< Locale > aAvailLocales( getAvailableLocales(aService) );
658 for (const Locale& rAvailLocale : aAvailLocales)
660 OUString aCfgLocaleStr( LanguageTag::convertToBcp47( rAvailLocale));
662 Sequence< OUString > aAvailSvcs( getAvailableServices( aService, rAvailLocale ));
664 aLastFoundSvcs[k][ aCfgLocaleStr ] = aAvailSvcs;
666 Sequence< OUString > aLastSvcs(
667 lcl_GetLastFoundSvcs( aCfg, aLastFoundList , aCfgLocaleStr ));
668 Sequence< OUString > aNewSvcs =
669 lcl_GetNewEntries( aLastSvcs, aAvailSvcs );
671 Sequence< OUString > aCfgSvcs( aCurSvcs[k][ aCfgLocaleStr ] );
673 // merge services list (previously configured to be listed first).
674 aCfgSvcs = lcl_MergeSeq( aCfgSvcs, aNewSvcs );
676 aCurSvcs[k][ aCfgLocaleStr ] = aCfgSvcs;
681 // write new data back to configuration
683 for (int k = 0; k < nNumServices; ++k)
685 for (int i = 0; i < 2; ++i)
687 const sal_Char *pSubNodeName = (i == 0) ? apCurLists[k] : apLastFoundLists[k];
688 OUString aSubNodeName( OUString::createFromAscii(pSubNodeName) );
690 list_entry_map_t &rCurMap = (i == 0) ? aCurSvcs[k] : aLastFoundSvcs[k];
691 sal_Int32 nVals = static_cast< sal_Int32 >( rCurMap.size() );
692 Sequence< PropertyValue > aNewValues( nVals );
693 PropertyValue *pNewValue = aNewValues.getArray();
694 for (auto const& elem : rCurMap)
696 pNewValue->Name = aSubNodeName + "/" + elem.first;
697 pNewValue->Value <<= elem.second;
698 ++pNewValue;
700 OSL_ENSURE( pNewValue - aNewValues.getConstArray() == nVals,
701 "possible mismatch of sequence size and property number" );
704 // add new or replace existing entries.
705 bool bRes = aCfg.ReplaceSetProperties( aSubNodeName, aNewValues );
706 SAL_WARN_IF(!bRes, "linguistic", "failed to set new configuration values");
711 //The new settings in the configuration get applied ! because we are
712 //listening to the configuration for changes of the relevant ! properties
713 //and Notify applies the new settings.
716 void LngSvcMgr::Notify( const uno::Sequence< OUString > &rPropertyNames )
718 const OUString aSpellCheckerList( "ServiceManager/SpellCheckerList" );
719 const OUString aGrammarCheckerList( "ServiceManager/GrammarCheckerList" );
720 const OUString aHyphenatorList( "ServiceManager/HyphenatorList" );
721 const OUString aThesaurusList( "ServiceManager/ThesaurusList" );
723 const uno::Sequence< OUString > aSpellCheckerListEntries( GetNodeNames( aSpellCheckerList ) );
724 const uno::Sequence< OUString > aGrammarCheckerListEntries( GetNodeNames( aGrammarCheckerList ) );
725 const uno::Sequence< OUString > aHyphenatorListEntries( GetNodeNames( aHyphenatorList ) );
726 const uno::Sequence< OUString > aThesaurusListEntries( GetNodeNames( aThesaurusList ) );
728 uno::Sequence< uno::Any > aValues;
729 uno::Sequence< OUString > aNames( 1 );
730 OUString *pNames = aNames.getArray();
732 for (const OUString& rName : rPropertyNames)
734 // property names look like
735 // "ServiceManager/ThesaurusList/de-CH"
737 sal_Int32 nKeyStart;
738 nKeyStart = rName.lastIndexOf( '/' );
739 OUString aKeyText;
740 if (nKeyStart != -1)
741 aKeyText = rName.copy( nKeyStart + 1 );
742 SAL_WARN_IF( aKeyText.isEmpty(), "linguistic", "unexpected key (lang::Locale) string" );
743 if (rName.startsWith( aSpellCheckerList ))
745 osl::MutexGuard aGuard(GetLinguMutex());
747 // delete old cached data, needs to be acquired new on demand
748 pAvailSpellSvcs.reset();
750 if (lcl_SeqHasString( aSpellCheckerListEntries, aKeyText ))
752 pNames[0] = aSpellCheckerList + "/" + aKeyText;
753 aValues = /*aCfg.*/GetProperties( aNames );
754 uno::Sequence< OUString > aSvcImplNames;
755 if (aValues.hasElements())
756 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
758 LanguageType nLang = LANGUAGE_NONE;
759 if (!aKeyText.isEmpty())
760 nLang = LanguageTag::convertToLanguageType( aKeyText );
762 GetSpellCheckerDsp_Impl( false ); // don't set service list, it will be done below
763 mxSpellDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
766 else if (rName.startsWith( aGrammarCheckerList ))
768 osl::MutexGuard aGuard(GetLinguMutex());
770 // delete old cached data, needs to be acquired new on demand
771 pAvailGrammarSvcs.reset();
773 if (lcl_SeqHasString( aGrammarCheckerListEntries, aKeyText ))
775 pNames[0] = aGrammarCheckerList + "/" + aKeyText;
776 aValues = /*aCfg.*/GetProperties( aNames );
777 uno::Sequence< OUString > aSvcImplNames;
778 if (aValues.hasElements())
779 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
781 LanguageType nLang = LANGUAGE_NONE;
782 if (!aKeyText.isEmpty())
783 nLang = LanguageTag::convertToLanguageType( aKeyText );
785 if (SvtLinguConfig().HasGrammarChecker())
787 GetGrammarCheckerDsp_Impl( false ); // don't set service list, it will be done below
788 mxGrammarDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
792 else if (rName.startsWith( aHyphenatorList ))
794 osl::MutexGuard aGuard(GetLinguMutex());
796 // delete old cached data, needs to be acquired new on demand
797 pAvailHyphSvcs.reset();
799 if (lcl_SeqHasString( aHyphenatorListEntries, aKeyText ))
801 pNames[0] = aHyphenatorList + "/" + aKeyText;
802 aValues = /*aCfg.*/GetProperties( aNames );
803 uno::Sequence< OUString > aSvcImplNames;
804 if (aValues.hasElements())
805 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
807 LanguageType nLang = LANGUAGE_NONE;
808 if (!aKeyText.isEmpty())
809 nLang = LanguageTag::convertToLanguageType( aKeyText );
811 GetHyphenatorDsp_Impl( false ); // don't set service list, it will be done below
812 mxHyphDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
815 else if (rName.startsWith( aThesaurusList ))
817 osl::MutexGuard aGuard(GetLinguMutex());
819 // delete old cached data, needs to be acquired new on demand
820 pAvailThesSvcs.reset();
822 if (lcl_SeqHasString( aThesaurusListEntries, aKeyText ))
824 pNames[0] = aThesaurusList + "/" + aKeyText;
825 aValues = /*aCfg.*/GetProperties( aNames );
826 uno::Sequence< OUString > aSvcImplNames;
827 if (aValues.hasElements())
828 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
830 LanguageType nLang = LANGUAGE_NONE;
831 if (!aKeyText.isEmpty())
832 nLang = LanguageTag::convertToLanguageType( aKeyText );
834 GetThesaurusDsp_Impl( false ); // don't set service list, it will be done below
835 mxThesDsp->SetServiceList( LanguageTag::convertToLocale(nLang), aSvcImplNames );
838 else
840 SAL_WARN( "linguistic", "notified for unexpected property" );
846 void LngSvcMgr::ImplCommit()
848 // everything necessary should have already been done by 'SaveCfgSvcs'
849 // called from within 'setConfiguredServices'.
850 // Also this class usually exits only when the Office is being shutdown.
854 void LngSvcMgr::GetListenerHelper_Impl()
856 if (!mxListenerHelper.is())
858 mxListenerHelper = new LngSvcMgrListenerHelper( *this, linguistic::GetDictionaryList() );
863 void LngSvcMgr::GetSpellCheckerDsp_Impl( bool bSetSvcList )
865 if (!mxSpellDsp.is())
867 mxSpellDsp = new SpellCheckerDispatcher( *this );
868 if (bSetSvcList)
869 SetCfgServiceLists( *mxSpellDsp );
874 void LngSvcMgr::GetGrammarCheckerDsp_Impl( bool bSetSvcList )
876 if (!mxGrammarDsp.is() && SvtLinguConfig().HasGrammarChecker())
878 //! since the grammar checking iterator needs to be a one instance service
879 //! we need to create it the correct way!
880 uno::Reference< linguistic2::XProofreadingIterator > xGCI;
883 xGCI = linguistic2::ProofreadingIterator::create( comphelper::getProcessComponentContext() );
885 catch (const uno::Exception &)
888 SAL_WARN_IF( !xGCI.is(), "linguistic", "instantiating grammar checking iterator failed" );
890 if (xGCI.is())
892 mxGrammarDsp = dynamic_cast< GrammarCheckingIterator * >(xGCI.get());
893 SAL_WARN_IF( mxGrammarDsp == nullptr, "linguistic", "failed to get implementation" );
894 if (bSetSvcList && mxGrammarDsp.is())
895 SetCfgServiceLists( *mxGrammarDsp );
901 void LngSvcMgr::GetHyphenatorDsp_Impl( bool bSetSvcList )
903 if (!mxHyphDsp.is())
905 mxHyphDsp = new HyphenatorDispatcher( *this );
906 if (bSetSvcList)
907 SetCfgServiceLists( *mxHyphDsp );
912 void LngSvcMgr::GetThesaurusDsp_Impl( bool bSetSvcList )
914 if (!mxThesDsp.is())
916 mxThesDsp = new ThesaurusDispatcher;
917 if (bSetSvcList)
918 SetCfgServiceLists( *mxThesDsp );
923 void LngSvcMgr::GetAvailableSpellSvcs_Impl()
925 if (!pAvailSpellSvcs)
927 pAvailSpellSvcs.reset(new SvcInfoArray);
929 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
931 uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
932 uno::Reference< container::XEnumeration > xEnum;
933 if (xEnumAccess.is())
934 xEnum = xEnumAccess->createContentEnumeration( SN_SPELLCHECKER );
936 if (xEnum.is())
938 while (xEnum->hasMoreElements())
940 uno::Any aCurrent = xEnum->nextElement();
941 uno::Reference< lang::XSingleComponentFactory > xCompFactory;
942 uno::Reference< lang::XSingleServiceFactory > xFactory;
944 uno::Reference< linguistic2::XSpellChecker > xSvc;
945 xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
946 if (!xCompFactory.is())
948 xFactory.set(aCurrent, css::uno::UNO_QUERY);
950 if ( xCompFactory.is() || xFactory.is() )
954 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
956 catch (const uno::Exception &)
958 SAL_WARN( "linguistic", "createInstance failed" );
962 if (xSvc.is())
964 OUString aImplName;
965 std::vector< LanguageType > aLanguages;
966 uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
967 if (xInfo.is())
968 aImplName = xInfo->getImplementationName();
969 SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
970 uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
971 aLanguages = LocaleSeqToLangVec( aLocaleSequence );
973 pAvailSpellSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
981 void LngSvcMgr::GetAvailableGrammarSvcs_Impl()
983 if (!pAvailGrammarSvcs)
985 pAvailGrammarSvcs.reset(new SvcInfoArray);
987 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
989 uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
990 uno::Reference< container::XEnumeration > xEnum;
991 if (xEnumAccess.is())
992 xEnum = xEnumAccess->createContentEnumeration( SN_GRAMMARCHECKER );
994 if (xEnum.is())
996 while (xEnum->hasMoreElements())
998 uno::Any aCurrent = xEnum->nextElement();
999 uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1000 uno::Reference< lang::XSingleServiceFactory > xFactory;
1002 uno::Reference< linguistic2::XProofreader > xSvc;
1003 xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1004 if (!xCompFactory.is())
1006 xFactory.set(aCurrent, css::uno::UNO_QUERY);
1008 if ( xCompFactory.is() || xFactory.is() )
1012 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1014 catch (const uno::Exception &)
1016 SAL_WARN( "linguistic", "createInstance failed" );
1020 if (xSvc.is() && pAvailGrammarSvcs)
1022 OUString aImplName;
1023 std::vector< LanguageType > aLanguages;
1024 uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1025 if (xInfo.is())
1026 aImplName = xInfo->getImplementationName();
1027 SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1028 uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1029 aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1031 pAvailGrammarSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1039 void LngSvcMgr::GetAvailableHyphSvcs_Impl()
1041 if (!pAvailHyphSvcs)
1043 pAvailHyphSvcs.reset(new SvcInfoArray);
1044 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
1046 uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
1047 uno::Reference< container::XEnumeration > xEnum;
1048 if (xEnumAccess.is())
1049 xEnum = xEnumAccess->createContentEnumeration( SN_HYPHENATOR );
1051 if (xEnum.is())
1053 while (xEnum->hasMoreElements())
1055 uno::Any aCurrent = xEnum->nextElement();
1056 uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1057 uno::Reference< lang::XSingleServiceFactory > xFactory;
1059 uno::Reference< linguistic2::XHyphenator > xSvc;
1060 xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1061 if (!xCompFactory.is())
1063 xFactory.set(aCurrent, css::uno::UNO_QUERY);
1065 if ( xCompFactory.is() || xFactory.is() )
1069 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1071 catch (const uno::Exception &)
1073 SAL_WARN( "linguistic", "createInstance failed" );
1076 if (xSvc.is())
1078 OUString aImplName;
1079 std::vector< LanguageType > aLanguages;
1080 uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1081 if (xInfo.is())
1082 aImplName = xInfo->getImplementationName();
1083 SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1084 uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1085 aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1086 pAvailHyphSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1094 void LngSvcMgr::GetAvailableThesSvcs_Impl()
1096 if (!pAvailThesSvcs)
1098 pAvailThesSvcs.reset(new SvcInfoArray);
1100 uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
1102 uno::Reference< container::XContentEnumerationAccess > xEnumAccess( xContext->getServiceManager(), uno::UNO_QUERY );
1103 uno::Reference< container::XEnumeration > xEnum;
1104 if (xEnumAccess.is())
1105 xEnum = xEnumAccess->createContentEnumeration( SN_THESAURUS );
1107 if (xEnum.is())
1109 while (xEnum->hasMoreElements())
1111 uno::Any aCurrent = xEnum->nextElement();
1112 uno::Reference< lang::XSingleComponentFactory > xCompFactory;
1113 uno::Reference< lang::XSingleServiceFactory > xFactory;
1115 uno::Reference< linguistic2::XThesaurus > xSvc;
1116 xCompFactory.set(aCurrent, css::uno::UNO_QUERY);
1117 if (!xCompFactory.is())
1119 xFactory.set(aCurrent, css::uno::UNO_QUERY);
1121 if ( xCompFactory.is() || xFactory.is() )
1125 xSvc.set( ( xCompFactory.is() ? xCompFactory->createInstanceWithContext( xContext ) : xFactory->createInstance() ), uno::UNO_QUERY );
1127 catch (const uno::Exception &)
1129 SAL_WARN( "linguistic", "createInstance failed" );
1132 if (xSvc.is())
1134 OUString aImplName;
1135 std::vector< LanguageType > aLanguages;
1136 uno::Reference< XServiceInfo > xInfo( xSvc, uno::UNO_QUERY );
1137 if (xInfo.is())
1138 aImplName = xInfo->getImplementationName();
1139 SAL_WARN_IF( aImplName.isEmpty(), "linguistic", "empty implementation name" );
1140 uno::Sequence<lang::Locale> aLocaleSequence(xSvc->getLocales());
1141 aLanguages = LocaleSeqToLangVec( aLocaleSequence );
1143 pAvailThesSvcs->push_back( std::make_unique<SvcInfo>( aImplName, aLanguages ) );
1151 void LngSvcMgr::SetCfgServiceLists( SpellCheckerDispatcher &rSpellDsp )
1153 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Spell" );
1155 OUString aNode("ServiceManager/SpellCheckerList");
1156 uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1158 // append path prefix need for 'GetProperties' call below
1159 OUString aPrefix = aNode + "/";
1160 for (OUString & name : aNames)
1162 name = aPrefix + name;
1165 const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1166 if (aNames.hasElements() && aNames.getLength() == aValues.getLength())
1168 const OUString *pNames = aNames.getConstArray();
1169 for (const uno::Any& rValue : aValues)
1171 uno::Sequence< OUString > aSvcImplNames;
1172 if (rValue >>= aSvcImplNames)
1174 OUString aLocaleStr( *pNames++ );
1175 sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1176 aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1177 rSpellDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1184 void LngSvcMgr::SetCfgServiceLists( GrammarCheckingIterator &rGrammarDsp )
1186 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Grammar" );
1188 OUString aNode("ServiceManager/GrammarCheckerList");
1189 uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1191 // append path prefix need for 'GetProperties' call below
1192 OUString aPrefix = aNode + "/";
1193 for (OUString & name : aNames)
1195 name = aPrefix + name;
1198 const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1199 if (aNames.hasElements() && aNames.getLength() == aValues.getLength())
1201 const OUString *pNames = aNames.getConstArray();
1202 for (const uno::Any& rValue : aValues)
1204 uno::Sequence< OUString > aSvcImplNames;
1205 if (rValue >>= aSvcImplNames)
1207 // there should only be one grammar checker in use per language...
1208 if (aSvcImplNames.getLength() > 1)
1209 aSvcImplNames.realloc(1);
1211 OUString aLocaleStr( *pNames++ );
1212 sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1213 aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1214 rGrammarDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1221 void LngSvcMgr::SetCfgServiceLists( HyphenatorDispatcher &rHyphDsp )
1223 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Hyph" );
1225 OUString aNode("ServiceManager/HyphenatorList");
1226 uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1228 // append path prefix need for 'GetProperties' call below
1229 OUString aPrefix = aNode + "/";
1230 for (OUString & name : aNames)
1232 name = aPrefix + name;
1235 const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1236 if (aNames.hasElements() && aNames.getLength() == aValues.getLength())
1238 const OUString *pNames = aNames.getConstArray();
1239 for (const uno::Any& rValue : aValues)
1241 uno::Sequence< OUString > aSvcImplNames;
1242 if (rValue >>= aSvcImplNames)
1244 // there should only be one hyphenator in use per language...
1245 if (aSvcImplNames.getLength() > 1)
1246 aSvcImplNames.realloc(1);
1248 OUString aLocaleStr( *pNames++ );
1249 sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1250 aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1251 rHyphDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1258 void LngSvcMgr::SetCfgServiceLists( ThesaurusDispatcher &rThesDsp )
1260 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SetCfgServiceLists - Thes" );
1262 OUString aNode("ServiceManager/ThesaurusList");
1263 uno::Sequence< OUString > aNames( /*aCfg.*/GetNodeNames( aNode ) );
1265 // append path prefix need for 'GetProperties' call below
1266 OUString aPrefix = aNode + "/";
1267 for (OUString & name : aNames)
1269 name = aPrefix + name;
1272 const uno::Sequence< uno::Any > aValues( /*aCfg.*/GetProperties( aNames ) );
1273 if (aNames.hasElements() && aNames.getLength() == aValues.getLength())
1275 const OUString *pNames = aNames.getConstArray();
1276 for (const uno::Any& rValue : aValues)
1278 uno::Sequence< OUString > aSvcImplNames;
1279 if (rValue >>= aSvcImplNames)
1281 OUString aLocaleStr( *pNames++ );
1282 sal_Int32 nSeparatorPos = aLocaleStr.lastIndexOf( '/' );
1283 aLocaleStr = aLocaleStr.copy( nSeparatorPos + 1 );
1284 rThesDsp.SetServiceList( LanguageTag::convertToLocale(aLocaleStr), aSvcImplNames );
1291 uno::Reference< linguistic2::XSpellChecker > SAL_CALL
1292 LngSvcMgr::getSpellChecker()
1294 osl::MutexGuard aGuard( GetLinguMutex() );
1295 #if OSL_DEBUG_LEVEL > 0
1296 getAvailableLocales(SN_SPELLCHECKER);
1297 #endif
1299 uno::Reference< linguistic2::XSpellChecker > xRes;
1300 if (!bDisposing)
1302 if (!mxSpellDsp.is())
1303 GetSpellCheckerDsp_Impl();
1304 xRes = mxSpellDsp.get();
1306 return xRes;
1310 uno::Reference< linguistic2::XHyphenator > SAL_CALL
1311 LngSvcMgr::getHyphenator()
1313 osl::MutexGuard aGuard( GetLinguMutex() );
1314 #if OSL_DEBUG_LEVEL > 0
1315 getAvailableLocales(SN_HYPHENATOR);
1316 #endif
1317 uno::Reference< linguistic2::XHyphenator > xRes;
1318 if (!bDisposing)
1320 if (!mxHyphDsp.is())
1321 GetHyphenatorDsp_Impl();
1322 xRes = mxHyphDsp.get();
1324 return xRes;
1328 uno::Reference< linguistic2::XThesaurus > SAL_CALL
1329 LngSvcMgr::getThesaurus()
1331 osl::MutexGuard aGuard( GetLinguMutex() );
1332 #if OSL_DEBUG_LEVEL > 0
1333 getAvailableLocales(SN_THESAURUS);
1334 #endif
1335 uno::Reference< linguistic2::XThesaurus > xRes;
1336 if (!bDisposing)
1338 if (!mxThesDsp.is())
1339 GetThesaurusDsp_Impl();
1340 xRes = mxThesDsp.get();
1342 return xRes;
1346 sal_Bool SAL_CALL
1347 LngSvcMgr::addLinguServiceManagerListener(
1348 const uno::Reference< lang::XEventListener >& xListener )
1350 osl::MutexGuard aGuard( GetLinguMutex() );
1352 if (bDisposing || !xListener.is())
1353 return false;
1355 if (!mxListenerHelper.is())
1356 GetListenerHelper_Impl();
1357 mxListenerHelper->AddLngSvcMgrListener( xListener );
1358 return true;
1362 sal_Bool SAL_CALL
1363 LngSvcMgr::removeLinguServiceManagerListener(
1364 const uno::Reference< lang::XEventListener >& xListener )
1366 osl::MutexGuard aGuard( GetLinguMutex() );
1368 if (bDisposing || !xListener.is())
1369 return false;
1371 DBG_ASSERT( mxListenerHelper.is(), "listener removed without being added" );
1372 if (!mxListenerHelper.is())
1373 GetListenerHelper_Impl();
1374 mxListenerHelper->RemoveLngSvcMgrListener( xListener );
1375 return true;
1379 uno::Sequence< OUString > SAL_CALL
1380 LngSvcMgr::getAvailableServices(
1381 const OUString& rServiceName,
1382 const lang::Locale& rLocale )
1384 osl::MutexGuard aGuard( GetLinguMutex() );
1386 uno::Sequence< OUString > aRes;
1387 const SvcInfoArray *pInfoArray = nullptr;
1389 if (rServiceName == SN_SPELLCHECKER)
1391 GetAvailableSpellSvcs_Impl();
1392 pInfoArray = pAvailSpellSvcs.get();
1394 else if (rServiceName == SN_GRAMMARCHECKER)
1396 GetAvailableGrammarSvcs_Impl();
1397 pInfoArray = pAvailGrammarSvcs.get();
1399 else if (rServiceName == SN_HYPHENATOR)
1401 GetAvailableHyphSvcs_Impl();
1402 pInfoArray = pAvailHyphSvcs.get();
1404 else if (rServiceName == SN_THESAURUS)
1406 GetAvailableThesSvcs_Impl();
1407 pInfoArray = pAvailThesSvcs.get();
1410 if (pInfoArray)
1412 std::vector<OUString> aVec;
1413 aVec.reserve(pInfoArray->size());
1415 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
1416 for (const auto& pInfo : *pInfoArray)
1418 if (LinguIsUnspecified( nLanguage )
1419 || pInfo->HasLanguage( nLanguage ))
1421 aVec.push_back(pInfo->aSvcImplName);
1425 aRes = comphelper::containerToSequence(aVec);
1428 return aRes;
1432 uno::Sequence< lang::Locale > SAL_CALL
1433 LngSvcMgr::getAvailableLocales(
1434 const OUString& rServiceName )
1436 osl::MutexGuard aGuard( GetLinguMutex() );
1438 uno::Sequence< lang::Locale > aRes;
1440 uno::Sequence< lang::Locale > *pAvailLocales = nullptr;
1441 if (rServiceName == SN_SPELLCHECKER)
1442 pAvailLocales = &aAvailSpellLocales;
1443 else if (rServiceName == SN_GRAMMARCHECKER)
1444 pAvailLocales = &aAvailGrammarLocales;
1445 else if (rServiceName == SN_HYPHENATOR)
1446 pAvailLocales = &aAvailHyphLocales;
1447 else if (rServiceName == SN_THESAURUS)
1448 pAvailLocales = &aAvailThesLocales;
1450 // Nowadays (with OOo lingu in SO) we want to know immediately about
1451 // new downloaded dictionaries and have them ready right away if the Tools/Options...
1452 // is used to activate them. Thus we can not rely anymore on buffered data.
1453 if (pAvailLocales)
1455 *pAvailLocales = GetAvailLocales(getAvailableServices(rServiceName, lang::Locale()));
1456 aRes = *pAvailLocales;
1459 return aRes;
1462 static bool IsEqSvcList( const uno::Sequence< OUString > &rList1,
1463 const uno::Sequence< OUString > &rList2 )
1465 // returns true if both sequences are equal
1466 return rList1.getLength() == rList2.getLength()
1467 && std::equal(rList1.begin(), rList1.end(), rList2.begin(), rList2.end());
1471 void SAL_CALL
1472 LngSvcMgr::setConfiguredServices(
1473 const OUString& rServiceName,
1474 const lang::Locale& rLocale,
1475 const uno::Sequence< OUString >& rServiceImplNames )
1477 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::setConfiguredServices" );
1479 osl::MutexGuard aGuard( GetLinguMutex() );
1481 LanguageType nLanguage = LinguLocaleToLanguage( rLocale );
1482 if (!LinguIsUnspecified( nLanguage))
1484 if (rServiceName == SN_SPELLCHECKER)
1486 if (!mxSpellDsp.is())
1487 GetSpellCheckerDsp_Impl();
1488 bool bChanged = !IsEqSvcList( rServiceImplNames,
1489 mxSpellDsp->GetServiceList( rLocale ) );
1490 if (bChanged)
1492 mxSpellDsp->SetServiceList( rLocale, rServiceImplNames );
1493 SaveCfgSvcs( SN_SPELLCHECKER );
1495 if (mxListenerHelper)
1496 mxListenerHelper->AddLngSvcEvt(
1497 linguistic2::LinguServiceEventFlags::SPELL_CORRECT_WORDS_AGAIN |
1498 linguistic2::LinguServiceEventFlags::SPELL_WRONG_WORDS_AGAIN );
1501 else if (rServiceName == SN_GRAMMARCHECKER)
1503 if (!mxGrammarDsp.is())
1504 GetGrammarCheckerDsp_Impl();
1505 bool bChanged = !IsEqSvcList( rServiceImplNames,
1506 mxGrammarDsp->GetServiceList( rLocale ) );
1507 if (bChanged)
1509 mxGrammarDsp->SetServiceList( rLocale, rServiceImplNames );
1510 SaveCfgSvcs( SN_GRAMMARCHECKER );
1512 if (mxListenerHelper)
1513 mxListenerHelper->AddLngSvcEvt(
1514 linguistic2::LinguServiceEventFlags::PROOFREAD_AGAIN );
1517 else if (rServiceName == SN_HYPHENATOR)
1519 if (!mxHyphDsp.is())
1520 GetHyphenatorDsp_Impl();
1521 bool bChanged = !IsEqSvcList( rServiceImplNames,
1522 mxHyphDsp->GetServiceList( rLocale ) );
1523 if (bChanged)
1525 mxHyphDsp->SetServiceList( rLocale, rServiceImplNames );
1526 SaveCfgSvcs( SN_HYPHENATOR );
1528 if (mxListenerHelper)
1529 mxListenerHelper->AddLngSvcEvt(
1530 linguistic2::LinguServiceEventFlags::HYPHENATE_AGAIN );
1533 else if (rServiceName == SN_THESAURUS)
1535 if (!mxThesDsp.is())
1536 GetThesaurusDsp_Impl();
1537 bool bChanged = !IsEqSvcList( rServiceImplNames,
1538 mxThesDsp->GetServiceList( rLocale ) );
1539 if (bChanged)
1541 mxThesDsp->SetServiceList( rLocale, rServiceImplNames );
1542 SaveCfgSvcs( SN_THESAURUS );
1549 bool LngSvcMgr::SaveCfgSvcs( const OUString &rServiceName )
1551 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs" );
1553 bool bRes = false;
1555 LinguDispatcher *pDsp = nullptr;
1556 uno::Sequence< lang::Locale > aLocales;
1558 if (rServiceName == SN_SPELLCHECKER)
1560 if (!mxSpellDsp)
1561 GetSpellCheckerDsp_Impl();
1562 pDsp = mxSpellDsp.get();
1563 aLocales = getAvailableLocales( SN_SPELLCHECKER );
1565 else if (rServiceName == SN_GRAMMARCHECKER)
1567 if (!mxGrammarDsp.is())
1568 GetGrammarCheckerDsp_Impl();
1569 pDsp = mxGrammarDsp.get();
1570 aLocales = getAvailableLocales( SN_GRAMMARCHECKER );
1572 else if (rServiceName == SN_HYPHENATOR)
1574 if (!mxHyphDsp.is())
1575 GetHyphenatorDsp_Impl();
1576 pDsp = mxHyphDsp.get();
1577 aLocales = getAvailableLocales( SN_HYPHENATOR );
1579 else if (rServiceName == SN_THESAURUS)
1581 if (!mxThesDsp.is())
1582 GetThesaurusDsp_Impl();
1583 pDsp = mxThesDsp.get();
1584 aLocales = getAvailableLocales( SN_THESAURUS );
1587 if (pDsp && aLocales.hasElements())
1589 uno::Sequence< beans::PropertyValue > aValues( aLocales.getLength() );
1590 beans::PropertyValue *pValue = aValues.getArray();
1592 // get node name to be used
1593 const char *pNodeName = nullptr;
1594 if (pDsp == mxSpellDsp.get())
1595 pNodeName = "ServiceManager/SpellCheckerList";
1596 else if (pDsp == mxGrammarDsp.get())
1597 pNodeName = "ServiceManager/GrammarCheckerList";
1598 else if (pDsp == mxHyphDsp.get())
1599 pNodeName = "ServiceManager/HyphenatorList";
1600 else if (pDsp == mxThesDsp.get())
1601 pNodeName = "ServiceManager/ThesaurusList";
1602 else
1604 SAL_WARN( "linguistic", "node name missing" );
1606 OUString aNodeName( OUString::createFromAscii(pNodeName) );
1608 for (const lang::Locale& rLocale : std::as_const(aLocales))
1610 uno::Sequence< OUString > aSvcImplNames = pDsp->GetServiceList( rLocale );
1612 // build value to be written back to configuration
1613 uno::Any aCfgAny;
1614 if ((pDsp == mxHyphDsp.get() || pDsp == mxGrammarDsp.get()) && aSvcImplNames.getLength() > 1)
1615 aSvcImplNames.realloc(1); // there should be only one entry for hyphenators or grammar checkers (because they are not chained)
1616 aCfgAny <<= aSvcImplNames;
1617 DBG_ASSERT( aCfgAny.hasValue(), "missing value for 'Any' type" );
1619 OUString aCfgLocaleStr( LanguageTag::convertToBcp47( rLocale));
1620 pValue->Value = aCfgAny;
1621 pValue->Name = aNodeName + "/" + aCfgLocaleStr;
1622 pValue++;
1625 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" );
1626 // change, add new or replace existing entries.
1627 bRes |= /*aCfg.*/ReplaceSetProperties( aNodeName, aValues );
1631 return bRes;
1635 static uno::Sequence< OUString > GetLangSvcList( const uno::Any &rVal )
1637 uno::Sequence< OUString > aRes;
1639 if (rVal.hasValue())
1641 rVal >>= aRes;
1642 #if OSL_DEBUG_LEVEL > 0
1643 for (const OUString& rSvcName : std::as_const(aRes))
1645 SAL_WARN_IF( rSvcName.isEmpty(), "linguistic", "service impl-name missing" );
1647 #endif
1650 return aRes;
1654 static uno::Sequence< OUString > GetLangSvc( const uno::Any &rVal )
1656 uno::Sequence< OUString > aRes;
1657 if (!rVal.hasValue())
1658 return aRes;
1660 // allowing for a sequence here as well (even though it should only
1661 // be a string) makes coding easier in other places since one needs
1662 // not make a special case for writing a string only and not a
1663 // sequence of strings.
1664 if (rVal >>= aRes)
1666 // but only the first string should be used.
1667 if (aRes.getLength() > 1)
1668 aRes.realloc(1);
1670 else
1672 OUString aImplName;
1673 if ((rVal >>= aImplName) && !aImplName.isEmpty())
1675 aRes.realloc(1);
1676 aRes.getArray()[0] = aImplName;
1678 else
1680 SAL_WARN( "linguistic", "GetLangSvc: unexpected type encountered" );
1684 return aRes;
1688 uno::Sequence< OUString > SAL_CALL
1689 LngSvcMgr::getConfiguredServices(
1690 const OUString& rServiceName,
1691 const lang::Locale& rLocale )
1693 osl::MutexGuard aGuard( GetLinguMutex() );
1695 uno::Sequence< OUString > aSvcImplNames;
1697 OUString aCfgLocale( LanguageTag::convertToBcp47( rLocale) );
1699 uno::Sequence< uno::Any > aValues;
1700 uno::Sequence< OUString > aNames( 1 );
1701 OUString *pNames = aNames.getArray();
1702 if ( rServiceName == SN_SPELLCHECKER )
1704 OUString aNode( "ServiceManager/SpellCheckerList");
1705 const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1706 if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1708 pNames[0] = aNode + "/" + aCfgLocale;
1709 aValues = /*aCfg.*/GetProperties( aNames );
1710 if (aValues.hasElements())
1711 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1714 else if ( rServiceName == SN_GRAMMARCHECKER )
1716 OUString aNode( "ServiceManager/GrammarCheckerList");
1717 const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1718 if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1720 pNames[0] = aNode + "/" + aCfgLocale;
1721 aValues = /*aCfg.*/GetProperties( aNames );
1722 if (aValues.hasElements())
1723 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1726 else if ( rServiceName == SN_HYPHENATOR )
1728 OUString aNode( "ServiceManager/HyphenatorList");
1729 const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1730 if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1732 pNames[0] = aNode + "/" + aCfgLocale;
1733 aValues = /*aCfg.*/GetProperties( aNames );
1734 if (aValues.hasElements())
1735 aSvcImplNames = GetLangSvc( aValues.getConstArray()[0] );
1738 else if ( rServiceName == SN_THESAURUS )
1740 OUString aNode( "ServiceManager/ThesaurusList");
1741 const uno::Sequence< OUString > aNodeEntries( GetNodeNames( aNode ) );
1742 if (lcl_SeqHasString( aNodeEntries, aCfgLocale ))
1744 pNames[0] = aNode + "/" + aCfgLocale;
1745 aValues = /*aCfg.*/GetProperties( aNames );
1746 if (aValues.hasElements())
1747 aSvcImplNames = GetLangSvcList( aValues.getConstArray()[0] );
1751 return aSvcImplNames;
1755 void SAL_CALL
1756 LngSvcMgr::dispose()
1758 osl::MutexGuard aGuard( GetLinguMutex() );
1760 if (!bDisposing)
1762 bDisposing = true;
1764 // require listeners to release this object
1765 lang::EventObject aEvtObj( static_cast<XLinguServiceManager*>(this) );
1766 aEvtListeners.disposeAndClear( aEvtObj );
1768 if (mxListenerHelper.is())
1769 mxListenerHelper->DisposeAndClear( aEvtObj );
1774 void SAL_CALL
1775 LngSvcMgr::addEventListener(
1776 const uno::Reference< lang::XEventListener >& xListener )
1778 osl::MutexGuard aGuard( GetLinguMutex() );
1780 if (!bDisposing && xListener.is())
1782 aEvtListeners.addInterface( xListener );
1787 void SAL_CALL
1788 LngSvcMgr::removeEventListener(
1789 const uno::Reference< lang::XEventListener >& xListener )
1791 osl::MutexGuard aGuard( GetLinguMutex() );
1793 if (xListener.is())
1795 aEvtListeners.removeInterface( xListener );
1800 bool LngSvcMgr::AddLngSvcEvtBroadcaster(
1801 const uno::Reference< linguistic2::XLinguServiceEventBroadcaster > &rxBroadcaster )
1803 if (!rxBroadcaster.is())
1804 return false;
1805 if (!mxListenerHelper.is())
1806 GetListenerHelper_Impl();
1807 mxListenerHelper->AddLngSvcEvtBroadcaster( rxBroadcaster );
1808 return true;
1812 OUString SAL_CALL
1813 LngSvcMgr::getImplementationName()
1815 return getImplementationName_Static();
1819 sal_Bool SAL_CALL
1820 LngSvcMgr::supportsService( const OUString& ServiceName )
1822 return cppu::supportsService(this, ServiceName);
1826 uno::Sequence< OUString > SAL_CALL
1827 LngSvcMgr::getSupportedServiceNames()
1829 return getSupportedServiceNames_Static();
1833 uno::Sequence< OUString > LngSvcMgr::getSupportedServiceNames_Static()
1834 throw()
1836 return { "com.sun.star.linguistic2.LinguServiceManager" };
1839 /// @throws uno::Exception
1840 static uno::Reference< uno::XInterface > LngSvcMgr_CreateInstance(
1841 const uno::Reference< lang::XMultiServiceFactory > & /*rSMgr*/ )
1843 uno::Reference< uno::XInterface > xService = static_cast<cppu::OWeakObject*>(new LngSvcMgr);
1844 return xService;
1847 void * LngSvcMgr_getFactory(
1848 const sal_Char * pImplName,
1849 lang::XMultiServiceFactory * pServiceManager )
1852 void * pRet = nullptr;
1853 if ( LngSvcMgr::getImplementationName_Static().equalsAscii( pImplName ) )
1855 uno::Reference< lang::XSingleServiceFactory > xFactory =
1856 cppu::createOneInstanceFactory(
1857 pServiceManager,
1858 LngSvcMgr::getImplementationName_Static(),
1859 LngSvcMgr_CreateInstance,
1860 LngSvcMgr::getSupportedServiceNames_Static());
1861 // acquire, because we return an interface pointer instead of a reference
1862 xFactory->acquire();
1863 pRet = xFactory.get();
1865 return pRet;
1868 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */