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/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"
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
;
90 xSuppLoc
.set( xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
91 rImplName
, aArgs
, xContext
),
94 catch (uno::Exception
&)
96 SAL_WARN( "linguistic", "createInstanceWithArguments failed" );
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
);
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
);
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
)
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
);
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
)
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
);
234 aLngSvcMgrListeners
.removeInterface( xRef
);
235 aLngSvcEvtBroadcasters
.removeInterface( xRef
);
236 if (xDicList
== xRef
)
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
;
270 LngSvcMgrListenerHelper::processLinguServiceEvent(
271 const linguistic2::LinguServiceEvent
& rLngSvcEvent
)
273 osl::MutexGuard
aGuard( GetLinguMutex() );
274 AddLngSvcEvt( rLngSvcEvent
.nEvent
);
279 LngSvcMgrListenerHelper::processDictionaryListEvent(
280 const linguistic2::DictionaryListEvent
& rDicListEvent
)
282 osl::MutexGuard
aGuard( GetLinguMutex() );
284 sal_Int16 nDlEvt
= rDicListEvent
.nCondensedEvent
;
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();
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
);
361 RemoveLngSvcEvtBroadcaster( xRef
);
364 // remove reference to this object hold by the dictionary-list
367 xDicList
->removeDictionaryListEventListener(
368 static_cast<linguistic2::XDictionaryListEventListener
*>(this) );
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())
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
);
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
;
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
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() );
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());
487 uno::Reference
<util::XModifyListener
> xListener(this);
488 xMB
->removeModifyListener(xListener
);
490 catch (const uno::Exception
&)
498 void LngSvcMgr::disposing(const lang::EventObject
&)
503 LngSvcMgr::~LngSvcMgr()
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();
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
);
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
;
555 SAL_WARN( "linguistic", "type mismatch" );
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
;
617 typedef Sequence
< OUString
> Sequence_OUString_t
;
618 typedef std::map
< OUString
, Sequence_OUString_t
> list_entry_map_t
;
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
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
;
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"
738 nKeyStart
= rName
.lastIndexOf( '/' );
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
);
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 );
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" );
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
)
905 mxHyphDsp
= new HyphenatorDispatcher( *this );
907 SetCfgServiceLists( *mxHyphDsp
);
912 void LngSvcMgr::GetThesaurusDsp_Impl( bool bSetSvcList
)
916 mxThesDsp
= new ThesaurusDispatcher
;
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
);
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" );
965 std::vector
< LanguageType
> aLanguages
;
966 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
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
);
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
)
1023 std::vector
< LanguageType
> aLanguages
;
1024 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
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
);
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" );
1079 std::vector
< LanguageType
> aLanguages
;
1080 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
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
);
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" );
1135 std::vector
< LanguageType
> aLanguages
;
1136 uno::Reference
< XServiceInfo
> xInfo( xSvc
, uno::UNO_QUERY
);
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
);
1299 uno::Reference
< linguistic2::XSpellChecker
> xRes
;
1302 if (!mxSpellDsp
.is())
1303 GetSpellCheckerDsp_Impl();
1304 xRes
= mxSpellDsp
.get();
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
);
1317 uno::Reference
< linguistic2::XHyphenator
> xRes
;
1320 if (!mxHyphDsp
.is())
1321 GetHyphenatorDsp_Impl();
1322 xRes
= mxHyphDsp
.get();
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
);
1335 uno::Reference
< linguistic2::XThesaurus
> xRes
;
1338 if (!mxThesDsp
.is())
1339 GetThesaurusDsp_Impl();
1340 xRes
= mxThesDsp
.get();
1347 LngSvcMgr::addLinguServiceManagerListener(
1348 const uno::Reference
< lang::XEventListener
>& xListener
)
1350 osl::MutexGuard
aGuard( GetLinguMutex() );
1352 if (bDisposing
|| !xListener
.is())
1355 if (!mxListenerHelper
.is())
1356 GetListenerHelper_Impl();
1357 mxListenerHelper
->AddLngSvcMgrListener( xListener
);
1363 LngSvcMgr::removeLinguServiceManagerListener(
1364 const uno::Reference
< lang::XEventListener
>& xListener
)
1366 osl::MutexGuard
aGuard( GetLinguMutex() );
1368 if (bDisposing
|| !xListener
.is())
1371 DBG_ASSERT( mxListenerHelper
.is(), "listener removed without being added" );
1372 if (!mxListenerHelper
.is())
1373 GetListenerHelper_Impl();
1374 mxListenerHelper
->RemoveLngSvcMgrListener( xListener
);
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();
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
);
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.
1455 *pAvailLocales
= GetAvailLocales(getAvailableServices(rServiceName
, lang::Locale()));
1456 aRes
= *pAvailLocales
;
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());
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
) );
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
) );
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
) );
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
) );
1541 mxThesDsp
->SetServiceList( rLocale
, rServiceImplNames
);
1542 SaveCfgSvcs( SN_THESAURUS
);
1549 bool LngSvcMgr::SaveCfgSvcs( const OUString
&rServiceName
)
1551 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs" );
1555 LinguDispatcher
*pDsp
= nullptr;
1556 uno::Sequence
< lang::Locale
> aLocales
;
1558 if (rServiceName
== SN_SPELLCHECKER
)
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";
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
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
;
1625 SAL_INFO( "linguistic", "linguistic: LngSvcMgr::SaveCfgSvcs - ReplaceSetProperties" );
1626 // change, add new or replace existing entries.
1627 bRes
|= /*aCfg.*/ReplaceSetProperties( aNodeName
, aValues
);
1635 static uno::Sequence
< OUString
> GetLangSvcList( const uno::Any
&rVal
)
1637 uno::Sequence
< OUString
> aRes
;
1639 if (rVal
.hasValue())
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" );
1654 static uno::Sequence
< OUString
> GetLangSvc( const uno::Any
&rVal
)
1656 uno::Sequence
< OUString
> aRes
;
1657 if (!rVal
.hasValue())
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.
1666 // but only the first string should be used.
1667 if (aRes
.getLength() > 1)
1673 if ((rVal
>>= aImplName
) && !aImplName
.isEmpty())
1676 aRes
.getArray()[0] = aImplName
;
1680 SAL_WARN( "linguistic", "GetLangSvc: unexpected type encountered" );
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
;
1756 LngSvcMgr::dispose()
1758 osl::MutexGuard
aGuard( GetLinguMutex() );
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
);
1775 LngSvcMgr::addEventListener(
1776 const uno::Reference
< lang::XEventListener
>& xListener
)
1778 osl::MutexGuard
aGuard( GetLinguMutex() );
1780 if (!bDisposing
&& xListener
.is())
1782 aEvtListeners
.addInterface( xListener
);
1788 LngSvcMgr::removeEventListener(
1789 const uno::Reference
< lang::XEventListener
>& xListener
)
1791 osl::MutexGuard
aGuard( GetLinguMutex() );
1795 aEvtListeners
.removeInterface( xListener
);
1800 bool LngSvcMgr::AddLngSvcEvtBroadcaster(
1801 const uno::Reference
< linguistic2::XLinguServiceEventBroadcaster
> &rxBroadcaster
)
1803 if (!rxBroadcaster
.is())
1805 if (!mxListenerHelper
.is())
1806 GetListenerHelper_Impl();
1807 mxListenerHelper
->AddLngSvcEvtBroadcaster( rxBroadcaster
);
1813 LngSvcMgr::getImplementationName()
1815 return getImplementationName_Static();
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()
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
);
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(
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();
1868 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */