LibreOffice Module svx (master) 1
langbox.cxx
Go to the documentation of this file.
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 <com/sun/star/linguistic2/XAvailableLocales.hpp>
21#include <com/sun/star/linguistic2/XLinguServiceManager2.hpp>
22#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
23#include <linguistic/misc.hxx>
24#include <rtl/ustring.hxx>
25#include <sal/log.hxx>
27#include <tools/urlobj.hxx>
28#include <svtools/langtab.hxx>
30#include <i18nlangtag/lang.h>
31#include <editeng/unolingu.hxx>
33#include <svx/langbox.hxx>
34#include <svx/dialmgr.hxx>
35#include <svx/strings.hrc>
36#include <bitmaps.hlst>
37
38#include <comphelper/string.hxx>
40#include <vcl/svapp.hxx>
41#include <vcl/settings.hxx>
42
43using namespace ::com::sun::star::util;
44using namespace ::com::sun::star::linguistic2;
45using namespace ::com::sun::star::uno;
46
47OUString GetDicInfoStr( std::u16string_view rName, const LanguageType nLang, bool bNeg )
48{
49 INetURLObject aURLObj;
50 aURLObj.SetSmartProtocol( INetProtocol::File );
52 OUString aTmp( aURLObj.GetBase() + " " );
53
54 if ( bNeg )
55 {
56 aTmp += " (-) ";
57 }
58
59 if ( LANGUAGE_NONE == nLang )
60 aTmp += SvxResId(RID_SVXSTR_LANGUAGE_ALL);
61 else
62 {
63 aTmp += "[" + SvtLanguageTable::GetLanguageString( nLang ) + "]";
64 }
65
66 return aTmp;
67}
68
69// misc local helper functions
70static std::vector< LanguageType > lcl_LocaleSeqToLangSeq( Sequence< css::lang::Locale > const &rSeq )
71{
72 sal_Int32 nCount = rSeq.getLength();
73
74 std::vector< LanguageType > aLangs;
75 aLangs.reserve(nCount);
76
77 std::transform(rSeq.begin(), rSeq.end(), std::back_inserter(aLangs),
78 [](const css::lang::Locale& rLocale) -> LanguageType {
79 return LanguageTag::convertToLanguageType(rLocale); });
80
81 return aLangs;
82}
83
84static bool lcl_SeqHasLang( const Sequence< sal_Int16 > & rLangSeq, sal_Int16 nLang )
85{
86 return rLangSeq.hasElements()
87 && std::find(rLangSeq.begin(), rLangSeq.end(), nLang) != rLangSeq.end();
88}
89
90namespace {
91
92bool lcl_isPrerequisite( LanguageType nLangType )
93{
94 return
95 nLangType != LANGUAGE_DONTKNOW &&
96 nLangType != LANGUAGE_SYSTEM &&
97 nLangType != LANGUAGE_NONE &&
98 nLangType != LANGUAGE_USER_KEYID &&
99 !MsLangId::isLegacy( nLangType) &&
100 MsLangId::getSubLanguage( nLangType);
101}
102
103bool lcl_isScriptTypeRequested( LanguageType nLangType, SvxLanguageListFlags nLangList )
104{
105 return
106 bool(nLangList & SvxLanguageListFlags::ALL) ||
107 (bool(nLangList & SvxLanguageListFlags::WESTERN) &&
108 (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::LATIN)) ||
109 (bool(nLangList & SvxLanguageListFlags::CTL) &&
110 (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::COMPLEX)) ||
111 (bool(nLangList & SvxLanguageListFlags::CJK) &&
112 (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::ASIAN));
113}
114
115}
116
117
119{
120 OUString sLang = m_xControl->get_active_id();
121 if (!sLang.isEmpty())
122 return LanguageType(sLang.toInt32());
123 else
124 return LANGUAGE_DONTKNOW;
125}
126
127int SvxLanguageBox::find_id(const LanguageType eLangType) const
128{
129 return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
130}
131
132void SvxLanguageBox::set_id(int pos, const LanguageType eLangType)
133{
134 m_xControl->set_id(pos, OUString::number(static_cast<sal_uInt16>(eLangType)));
135}
136
138{
139 return LanguageType(m_xControl->get_id(pos).toInt32());
140}
141
143{
144 m_xControl->remove_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
145}
146
147void SvxLanguageBox::append(const LanguageType eLangType, const OUString& rStr)
148{
149 m_xControl->append(OUString::number(static_cast<sal_uInt16>(eLangType)), rStr);
150}
151
153{
154 // If the core uses a LangID of an imported MS document and wants to select
155 // a language that is replaced, we need to select the replacement instead.
157
158 sal_Int32 nAt = ImplTypeToPos( nLang );
159
160 if (nAt == -1)
161 {
162 InsertLanguage( nLang ); // on-the-fly-ID
163 nAt = ImplTypeToPos( nLang );
164 }
165
166 if (nAt != -1)
167 m_xControl->set_active(nAt);
168}
169
170void SvxLanguageBox::AddLanguages(const std::vector< LanguageType >& rLanguageTypes,
171 SvxLanguageListFlags nLangList, std::vector<weld::ComboBoxEntry>& rEntries)
172{
173 for ( auto const & nLangType : rLanguageTypes )
174 {
175 if (lcl_isPrerequisite( nLangType ))
176 {
178 if (lcl_isScriptTypeRequested( nLang, nLangList))
179 {
180 int nAt = ImplTypeToPos(nLang);
181 if (nAt != -1)
182 continue;
183 weld::ComboBoxEntry aNewEntry(BuildEntry(nLang));
184 if (aNewEntry.sString.isEmpty())
185 continue;
186 if (std::find_if(rEntries.begin(), rEntries.end(),
187 [=](const weld::ComboBoxEntry& rEntry){ return rEntry.sId == aNewEntry.sId; }) != rEntries.end())
188 continue;
189 rEntries.push_back(aNewEntry);
190 }
191 }
192 }
193}
194
196 bool bLangNoneIsLangAll, bool bCheckSpellAvail,
197 bool bDefaultLangExist, LanguageType eDefaultLangType,
198 sal_Int16 nDefaultType)
199{
200 m_bHasLangNone = bHasLangNone;
201 m_bLangNoneIsLangAll = bLangNoneIsLangAll;
202 m_bWithCheckmark = bCheckSpellAvail;
203
204 if (SvxLanguageListFlags::EMPTY == nLangList)
205 {
206 m_xControl->clear();
207 return;
208 }
209
210 bool bAddAvailable = (!(nLangList & SvxLanguageListFlags::ONLY_KNOWN) &&
211 ((nLangList & SvxLanguageListFlags::ALL) ||
212 (nLangList & SvxLanguageListFlags::WESTERN) ||
213 (nLangList & SvxLanguageListFlags::CTL) ||
214 (nLangList & SvxLanguageListFlags::CJK)));
215 std::vector< LanguageType > aSpellAvailLang;
216 std::vector< LanguageType > aHyphAvailLang;
217 std::vector< LanguageType > aThesAvailLang;
218 Sequence< sal_Int16 > aSpellUsedLang;
219 Reference< XAvailableLocales > xAvail( LinguMgr::GetLngSvcMgr() );
220 if (xAvail.is())
221 {
222 Sequence< css::lang::Locale > aTmp;
223
224 if (bAddAvailable)
225 {
226 aTmp = xAvail->getAvailableLocales( SN_SPELLCHECKER );
227 aSpellAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
228
229 aTmp = xAvail->getAvailableLocales( SN_HYPHENATOR );
230 aHyphAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
231
232 aTmp = xAvail->getAvailableLocales( SN_THESAURUS );
233 aThesAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
234 }
235 }
236 if (SvxLanguageListFlags::SPELL_USED & nLangList)
237 {
238 Reference< XSpellChecker1 > xTmp1 = LinguMgr::GetSpellChecker();
239 if (xTmp1.is())
240 aSpellUsedLang = xTmp1->getLanguages();
241 }
242
243 std::vector<LanguageType> aKnown;
244 sal_uInt32 nCount;
245 if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
246 {
248 nCount = aKnown.size();
249 }
250 else
251 {
253 }
254
255 std::vector<weld::ComboBoxEntry> aEntries;
256 for ( sal_uInt32 i = 0; i < nCount; i++ )
257 {
258 LanguageType nLangType;
259 if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
260 nLangType = aKnown[i];
261 else
263 if ( lcl_isPrerequisite( nLangType ) &&
264 (lcl_isScriptTypeRequested( nLangType, nLangList) ||
265 (bool(nLangList & SvxLanguageListFlags::FBD_CHARS) &&
267 (bool(nLangList & SvxLanguageListFlags::SPELL_USED) &&
268 lcl_SeqHasLang(aSpellUsedLang, static_cast<sal_uInt16>(nLangType)))
269 ) )
270 {
271 aEntries.push_back(BuildEntry(nLangType));
272 if (aEntries.back().sString.isEmpty())
273 aEntries.pop_back();
274 }
275 }
276
277 if (bAddAvailable)
278 {
279 // Spell checkers, hyphenators and thesauri may add language tags
280 // unknown so far.
281 AddLanguages(aSpellAvailLang, nLangList, aEntries);
282 AddLanguages(aHyphAvailLang, nLangList, aEntries);
283 AddLanguages(aThesAvailLang, nLangList, aEntries);
284 }
285
286 std::sort(aEntries.begin(), aEntries.end(),
287 [](const weld::ComboBoxEntry e1, const weld::ComboBoxEntry e2) {
288 static const auto aSorter = comphelper::string::NaturalStringSorter(
289 ::comphelper::getProcessComponentContext(),
290 Application::GetSettings().GetLanguageTag().getLocale());
291 return aSorter.compare(e1.sString, e2.sString) < 0;
292 });
293
294 int nSeparatorPosition = 0;
295 if (bDefaultLangExist)
296 {
297 aEntries.insert(aEntries.begin(), BuildEntry(eDefaultLangType, nDefaultType));
298 nSeparatorPosition++;
299 }
300
301 if (bHasLangNone)
302 {
303 aEntries.insert(aEntries.begin(), BuildEntry(LANGUAGE_NONE));
304 nSeparatorPosition++;
305 }
306
307 m_xControl->insert_vector(aEntries, false);
308 if (nSeparatorPosition > 0)
309 m_xControl->insert_separator(nSeparatorPosition, "");
310}
311
313{
314 return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eType)));
315}
316
317void SvxLanguageBox::InsertLanguage(const LanguageType nLangType, sal_Int16 nType)
318{
319 weld::ComboBoxEntry aEntry = BuildEntry(nLangType, nType);
320 if (aEntry.sString.isEmpty())
321 return;
322 if (aEntry.sImage.isEmpty())
323 m_xControl->append(aEntry.sId, aEntry.sString);
324 else
325 m_xControl->append(aEntry.sId, aEntry.sString, aEntry.sImage);
326}
327
329{
330 InsertLanguage(nLangType, css::i18n::ScriptType::WEAK);
331}
332
334{
336 // For obsolete and to be replaced languages check whether an entry of the
337 // replacement already exists and if so don't add an entry with identical
338 // string as would be returned by SvtLanguageTable::GetString().
339 if (nLang != nLangType)
340 {
341 int nAt = ImplTypeToPos( nLang );
342 if (nAt != -1)
343 return weld::ComboBoxEntry("");
344 }
345
346 OUString aStrEntry = SvtLanguageTable::GetLanguageString( nLang );
348 aStrEntry = m_aAllString;
349
350 LanguageType nRealLang = nLang;
351 if (nRealLang == LANGUAGE_SYSTEM)
352 {
354 aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
355 }
356 else if (nRealLang == LANGUAGE_USER_SYSTEM_CONFIG)
357 {
358 nRealLang = MsLangId::getSystemLanguage();
359 // Whatever we obtained, ensure a known supported locale.
360 nRealLang = LanguageTag(nRealLang).makeFallback().getLanguageType();
361 aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
362 }
363
365 {
366 if (!m_xSpellUsedLang)
367 {
368 Reference<XSpellChecker1> xSpell = LinguMgr::GetSpellChecker();
369 if (xSpell.is())
370 m_xSpellUsedLang.reset(new Sequence<sal_Int16>(xSpell->getLanguages()));
371 }
372
373 bool bFound = m_xSpellUsedLang && lcl_SeqHasLang(*m_xSpellUsedLang, static_cast<sal_uInt16>(nRealLang));
374
375 return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLangType)), bFound ? OUString(RID_SVXBMP_CHECKED) : OUString(RID_SVXBMP_NOTCHECKED));
376 }
377 else
378 return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLangType)));
379}
380
381IMPL_LINK(SvxLanguageBox, ChangeHdl, weld::ComboBox&, rControl, void)
382{
383 if (rControl.has_entry())
384 {
385 EditedAndValid eOldState = m_eEditedAndValid;
386 OUString aStr(rControl.get_active_text());
387 if (aStr.isEmpty())
388 m_eEditedAndValid = EditedAndValid::Invalid;
389 else
390 {
391 const int nPos = rControl.find_text(aStr);
392 if (nPos != -1)
393 {
394 int nStartSelectPos, nEndSelectPos;
395 rControl.get_entry_selection_bounds(nStartSelectPos, nEndSelectPos);
396
397 // Select the corresponding listbox entry if not current. This
398 // invalidates the Edit Selection thus has to happen between
399 // obtaining the Selection and setting the new Selection.
400 int nSelPos = m_xControl->get_active();
401 bool bSetEditSelection;
402 if (nSelPos == nPos)
403 bSetEditSelection = false;
404 else
405 {
406 m_xControl->set_active(nPos);
407 bSetEditSelection = true;
408 }
409
410 // If typing into the Edit control led us here, advance start of a
411 // full selection by one so the next character will already
412 // continue the string instead of having to type the same character
413 // again to start a new string. The selection is in reverse
414 // when obtained from the Edit control.
415 if (nEndSelectPos == 0)
416 {
417 OUString aText(m_xControl->get_active_text());
418 if (nStartSelectPos == aText.getLength())
419 {
420 ++nEndSelectPos;
421 bSetEditSelection = true;
422 }
423 }
424
425 if (bSetEditSelection)
426 rControl.select_entry_region(nStartSelectPos, nEndSelectPos);
427
428 m_eEditedAndValid = EditedAndValid::No;
429 }
430 else
431 {
432 OUString aCanonicalized;
433 bool bValid = LanguageTag::isValidBcp47( aStr, &aCanonicalized, true);
434 m_eEditedAndValid = (bValid ? EditedAndValid::Valid : EditedAndValid::Invalid);
435 if (bValid && aCanonicalized != aStr)
436 {
437 m_xControl->set_entry_text(aCanonicalized);
438 const auto nCursorPos = aCanonicalized.getLength();
439 m_xControl->select_entry_region(nCursorPos, nCursorPos);
440 }
441 }
442 }
443 if (eOldState != m_eEditedAndValid)
444 {
445 if (m_eEditedAndValid == EditedAndValid::Invalid)
446 rControl.set_entry_message_type(weld::EntryMessageType::Error);
447 else
448 rControl.set_entry_message_type(weld::EntryMessageType::Normal);
449 }
450 }
451 m_aChangeHdl.Call(rControl);
452}
453
454SvxLanguageBox::SvxLanguageBox(std::unique_ptr<weld::ComboBox> pControl)
455 : m_xControl(std::move(pControl))
456 , m_aAllString(SvxResId(RID_SVXSTR_LANGUAGE_ALL))
457 , m_eSavedLanguage(LANGUAGE_DONTKNOW)
458 , m_eEditedAndValid(EditedAndValid::No)
459 , m_bHasLangNone(false)
460 , m_bLangNoneIsLangAll(false)
461 , m_bWithCheckmark(false)
462{
463 m_xControl->connect_changed(LINK(this, SvxLanguageBox, ChangeHdl));
464}
465
467{
469 return -1;
470
471 LanguageTag aLanguageTag(m_xControl->get_active_text());
472 LanguageType nLang = aLanguageTag.getLanguageType();
473 if (nLang == LANGUAGE_DONTKNOW)
474 {
475 SAL_WARN( "svx.dialog", "SvxLanguageComboBox::SaveEditedAsEntry: unknown tag");
476 return -1;
477 }
478
479 int nPos = ImplTypeToPos( nLang);
480 if (nPos != -1)
481 return nPos; // Already present but with a different string.
482
484 {
485 // In SvtLanguageTable but not in SvxLanguageComboBox. On purpose? This
486 // may be an entry with different settings or CTL instead of Western or
487 // ... all things we don't handle yet.
488 SAL_WARN( "svx.dialog", "SvxLanguageComboBox::SaveEditedAsEntry: already in SvtLanguageTable: " <<
489 SvtLanguageTable::GetLanguageString( nLang) << ", " << nLang);
490 }
491 else
492 {
493 // Add to both, SvtLanguageTable and SvxLanguageComboBox.
494 /* TODO: a descriptive user comment would be a nice to have here. */
495 SvtLanguageTable::AddLanguageTag( aLanguageTag );
496 }
497
498 InsertLanguage(nLang);
499 return ImplTypeToPos(nLang);
500}
501
502/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetSmartProtocol(INetProtocol eTheSmartScheme)
bool SetSmartURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
OUString GetBase() const
LanguageType getLanguageType(bool bResolveSystem=true) const
LanguageTag & makeFallback()
bool isValidBcp47() const
static css::uno::Reference< css::linguistic2::XSpellChecker1 > GetSpellChecker()
static css::uno::Reference< css::linguistic2::XLinguServiceManager2 > GetLngSvcMgr()
static const std::vector< LanguageType > & getInstalledLanguageTypes()
static bool isLegacy(LanguageType nLang)
static LanguageType resolveSystemLanguageByScriptType(LanguageType nLang, sal_Int16 nType)
static LanguageType getSubLanguage(LanguageType nLangID)
static LanguageType getReplacementForObsoleteLanguage(LanguageType nLang)
static LanguageType getSystemLanguage()
static bool hasForbiddenCharacters(LanguageType nLang)
static sal_uInt32 GetLanguageEntryCount()
static OUString GetLanguageString(const LanguageType eType)
static LanguageType GetLanguageTypeAtIndex(sal_uInt32 nIndex)
static bool HasLanguageType(const LanguageType eType)
static sal_uInt32 AddLanguageTag(const LanguageTag &rLanguageTag)
bool m_bLangNoneIsLangAll
Definition: langbox.hxx:67
SVX_DLLPRIVATE int ImplTypeToPos(LanguageType eType) const
Definition: langbox.cxx:312
bool m_bWithCheckmark
Definition: langbox.hxx:68
void remove_id(const LanguageType eLangType)
Definition: langbox.cxx:142
LanguageType get_active_id() const
Definition: langbox.cxx:118
void set_id(int nPos, const LanguageType eLangType)
Definition: langbox.cxx:132
EditedAndValid m_eEditedAndValid
Definition: langbox.hxx:65
void SetLanguageList(SvxLanguageListFlags nLangList, bool bHasLangNone, bool bLangNoneIsLangAll=false, bool bCheckSpellAvail=false, bool bDefaultLangExist=false, LanguageType eDefaultLangType=LANGUAGE_NONE, sal_Int16 nDefaultType=0)
Definition: langbox.cxx:195
sal_Int32 SaveEditedAsEntry()
Definition: langbox.cxx:466
bool m_bHasLangNone
Definition: langbox.hxx:66
SvxLanguageBox(std::unique_ptr< weld::ComboBox > pControl)
Definition: langbox.cxx:454
std::unique_ptr< weld::ComboBox > m_xControl
Definition: langbox.hxx:60
LanguageType get_id(int nPos) const
Definition: langbox.cxx:137
void set_active_id(const LanguageType eLangType)
Definition: langbox.cxx:152
SVX_DLLPRIVATE void InsertLanguage(const LanguageType nLangType, sal_Int16 nType)
Definition: langbox.cxx:317
SVX_DLLPRIVATE void AddLanguages(const std::vector< LanguageType > &rLanguageTypes, SvxLanguageListFlags nLangList, std::vector< weld::ComboBoxEntry > &rEntries)
Definition: langbox.cxx:170
void append(const LanguageType eLangType, const OUString &rStr)
Definition: langbox.cxx:147
std::unique_ptr< css::uno::Sequence< sal_Int16 > > m_xSpellUsedLang
Definition: langbox.hxx:63
OUString m_aAllString
Definition: langbox.hxx:62
int find_id(const LanguageType eLangType) const
Definition: langbox.cxx:127
SVX_DLLPRIVATE weld::ComboBoxEntry BuildEntry(const LanguageType nLangType, sal_Int16 nType=css::i18n::ScriptType::WEAK)
Definition: langbox.cxx:333
int nCount
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
ScXMLEditAttributeMap::Entry const aEntries[]
DocumentType eType
#define LANGUAGE_SYSTEM
#define LANGUAGE_NONE
#define LANGUAGE_USER_SYSTEM_CONFIG
#define LANGUAGE_USER_KEYID
#define LANGUAGE_DONTKNOW
static bool lcl_SeqHasLang(const Sequence< sal_Int16 > &rLangSeq, sal_Int16 nLang)
Definition: langbox.cxx:84
static std::vector< LanguageType > lcl_LocaleSeqToLangSeq(Sequence< css::lang::Locale > const &rSeq)
Definition: langbox.cxx:70
OUString GetDicInfoStr(std::u16string_view rName, const LanguageType nLang, bool bNeg)
Definition: langbox.cxx:47
IMPL_LINK(SvxLanguageBox, ChangeHdl, weld::ComboBox &, rControl, void)
Definition: langbox.cxx:381
SvxLanguageListFlags
Definition: langbox.hxx:30
sal_uInt16 nPos
#define SAL_WARN(area, stream)
aStr
constexpr OUStringLiteral SN_SPELLCHECKER
constexpr OUStringLiteral SN_HYPHENATOR
constexpr OUStringLiteral SN_THESAURUS
No
SvtScriptType GetScriptTypeOfLanguage(LanguageType nLang)
int i
QPRO_FUNC_TYPE nType
Reference< XControl > m_xControl
size_t pos