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/XSpellChecker1.hpp>
22 #include <linguistic/misc.hxx>
23 #include <rtl/ustring.hxx>
24 #include <sal/log.hxx>
26 #include <tools/urlobj.hxx>
27 #include <svtools/langtab.hxx>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <i18nlangtag/lang.h>
30 #include <editeng/unolingu.hxx>
31 #include <svl/languageoptions.hxx>
32 #include <svx/langbox.hxx>
33 #include <svx/dialmgr.hxx>
34 #include <svx/strings.hrc>
35 #include <bitmaps.hlst>
36 
37 #include <comphelper/string.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/settings.hxx>
41 
42 using namespace ::com::sun::star::util;
43 using namespace ::com::sun::star::linguistic2;
44 using namespace ::com::sun::star::uno;
45 
46 OUString GetDicInfoStr( const OUString& rName, const LanguageType nLang, bool bNeg )
47 {
48  INetURLObject aURLObj;
49  aURLObj.SetSmartProtocol( INetProtocol::File );
51  OUString aTmp( aURLObj.GetBase() + " " );
52 
53  if ( bNeg )
54  {
55  aTmp += " (-) ";
56  }
57 
58  if ( LANGUAGE_NONE == nLang )
59  aTmp += SvxResId(RID_SVXSTR_LANGUAGE_ALL);
60  else
61  {
62  aTmp += "[" + SvtLanguageTable::GetLanguageString( nLang ) + "]";
63  }
64 
65  return aTmp;
66 }
67 
68 // misc local helper functions
69 static std::vector< LanguageType > lcl_LocaleSeqToLangSeq( Sequence< css::lang::Locale > const &rSeq )
70 {
71  sal_Int32 nCount = rSeq.getLength();
72 
73  std::vector< LanguageType > aLangs;
74  aLangs.reserve(nCount);
75 
76  std::transform(rSeq.begin(), rSeq.end(), std::back_inserter(aLangs),
77  [](const css::lang::Locale& rLocale) -> LanguageType {
78  return LanguageTag::convertToLanguageType(rLocale); });
79 
80  return aLangs;
81 }
82 
83 static bool lcl_SeqHasLang( const Sequence< sal_Int16 > & rLangSeq, sal_Int16 nLang )
84 {
85  return rLangSeq.hasElements()
86  && std::find(rLangSeq.begin(), rLangSeq.end(), nLang) != rLangSeq.end();
87 }
88 
89 namespace {
90 
91 bool lcl_isPrerequisite( LanguageType nLangType )
92 {
93  return
94  nLangType != LANGUAGE_DONTKNOW &&
95  nLangType != LANGUAGE_SYSTEM &&
96  nLangType != LANGUAGE_NONE &&
97  !MsLangId::isLegacy( nLangType) &&
98  MsLangId::getSubLanguage( nLangType);
99 }
100 
101 bool lcl_isScriptTypeRequested( LanguageType nLangType, SvxLanguageListFlags nLangList )
102 {
103  return
104  bool(nLangList & SvxLanguageListFlags::ALL) ||
105  (bool(nLangList & SvxLanguageListFlags::WESTERN) &&
106  (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::LATIN)) ||
107  (bool(nLangList & SvxLanguageListFlags::CTL) &&
108  (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::COMPLEX)) ||
109  (bool(nLangList & SvxLanguageListFlags::CJK) &&
110  (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::ASIAN));
111 }
112 
113 }
114 
115 
117 {
118  OUString sLang = m_xControl->get_active_id();
119  if (!sLang.isEmpty())
120  return LanguageType(sLang.toInt32());
121  else
122  return LANGUAGE_DONTKNOW;
123 }
124 
125 int SvxLanguageBox::find_id(const LanguageType eLangType) const
126 {
127  return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
128 }
129 
130 void SvxLanguageBox::set_id(int pos, const LanguageType eLangType)
131 {
132  m_xControl->set_id(pos, OUString::number(static_cast<sal_uInt16>(eLangType)));
133 }
134 
136 {
137  return LanguageType(m_xControl->get_id(pos).toInt32());
138 }
139 
141 {
142  m_xControl->remove_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
143 }
144 
145 void SvxLanguageBox::append(const LanguageType eLangType, const OUString& rStr)
146 {
147  m_xControl->append(OUString::number(static_cast<sal_uInt16>(eLangType)), rStr);
148 }
149 
151 {
152  // If the core uses a LangID of an imported MS document and wants to select
153  // a language that is replaced, we need to select the replacement instead.
155 
156  sal_Int32 nAt = ImplTypeToPos( nLang );
157 
158  if (nAt == -1)
159  {
160  InsertLanguage( nLang ); // on-the-fly-ID
161  nAt = ImplTypeToPos( nLang );
162  }
163 
164  if (nAt != -1)
165  m_xControl->set_active(nAt);
166 }
167 
168 void SvxLanguageBox::AddLanguages(const std::vector< LanguageType >& rLanguageTypes,
169  SvxLanguageListFlags nLangList, std::vector<weld::ComboBoxEntry>& rEntries)
170 {
171  for ( auto const & nLangType : rLanguageTypes )
172  {
173  if (lcl_isPrerequisite( nLangType ))
174  {
176  if (lcl_isScriptTypeRequested( nLang, nLangList))
177  {
178  int nAt = ImplTypeToPos(nLang);
179  if (nAt != -1)
180  continue;
181  weld::ComboBoxEntry aNewEntry(BuildEntry(nLang));
182  if (aNewEntry.sString.isEmpty())
183  continue;
184  if (std::find_if(rEntries.begin(), rEntries.end(),
185  [=](const weld::ComboBoxEntry& rEntry){ return rEntry.sId == aNewEntry.sId; }) != rEntries.end())
186  continue;
187  rEntries.push_back(aNewEntry);
188  }
189  }
190  }
191 }
192 
193 void SvxLanguageBox::SetLanguageList(SvxLanguageListFlags nLangList, bool bHasLangNone,
194  bool bLangNoneIsLangAll, bool bCheckSpellAvail,
195  bool bDefaultLangExist, LanguageType eDefaultLangType,
196  sal_Int16 nDefaultType)
197 {
198  m_bHasLangNone = bHasLangNone;
199  m_bLangNoneIsLangAll = bLangNoneIsLangAll;
200  m_bWithCheckmark = bCheckSpellAvail;
201 
202  if (SvxLanguageListFlags::EMPTY == nLangList)
203  {
204  m_xControl->clear();
205  return;
206  }
207 
208  bool bAddAvailable = (!(nLangList & SvxLanguageListFlags::ONLY_KNOWN) &&
209  ((nLangList & SvxLanguageListFlags::ALL) ||
210  (nLangList & SvxLanguageListFlags::WESTERN) ||
211  (nLangList & SvxLanguageListFlags::CTL) ||
212  (nLangList & SvxLanguageListFlags::CJK)));
213  std::vector< LanguageType > aSpellAvailLang;
214  std::vector< LanguageType > aHyphAvailLang;
215  std::vector< LanguageType > aThesAvailLang;
216  Sequence< sal_Int16 > aSpellUsedLang;
217  Reference< XAvailableLocales > xAvail( LinguMgr::GetLngSvcMgr(), UNO_QUERY );
218  if (xAvail.is())
219  {
220  Sequence< css::lang::Locale > aTmp;
221 
222  if (bAddAvailable)
223  {
224  aTmp = xAvail->getAvailableLocales( SN_SPELLCHECKER );
225  aSpellAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
226  }
227  if (bAddAvailable)
228  {
229  aTmp = xAvail->getAvailableLocales( SN_HYPHENATOR );
230  aHyphAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
231  }
232  if (bAddAvailable)
233  {
234  aTmp = xAvail->getAvailableLocales( SN_THESAURUS );
235  aThesAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
236  }
237  }
238  if (SvxLanguageListFlags::SPELL_USED & nLangList)
239  {
240  Reference< XSpellChecker1 > xTmp1 = LinguMgr::GetSpellChecker();
241  if (xTmp1.is())
242  aSpellUsedLang = xTmp1->getLanguages();
243  }
244 
245  std::vector<LanguageType> aKnown;
246  sal_uInt32 nCount;
247  if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
248  {
250  nCount = aKnown.size();
251  }
252  else
253  {
255  }
256 
257  std::vector<weld::ComboBoxEntry> aEntries;
258  for ( sal_uInt32 i = 0; i < nCount; i++ )
259  {
260  LanguageType nLangType;
261  if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
262  nLangType = aKnown[i];
263  else
265  if ( lcl_isPrerequisite( nLangType ) &&
266  (lcl_isScriptTypeRequested( nLangType, nLangList) ||
267  (bool(nLangList & SvxLanguageListFlags::FBD_CHARS) &&
268  MsLangId::hasForbiddenCharacters(nLangType)) ||
269  (bool(nLangList & SvxLanguageListFlags::SPELL_USED) &&
270  lcl_SeqHasLang(aSpellUsedLang, static_cast<sal_uInt16>(nLangType)))
271  ) )
272  {
273  aEntries.push_back(BuildEntry(nLangType));
274  if (aEntries.back().sString.isEmpty())
275  aEntries.pop_back();
276  }
277  }
278 
279  if (bAddAvailable)
280  {
281  // Spell checkers, hyphenators and thesauri may add language tags
282  // unknown so far.
283  AddLanguages(aSpellAvailLang, nLangList, aEntries);
284  AddLanguages(aHyphAvailLang, nLangList, aEntries);
285  AddLanguages(aThesAvailLang, nLangList, aEntries);
286  }
287 
288  std::sort(aEntries.begin(), aEntries.end(),
289  [](const weld::ComboBoxEntry e1, const weld::ComboBoxEntry e2) {
290  static const auto aSorter = comphelper::string::NaturalStringSorter(
292  Application::GetSettings().GetLanguageTag().getLocale());
293  return aSorter.compare(e1.sString, e2.sString) < 0;
294  });
295 
296  int nSeparatorPosition = 0;
297  if (bDefaultLangExist)
298  {
299  aEntries.insert(aEntries.begin(), BuildEntry(eDefaultLangType, nDefaultType));
300  nSeparatorPosition++;
301  }
302 
303  if (bHasLangNone)
304  {
305  aEntries.insert(aEntries.begin(), BuildEntry(LANGUAGE_NONE));
306  nSeparatorPosition++;
307  }
308 
309  m_xControl->insert_vector(aEntries, false);
310  if (nSeparatorPosition > 0)
311  m_xControl->insert_separator(nSeparatorPosition, "");
312 }
313 
315 {
316  return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eType)));
317 }
318 
319 void SvxLanguageBox::InsertLanguage(const LanguageType nLangType, sal_Int16 nType)
320 {
321  weld::ComboBoxEntry aEntry = BuildEntry(nLangType, nType);
322  if (aEntry.sString.isEmpty())
323  return;
324  if (aEntry.sImage.isEmpty())
325  m_xControl->append(aEntry.sId, aEntry.sString);
326  else
327  m_xControl->append(aEntry.sId, aEntry.sString, aEntry.sImage);
328 }
329 
331 {
332  InsertLanguage(nLangType, css::i18n::ScriptType::WEAK);
333 }
334 
336 {
338  // For obsolete and to be replaced languages check whether an entry of the
339  // replacement already exists and if so don't add an entry with identical
340  // string as would be returned by SvtLanguageTable::GetString().
341  if (nLang != nLangType)
342  {
343  int nAt = ImplTypeToPos( nLang );
344  if (nAt != -1)
345  return weld::ComboBoxEntry("");
346  }
347 
348  OUString aStrEntry = SvtLanguageTable::GetLanguageString( nLang );
350  aStrEntry = m_aAllString;
351 
352  LanguageType nRealLang = nLang;
353  if (nRealLang == LANGUAGE_SYSTEM)
354  {
355  nRealLang = MsLangId::resolveSystemLanguageByScriptType(nRealLang, nType);
356  aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
357  }
358  else if (nRealLang == LANGUAGE_USER_SYSTEM_CONFIG)
359  {
360  nRealLang = MsLangId::getSystemLanguage();
361  aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
362  }
363 
364  if (m_bWithCheckmark)
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 
381 IMPL_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 
454 SvxLanguageBox::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: */
#define LANGUAGE_NONE
void remove_id(const LanguageType eLangType)
Definition: langbox.cxx:140
Reference< XControl > m_xControl
std::unique_ptr< css::uno::Sequence< sal_Int16 > > m_xSpellUsedLang
Definition: langbox.hxx:62
static std::vector< LanguageType > lcl_LocaleSeqToLangSeq(Sequence< css::lang::Locale > const &rSeq)
Definition: langbox.cxx:69
LanguageType get_active_id() const
Definition: langbox.cxx:116
static LanguageType convertToLanguageType(const css::lang::Locale &rLocale, bool bResolveSystem=true)
static const AllSettings & GetSettings()
EditedAndValid m_eEditedAndValid
Definition: langbox.hxx:64
static LanguageType getSubLanguage(LanguageType nLangID)
static LanguageType resolveSystemLanguageByScriptType(LanguageType nLang, sal_Int16 nType)
IMPL_LINK(SvxLanguageBox, ChangeHdl, weld::ComboBox &, rControl, void)
Definition: langbox.cxx:381
bool m_bHasLangNone
Definition: langbox.hxx:65
SVX_DLLPRIVATE void InsertLanguage(const LanguageType nLangType, sal_Int16 nType)
Definition: langbox.cxx:319
OUString const m_aAllString
Definition: langbox.hxx:61
SVX_DLLPRIVATE int ImplTypeToPos(LanguageType eType) const
Definition: langbox.cxx:314
OUString GetBase() const
static bool hasForbiddenCharacters(LanguageType nLang)
OUString SvxResId(const char *pId)
Definition: dialmgr.cxx:28
int nCount
SVX_DLLPRIVATE weld::ComboBoxEntry BuildEntry(const LanguageType nLangType, sal_Int16 nType=css::i18n::ScriptType::WEAK)
Definition: langbox.cxx:335
static bool HasLanguageType(const LanguageType eType)
static SvtScriptType GetScriptTypeOfLanguage(LanguageType nLang)
static bool isLegacy(LanguageType nLang)
OUString GetDicInfoStr(const OUString &rName, const LanguageType nLang, bool bNeg)
Definition: langbox.cxx:46
static LanguageType getSystemLanguage()
static css::uno::Reference< css::linguistic2::XSpellChecker1 > GetSpellChecker()
void SetSmartProtocol(INetProtocol eTheSmartScheme)
#define LANGUAGE_SYSTEM
int i
SVX_DLLPRIVATE void AddLanguages(const std::vector< LanguageType > &rLanguageTypes, SvxLanguageListFlags nLangList, std::vector< weld::ComboBoxEntry > &rEntries)
Definition: langbox.cxx:168
std::unique_ptr< weld::ComboBox > m_xControl
Definition: langbox.hxx:59
static bool lcl_SeqHasLang(const Sequence< sal_Int16 > &rLangSeq, sal_Int16 nLang)
Definition: langbox.cxx:83
#define LANGUAGE_DONTKNOW
#define SN_HYPHENATOR
static sal_uInt32 AddLanguageTag(const LanguageTag &rLanguageTag)
static css::uno::Reference< css::linguistic2::XLinguServiceManager2 > GetLngSvcMgr()
void set_active_id(const LanguageType eLangType)
Definition: langbox.cxx:150
DocumentType const eType
void set_id(int nPos, const LanguageType eLangType)
Definition: langbox.cxx:130
SvxLanguageBox(std::unique_ptr< weld::ComboBox > pControl)
Definition: langbox.cxx:454
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:193
LanguageType get_id(int nPos) const
Definition: langbox.cxx:135
int find_id(const LanguageType eLangType) const
Definition: langbox.cxx:125
const LanguageTag & getLocale()
static std::vector< LanguageType > getInstalledLanguageTypes()
bool isValidBcp47() const
#define SN_THESAURUS
#define LANGUAGE_USER_SYSTEM_CONFIG
sal_Int32 SaveEditedAsEntry()
Definition: langbox.cxx:466
static LanguageType GetLanguageTypeAtIndex(sal_uInt32 nIndex)
bool m_bWithCheckmark
Definition: langbox.hxx:67
SvxLanguageListFlags
Definition: langbox.hxx:29
void append(const LanguageType eLangType, const OUString &rStr)
Definition: langbox.cxx:145
Reference< XComponentContext > getProcessComponentContext()
static OUString GetLanguageString(const LanguageType eType)
ScXMLEditAttributeMap::Entry const aEntries[]
static sal_uInt32 GetLanguageEntryCount()
#define SAL_WARN(area, stream)
bool m_bLangNoneIsLangAll
Definition: langbox.hxx:66
static LanguageType getReplacementForObsoleteLanguage(LanguageType nLang)
aStr
sal_uInt16 nPos
#define SN_SPELLCHECKER
bool SetSmartURL(OUString const &rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)