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