1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <vcl/settings.hxx>
21 #include <vcl/weld.hxx>
22 #include <i18nlangtag/languagetag.hxx>
23 #include <i18nlangtag/mslangid.hxx>
24 #include <officecfg/Office/Security.hxx>
25 #include <unotools/lingucfg.hxx>
26 #include <unotools/linguprops.hxx>
27 #include <editeng/unolingu.hxx>
28 #include <linguistic/misc.hxx>
29 #include <sfx2/sfxsids.hrc>
30 #include <tools/debug.hxx>
31 #include <tools/urlobj.hxx>
32 #include <tools/diagnose_ex.h>
33 #include <comphelper/dispatchcommand.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <com/sun/star/linguistic2/LinguServiceManager.hpp>
36 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
37 #include <com/sun/star/linguistic2/XSpellChecker.hpp>
38 #include <com/sun/star/linguistic2/XProofreader.hpp>
39 #include <com/sun/star/linguistic2/XHyphenator.hpp>
40 #include <com/sun/star/linguistic2/XThesaurus.hpp>
41 #include <com/sun/star/linguistic2/XDictionary.hpp>
42 #include <com/sun/star/linguistic2/XDictionaryList.hpp>
43 #include <com/sun/star/linguistic2/XLinguProperties.hpp>
44 #include <com/sun/star/lang/XServiceDisplayName.hpp>
45 #include <com/sun/star/frame/XStorable.hpp>
46 #include <unotools/extendedsecurityoptions.hxx>
47 #include <svl/eitem.hxx>
48 #include <vcl/svapp.hxx>
49 #include <sal/log.hxx>
50 #include <osl/diagnose.h>
51
52 #include <svx/svxdlg.hxx>
53 #include <editeng/optitems.hxx>
54 #include <optlingu.hxx>
55 #include <dialmgr.hxx>
56 #include <strings.hrc>
57
58 #include <ucbhelper/content.hxx>
59
60 #include <vector>
61 #include <map>
62
63 using namespace ::ucbhelper;
64 using namespace ::com::sun::star;
65 using namespace css::lang;
66 using namespace css::uno;
67 using namespace css::linguistic2;
68 using namespace css::beans;
69
70 constexpr OUStringLiteral cSpell(SN_SPELLCHECKER);
71 constexpr OUStringLiteral cGrammar(SN_GRAMMARCHECKER);
72 constexpr OUStringLiteral cHyph(SN_HYPHENATOR);
73 constexpr OUStringLiteral cThes(SN_THESAURUS);
74
75 // static ----------------------------------------------------------------
76
lcl_SeqGetEntryPos(const Sequence<OUString> & rSeq,std::u16string_view rEntry)77 static sal_Int32 lcl_SeqGetEntryPos(
78 const Sequence< OUString > &rSeq, std::u16string_view rEntry )
79 {
80 sal_Int32 i;
81 sal_Int32 nLen = rSeq.getLength();
82 const OUString *pItem = rSeq.getConstArray();
83 for (i = 0; i < nLen; ++i)
84 {
85 if (rEntry == pItem[i])
86 break;
87 }
88 return i < nLen ? i : -1;
89 }
90
KillFile_Impl(const OUString & rURL)91 static bool KillFile_Impl( const OUString& rURL )
92 {
93 bool bRet = true;
94 try
95 {
96 Content aCnt( rURL, uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
97 aCnt.executeCommand( "delete", Any( true ) );
98 }
99 catch( ... )
100 {
101 TOOLS_WARN_EXCEPTION( "cui.options", "KillFile" );
102 bRet = false;
103 }
104
105 return bRet;
106 }
107
108 // 0x 0p 0t 0c nn
109 // p: 1 -> parent
110 // t: 1 -> spell, 2 -> hyph, 3 -> thes, 4 -> grammar
111 // c: 1 -> checked 0 -> unchecked
112 // n: index
113
114 #define TYPE_SPELL sal_uInt8(1)
115 #define TYPE_GRAMMAR sal_uInt8(2)
116 #define TYPE_HYPH sal_uInt8(3)
117 #define TYPE_THES sal_uInt8(4)
118
119 namespace {
120
121 class ModuleUserData_Impl
122 {
123 bool bParent;
124 bool bIsChecked;
125 sal_uInt8 nType;
126 sal_uInt8 nIndex;
127 OUString sImplName;
128
129 public:
ModuleUserData_Impl(const OUString & sImpName,bool bIsParent,bool bChecked,sal_uInt8 nSetType,sal_uInt8 nSetIndex)130 ModuleUserData_Impl( const OUString& sImpName, bool bIsParent, bool bChecked, sal_uInt8 nSetType, sal_uInt8 nSetIndex ) :
131 bParent(bIsParent),
132 bIsChecked(bChecked),
133 nType(nSetType),
134 nIndex(nSetIndex),
135 sImplName(sImpName)
136 {
137 }
IsParent() const138 bool IsParent() const {return bParent;}
GetType() const139 sal_uInt8 GetType() const {return nType;}
IsChecked() const140 bool IsChecked() const {return bIsChecked;}
GetIndex() const141 sal_uInt8 GetIndex() const {return nIndex;}
GetImplName() const142 const OUString& GetImplName() const {return sImplName;}
143
144 };
145
146
147 // User for user-dictionaries (XDictionary interface)
148
149 class DicUserData
150 {
151 sal_uInt32 nVal;
152
153 public:
DicUserData(sal_uInt32 nUserData)154 explicit DicUserData(sal_uInt32 nUserData) : nVal( nUserData ) {}
155 DicUserData( sal_uInt16 nEID,
156 bool bChecked, bool bEditable, bool bDeletable );
157
GetUserData() const158 sal_uInt32 GetUserData() const { return nVal; }
GetEntryId() const159 sal_uInt16 GetEntryId() const { return static_cast<sal_uInt16>(nVal >> 16); }
IsChecked() const160 bool IsChecked() const { return static_cast<bool>((nVal >> 8) & 0x01); }
IsDeletable() const161 bool IsDeletable() const { return static_cast<bool>((nVal >> 10) & 0x01); }
162 };
163
164 }
165
DicUserData(sal_uInt16 nEID,bool bChecked,bool bEditable,bool bDeletable)166 DicUserData::DicUserData(
167 sal_uInt16 nEID,
168 bool bChecked, bool bEditable, bool bDeletable )
169 {
170 DBG_ASSERT( nEID < 65000, "Entry Id out of range" );
171 nVal = (static_cast<sal_uInt32>(0xFFFF & nEID) << 16) |
172 (static_cast<sal_uInt32>(bChecked ? 1 : 0) << 8) |
173 (static_cast<sal_uInt32>(bEditable ? 1 : 0) << 9) |
174 (static_cast<sal_uInt32>(bDeletable ? 1 : 0) << 10);
175 }
176
177 /*--------------------------------------------------
178 Entry IDs for options listbox of dialog
179 --------------------------------------------------*/
180
181 namespace {
182
183 enum EID_OPTIONS
184 {
185 EID_SPELL_AUTO,
186 EID_GRAMMAR_AUTO,
187 EID_CAPITAL_WORDS,
188 EID_WORDS_WITH_DIGITS,
189 EID_SPELL_SPECIAL,
190 EID_NUM_MIN_WORDLEN,
191 EID_NUM_PRE_BREAK,
192 EID_NUM_POST_BREAK,
193 EID_HYPH_AUTO,
194 EID_HYPH_SPECIAL
195 };
196
197 }
198
199 //! this array must have an entry for every value of EID_OPTIONS.
200 // It is used to get the respective property name.
201 static const char * aEidToPropName[] =
202 {
203 UPN_IS_SPELL_AUTO, // EID_SPELL_AUTO
204 UPN_IS_GRAMMAR_AUTO, // EID_GRAMMAR_AUTO
205 UPN_IS_SPELL_UPPER_CASE, // EID_CAPITAL_WORDS
206 UPN_IS_SPELL_WITH_DIGITS, // EID_WORDS_WITH_DIGITS
207 UPN_IS_SPELL_SPECIAL, // EID_SPELL_SPECIAL
208 UPN_HYPH_MIN_WORD_LENGTH, // EID_NUM_MIN_WORDLEN,
209 UPN_HYPH_MIN_LEADING, // EID_NUM_PRE_BREAK
210 UPN_HYPH_MIN_TRAILING, // EID_NUM_POST_BREAK
211 UPN_IS_HYPH_AUTO, // EID_HYPH_AUTO
212 UPN_IS_HYPH_SPECIAL // EID_HYPH_SPECIAL
213 };
214
lcl_GetPropertyName(EID_OPTIONS eEntryId)215 static OUString lcl_GetPropertyName( EID_OPTIONS eEntryId )
216 {
217 DBG_ASSERT( static_cast<unsigned int>(eEntryId) < SAL_N_ELEMENTS(aEidToPropName), "index out of range" );
218 return OUString::createFromAscii( aEidToPropName[ static_cast<int>(eEntryId) ] );
219 }
220
221 namespace {
222
223 class OptionsBreakSet : public weld::GenericDialogController
224 {
225 std::unique_ptr<weld::Widget> m_xBeforeFrame;
226 std::unique_ptr<weld::Widget> m_xAfterFrame;
227 std::unique_ptr<weld::Widget> m_xMinimalFrame;
228 std::unique_ptr<weld::SpinButton> m_xBreakNF;
229
230 public:
OptionsBreakSet(weld::Window * pParent,sal_uInt16 nRID)231 OptionsBreakSet(weld::Window* pParent, sal_uInt16 nRID)
232 : GenericDialogController(pParent, "cui/ui/breaknumberoption.ui", "BreakNumberOption")
233 , m_xBeforeFrame(m_xBuilder->weld_widget("beforeframe"))
234 , m_xAfterFrame(m_xBuilder->weld_widget("afterframe"))
235 , m_xMinimalFrame(m_xBuilder->weld_widget("miniframe"))
236 {
237 assert(EID_NUM_PRE_BREAK == nRID || EID_NUM_POST_BREAK == nRID || EID_NUM_MIN_WORDLEN == nRID); //unexpected ID
238
239 if (nRID == EID_NUM_PRE_BREAK)
240 {
241 m_xBeforeFrame->show();
242 m_xBreakNF = m_xBuilder->weld_spin_button("beforebreak");
243 }
244 else if(nRID == EID_NUM_POST_BREAK)
245 {
246 m_xAfterFrame->show();
247 m_xBreakNF = m_xBuilder->weld_spin_button("afterbreak");
248 }
249 else if(nRID == EID_NUM_MIN_WORDLEN)
250 {
251 m_xMinimalFrame->show();
252 m_xBreakNF = m_xBuilder->weld_spin_button("wordlength");
253 }
254 }
255
GetNumericFld()256 weld::SpinButton& GetNumericFld()
257 {
258 return *m_xBreakNF;
259 }
260 };
261
262 // class OptionsUserData -------------------------------------------------
263
264 class OptionsUserData
265 {
266 sal_uInt32 nVal;
267
268 public:
OptionsUserData(sal_uInt32 nUserData)269 explicit OptionsUserData( sal_uInt32 nUserData ) : nVal( nUserData ) {}
270 OptionsUserData( sal_uInt16 nEID,
271 bool bHasNV, sal_uInt16 nNumVal,
272 bool bCheckable, bool bChecked );
273
GetUserData() const274 sal_uInt32 GetUserData() const { return nVal; }
GetEntryId() const275 sal_uInt16 GetEntryId() const { return static_cast<sal_uInt16>(nVal >> 16); }
HasNumericValue() const276 bool HasNumericValue() const { return static_cast<bool>((nVal >> 10) & 0x01); }
GetNumericValue() const277 sal_uInt16 GetNumericValue() const { return static_cast<sal_uInt16>(nVal & 0xFF); }
IsCheckable() const278 bool IsCheckable() const { return static_cast<bool>((nVal >> 9) & 0x01); }
IsModified() const279 bool IsModified() const { return static_cast<bool>((nVal >> 11) & 0x01); }
280
281 void SetNumericValue( sal_uInt8 nNumVal );
282 };
283
284 }
285
OptionsUserData(sal_uInt16 nEID,bool bHasNV,sal_uInt16 nNumVal,bool bCheckable,bool bChecked)286 OptionsUserData::OptionsUserData( sal_uInt16 nEID,
287 bool bHasNV, sal_uInt16 nNumVal,
288 bool bCheckable, bool bChecked )
289 {
290 DBG_ASSERT( nEID < 65000, "Entry Id out of range" );
291 DBG_ASSERT( nNumVal < 256, "value out of range" );
292 nVal = (static_cast<sal_uInt32>(0xFFFF & nEID) << 16) |
293 (static_cast<sal_uInt32>(bHasNV ? 1 : 0) << 10) |
294 (static_cast<sal_uInt32>(bCheckable ? 1 : 0) << 9) |
295 (static_cast<sal_uInt32>(bChecked ? 1 : 0) << 8) |
296 static_cast<sal_uInt32>(0xFF & nNumVal);
297 }
298
SetNumericValue(sal_uInt8 nNumVal)299 void OptionsUserData::SetNumericValue( sal_uInt8 nNumVal )
300 {
301 if (HasNumericValue() && (GetNumericValue() != nNumVal))
302 {
303 nVal &= 0xffffff00;
304 nVal |= nNumVal;
305 nVal |= sal_uInt32(1) << 11; // mark as modified
306 }
307 }
308
309 // ServiceInfo_Impl ----------------------------------------------------
310
311 namespace {
312
313 struct ServiceInfo_Impl
314 {
315 OUString sDisplayName;
316 OUString sSpellImplName;
317 OUString sHyphImplName;
318 OUString sThesImplName;
319 OUString sGrammarImplName;
320 uno::Reference< XSpellChecker > xSpell;
321 uno::Reference< XHyphenator > xHyph;
322 uno::Reference< XThesaurus > xThes;
323 uno::Reference< XProofreader > xGrammar;
324 bool bConfigured;
325
ServiceInfo_Impl__anonf521eeeb0411::ServiceInfo_Impl326 ServiceInfo_Impl() : bConfigured(false) {}
327 };
328
329 }
330
331 typedef std::vector< ServiceInfo_Impl > ServiceInfoArr;
332 typedef std::map< LanguageType, Sequence< OUString > > LangImplNameTable;
333
334
335 // SvxLinguData_Impl ----------------------------------------------------
336
337 class SvxLinguData_Impl
338 {
339 //contains services and implementation names sorted by implementation names
340 ServiceInfoArr aDisplayServiceArr;
341 sal_uInt32 nDisplayServices;
342
343 Sequence< Locale > aAllServiceLocales;
344 LangImplNameTable aCfgSpellTable;
345 LangImplNameTable aCfgHyphTable;
346 LangImplNameTable aCfgThesTable;
347 LangImplNameTable aCfgGrammarTable;
348 uno::Reference< XLinguServiceManager2 > xLinguSrvcMgr;
349
350
351 static bool AddRemove( Sequence< OUString > &rConfigured,
352 const OUString &rImplName, bool bAdd );
353
354 public:
355 SvxLinguData_Impl();
356
GetManager()357 uno::Reference<XLinguServiceManager2> & GetManager() { return xLinguSrvcMgr; }
358
359 void SetChecked( const Sequence< OUString > &rConfiguredServices );
360 void Reconfigure( std::u16string_view rDisplayName, bool bEnable );
361
GetAllSupportedLocales() const362 const Sequence<Locale> & GetAllSupportedLocales() const { return aAllServiceLocales; }
363
GetSpellTable()364 LangImplNameTable & GetSpellTable() { return aCfgSpellTable; }
GetHyphTable()365 LangImplNameTable & GetHyphTable() { return aCfgHyphTable; }
GetThesTable()366 LangImplNameTable & GetThesTable() { return aCfgThesTable; }
GetGrammarTable()367 LangImplNameTable & GetGrammarTable() { return aCfgGrammarTable; }
368
GetDisplayServiceArray()369 ServiceInfoArr & GetDisplayServiceArray() { return aDisplayServiceArr; }
370
GetDisplayServiceCount() const371 const sal_uInt32 & GetDisplayServiceCount() const { return nDisplayServices; }
SetDisplayServiceCount(sal_uInt32 nVal)372 void SetDisplayServiceCount( sal_uInt32 nVal ) { nDisplayServices = nVal; }
373
374 // returns the list of service implementation names for the specified
375 // language and service (TYPE_SPELL, TYPE_HYPH, TYPE_THES) sorted in
376 // the proper order for the SvxEditModulesDlg (the ones from the
377 // configuration (keeping that order!) first and then the other ones.
378 // I.e. the ones available but not configured in arbitrary order).
379 // They available ones may contain names that do not(!) support that
380 // language.
381 Sequence< OUString > GetSortedImplNames( LanguageType nLang, sal_uInt8 nType );
382
383 ServiceInfo_Impl * GetInfoByImplName( std::u16string_view rSvcImplName );
384 };
385
386
lcl_SeqGetIndex(const Sequence<OUString> & rSeq,std::u16string_view rTxt)387 static sal_Int32 lcl_SeqGetIndex( const Sequence< OUString > &rSeq, std::u16string_view rTxt )
388 {
389 sal_Int32 nRes = -1;
390 sal_Int32 nLen = rSeq.getLength();
391 const OUString *pString = rSeq.getConstArray();
392 for (sal_Int32 i = 0; i < nLen && nRes == -1; ++i)
393 {
394 if (pString[i] == rTxt)
395 nRes = i;
396 }
397 return nRes;
398 }
399
400
GetSortedImplNames(LanguageType nLang,sal_uInt8 nType)401 Sequence< OUString > SvxLinguData_Impl::GetSortedImplNames( LanguageType nLang, sal_uInt8 nType )
402 {
403 LangImplNameTable *pTable = nullptr;
404 switch (nType)
405 {
406 case TYPE_SPELL : pTable = &aCfgSpellTable; break;
407 case TYPE_HYPH : pTable = &aCfgHyphTable; break;
408 case TYPE_THES : pTable = &aCfgThesTable; break;
409 case TYPE_GRAMMAR : pTable = &aCfgGrammarTable; break;
410 }
411 Sequence< OUString > aRes;
412 if (!pTable)
413 {
414 SAL_WARN( "cui.options", "unknown linguistic type" );
415 return aRes;
416 }
417 if (pTable->count( nLang ))
418 aRes = (*pTable)[ nLang ]; // add configured services
419 sal_Int32 nIdx = aRes.getLength();
420 DBG_ASSERT( static_cast<sal_Int32>(nDisplayServices) >= nIdx, "size mismatch" );
421 aRes.realloc( nDisplayServices );
422 OUString *pRes = aRes.getArray();
423
424 // add not configured services
425 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(nDisplayServices); ++i)
426 {
427 const ServiceInfo_Impl &rInfo = aDisplayServiceArr[ i ];
428 OUString aImplName;
429 switch (nType)
430 {
431 case TYPE_SPELL : aImplName = rInfo.sSpellImplName; break;
432 case TYPE_HYPH : aImplName = rInfo.sHyphImplName; break;
433 case TYPE_THES : aImplName = rInfo.sThesImplName; break;
434 case TYPE_GRAMMAR : aImplName = rInfo.sGrammarImplName; break;
435 }
436
437 if (!aImplName.isEmpty() && (lcl_SeqGetIndex( aRes, aImplName) == -1)) // name not yet added
438 {
439 DBG_ASSERT( nIdx < aRes.getLength(), "index out of range" );
440 if (nIdx < aRes.getLength())
441 pRes[ nIdx++ ] = aImplName;
442 }
443 }
444 // don't forget to put aRes back to its actual size just in case you allocated too much
445 // since all of the names may have already been added
446 // otherwise you get duplicate entries in the edit dialog
447 aRes.realloc( nIdx );
448 return aRes;
449 }
450
451
GetInfoByImplName(std::u16string_view rSvcImplName)452 ServiceInfo_Impl * SvxLinguData_Impl::GetInfoByImplName( std::u16string_view rSvcImplName )
453 {
454 for (sal_uInt32 i = 0; i < nDisplayServices; ++i)
455 {
456 ServiceInfo_Impl &rTmp = aDisplayServiceArr[ i ];
457 if (rTmp.sSpellImplName == rSvcImplName ||
458 rTmp.sHyphImplName == rSvcImplName ||
459 rTmp.sThesImplName == rSvcImplName ||
460 rTmp.sGrammarImplName == rSvcImplName)
461 {
462 return &rTmp;
463 }
464 }
465 return nullptr;
466 }
467
468
lcl_MergeLocales(Sequence<Locale> & aAllLocales,const Sequence<Locale> & rAdd)469 static void lcl_MergeLocales(Sequence< Locale >& aAllLocales, const Sequence< Locale >& rAdd)
470 {
471 Sequence<Locale> aLocToAdd(rAdd.getLength());
472 Locale* pLocToAdd = aLocToAdd.getArray();
473 sal_Int32 nFound = 0;
474 for(const Locale& i : rAdd)
475 {
476 bool bFound = false;
477 for(const Locale& j : std::as_const(aAllLocales))
478 {
479 if (i.Language == j.Language &&
480 i.Country == j.Country &&
481 i.Variant == j.Variant)
482 {
483 bFound = true;
484 break;
485 }
486 }
487 if(!bFound)
488 {
489 pLocToAdd[nFound++] = i;
490 }
491 }
492 sal_Int32 nLength = aAllLocales.getLength();
493 aAllLocales.realloc( nLength + nFound);
494 Locale* pAllLocales2 = aAllLocales.getArray();
495 for(sal_Int32 i = 0; i < nFound; i++)
496 pAllLocales2[nLength++] = pLocToAdd[i];
497 }
498
lcl_MergeDisplayArray(SvxLinguData_Impl & rData,const ServiceInfo_Impl & rToAdd)499 static void lcl_MergeDisplayArray(
500 SvxLinguData_Impl &rData,
501 const ServiceInfo_Impl &rToAdd )
502 {
503 sal_uInt32 nCnt = 0;
504
505 ServiceInfoArr &rSvcInfoArr = rData.GetDisplayServiceArray();
506 sal_uInt32 nEntries = rData.GetDisplayServiceCount();
507
508 for (sal_uInt32 i = 0; i < nEntries; ++i)
509 {
510 ServiceInfo_Impl& rEntry = rSvcInfoArr[i];
511 if (rEntry.sDisplayName == rToAdd.sDisplayName)
512 {
513 if(rToAdd.xSpell.is())
514 {
515 DBG_ASSERT( !rEntry.xSpell.is() &&
516 rEntry.sSpellImplName.isEmpty(),
517 "merge conflict" );
518 rEntry.sSpellImplName = rToAdd.sSpellImplName;
519 rEntry.xSpell = rToAdd.xSpell;
520 }
521 if(rToAdd.xGrammar.is())
522 {
523 DBG_ASSERT( !rEntry.xGrammar.is() &&
524 rEntry.sGrammarImplName.isEmpty(),
525 "merge conflict" );
526 rEntry.sGrammarImplName = rToAdd.sGrammarImplName;
527 rEntry.xGrammar = rToAdd.xGrammar;
528 }
529 if(rToAdd.xHyph.is())
530 {
531 DBG_ASSERT( !rEntry.xHyph.is() &&
532 rEntry.sHyphImplName.isEmpty(),
533 "merge conflict" );
534 rEntry.sHyphImplName = rToAdd.sHyphImplName;
535 rEntry.xHyph = rToAdd.xHyph;
536 }
537 if(rToAdd.xThes.is())
538 {
539 DBG_ASSERT( !rEntry.xThes.is() &&
540 rEntry.sThesImplName.isEmpty(),
541 "merge conflict" );
542 rEntry.sThesImplName = rToAdd.sThesImplName;
543 rEntry.xThes = rToAdd.xThes;
544 }
545 return ;
546 }
547 ++nCnt;
548 }
549 rData.GetDisplayServiceArray().push_back( rToAdd );
550 rData.SetDisplayServiceCount( nCnt + 1 );
551 }
552
SvxLinguData_Impl()553 SvxLinguData_Impl::SvxLinguData_Impl() :
554 nDisplayServices (0)
555 {
556 uno::Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
557 xLinguSrvcMgr = LinguServiceManager::create(xContext);
558
559 const Locale& rCurrentLocale = Application::GetSettings().GetLanguageTag().getLocale();
560 Sequence<Any> aArgs(2);//second arguments has to be empty!
561 aArgs.getArray()[0] <<= LinguMgr::GetLinguPropertySet();
562
563 //read spell checker
564 const Sequence< OUString > aSpellNames = xLinguSrvcMgr->getAvailableServices(
565 cSpell, Locale() );
566
567 for(const OUString& spellName : aSpellNames)
568 {
569 ServiceInfo_Impl aInfo;
570 aInfo.sSpellImplName = spellName;
571 aInfo.xSpell.set(
572 xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aInfo.sSpellImplName, aArgs, xContext), UNO_QUERY);
573
574 uno::Reference<XServiceDisplayName> xDispName(aInfo.xSpell, UNO_QUERY);
575 if(xDispName.is())
576 aInfo.sDisplayName = xDispName->getServiceDisplayName( rCurrentLocale );
577
578 const Sequence< Locale > aLocales( aInfo.xSpell->getLocales() );
579 //! suppress display of entries with no supported languages (see feature 110994)
580 if (aLocales.hasElements())
581 {
582 lcl_MergeLocales( aAllServiceLocales, aLocales );
583 lcl_MergeDisplayArray( *this, aInfo );
584 }
585 }
586
587 //read grammar checker
588 const Sequence< OUString > aGrammarNames = xLinguSrvcMgr->getAvailableServices(
589 cGrammar, Locale() );
590 for(const OUString& grammarName : aGrammarNames)
591 {
592 ServiceInfo_Impl aInfo;
593 aInfo.sGrammarImplName = grammarName;
594 aInfo.xGrammar.set(
595 xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aInfo.sGrammarImplName, aArgs, xContext), UNO_QUERY);
596
597 uno::Reference<XServiceDisplayName> xDispName(aInfo.xGrammar, UNO_QUERY);
598 if(xDispName.is())
599 aInfo.sDisplayName = xDispName->getServiceDisplayName( rCurrentLocale );
600
601 const Sequence< Locale > aLocales( aInfo.xGrammar->getLocales() );
602 //! suppress display of entries with no supported languages (see feature 110994)
603 if (aLocales.hasElements())
604 {
605 lcl_MergeLocales( aAllServiceLocales, aLocales );
606 lcl_MergeDisplayArray( *this, aInfo );
607 }
608 }
609
610 //read hyphenator
611 const Sequence< OUString > aHyphNames = xLinguSrvcMgr->getAvailableServices(
612 cHyph, Locale() );
613 for(const OUString& hyphName : aHyphNames)
614 {
615 ServiceInfo_Impl aInfo;
616 aInfo.sHyphImplName = hyphName;
617 aInfo.xHyph.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aInfo.sHyphImplName, aArgs, xContext), UNO_QUERY);
618
619 uno::Reference<XServiceDisplayName> xDispName(aInfo.xHyph, UNO_QUERY);
620 if(xDispName.is())
621 aInfo.sDisplayName = xDispName->getServiceDisplayName( rCurrentLocale );
622
623 const Sequence< Locale > aLocales( aInfo.xHyph->getLocales() );
624 //! suppress display of entries with no supported languages (see feature 110994)
625 if (aLocales.hasElements())
626 {
627 lcl_MergeLocales( aAllServiceLocales, aLocales );
628 lcl_MergeDisplayArray( *this, aInfo );
629 }
630 }
631
632 //read thesauri
633 const Sequence< OUString > aThesNames = xLinguSrvcMgr->getAvailableServices(
634 cThes, Locale() );
635 for(const OUString& thesName : aThesNames)
636 {
637 ServiceInfo_Impl aInfo;
638 aInfo.sThesImplName = thesName;
639 aInfo.xThes.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aInfo.sThesImplName, aArgs, xContext), UNO_QUERY);
640
641 uno::Reference<XServiceDisplayName> xDispName(aInfo.xThes, UNO_QUERY);
642 if(xDispName.is())
643 aInfo.sDisplayName = xDispName->getServiceDisplayName( rCurrentLocale );
644
645 const Sequence< Locale > aLocales( aInfo.xThes->getLocales() );
646 //! suppress display of entries with no supported languages (see feature 110994)
647 if (aLocales.hasElements())
648 {
649 lcl_MergeLocales( aAllServiceLocales, aLocales );
650 lcl_MergeDisplayArray( *this, aInfo );
651 }
652 }
653
654 Sequence< OUString > aCfgSvcs;
655 for(auto const & locale : std::as_const(aAllServiceLocales))
656 {
657 LanguageType nLang = LanguageTag::convertToLanguageType( locale );
658
659 aCfgSvcs = xLinguSrvcMgr->getConfiguredServices(cSpell, locale);
660 SetChecked( aCfgSvcs );
661 if (aCfgSvcs.hasElements())
662 aCfgSpellTable[ nLang ] = aCfgSvcs;
663
664 aCfgSvcs = xLinguSrvcMgr->getConfiguredServices(cGrammar, locale);
665 SetChecked( aCfgSvcs );
666 if (aCfgSvcs.hasElements())
667 aCfgGrammarTable[ nLang ] = aCfgSvcs;
668
669 aCfgSvcs = xLinguSrvcMgr->getConfiguredServices(cHyph, locale);
670 SetChecked( aCfgSvcs );
671 if (aCfgSvcs.hasElements())
672 aCfgHyphTable[ nLang ] = aCfgSvcs;
673
674 aCfgSvcs = xLinguSrvcMgr->getConfiguredServices(cThes, locale);
675 SetChecked( aCfgSvcs );
676 if (aCfgSvcs.hasElements())
677 aCfgThesTable[ nLang ] = aCfgSvcs;
678 }
679 }
680
SetChecked(const Sequence<OUString> & rConfiguredServices)681 void SvxLinguData_Impl::SetChecked(const Sequence<OUString>& rConfiguredServices)
682 {
683 for(OUString const & configService : rConfiguredServices)
684 {
685 for (sal_uInt32 i = 0; i < nDisplayServices; ++i)
686 {
687 ServiceInfo_Impl& rEntry = aDisplayServiceArr[i];
688 if (!rEntry.bConfigured)
689 {
690 const OUString &rSrvcImplName = configService;
691 if (!rSrvcImplName.isEmpty() &&
692 (rEntry.sSpellImplName == rSrvcImplName ||
693 rEntry.sGrammarImplName == rSrvcImplName ||
694 rEntry.sHyphImplName == rSrvcImplName ||
695 rEntry.sThesImplName == rSrvcImplName))
696 {
697 rEntry.bConfigured = true;
698 break;
699 }
700 }
701 }
702 }
703 }
704
AddRemove(Sequence<OUString> & rConfigured,const OUString & rImplName,bool bAdd)705 bool SvxLinguData_Impl::AddRemove(
706 Sequence< OUString > &rConfigured,
707 const OUString &rImplName, bool bAdd )
708 {
709 bool bRet = false; // modified?
710
711 sal_Int32 nEntries = rConfigured.getLength();
712 sal_Int32 nPos = lcl_SeqGetEntryPos(rConfigured, rImplName);
713 if (bAdd && nPos < 0) // add new entry
714 {
715 rConfigured.realloc( ++nEntries );
716 OUString *pConfigured = rConfigured.getArray();
717 pConfigured[nEntries - 1] = rImplName;
718 bRet = true;
719 }
720 else if (!bAdd && nPos >= 0) // remove existing entry
721 {
722 OUString *pConfigured = rConfigured.getArray();
723 for (sal_Int32 i = nPos; i < nEntries - 1; ++i)
724 pConfigured[i] = pConfigured[i + 1];
725 rConfigured.realloc(--nEntries);
726 bRet = true;
727 }
728
729 return bRet;
730 }
731
732
Reconfigure(std::u16string_view rDisplayName,bool bEnable)733 void SvxLinguData_Impl::Reconfigure( std::u16string_view rDisplayName, bool bEnable )
734 {
735 DBG_ASSERT( !rDisplayName.empty(), "empty DisplayName" );
736
737 ServiceInfo_Impl *pInfo = nullptr;
738 for (sal_uInt32 i = 0; i < nDisplayServices; ++i)
739 {
740 ServiceInfo_Impl& rTmp = aDisplayServiceArr[i];
741 if (rTmp.sDisplayName == rDisplayName)
742 {
743 pInfo = &rTmp;
744 break;
745 }
746 }
747 DBG_ASSERT( pInfo, "DisplayName entry not found" );
748 if (!pInfo)
749 return;
750
751 pInfo->bConfigured = bEnable;
752
753 Sequence< Locale > aLocales;
754 const Locale *pLocale = nullptr;
755 sal_Int32 nLocales = 0;
756 sal_Int32 i;
757
758 // update configured spellchecker entries
759 if (pInfo->xSpell.is())
760 {
761 aLocales = pInfo->xSpell->getLocales();
762 pLocale = aLocales.getConstArray();
763 nLocales = aLocales.getLength();
764 for (i = 0; i < nLocales; ++i)
765 {
766 LanguageType nLang = LanguageTag::convertToLanguageType( pLocale[i] );
767 if (!aCfgSpellTable.count( nLang ) && bEnable)
768 aCfgSpellTable[ nLang ] = Sequence< OUString >();
769 if (aCfgSpellTable.count( nLang ))
770 AddRemove( aCfgSpellTable[ nLang ], pInfo->sSpellImplName, bEnable );
771 }
772 }
773
774 // update configured grammar checker entries
775 if (pInfo->xGrammar.is())
776 {
777 aLocales = pInfo->xGrammar->getLocales();
778 pLocale = aLocales.getConstArray();
779 nLocales = aLocales.getLength();
780 for (i = 0; i < nLocales; ++i)
781 {
782 LanguageType nLang = LanguageTag::convertToLanguageType( pLocale[i] );
783 if (!aCfgGrammarTable.count( nLang ) && bEnable)
784 aCfgGrammarTable[ nLang ] = Sequence< OUString >();
785 if (aCfgGrammarTable.count( nLang ))
786 AddRemove( aCfgGrammarTable[ nLang ], pInfo->sGrammarImplName, bEnable );
787 }
788 }
789
790 // update configured hyphenator entries
791 if (pInfo->xHyph.is())
792 {
793 aLocales = pInfo->xHyph->getLocales();
794 pLocale = aLocales.getConstArray();
795 nLocales = aLocales.getLength();
796 for (i = 0; i < nLocales; ++i)
797 {
798 LanguageType nLang = LanguageTag::convertToLanguageType( pLocale[i] );
799 if (!aCfgHyphTable.count( nLang ) && bEnable)
800 aCfgHyphTable[ nLang ] = Sequence< OUString >();
801 if (aCfgHyphTable.count( nLang ))
802 AddRemove( aCfgHyphTable[ nLang ], pInfo->sHyphImplName, bEnable );
803 }
804 }
805
806 // update configured spellchecker entries
807 if (!pInfo->xThes.is())
808 return;
809
810 aLocales = pInfo->xThes->getLocales();
811 pLocale = aLocales.getConstArray();
812 nLocales = aLocales.getLength();
813 for (i = 0; i < nLocales; ++i)
814 {
815 LanguageType nLang = LanguageTag::convertToLanguageType( pLocale[i] );
816 if (!aCfgThesTable.count( nLang ) && bEnable)
817 aCfgThesTable[ nLang ] = Sequence< OUString >();
818 if (aCfgThesTable.count( nLang ))
819 AddRemove( aCfgThesTable[ nLang ], pInfo->sThesImplName, bEnable );
820 }
821 }
822
823
824 // class SvxLinguTabPage -------------------------------------------------
825
SvxLinguTabPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet & rSet)826 SvxLinguTabPage::SvxLinguTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
827 : SfxTabPage(pPage, pController, "cui/ui/optlingupage.ui", "OptLinguPage", &rSet)
828 , sCapitalWords (CuiResId(RID_SVXSTR_CAPITAL_WORDS))
829 , sWordsWithDigits(CuiResId(RID_SVXSTR_WORDS_WITH_DIGITS))
830 , sSpellSpecial (CuiResId(RID_SVXSTR_SPELL_SPECIAL))
831 , sSpellAuto (CuiResId(RID_SVXSTR_SPELL_AUTO))
832 , sGrammarAuto (CuiResId(RID_SVXSTR_GRAMMAR_AUTO))
833 , sNumMinWordlen (CuiResId(RID_SVXSTR_NUM_MIN_WORDLEN))
834 , sNumPreBreak (CuiResId(RID_SVXSTR_NUM_PRE_BREAK))
835 , sNumPostBreak (CuiResId(RID_SVXSTR_NUM_POST_BREAK))
836 , sHyphAuto (CuiResId(RID_SVXSTR_HYPH_AUTO))
837 , sHyphSpecial (CuiResId(RID_SVXSTR_HYPH_SPECIAL))
838 , nUPN_HYPH_MIN_WORD_LENGTH(-1)
839 , nUPN_HYPH_MIN_LEADING(-1)
840 , nUPN_HYPH_MIN_TRAILING(-1)
841 , m_nDlbClickEventId(nullptr)
842 , m_xLinguModulesFT(m_xBuilder->weld_label("lingumodulesft"))
843 , m_xLinguModulesCLB(m_xBuilder->weld_tree_view("lingumodules"))
844 , m_xLinguModulesEditPB(m_xBuilder->weld_button("lingumodulesedit"))
845 , m_xLinguDicsFT(m_xBuilder->weld_label("lingudictsft"))
846 , m_xLinguDicsCLB(m_xBuilder->weld_tree_view("lingudicts"))
847 , m_xLinguDicsNewPB(m_xBuilder->weld_button("lingudictsnew"))
848 , m_xLinguDicsEditPB(m_xBuilder->weld_button("lingudictsedit"))
849 , m_xLinguDicsDelPB(m_xBuilder->weld_button("lingudictsdelete"))
850 , m_xLinguOptionsCLB(m_xBuilder->weld_tree_view("linguoptions"))
851 , m_xLinguOptionsEditPB(m_xBuilder->weld_button("linguoptionsedit"))
852 , m_xMoreDictsLink(m_xBuilder->weld_link_button("moredictslink"))
853 {
854 m_xLinguModulesCLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
855 m_xLinguDicsCLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
856 m_xLinguOptionsCLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
857
858 m_xLinguModulesCLB->connect_changed( LINK( this, SvxLinguTabPage, SelectHdl_Impl ));
859 m_xLinguModulesCLB->connect_row_activated(LINK(this, SvxLinguTabPage, BoxDoubleClickHdl_Impl));
860 m_xLinguModulesCLB->connect_toggled(LINK(this, SvxLinguTabPage, ModulesBoxCheckButtonHdl_Impl));
861
862 m_xLinguModulesEditPB->connect_clicked( LINK( this, SvxLinguTabPage, ClickHdl_Impl ));
863 m_xLinguOptionsEditPB->connect_clicked( LINK( this, SvxLinguTabPage, ClickHdl_Impl ));
864
865 m_xLinguDicsCLB->connect_changed( LINK( this, SvxLinguTabPage, SelectHdl_Impl ));
866 m_xLinguDicsCLB->connect_toggled(LINK(this, SvxLinguTabPage, DicsBoxCheckButtonHdl_Impl));
867
868 m_xLinguDicsNewPB->connect_clicked( LINK( this, SvxLinguTabPage, ClickHdl_Impl ));
869 m_xLinguDicsEditPB->connect_clicked( LINK( this, SvxLinguTabPage, ClickHdl_Impl ));
870 m_xLinguDicsDelPB->connect_clicked( LINK( this, SvxLinguTabPage, ClickHdl_Impl ));
871
872 m_xLinguOptionsCLB->connect_changed( LINK( this, SvxLinguTabPage, SelectHdl_Impl ));
873 m_xLinguOptionsCLB->connect_row_activated(LINK(this, SvxLinguTabPage, BoxDoubleClickHdl_Impl));
874
875 m_xMoreDictsLink->connect_activate_link(LINK(this, SvxLinguTabPage, OnLinkClick));
876 if (officecfg::Office::Security::Hyperlinks::Open::get() == SvtExtendedSecurityOptions::OPEN_NEVER)
877 m_xMoreDictsLink->hide();
878
879 xProp = LinguMgr::GetLinguPropertySet();
880 xDicList.set( LinguMgr::GetDictionaryList() );
881 if (xDicList.is())
882 {
883 // keep references to all **currently** available dictionaries,
884 // since the diclist may get changed meanwhile (e.g. through the API).
885 // We want the dialog to operate on the same set of dictionaries it
886 // was started with.
887 // Also we have to take care to not lose the last reference when
888 // someone else removes a dictionary from the list.
889 // removed dics will be replaced by NULL new entries be added to the end
890 // Thus we may use indices as consistent references.
891 aDics = xDicList->getDictionaries();
892
893 UpdateDicBox_Impl();
894 }
895 else
896 {
897 m_xLinguDicsFT->set_sensitive(false);
898 m_xLinguDicsCLB->set_sensitive(false);
899 m_xLinguDicsNewPB->set_sensitive(false);
900 m_xLinguDicsEditPB->set_sensitive(false);
901 m_xLinguDicsDelPB->set_sensitive(false);
902 }
903 }
904
~SvxLinguTabPage()905 SvxLinguTabPage::~SvxLinguTabPage()
906 {
907 if (m_nDlbClickEventId)
908 {
909 Application::RemoveUserEvent(m_nDlbClickEventId);
910 m_nDlbClickEventId = nullptr;
911 }
912 pLinguData.reset();
913 }
914
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * rAttrSet)915 std::unique_ptr<SfxTabPage> SvxLinguTabPage::Create( weld::Container* pPage, weld::DialogController* pController,
916 const SfxItemSet* rAttrSet )
917 {
918 return std::make_unique<SvxLinguTabPage>( pPage, pController, *rAttrSet );
919 }
920
FillItemSet(SfxItemSet * rCoreSet)921 bool SvxLinguTabPage::FillItemSet( SfxItemSet* rCoreSet )
922 {
923 bool bModified = true; // !!!!
924
925 // if not HideGroups was called with GROUP_MODULES...
926 if (m_xLinguModulesCLB->get_visible())
927 {
928 DBG_ASSERT( pLinguData, "pLinguData not yet initialized" );
929 if (!pLinguData)
930 pLinguData.reset( new SvxLinguData_Impl );
931
932 // update spellchecker configuration entries
933 const LangImplNameTable *pTable = &pLinguData->GetSpellTable();
934 for (auto const& elem : *pTable)
935 {
936 LanguageType nLang = elem.first;
937 const Sequence< OUString > aImplNames(elem.second);
938 uno::Reference< XLinguServiceManager2 > xMgr( pLinguData->GetManager() );
939 Locale aLocale( LanguageTag::convertToLocale(nLang) );
940 if (xMgr.is())
941 xMgr->setConfiguredServices( cSpell, aLocale, aImplNames );
942 }
943
944 // update grammar checker configuration entries
945 pTable = &pLinguData->GetGrammarTable();
946 for (auto const& elem : *pTable)
947 {
948 LanguageType nLang = elem.first;
949 const Sequence< OUString > aImplNames(elem.second);
950 uno::Reference< XLinguServiceManager2 > xMgr( pLinguData->GetManager() );
951 Locale aLocale( LanguageTag::convertToLocale(nLang) );
952 if (xMgr.is())
953 xMgr->setConfiguredServices( cGrammar, aLocale, aImplNames );
954 }
955
956 // update hyphenator configuration entries
957 pTable = &pLinguData->GetHyphTable();
958 for (auto const& elem : *pTable)
959 {
960 LanguageType nLang = elem.first;
961 const Sequence< OUString > aImplNames(elem.second);
962 uno::Reference< XLinguServiceManager2 > xMgr( pLinguData->GetManager() );
963 Locale aLocale( LanguageTag::convertToLocale(nLang) );
964 if (xMgr.is())
965 xMgr->setConfiguredServices( cHyph, aLocale, aImplNames );
966 }
967
968 // update thesaurus configuration entries
969 pTable = &pLinguData->GetThesTable();
970 for (auto const& elem : *pTable)
971 {
972 LanguageType nLang = elem.first;
973 const Sequence< OUString > aImplNames(elem.second);
974 uno::Reference< XLinguServiceManager2 > xMgr( pLinguData->GetManager() );
975 Locale aLocale( LanguageTag::convertToLocale(nLang) );
976 if (xMgr.is())
977 xMgr->setConfiguredServices( cThes, aLocale, aImplNames );
978 }
979 }
980
981
982 // activate dictionaries according to checkbox state
983
984 Sequence< OUString > aActiveDics;
985 sal_Int32 nActiveDics = 0;
986 int nEntries = m_xLinguDicsCLB->n_children();
987 for (int i = 0; i < nEntries; ++i)
988 {
989 sal_Int32 nDics = aDics.getLength();
990
991 aActiveDics.realloc( nDics );
992 OUString *pActiveDic = aActiveDics.getArray();
993
994 DicUserData aData(m_xLinguDicsCLB->get_id(i).toUInt32());
995 if (aData.GetEntryId() < nDics)
996 {
997 bool bChecked = m_xLinguDicsCLB->get_toggle(i) == TRISTATE_TRUE;
998 uno::Reference< XDictionary > xDic( aDics.getConstArray()[ i ] );
999 if (xDic.is())
1000 {
1001 if (LinguMgr::GetIgnoreAllList() == xDic)
1002 bChecked = true;
1003 xDic->setActive( bChecked );
1004
1005 if (bChecked)
1006 {
1007 OUString aDicName( xDic->getName() );
1008 pActiveDic[ nActiveDics++ ] = aDicName;
1009 }
1010 }
1011 }
1012 }
1013
1014 aActiveDics.realloc( nActiveDics );
1015 Any aTmp;
1016 aTmp <<= aActiveDics;
1017 SvtLinguConfig aLngCfg;
1018 aLngCfg.SetProperty( UPH_ACTIVE_DICTIONARIES, aTmp );
1019
1020
1021 nEntries = m_xLinguOptionsCLB->n_children();
1022 for (int j = 0; j < nEntries; ++j)
1023 {
1024 OptionsUserData aData(m_xLinguOptionsCLB->get_id(j).toUInt32());
1025 OUString aPropName( lcl_GetPropertyName( static_cast<EID_OPTIONS>(aData.GetEntryId()) ) );
1026
1027 Any aAny;
1028 if (aData.IsCheckable())
1029 {
1030 bool bChecked = m_xLinguOptionsCLB->get_toggle(j) == TRISTATE_TRUE;
1031 aAny <<= bChecked;
1032 }
1033 else if (aData.HasNumericValue())
1034 {
1035 sal_Int16 nVal = aData.GetNumericValue();
1036 aAny <<= nVal;
1037 }
1038
1039 if (xProp.is())
1040 xProp->setPropertyValue( aPropName, aAny );
1041 aLngCfg.SetProperty( aPropName, aAny );
1042 }
1043
1044 OptionsUserData aPreBreakData(m_xLinguOptionsCLB->get_id(EID_NUM_PRE_BREAK).toUInt32());
1045 OptionsUserData aPostBreakData(m_xLinguOptionsCLB->get_id(EID_NUM_POST_BREAK).toUInt32());
1046 if ( aPreBreakData.IsModified() || aPostBreakData.IsModified() )
1047 {
1048 SfxHyphenRegionItem aHyp( GetWhich( SID_ATTR_HYPHENREGION ) );
1049 aHyp.GetMinLead() = static_cast<sal_uInt8>(aPreBreakData.GetNumericValue());
1050 aHyp.GetMinTrail() = static_cast<sal_uInt8>(aPostBreakData.GetNumericValue());
1051 rCoreSet->Put( aHyp );
1052 }
1053
1054 // automatic spell checking
1055 bool bNewAutoCheck = m_xLinguOptionsCLB->get_toggle(EID_SPELL_AUTO) == TRISTATE_TRUE;
1056 const SfxPoolItem* pOld = GetOldItem( *rCoreSet, SID_AUTOSPELL_CHECK );
1057 if ( !pOld || static_cast<const SfxBoolItem*>(pOld)->GetValue() != bNewAutoCheck )
1058 {
1059 rCoreSet->Put( SfxBoolItem( GetWhich( SID_AUTOSPELL_CHECK ),
1060 bNewAutoCheck ) );
1061 bModified = true;
1062 }
1063
1064 return bModified;
1065 }
1066
GetDicUserData(const uno::Reference<XDictionary> & rxDic,sal_uInt16 nIdx)1067 sal_uInt32 SvxLinguTabPage::GetDicUserData( const uno::Reference< XDictionary > &rxDic, sal_uInt16 nIdx )
1068 {
1069 sal_uInt32 nRes = 0;
1070 DBG_ASSERT( rxDic.is(), "dictionary not supplied" );
1071 if (rxDic.is())
1072 {
1073 uno::Reference< frame::XStorable > xStor( rxDic, UNO_QUERY );
1074
1075 bool bChecked = rxDic->isActive();
1076 bool bEditable = !xStor.is() || !xStor->isReadonly();
1077 bool bDeletable = bEditable;
1078
1079 nRes = DicUserData( nIdx,
1080 bChecked, bEditable, bDeletable ).GetUserData();
1081 }
1082 return nRes;
1083 }
1084
1085
AddDicBoxEntry(const uno::Reference<XDictionary> & rxDic,sal_uInt16 nIdx)1086 void SvxLinguTabPage::AddDicBoxEntry(
1087 const uno::Reference< XDictionary > &rxDic,
1088 sal_uInt16 nIdx )
1089 {
1090 m_xLinguDicsCLB->freeze();
1091
1092 OUString aTxt( ::GetDicInfoStr( rxDic->getName(),
1093 LanguageTag( rxDic->getLocale() ).getLanguageType(),
1094 DictionaryType_NEGATIVE == rxDic->getDictionaryType() ) );
1095 m_xLinguDicsCLB->append(); // append at end
1096 int nEntry = m_xLinguDicsCLB->n_children() - 1;
1097 DicUserData aData( GetDicUserData( rxDic, nIdx ) );
1098 m_xLinguDicsCLB->set_id(nEntry, OUString::number(aData.GetUserData()));
1099 m_xLinguDicsCLB->set_toggle(nEntry, aData.IsChecked() ? TRISTATE_TRUE : TRISTATE_FALSE);
1100 m_xLinguDicsCLB->set_text(nEntry, aTxt, 0); // append at end
1101
1102 m_xLinguDicsCLB->thaw();
1103 }
1104
UpdateDicBox_Impl()1105 void SvxLinguTabPage::UpdateDicBox_Impl()
1106 {
1107 m_xLinguDicsCLB->freeze();
1108 m_xLinguDicsCLB->clear();
1109
1110 sal_Int32 nDics = aDics.getLength();
1111 const uno::Reference< XDictionary > *pDic = aDics.getConstArray();
1112 for (sal_Int32 i = 0; i < nDics; ++i)
1113 {
1114 const uno::Reference< XDictionary > &rDic = pDic[i];
1115 if (rDic.is())
1116 AddDicBoxEntry( rDic, static_cast<sal_uInt16>(i) );
1117 }
1118
1119 m_xLinguDicsCLB->thaw();
1120 if (m_xLinguDicsCLB->n_children())
1121 {
1122 m_xLinguDicsCLB->select(0);
1123 SelectHdl_Impl(*m_xLinguDicsCLB);
1124 }
1125 }
1126
UpdateModulesBox_Impl()1127 void SvxLinguTabPage::UpdateModulesBox_Impl()
1128 {
1129 if (!pLinguData)
1130 return;
1131
1132 const ServiceInfoArr &rAllDispSrvcArr = pLinguData->GetDisplayServiceArray();
1133 const sal_uInt32 nDispSrvcCount = pLinguData->GetDisplayServiceCount();
1134
1135 m_xLinguModulesCLB->clear();
1136
1137 for (sal_uInt32 i = 0; i < nDispSrvcCount; ++i)
1138 {
1139 const ServiceInfo_Impl &rInfo = rAllDispSrvcArr[i];
1140 m_xLinguModulesCLB->append();
1141 m_xLinguModulesCLB->set_id(i, OUString::number(reinterpret_cast<sal_Int64>(&rInfo)));
1142 m_xLinguModulesCLB->set_toggle(i, rInfo.bConfigured ? TRISTATE_TRUE : TRISTATE_FALSE);
1143 m_xLinguModulesCLB->set_text(i, rInfo.sDisplayName, 0);
1144 }
1145 if (nDispSrvcCount)
1146 {
1147 m_xLinguModulesCLB->select(0);
1148 SelectHdl_Impl(*m_xLinguModulesCLB);
1149 }
1150 m_xLinguModulesEditPB->set_sensitive( nDispSrvcCount > 0 );
1151 }
1152
Reset(const SfxItemSet * rSet)1153 void SvxLinguTabPage::Reset( const SfxItemSet* rSet )
1154 {
1155 // if not HideGroups was called with GROUP_MODULES...
1156 if (m_xLinguModulesCLB->get_visible())
1157 {
1158 if (!pLinguData)
1159 pLinguData.reset( new SvxLinguData_Impl );
1160 UpdateModulesBox_Impl();
1161 }
1162
1163
1164 // get data from configuration
1165 SvtLinguConfig aLngCfg;
1166
1167 m_xLinguOptionsCLB->freeze();
1168 m_xLinguOptionsCLB->clear();
1169
1170 sal_Int16 nVal = 0;
1171 bool bVal = false;
1172 sal_uInt32 nUserData = 0;
1173
1174 m_xLinguOptionsCLB->append();
1175 int nEntry = 0;
1176
1177 aLngCfg.GetProperty( UPN_IS_SPELL_AUTO ) >>= bVal;
1178 const SfxPoolItem* pItem = GetItem( *rSet, SID_AUTOSPELL_CHECK );
1179 if (pItem)
1180 bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
1181 nUserData = OptionsUserData( EID_SPELL_AUTO, false, 0, true, bVal).GetUserData();
1182 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1183 m_xLinguOptionsCLB->set_text(nEntry, sSpellAuto, 0);
1184 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1185
1186 m_xLinguOptionsCLB->append();
1187 ++nEntry;
1188
1189 aLngCfg.GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bVal;
1190 nUserData = OptionsUserData( EID_GRAMMAR_AUTO, false, 0, true, bVal).GetUserData();
1191 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1192 m_xLinguOptionsCLB->set_text(nEntry, sGrammarAuto, 0);
1193 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1194
1195 m_xLinguOptionsCLB->append();
1196 ++nEntry;
1197
1198 aLngCfg.GetProperty( UPN_IS_SPELL_UPPER_CASE ) >>= bVal;
1199 nUserData = OptionsUserData( EID_CAPITAL_WORDS, false, 0, true, bVal).GetUserData();
1200 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1201 m_xLinguOptionsCLB->set_text(nEntry, sCapitalWords, 0);
1202 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1203
1204 m_xLinguOptionsCLB->append();
1205 ++nEntry;
1206
1207 aLngCfg.GetProperty( UPN_IS_SPELL_WITH_DIGITS ) >>= bVal;
1208 nUserData = OptionsUserData( EID_WORDS_WITH_DIGITS, false, 0, true, bVal).GetUserData();
1209 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1210 m_xLinguOptionsCLB->set_text(nEntry, sWordsWithDigits, 0);
1211 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1212
1213 m_xLinguOptionsCLB->append();
1214 ++nEntry;
1215
1216 aLngCfg.GetProperty( UPN_IS_SPELL_SPECIAL ) >>= bVal;
1217 nUserData = OptionsUserData( EID_SPELL_SPECIAL, false, 0, true, bVal).GetUserData();
1218 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1219 m_xLinguOptionsCLB->set_text(nEntry, sSpellSpecial, 0);
1220 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1221
1222 m_xLinguOptionsCLB->append();
1223 ++nEntry;
1224
1225 aLngCfg.GetProperty( UPN_HYPH_MIN_WORD_LENGTH ) >>= nVal;
1226 nUserData = OptionsUserData( EID_NUM_MIN_WORDLEN, true, static_cast<sal_uInt16>(nVal), false, false).GetUserData();
1227 m_xLinguOptionsCLB->set_text(nEntry, sNumMinWordlen + " " + OUString::number(nVal), 0);
1228 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1229 nUPN_HYPH_MIN_WORD_LENGTH = nEntry;
1230
1231 const SfxHyphenRegionItem *pHyp = nullptr;
1232 sal_uInt16 nWhich = GetWhich( SID_ATTR_HYPHENREGION );
1233 if ( rSet->GetItemState( nWhich, false ) == SfxItemState::SET )
1234 pHyp = &static_cast<const SfxHyphenRegionItem &>( rSet->Get( nWhich ) );
1235
1236 m_xLinguOptionsCLB->append();
1237 ++nEntry;
1238
1239 aLngCfg.GetProperty( UPN_HYPH_MIN_LEADING ) >>= nVal;
1240 if (pHyp)
1241 nVal = static_cast<sal_Int16>(pHyp->GetMinLead());
1242 nUserData = OptionsUserData( EID_NUM_PRE_BREAK, true, static_cast<sal_uInt16>(nVal), false, false).GetUserData();
1243 m_xLinguOptionsCLB->set_text(nEntry, sNumPreBreak + " " + OUString::number(nVal), 0);
1244 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1245 nUPN_HYPH_MIN_LEADING = nEntry;
1246
1247 m_xLinguOptionsCLB->append();
1248 ++nEntry;
1249
1250 aLngCfg.GetProperty( UPN_HYPH_MIN_TRAILING ) >>= nVal;
1251 if (pHyp)
1252 nVal = static_cast<sal_Int16>(pHyp->GetMinTrail());
1253 nUserData = OptionsUserData( EID_NUM_POST_BREAK, true, static_cast<sal_uInt16>(nVal), false, false).GetUserData();
1254 m_xLinguOptionsCLB->set_text(nEntry, sNumPostBreak + " " + OUString::number(nVal), 0);
1255 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1256 nUPN_HYPH_MIN_TRAILING = nEntry;
1257
1258 m_xLinguOptionsCLB->append();
1259 ++nEntry;
1260
1261 aLngCfg.GetProperty( UPN_IS_HYPH_AUTO ) >>= bVal;
1262 nUserData = OptionsUserData( EID_HYPH_AUTO, false, 0, true, bVal).GetUserData();
1263 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1264 m_xLinguOptionsCLB->set_text(nEntry, sHyphAuto, 0);
1265 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1266
1267 m_xLinguOptionsCLB->append();
1268 ++nEntry;
1269
1270 aLngCfg.GetProperty( UPN_IS_HYPH_SPECIAL ) >>= bVal;
1271 nUserData = OptionsUserData( EID_HYPH_SPECIAL, false, 0, true, bVal).GetUserData();
1272 m_xLinguOptionsCLB->set_toggle(nEntry, bVal ? TRISTATE_TRUE : TRISTATE_FALSE);
1273 m_xLinguOptionsCLB->set_text(nEntry, sHyphSpecial, 0);
1274 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(nUserData));
1275
1276 m_xLinguOptionsCLB->thaw();
1277
1278 m_xLinguOptionsCLB->select(0);
1279 SelectHdl_Impl(*m_xLinguOptionsCLB);
1280
1281 m_xLinguModulesCLB->set_size_request(m_xLinguModulesCLB->get_preferred_size().Width(),
1282 m_xLinguModulesCLB->get_height_rows(3));
1283 m_xLinguDicsCLB->set_size_request(m_xLinguDicsCLB->get_preferred_size().Width(),
1284 m_xLinguDicsCLB->get_height_rows(5));
1285 m_xLinguOptionsCLB->set_size_request(m_xLinguOptionsCLB->get_preferred_size().Width(),
1286 m_xLinguOptionsCLB->get_height_rows(5));
1287 }
1288
IMPL_LINK(SvxLinguTabPage,BoxDoubleClickHdl_Impl,weld::TreeView &,rBox,bool)1289 IMPL_LINK(SvxLinguTabPage, BoxDoubleClickHdl_Impl, weld::TreeView&, rBox, bool)
1290 {
1291 if (&rBox == m_xLinguModulesCLB.get() && !m_nDlbClickEventId)
1292 {
1293 //! in order to avoid a bug causing a GPF when double clicking
1294 //! on a module entry and exiting the "Edit Modules" dialog
1295 //! after that.
1296 m_nDlbClickEventId = Application::PostUserEvent(LINK(this, SvxLinguTabPage, PostDblClickHdl_Impl));
1297 }
1298 else if (&rBox == m_xLinguOptionsCLB.get())
1299 {
1300 ClickHdl_Impl(*m_xLinguOptionsEditPB);
1301 }
1302 return true;
1303 }
1304
IMPL_LINK_NOARG(SvxLinguTabPage,PostDblClickHdl_Impl,void *,void)1305 IMPL_LINK_NOARG(SvxLinguTabPage, PostDblClickHdl_Impl, void*, void)
1306 {
1307 m_nDlbClickEventId = nullptr;
1308 ClickHdl_Impl(*m_xLinguModulesEditPB);
1309 }
1310
IMPL_LINK(SvxLinguTabPage,ModulesBoxCheckButtonHdl_Impl,const weld::TreeView::iter_col &,rRowCol,void)1311 IMPL_LINK(SvxLinguTabPage, ModulesBoxCheckButtonHdl_Impl, const weld::TreeView::iter_col&, rRowCol, void)
1312 {
1313 if (!pLinguData)
1314 return;
1315 pLinguData->Reconfigure(m_xLinguModulesCLB->get_text(rRowCol.first),
1316 m_xLinguModulesCLB->get_toggle(rRowCol.first) == TRISTATE_TRUE);
1317 }
1318
IMPL_LINK(SvxLinguTabPage,DicsBoxCheckButtonHdl_Impl,const weld::TreeView::iter_col &,rRowCol,void)1319 IMPL_LINK(SvxLinguTabPage, DicsBoxCheckButtonHdl_Impl, const weld::TreeView::iter_col&, rRowCol, void)
1320 {
1321 const uno::Reference<XDictionary> &rDic = aDics.getConstArray()[m_xLinguDicsCLB->get_iter_index_in_parent(rRowCol.first)];
1322 if (LinguMgr::GetIgnoreAllList() == rDic)
1323 m_xLinguDicsCLB->set_toggle(rRowCol.first, TRISTATE_TRUE);
1324 }
1325
IMPL_LINK(SvxLinguTabPage,ClickHdl_Impl,weld::Button &,rBtn,void)1326 IMPL_LINK(SvxLinguTabPage, ClickHdl_Impl, weld::Button&, rBtn, void)
1327 {
1328 if (m_xLinguModulesEditPB.get() == &rBtn)
1329 {
1330 if (!pLinguData)
1331 pLinguData.reset( new SvxLinguData_Impl );
1332
1333 SvxLinguData_Impl aOldLinguData( *pLinguData );
1334 SvxEditModulesDlg aDlg(GetFrameWeld(), *pLinguData);
1335 if (aDlg.run() != RET_OK)
1336 *pLinguData = aOldLinguData;
1337
1338 // evaluate new status of 'bConfigured' flag
1339 sal_uInt32 nLen = pLinguData->GetDisplayServiceCount();
1340 for (sal_uInt32 i = 0; i < nLen; ++i)
1341 pLinguData->GetDisplayServiceArray()[i].bConfigured = false;
1342 const Locale* pAllLocales = pLinguData->GetAllSupportedLocales().getConstArray();
1343 sal_Int32 nLocales = pLinguData->GetAllSupportedLocales().getLength();
1344 for (sal_Int32 k = 0; k < nLocales; ++k)
1345 {
1346 LanguageType nLang = LanguageTag::convertToLanguageType( pAllLocales[k] );
1347 if (pLinguData->GetSpellTable().count( nLang ))
1348 pLinguData->SetChecked( pLinguData->GetSpellTable()[ nLang ] );
1349 if (pLinguData->GetGrammarTable().count( nLang ))
1350 pLinguData->SetChecked( pLinguData->GetGrammarTable()[ nLang ] );
1351 if (pLinguData->GetHyphTable().count( nLang ))
1352 pLinguData->SetChecked( pLinguData->GetHyphTable()[ nLang ] );
1353 if (pLinguData->GetThesTable().count( nLang ))
1354 pLinguData->SetChecked( pLinguData->GetThesTable()[ nLang ] );
1355 }
1356
1357 // show new status of modules
1358 UpdateModulesBox_Impl();
1359 }
1360 else if (m_xLinguDicsNewPB.get() == &rBtn)
1361 {
1362 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1363 ScopedVclPtr<AbstractSvxNewDictionaryDialog> aDlg(pFact->CreateSvxNewDictionaryDialog(GetFrameWeld()));
1364 uno::Reference< XDictionary > xNewDic;
1365 if ( aDlg->Execute() == RET_OK )
1366 xNewDic = aDlg->GetNewDictionary();
1367 if ( xNewDic.is() )
1368 {
1369 // add new dics to the end
1370 sal_Int32 nLen = aDics.getLength();
1371 aDics.realloc( nLen + 1 );
1372
1373 aDics.getArray()[ nLen ] = xNewDic;
1374
1375 AddDicBoxEntry( xNewDic, static_cast<sal_uInt16>(nLen) );
1376 }
1377 }
1378 else if (m_xLinguDicsEditPB.get() == &rBtn)
1379 {
1380 int nEntry = m_xLinguDicsCLB->get_selected_index();
1381 if (nEntry != -1)
1382 {
1383 DicUserData aData(m_xLinguDicsCLB->get_id(nEntry).toUInt32());
1384 sal_uInt16 nDicPos = aData.GetEntryId();
1385 sal_Int32 nDics = aDics.getLength();
1386 if (nDicPos < nDics)
1387 {
1388 uno::Reference< XDictionary > xDic = aDics.getConstArray()[ nDicPos ];
1389 if (xDic.is())
1390 {
1391 SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
1392 ScopedVclPtr<VclAbstractDialog> aDlg(pFact->CreateSvxEditDictionaryDialog(GetFrameWeld(), xDic->getName()));
1393 aDlg->Execute();
1394 }
1395 }
1396 }
1397 }
1398 else if (m_xLinguDicsDelPB.get() == &rBtn)
1399 {
1400 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetFrameWeld(), "cui/ui/querydeletedictionarydialog.ui"));
1401 std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("QueryDeleteDictionaryDialog"));
1402 if (RET_NO == xQuery->run())
1403 return;
1404
1405 int nEntry = m_xLinguDicsCLB->get_selected_index();
1406 if (nEntry != -1)
1407 {
1408 DicUserData aData(m_xLinguDicsCLB->get_id(nEntry).toUInt32());
1409 sal_uInt16 nDicPos = aData.GetEntryId();
1410 sal_Int32 nDics = aDics.getLength();
1411 if (nDicPos < nDics)
1412 {
1413 uno::Reference< XDictionary > xDic = aDics.getConstArray()[ nDicPos ];
1414 if (xDic.is())
1415 {
1416 if (LinguMgr::GetIgnoreAllList() == xDic)
1417 xDic->clear();
1418 else
1419 {
1420 if (xDicList.is())
1421 xDicList->removeDictionary( xDic );
1422
1423 uno::Reference< frame::XStorable > xStor( xDic, UNO_QUERY );
1424 if ( xStor->hasLocation() && !xStor->isReadonly() )
1425 {
1426 OUString sURL = xStor->getLocation();
1427 INetURLObject aObj(sURL);
1428 DBG_ASSERT( aObj.GetProtocol() == INetProtocol::File,
1429 "non-file URLs cannot be deleted" );
1430 if ( aObj.GetProtocol() == INetProtocol::File )
1431 {
1432 KillFile_Impl( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
1433 }
1434 }
1435
1436 aDics.getArray()[ nDicPos ] = nullptr;
1437
1438 // remove entry from checklistbox
1439 int nCnt = m_xLinguDicsCLB->n_children();
1440 for (int i = 0; i < nCnt; ++i)
1441 {
1442 DicUserData aDicData(m_xLinguDicsCLB->get_id(i).toUInt32());
1443 if (aDicData.GetEntryId() == nDicPos )
1444 {
1445 m_xLinguDicsCLB->remove(i);
1446 break;
1447 }
1448 }
1449 DBG_ASSERT( nCnt > m_xLinguDicsCLB->n_children(),
1450 "remove failed ?");
1451 }
1452 }
1453 }
1454 }
1455 }
1456 else if (m_xLinguOptionsEditPB.get() == &rBtn)
1457 {
1458 int nEntry = m_xLinguOptionsCLB->get_selected_index();
1459 DBG_ASSERT(nEntry != -1, "no entry selected");
1460 if (nEntry != -1)
1461 {
1462 OptionsUserData aData(m_xLinguOptionsCLB->get_id(nEntry).toUInt32());
1463 if (aData.HasNumericValue())
1464 {
1465 sal_uInt16 nRID = aData.GetEntryId();
1466 OptionsBreakSet aDlg(GetFrameWeld(), nRID);
1467 aDlg.GetNumericFld().set_value(aData.GetNumericValue());
1468 if (RET_OK == aDlg.run())
1469 {
1470 int nVal = aDlg.GetNumericFld().get_value();
1471 if (-1 != nVal && aData.GetNumericValue() != nVal)
1472 {
1473 aData.SetNumericValue( static_cast<sal_uInt8>(nVal) ); //! sets IsModified !
1474 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(aData.GetUserData()));
1475 if (nEntry == nUPN_HYPH_MIN_WORD_LENGTH)
1476 m_xLinguOptionsCLB->set_text(nEntry, sNumMinWordlen + " " + OUString::number(nVal), 0);
1477 else if (nEntry == nUPN_HYPH_MIN_LEADING)
1478 m_xLinguOptionsCLB->set_text(nEntry, sNumPreBreak + " " + OUString::number(nVal), 0);
1479 else if (nEntry == nUPN_HYPH_MIN_TRAILING)
1480 m_xLinguOptionsCLB->set_text(nEntry, sNumPostBreak + " " + OUString::number(nVal), 0);
1481 m_xLinguOptionsCLB->set_id(nEntry, OUString::number(aData.GetUserData()));
1482 }
1483 }
1484 }
1485 }
1486 }
1487 else
1488 {
1489 OSL_FAIL( "rBtn unexpected value" );
1490 }
1491 }
1492
IMPL_LINK(SvxLinguTabPage,SelectHdl_Impl,weld::TreeView &,rBox,void)1493 IMPL_LINK(SvxLinguTabPage, SelectHdl_Impl, weld::TreeView&, rBox, void)
1494 {
1495 if (m_xLinguModulesCLB.get() == &rBox)
1496 {
1497 }
1498 else if (m_xLinguDicsCLB.get() == &rBox)
1499 {
1500 int nEntry = rBox.get_selected_index();
1501 if (nEntry != -1)
1502 {
1503 DicUserData aData(rBox.get_id(nEntry).toUInt32());
1504
1505 // always allow to edit (i.e. at least view the content of the dictionary)
1506 m_xLinguDicsEditPB->set_sensitive( true );
1507 m_xLinguDicsDelPB->set_sensitive( aData.IsDeletable() );
1508 }
1509 }
1510 else if (m_xLinguOptionsCLB.get() == &rBox)
1511 {
1512 int nEntry = rBox.get_selected_index();
1513 if (nEntry != -1)
1514 {
1515 OptionsUserData aData(rBox.get_id(nEntry).toUInt32());
1516 m_xLinguOptionsEditPB->set_sensitive( aData.HasNumericValue() );
1517 }
1518 }
1519 else
1520 {
1521 OSL_FAIL( "rBox unexpected value" );
1522 }
1523 }
1524
HideGroups(sal_uInt16 nGrp)1525 void SvxLinguTabPage::HideGroups( sal_uInt16 nGrp )
1526 {
1527 if ( 0 != ( GROUP_MODULES & nGrp ) )
1528 {
1529 m_xLinguModulesFT->hide();
1530 m_xLinguModulesCLB->hide();
1531 m_xLinguModulesEditPB->hide();
1532
1533 if (officecfg::Office::Security::Hyperlinks::Open::get() != SvtExtendedSecurityOptions::OPEN_NEVER)
1534 {
1535 m_xMoreDictsLink->show();
1536 }
1537 }
1538 }
1539
IMPL_STATIC_LINK_NOARG(SvxLinguTabPage,OnLinkClick,weld::LinkButton &,bool)1540 IMPL_STATIC_LINK_NOARG(SvxLinguTabPage, OnLinkClick, weld::LinkButton&, bool)
1541 {
1542 comphelper::dispatchCommand(".uno:MoreDictionaries", {});
1543 return true;
1544 }
1545
SvxEditModulesDlg(weld::Window * pParent,SvxLinguData_Impl & rData)1546 SvxEditModulesDlg::SvxEditModulesDlg(weld::Window* pParent, SvxLinguData_Impl& rData)
1547 : GenericDialogController(pParent, "cui/ui/editmodulesdialog.ui", "EditModulesDialog")
1548 , sSpell(CuiResId(RID_SVXSTR_SPELL))
1549 , sHyph(CuiResId(RID_SVXSTR_HYPH))
1550 , sThes(CuiResId(RID_SVXSTR_THES))
1551 , sGrammar(CuiResId(RID_SVXSTR_GRAMMAR))
1552 , rLinguData(rData)
1553 , m_xModulesCLB(m_xBuilder->weld_tree_view("lingudicts"))
1554 , m_xPrioUpPB(m_xBuilder->weld_button("up"))
1555 , m_xPrioDownPB(m_xBuilder->weld_button("down"))
1556 , m_xBackPB(m_xBuilder->weld_button("back"))
1557 , m_xMoreDictsLink(m_xBuilder->weld_link_button("moredictslink"))
1558 , m_xClosePB(m_xBuilder->weld_button("close"))
1559 , m_xLanguageLB(new SvxLanguageBox(m_xBuilder->weld_combo_box("language")))
1560 {
1561 m_xModulesCLB->set_size_request(m_xModulesCLB->get_approximate_digit_width() * 40,
1562 m_xModulesCLB->get_height_rows(12));
1563
1564 m_xModulesCLB->enable_toggle_buttons(weld::ColumnToggleType::Check);
1565
1566 pDefaultLinguData.reset( new SvxLinguData_Impl( rLinguData ) );
1567
1568 m_xModulesCLB->connect_changed( LINK( this, SvxEditModulesDlg, SelectHdl_Impl ));
1569 m_xModulesCLB->connect_toggled(LINK(this, SvxEditModulesDlg, BoxCheckButtonHdl_Impl));
1570
1571 m_xClosePB->connect_clicked( LINK( this, SvxEditModulesDlg, ClickHdl_Impl ));
1572 m_xPrioUpPB->connect_clicked( LINK( this, SvxEditModulesDlg, UpDownHdl_Impl ));
1573 m_xPrioDownPB->connect_clicked( LINK( this, SvxEditModulesDlg, UpDownHdl_Impl ));
1574 m_xBackPB->connect_clicked( LINK( this, SvxEditModulesDlg, BackHdl_Impl ));
1575 // in case of not installed language modules
1576 m_xPrioUpPB->set_sensitive( false );
1577 m_xPrioDownPB->set_sensitive( false );
1578
1579 m_xMoreDictsLink->connect_activate_link(LINK(this, SvxEditModulesDlg, OnLinkClick));
1580 if (officecfg::Office::Security::Hyperlinks::Open::get() == SvtExtendedSecurityOptions::OPEN_NEVER)
1581 m_xMoreDictsLink->hide();
1582
1583 // set that we want the checkbox shown if spellchecking is available
1584 m_xLanguageLB->SetLanguageList(SvxLanguageListFlags::EMPTY, false, false, true);
1585
1586 //fill language box
1587 const Sequence< Locale >& rLoc = rLinguData.GetAllSupportedLocales();
1588 for (Locale const & locale : rLoc)
1589 {
1590 LanguageType nLang = LanguageTag::convertToLanguageType( locale );
1591 m_xLanguageLB->InsertLanguage(nLang);
1592 }
1593 LanguageType eSysLang = MsLangId::getSystemLanguage();
1594 m_xLanguageLB->set_active_id( eSysLang );
1595 if (m_xLanguageLB->get_active_id() != eSysLang)
1596 m_xLanguageLB->set_active(0);
1597
1598 m_xLanguageLB->connect_changed( LINK( this, SvxEditModulesDlg, LangSelectListBoxHdl_Impl ));
1599 LangSelectHdl_Impl(m_xLanguageLB.get());
1600 }
1601
~SvxEditModulesDlg()1602 SvxEditModulesDlg::~SvxEditModulesDlg()
1603 {
1604 for (int i = 0, nEntryCount = m_xModulesCLB->n_children(); i < nEntryCount; ++i)
1605 delete reinterpret_cast<ModuleUserData_Impl*>(m_xModulesCLB->get_id(i).toInt64());
1606 }
1607
IMPL_LINK(SvxEditModulesDlg,SelectHdl_Impl,weld::TreeView &,rBox,void)1608 IMPL_LINK( SvxEditModulesDlg, SelectHdl_Impl, weld::TreeView&, rBox, void )
1609 {
1610 int nCurPos = rBox.get_selected_index();
1611 if (nCurPos == -1)
1612 return;
1613
1614 bool bDisableUp = true;
1615 bool bDisableDown = true;
1616 ModuleUserData_Impl* pData = reinterpret_cast<ModuleUserData_Impl*>(rBox.get_id(nCurPos).toInt64());
1617 if (!pData->IsParent() && pData->GetType() != TYPE_HYPH)
1618 {
1619 if (nCurPos < rBox.n_children() - 1)
1620 {
1621 bDisableDown = reinterpret_cast<ModuleUserData_Impl*>(rBox.get_id(nCurPos + 1).toInt64())->IsParent();
1622 }
1623 if (nCurPos > 1)
1624 {
1625 bDisableUp = reinterpret_cast<ModuleUserData_Impl*>(rBox.get_id(nCurPos - 1).toInt64())->IsParent();
1626 }
1627 }
1628 m_xPrioUpPB->set_sensitive(!bDisableUp);
1629 m_xPrioDownPB->set_sensitive(!bDisableDown);
1630 }
1631
IMPL_LINK(SvxEditModulesDlg,BoxCheckButtonHdl_Impl,const weld::TreeView::iter_col &,rRowCol,void)1632 IMPL_LINK( SvxEditModulesDlg, BoxCheckButtonHdl_Impl, const weld::TreeView::iter_col&, rRowCol, void )
1633 {
1634 ModuleUserData_Impl* pData = reinterpret_cast<ModuleUserData_Impl*>(m_xModulesCLB->get_id(rRowCol.first).toInt64());
1635 if (pData->IsParent() || pData->GetType() != TYPE_HYPH)
1636 return;
1637
1638 // make hyphenator checkboxes function as radio-buttons
1639 // (at most one box may be checked)
1640 auto nPos = m_xModulesCLB->get_iter_index_in_parent(rRowCol.first);
1641 for (int i = 0, nEntryCount = m_xModulesCLB->n_children(); i < nEntryCount; ++i)
1642 {
1643 pData = reinterpret_cast<ModuleUserData_Impl*>(m_xModulesCLB->get_id(i).toInt64());
1644 if (!pData->IsParent() && pData->GetType() == TYPE_HYPH && i != nPos)
1645 {
1646 m_xModulesCLB->set_toggle(i, TRISTATE_FALSE);
1647 }
1648 }
1649 }
1650
IMPL_LINK_NOARG(SvxEditModulesDlg,LangSelectListBoxHdl_Impl,weld::ComboBox &,void)1651 IMPL_LINK_NOARG(SvxEditModulesDlg, LangSelectListBoxHdl_Impl, weld::ComboBox&, void)
1652 {
1653 LangSelectHdl_Impl(m_xLanguageLB.get());
1654 }
1655
LangSelectHdl_Impl(const SvxLanguageBox * pBox)1656 void SvxEditModulesDlg::LangSelectHdl_Impl(const SvxLanguageBox* pBox)
1657 {
1658 LanguageType eCurLanguage = m_xLanguageLB->get_active_id();
1659 static Locale aLastLocale;
1660 Locale aCurLocale( LanguageTag::convertToLocale( eCurLanguage));
1661
1662 if (pBox)
1663 {
1664 // save old probably changed settings
1665 // before switching to new language entries
1666
1667 LanguageType nLang = LanguageTag::convertToLanguageType( aLastLocale );
1668
1669 sal_Int32 nStart = 0, nLocalIndex = 0;
1670 Sequence< OUString > aChange;
1671 bool bChanged = false;
1672 for (int i = 0, nEntryCount = m_xModulesCLB->n_children(); i < nEntryCount; ++i)
1673 {
1674 ModuleUserData_Impl* pData = reinterpret_cast<ModuleUserData_Impl*>(m_xModulesCLB->get_id(i).toInt64());
1675 if (pData->IsParent())
1676 {
1677 if (bChanged)
1678 {
1679 LangImplNameTable *pTable = nullptr;
1680 sal_uInt8 nType = pData->GetType();
1681 switch (nType - 1)
1682 {
1683 case TYPE_SPELL : pTable = &rLinguData.GetSpellTable(); break;
1684 case TYPE_GRAMMAR : pTable = &rLinguData.GetGrammarTable(); break;
1685 case TYPE_HYPH : pTable = &rLinguData.GetHyphTable(); break;
1686 case TYPE_THES : pTable = &rLinguData.GetThesTable(); break;
1687 }
1688 if (pTable)
1689 {
1690 aChange.realloc(nStart);
1691 (*pTable)[ nLang ] = aChange;
1692 }
1693 }
1694 nLocalIndex = nStart = 0;
1695 aChange.realloc(nEntryCount);
1696 bChanged = false;
1697 }
1698 else
1699 {
1700 OUString* pChange = aChange.getArray();
1701 pChange[nStart] = pData->GetImplName();
1702 bChanged |= pData->GetIndex() != nLocalIndex ||
1703 static_cast<TriState>(pData->IsChecked()) != m_xModulesCLB->get_toggle(i);
1704 if (m_xModulesCLB->get_toggle(i))
1705 nStart++;
1706 ++nLocalIndex;
1707 }
1708 }
1709 if(bChanged)
1710 {
1711 aChange.realloc(nStart);
1712 rLinguData.GetThesTable()[ nLang ] = aChange;
1713 }
1714 }
1715
1716 for (int i = 0, nEntryCount = m_xModulesCLB->n_children(); i < nEntryCount; ++i)
1717 delete reinterpret_cast<ModuleUserData_Impl*>(m_xModulesCLB->get_id(i).toInt64());
1718 m_xModulesCLB->clear();
1719
1720 // display entries for new selected language
1721
1722 if (LANGUAGE_DONTKNOW != eCurLanguage)
1723 {
1724 sal_Int32 n;
1725 ServiceInfo_Impl* pInfo;
1726
1727 int nRow = 0;
1728 // spellchecker entries
1729
1730 ModuleUserData_Impl* pUserData = new ModuleUserData_Impl(
1731 OUString(), true, false, TYPE_SPELL, 0 );
1732 OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pUserData)));
1733 m_xModulesCLB->append(nullptr);
1734 m_xModulesCLB->set_id(nRow, sId);
1735 m_xModulesCLB->set_text(nRow, sSpell, 0);
1736 m_xModulesCLB->set_text_emphasis(nRow, true, 0);
1737 ++nRow;
1738
1739 Sequence< OUString > aNames( rLinguData.GetSortedImplNames( eCurLanguage, TYPE_SPELL ) );
1740 const OUString *pName = aNames.getConstArray();
1741 sal_Int32 nNames = aNames.getLength();
1742 sal_Int32 nLocalIndex = 0; // index relative to parent
1743 for (n = 0; n < nNames; ++n)
1744 {
1745 OUString aImplName;
1746 bool bIsSuppLang = false;
1747
1748 pInfo = rLinguData.GetInfoByImplName( pName[n] );
1749 if (pInfo)
1750 {
1751 bIsSuppLang = pInfo->xSpell.is() &&
1752 pInfo->xSpell->hasLocale( aCurLocale );
1753 aImplName = pInfo->sSpellImplName;
1754 }
1755 if (!aImplName.isEmpty() && bIsSuppLang)
1756 {
1757 OUString aTxt( pInfo->sDisplayName );
1758
1759 LangImplNameTable &rTable = rLinguData.GetSpellTable();
1760 const bool bHasLang = rTable.count( eCurLanguage );
1761 if (!bHasLang)
1762 {
1763 SAL_INFO( "cui.options", "language entry missing" ); // only relevant if all languages found should be supported
1764 }
1765 const bool bCheck = bHasLang && lcl_SeqGetEntryPos( rTable[ eCurLanguage ], aImplName ) >= 0;
1766 pUserData = new ModuleUserData_Impl( aImplName, false,
1767 bCheck, TYPE_SPELL, static_cast<sal_uInt8>(nLocalIndex++) );
1768 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1769
1770 m_xModulesCLB->append(nullptr);
1771 m_xModulesCLB->set_id(nRow, sId);
1772 m_xModulesCLB->set_toggle(nRow, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1773 m_xModulesCLB->set_text(nRow, aTxt, 0);
1774 m_xModulesCLB->set_text_emphasis(nRow, false, 0);
1775 ++nRow;
1776 }
1777 }
1778
1779 // grammar checker entries
1780
1781 pUserData = new ModuleUserData_Impl( OUString(), true, false, TYPE_GRAMMAR, 0 );
1782 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1783 m_xModulesCLB->append(nullptr);
1784 m_xModulesCLB->set_id(nRow, sId);
1785 m_xModulesCLB->set_text(nRow, sGrammar, 0);
1786 m_xModulesCLB->set_text_emphasis(nRow, true, 0);
1787 ++nRow;
1788
1789 aNames = rLinguData.GetSortedImplNames( eCurLanguage, TYPE_GRAMMAR );
1790 pName = aNames.getConstArray();
1791 nNames = aNames.getLength();
1792 nLocalIndex = 0;
1793 for (n = 0; n < nNames; ++n)
1794 {
1795 OUString aImplName;
1796 bool bIsSuppLang = false;
1797
1798 pInfo = rLinguData.GetInfoByImplName( pName[n] );
1799 if (pInfo)
1800 {
1801 bIsSuppLang = pInfo->xGrammar.is() &&
1802 pInfo->xGrammar->hasLocale( aCurLocale );
1803 aImplName = pInfo->sGrammarImplName;
1804 }
1805 if (!aImplName.isEmpty() && bIsSuppLang)
1806 {
1807 OUString aTxt( pInfo->sDisplayName );
1808
1809 LangImplNameTable &rTable = rLinguData.GetGrammarTable();
1810 const bool bHasLang = rTable.count( eCurLanguage );
1811 if (!bHasLang)
1812 {
1813 SAL_INFO( "cui.options", "language entry missing" ); // only relevant if all languages found should be supported
1814 }
1815 const bool bCheck = bHasLang && lcl_SeqGetEntryPos( rTable[ eCurLanguage ], aImplName ) >= 0;
1816 pUserData = new ModuleUserData_Impl( aImplName, false,
1817 bCheck, TYPE_GRAMMAR, static_cast<sal_uInt8>(nLocalIndex++) );
1818
1819 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1820
1821 m_xModulesCLB->append(nullptr);
1822 m_xModulesCLB->set_id(nRow, sId);
1823 m_xModulesCLB->set_toggle(nRow, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1824 m_xModulesCLB->set_text(nRow, aTxt, 0);
1825 m_xModulesCLB->set_text_emphasis(nRow, false, 0);
1826 ++nRow;
1827 }
1828 }
1829
1830 // hyphenator entries
1831
1832 pUserData = new ModuleUserData_Impl( OUString(), true, false, TYPE_HYPH, 0 );
1833 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1834 m_xModulesCLB->append(nullptr);
1835 m_xModulesCLB->set_id(nRow, sId);
1836 m_xModulesCLB->set_text(nRow, sHyph, 0);
1837 m_xModulesCLB->set_text_emphasis(nRow, true, 0);
1838 ++nRow;
1839
1840 aNames = rLinguData.GetSortedImplNames( eCurLanguage, TYPE_HYPH );
1841 pName = aNames.getConstArray();
1842 nNames = aNames.getLength();
1843 nLocalIndex = 0;
1844 for (n = 0; n < nNames; ++n)
1845 {
1846 OUString aImplName;
1847 bool bIsSuppLang = false;
1848
1849 pInfo = rLinguData.GetInfoByImplName( pName[n] );
1850 if (pInfo)
1851 {
1852 bIsSuppLang = pInfo->xHyph.is() &&
1853 pInfo->xHyph->hasLocale( aCurLocale );
1854 aImplName = pInfo->sHyphImplName;
1855 }
1856 if (!aImplName.isEmpty() && bIsSuppLang)
1857 {
1858 OUString aTxt( pInfo->sDisplayName );
1859
1860 LangImplNameTable &rTable = rLinguData.GetHyphTable();
1861 const bool bHasLang = rTable.count( eCurLanguage );
1862 if (!bHasLang)
1863 {
1864 SAL_INFO( "cui.options", "language entry missing" ); // only relevant if all languages found should be supported
1865 }
1866 const bool bCheck = bHasLang && lcl_SeqGetEntryPos( rTable[ eCurLanguage ], aImplName ) >= 0;
1867 pUserData = new ModuleUserData_Impl( aImplName, false,
1868 bCheck, TYPE_HYPH, static_cast<sal_uInt8>(nLocalIndex++) );
1869 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1870
1871 m_xModulesCLB->append(nullptr);
1872 m_xModulesCLB->set_id(nRow, sId);
1873 m_xModulesCLB->set_toggle(nRow, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1874 m_xModulesCLB->set_text(nRow, aTxt, 0);
1875 m_xModulesCLB->set_text_emphasis(nRow, false, 0);
1876 ++nRow;
1877 }
1878 }
1879
1880 // thesaurus entries
1881
1882 pUserData = new ModuleUserData_Impl( OUString(), true, false, TYPE_THES, 0 );
1883 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1884 m_xModulesCLB->append(nullptr);
1885 m_xModulesCLB->set_id(nRow, sId);
1886 m_xModulesCLB->set_text(nRow, sThes, 0);
1887 m_xModulesCLB->set_text_emphasis(nRow, true, 0);
1888 ++nRow;
1889
1890 aNames = rLinguData.GetSortedImplNames( eCurLanguage, TYPE_THES );
1891 pName = aNames.getConstArray();
1892 nNames = aNames.getLength();
1893 nLocalIndex = 0;
1894 for (n = 0; n < nNames; ++n)
1895 {
1896 OUString aImplName;
1897 bool bIsSuppLang = false;
1898
1899 pInfo = rLinguData.GetInfoByImplName( pName[n] );
1900 if (pInfo)
1901 {
1902 bIsSuppLang = pInfo->xThes.is() &&
1903 pInfo->xThes->hasLocale( aCurLocale );
1904 aImplName = pInfo->sThesImplName;
1905 }
1906 if (!aImplName.isEmpty() && bIsSuppLang)
1907 {
1908 OUString aTxt( pInfo->sDisplayName );
1909
1910 LangImplNameTable &rTable = rLinguData.GetThesTable();
1911 const bool bHasLang = rTable.count( eCurLanguage );
1912 if (!bHasLang)
1913 {
1914 SAL_INFO( "cui.options", "language entry missing" ); // only relevant if all languages found should be supported
1915 }
1916 const bool bCheck = bHasLang && lcl_SeqGetEntryPos( rTable[ eCurLanguage ], aImplName ) >= 0;
1917 pUserData = new ModuleUserData_Impl( aImplName, false,
1918 bCheck, TYPE_THES, static_cast<sal_uInt8>(nLocalIndex++) );
1919 sId = OUString::number(reinterpret_cast<sal_Int64>(pUserData));
1920
1921 m_xModulesCLB->append(nullptr);
1922 m_xModulesCLB->set_id(nRow, sId);
1923 m_xModulesCLB->set_toggle(nRow, bCheck ? TRISTATE_TRUE : TRISTATE_FALSE);
1924 m_xModulesCLB->set_text(nRow, aTxt, 0);
1925 m_xModulesCLB->set_text_emphasis(nRow, false, 0);
1926 ++nRow;
1927 }
1928 }
1929 }
1930 aLastLocale = aCurLocale;
1931 }
1932
IMPL_LINK(SvxEditModulesDlg,UpDownHdl_Impl,weld::Button &,rBtn,void)1933 IMPL_LINK( SvxEditModulesDlg, UpDownHdl_Impl, weld::Button&, rBtn, void )
1934 {
1935 bool bUp = m_xPrioUpPB.get() == &rBtn;
1936 int nCurPos = m_xModulesCLB->get_selected_index();
1937 if (nCurPos == -1)
1938 return;
1939
1940 m_xModulesCLB->freeze();
1941
1942 OUString sId(m_xModulesCLB->get_id(nCurPos));
1943 OUString sStr(m_xModulesCLB->get_text(nCurPos));
1944 bool bIsChecked = m_xModulesCLB->get_toggle(nCurPos);
1945
1946 m_xModulesCLB->remove(nCurPos);
1947
1948 int nDestPos = bUp ? nCurPos - 1 : nCurPos + 1;
1949
1950 m_xModulesCLB->insert_text(nDestPos, sStr);
1951 m_xModulesCLB->set_id(nDestPos, sId);
1952 m_xModulesCLB->set_toggle(nDestPos, bIsChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
1953
1954 m_xModulesCLB->thaw();
1955
1956 m_xModulesCLB->select(nDestPos);
1957 SelectHdl_Impl(*m_xModulesCLB);
1958 }
1959
IMPL_LINK_NOARG(SvxEditModulesDlg,ClickHdl_Impl,weld::Button &,void)1960 IMPL_LINK_NOARG(SvxEditModulesDlg, ClickHdl_Impl, weld::Button&, void)
1961 {
1962 // store language config
1963 LangSelectHdl_Impl(m_xLanguageLB.get());
1964 m_xDialog->response(RET_OK);
1965 }
1966
IMPL_LINK_NOARG(SvxEditModulesDlg,BackHdl_Impl,weld::Button &,void)1967 IMPL_LINK_NOARG(SvxEditModulesDlg, BackHdl_Impl, weld::Button&, void)
1968 {
1969 rLinguData = *pDefaultLinguData;
1970 LangSelectHdl_Impl(nullptr);
1971 }
1972
IMPL_STATIC_LINK_NOARG(SvxEditModulesDlg,OnLinkClick,weld::LinkButton &,bool)1973 IMPL_STATIC_LINK_NOARG(SvxEditModulesDlg, OnLinkClick, weld::LinkButton&, bool)
1974 {
1975 comphelper::dispatchCommand(".uno:MoreDictionaries", {});
1976 return true;
1977 }
1978
1979 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1980