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 <sal/config.h>
21 
22 #include <sal/log.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <unotools/charclass.hxx>
25 #include <unotools/syslocale.hxx>
26 #include <unotools/syslocaleoptions.hxx>
27 #include <comphelper/lok.hxx>
28 #include <comphelper/sequence.hxx>
29 #include <rtl/tencinfo.h>
30 #include <rtl/locale.h>
31 #include <osl/thread.h>
32 #include <osl/nlsupport.h>
33 
34 #include <vector>
35 #include <memory>
36 
37 using namespace osl;
38 using namespace com::sun::star;
39 
40 namespace {
41 
42 std::weak_ptr<SvtSysLocale_Impl> g_pSysLocale;
43 
44 }
45 
46 class SvtSysLocale_Impl : public utl::ConfigurationListener
47 {
48 public:
49         SvtSysLocaleOptions                    aSysLocaleOptions;
50         std::unique_ptr<LocaleDataWrapper>      pLocaleData;
51         std::unique_ptr<CharClass>              pCharClass;
52 
53                                 SvtSysLocale_Impl();
54     virtual                     ~SvtSysLocale_Impl() override;
55 
56     CharClass*                  GetCharClass();
57     virtual void                ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
58 
59 private:
60     std::vector<OUString>       getDateAcceptancePatternsConfig() const;
61 };
62 
SvtSysLocale_Impl()63 SvtSysLocale_Impl::SvtSysLocale_Impl()
64 {
65     pLocaleData.reset(new LocaleDataWrapper(
66         aSysLocaleOptions.GetRealLanguageTag(),
67         getDateAcceptancePatternsConfig() ));
68 
69     // listen for further changes
70     aSysLocaleOptions.AddListener( this );
71 }
72 
~SvtSysLocale_Impl()73 SvtSysLocale_Impl::~SvtSysLocale_Impl()
74 {
75     aSysLocaleOptions.RemoveListener( this );
76 }
77 
GetCharClass()78 CharClass* SvtSysLocale_Impl::GetCharClass()
79 {
80     if ( !pCharClass )
81         pCharClass.reset(new CharClass( aSysLocaleOptions.GetRealLanguageTag() ));
82     return pCharClass.get();
83 }
84 
ConfigurationChanged(utl::ConfigurationBroadcaster *,ConfigurationHints nHint)85 void SvtSysLocale_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints nHint )
86 {
87     if ( !(nHint & ConfigurationHints::Locale) &&
88          !(nHint & ConfigurationHints::DatePatterns) )
89         return;
90 
91     MutexGuard aGuard( SvtSysLocale::GetMutex() );
92 
93     const LanguageTag& rLanguageTag = aSysLocaleOptions.GetRealLanguageTag();
94     if ( nHint & ConfigurationHints::Locale )
95     {
96         GetCharClass()->setLanguageTag( rLanguageTag );
97     }
98     pLocaleData.reset(new LocaleDataWrapper(rLanguageTag, getDateAcceptancePatternsConfig()));
99 }
100 
getDateAcceptancePatternsConfig() const101 std::vector<OUString> SvtSysLocale_Impl::getDateAcceptancePatternsConfig() const
102 {
103     OUString aStr( aSysLocaleOptions.GetDatePatternsConfigString());
104     if (aStr.isEmpty())
105         return {};  // reset
106     ::std::vector< OUString > aVec;
107     for (sal_Int32 nIndex = 0; nIndex >= 0; /*nop*/)
108     {
109         OUString aTok( aStr.getToken( 0, ';', nIndex));
110         if (!aTok.isEmpty())
111             aVec.push_back( aTok);
112     }
113     return aVec;
114 }
115 
SvtSysLocale()116 SvtSysLocale::SvtSysLocale()
117 {
118     MutexGuard aGuard( GetMutex() );
119     pImpl = g_pSysLocale.lock();
120     if ( !pImpl )
121     {
122         pImpl = std::make_shared<SvtSysLocale_Impl>();
123         g_pSysLocale = pImpl;
124     }
125 }
126 
~SvtSysLocale()127 SvtSysLocale::~SvtSysLocale()
128 {
129     MutexGuard aGuard( GetMutex() );
130     pImpl.reset();
131 }
132 
133 // static
GetMutex()134 Mutex& SvtSysLocale::GetMutex()
135 {
136     // #i77768# Due to a static reference in the toolkit lib
137     // we need a mutex that lives longer than the svl library.
138     // Otherwise the dtor would use a destructed mutex!!
139     static Mutex* persistentMutex(new Mutex);
140 
141     return *persistentMutex;
142 }
143 
GetLocaleData() const144 const LocaleDataWrapper& SvtSysLocale::GetLocaleData() const
145 {
146     return *(pImpl->pLocaleData);
147 }
148 
GetCharClass() const149 const CharClass& SvtSysLocale::GetCharClass() const
150 {
151     return *(pImpl->GetCharClass());
152 }
153 
GetCharClassPtr() const154 const CharClass* SvtSysLocale::GetCharClassPtr() const
155 {
156     return pImpl->GetCharClass();
157 }
158 
GetOptions() const159 SvtSysLocaleOptions& SvtSysLocale::GetOptions() const
160 {
161     return pImpl->aSysLocaleOptions;
162 }
163 
GetLanguageTag() const164 const LanguageTag& SvtSysLocale::GetLanguageTag() const
165 {
166     if (comphelper::LibreOfficeKit::isActive())
167         return comphelper::LibreOfficeKit::getLocale();
168 
169     return pImpl->aSysLocaleOptions.GetRealLanguageTag();
170 }
171 
GetUILanguageTag() const172 const LanguageTag& SvtSysLocale::GetUILanguageTag() const
173 {
174     if (comphelper::LibreOfficeKit::isActive())
175         return comphelper::LibreOfficeKit::getLanguageTag();
176 
177     return pImpl->aSysLocaleOptions.GetRealUILanguageTag();
178 }
179 
180 // static
GetBestMimeEncoding()181 rtl_TextEncoding SvtSysLocale::GetBestMimeEncoding()
182 {
183     const char* pCharSet = rtl_getBestMimeCharsetFromTextEncoding(
184             osl_getThreadTextEncoding() );
185     if ( !pCharSet )
186     {
187         // If the system locale is unknown to us, e.g. LC_ALL=xx, match the UI
188         // language if possible.
189         SvtSysLocale aSysLocale;
190         const LanguageTag& rLanguageTag = aSysLocale.GetUILanguageTag();
191         // Converting blindly to Locale and then to rtl_Locale may feed the
192         // 'qlt' to rtl_locale_register() and the underlying system locale
193         // stuff, which doesn't know about it nor about BCP47 in the Variant
194         // field. So use the real language and for non-pure ISO cases hope for
195         // the best... the fallback to UTF-8 should solve these cases nowadays.
196         /* FIXME-BCP47: the script needs to go in here as well, so actually
197          * we'd need some variant fiddling or glibc locale string and tweak
198          * rtl_locale_register() to know about it! But then again the Windows
199          * implementation still wouldn't know anything about it ... */
200         SAL_WARN_IF( !rLanguageTag.isIsoLocale(), "unotools.i18n",
201                 "SvtSysLocale::GetBestMimeEncoding - non-ISO UI locale");
202         rtl_Locale * pLocale = rtl_locale_register( rLanguageTag.getLanguage().getStr(),
203                 rLanguageTag.getCountry().getStr(), OUString().getStr() );
204         rtl_TextEncoding nEnc = osl_getTextEncodingFromLocale( pLocale );
205         pCharSet = rtl_getBestMimeCharsetFromTextEncoding( nEnc );
206     }
207     rtl_TextEncoding nRet;
208     if ( pCharSet )
209         nRet = rtl_getTextEncodingFromMimeCharset( pCharSet );
210     else
211         nRet = RTL_TEXTENCODING_UTF8;
212     return nRet;
213 }
214 
215 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
216