1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_shell.hxx"
30 
31 #include "localebackend.hxx"
32 #include <com/sun/star/beans/Optional.hpp>
33 #include <osl/time.h>
34 
35 #include <stdio.h>
36 
37 #if defined(LINUX) || defined(SOLARIS) || defined(NETBSD) || defined(FREEBSD) || defined(OS2)
38 
39 #include <rtl/ustrbuf.hxx>
40 #include <locale.h>
41 #include <string.h>
42 
43 /*
44  * Note: setlocale is not at all thread safe, so is this code. It could
45  * especially interfere with the stuff VCL is doing, so make sure this
46  * is called from the main thread only.
47  */
48 
49 static rtl::OUString ImplGetLocale(int category)
50 {
51     const char *locale = setlocale(category, "");
52 
53     // Return "en-US" for C locales
54     if( (locale == NULL) || ( locale[0] == 'C' && locale[1] == '\0' ) )
55         return rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "en-US" ) );
56 
57 
58     const char *cp;
59     const char *uscore = NULL;
60 
61     // locale string have the format lang[_ctry][.encoding][@modifier]
62     // we are only interested in the first two items, so we handle
63     // '.' and '@' as string end.
64     for (cp = locale; *cp; cp++)
65     {
66         if (*cp == '_')
67             uscore = cp;
68         if (*cp == '.' || *cp == '@')
69             break;
70     }
71 
72     rtl::OUStringBuffer aLocaleBuffer;
73     if( uscore != NULL )
74     {
75         aLocaleBuffer.appendAscii(locale, uscore++ - locale);
76         aLocaleBuffer.appendAscii("-");
77         aLocaleBuffer.appendAscii(uscore, cp - uscore);
78     }
79     else
80     {
81         aLocaleBuffer.appendAscii(locale, cp - locale);
82     }
83 
84     return aLocaleBuffer.makeStringAndClear();
85 }
86 
87 #elif defined(MACOSX)
88 
89 #include <rtl/ustrbuf.hxx>
90 #include <locale.h>
91 #include <string.h>
92 
93 #include <premac.h>
94 #include <CoreServices/CoreServices.h>
95 #include <CoreFoundation/CoreFoundation.h>
96 #include <postmac.h>
97 
98 namespace /* private */
99 {
100 
101 	void OUStringBufferAppendCFString(rtl::OUStringBuffer& buffer, const CFStringRef s)
102 	{
103 		CFIndex lstr = CFStringGetLength(s);
104 		for (CFIndex i = 0; i < lstr; i++)
105 			buffer.append(CFStringGetCharacterAtIndex(s, i));
106 	}
107 
108 	template <typename T>
109 	class CFGuard
110 	{
111 	public:
112 		explicit CFGuard(T& rT) : rT_(rT) {}
113 		~CFGuard() { if (rT_) CFRelease(rT_); }
114 	private:
115 		T& rT_;
116 	};
117 
118 	typedef CFGuard<CFArrayRef> CFArrayGuard;
119 	typedef CFGuard<CFStringRef> CFStringGuard;
120 	typedef CFGuard<CFTypeRef> CFTypeRefGuard;
121 
122 	/* For more information on the Apple locale concept please refer to
123 	http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFLocales/Articles/CFLocaleConcepts.html
124 	According to this documentation a locale identifier has the format: language[_country][_variant]*
125 	e.g. es_ES_PREEURO -> spain prior Euro support
126 	Note: The calling code should be able to handle locales with only language information e.g. 'en' for certain
127 	UI languages just the language code will be returned.
128 	*/
129 
130 	CFStringRef ImplGetAppPreference(const char* pref)
131 	{
132 		CFStringRef csPref = CFStringCreateWithCString(NULL, pref, kCFStringEncodingASCII);
133 		CFStringGuard csRefGuard(csPref);
134 
135 		CFTypeRef ref = CFPreferencesCopyAppValue(csPref, kCFPreferencesCurrentApplication);
136 		CFTypeRefGuard refGuard(ref);
137 
138 		if (ref == NULL)
139 			return NULL;
140 
141 		CFStringRef sref = (CFGetTypeID(ref) == CFArrayGetTypeID()) ? (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)ref, 0) : (CFStringRef)ref;
142 
143 		// NOTE: this API is only available with Mac OS X >=10.3. We need to use it because
144 		// Apple used non-ISO values on systems <10.2 like "German" for instance but didn't
145 		// upgrade those values during upgrade to newer Mac OS X versions. See also #i54337#
146 		return CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, sref);
147 	}
148 
149 	rtl::OUString ImplGetLocale(const char* pref)
150 	{
151 		CFStringRef sref = ImplGetAppPreference(pref);
152 		CFStringGuard srefGuard(sref);
153 
154 		rtl::OUStringBuffer aLocaleBuffer;
155 		aLocaleBuffer.appendAscii("en-US"); // initialize with fallback value
156 
157 		if (sref != NULL)
158 		{
159 			// split the string into substrings; the first two (if there are two) substrings
160 			// are language and country
161 			CFArrayRef subs = CFStringCreateArrayBySeparatingStrings(NULL, sref, CFSTR("_"));
162 			CFArrayGuard subsGuard(subs);
163 
164 			if (subs != NULL)
165 			{
166 				aLocaleBuffer.setLength(0); // clear buffer which still contains fallback value
167 
168 				CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(subs, 0);
169 				OUStringBufferAppendCFString(aLocaleBuffer, lang);
170 
171 				// country also available? Assumption: if the array contains more than one
172 				// value the second value is always the country!
173 				if (CFArrayGetCount(subs) > 1)
174 				{
175 					aLocaleBuffer.appendAscii("-");
176 					CFStringRef country = (CFStringRef)CFArrayGetValueAtIndex(subs, 1);
177 					OUStringBufferAppendCFString(aLocaleBuffer, country);
178 				}
179 			}
180 		}
181 		return aLocaleBuffer.makeStringAndClear();
182 	}
183 
184 } // namespace /* private */
185 
186 #endif
187 
188 // -------------------------------------------------------------------------------
189 
190 #ifdef WNT
191 
192 #ifdef WINVER
193 #undef WINVER
194 #endif
195 #define WINVER 0x0501
196 
197 #if defined _MSC_VER
198 #pragma warning(push, 1)
199 #endif
200 #include <windows.h>
201 #if defined _MSC_VER
202 #pragma warning(pop)
203 #endif
204 
205 rtl::OUString ImplGetLocale(LCID lcid)
206 {
207     TCHAR buffer[8];
208     LPTSTR cp = buffer;
209 
210     cp += GetLocaleInfo( lcid, LOCALE_SISO639LANGNAME , buffer, 4 );
211     if( cp > buffer )
212     {
213         if( 0 < GetLocaleInfo( lcid, LOCALE_SISO3166CTRYNAME, cp, buffer + 8 - cp) )
214             // #i50822# minus character must be written before cp
215             *(cp - 1) = '-';
216 
217         return rtl::OUString::createFromAscii(buffer);
218     }
219 
220     return rtl::OUString();
221 }
222 
223 #endif // WNT
224 
225 // -------------------------------------------------------------------------------
226 
227 LocaleBackend::LocaleBackend()
228 {
229 }
230 
231 //------------------------------------------------------------------------------
232 
233 LocaleBackend::~LocaleBackend(void)
234 {
235 }
236 
237 //------------------------------------------------------------------------------
238 
239 LocaleBackend* LocaleBackend::createInstance()
240 {
241     return new LocaleBackend;
242 }
243 
244 // ---------------------------------------------------------------------------------------
245 
246 rtl::OUString LocaleBackend::getLocale(void)
247 {
248 #if defined(LINUX) || defined(SOLARIS) || defined(NETBSD) || defined(FREEBSD) || defined(OS2)
249     return ImplGetLocale(LC_CTYPE);
250 #elif defined (MACOSX)
251 	return ImplGetLocale("AppleLocale");
252 #elif defined WNT
253     return ImplGetLocale( GetUserDefaultLCID() );
254 #endif
255 }
256 
257 //------------------------------------------------------------------------------
258 
259 rtl::OUString LocaleBackend::getUILocale(void)
260 {
261 #if defined(LINUX) || defined(SOLARIS) || defined(NETBSD) || defined(FREEBSD) || defined(OS2)
262     return ImplGetLocale(LC_MESSAGES);
263 #elif defined(MACOSX)
264 	return ImplGetLocale("AppleLanguages");
265 #elif defined WNT
266     return ImplGetLocale( MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT) );
267 #endif
268 }
269 
270 // ---------------------------------------------------------------------------------------
271 
272 rtl::OUString LocaleBackend::getSystemLocale(void)
273 {
274 // note: the implementation differs from getLocale() only on Windows
275 #if defined WNT
276     return ImplGetLocale( GetSystemDefaultLCID() );
277 #else
278     return getLocale();
279 #endif
280 }
281 //------------------------------------------------------------------------------
282 
283 void LocaleBackend::setPropertyValue(
284     rtl::OUString const &, css::uno::Any const &)
285     throw (
286         css::beans::UnknownPropertyException, css::beans::PropertyVetoException,
287         css::lang::IllegalArgumentException, css::lang::WrappedTargetException,
288         css::uno::RuntimeException)
289 {
290     throw css::lang::IllegalArgumentException(
291         rtl::OUString(
292             RTL_CONSTASCII_USTRINGPARAM("setPropertyValue not supported")),
293         static_cast< cppu::OWeakObject * >(this), -1);
294 }
295 
296 css::uno::Any LocaleBackend::getPropertyValue(
297     rtl::OUString const & PropertyName)
298     throw (
299         css::beans::UnknownPropertyException, css::lang::WrappedTargetException,
300         css::uno::RuntimeException)
301 {
302     if (PropertyName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Locale"))) {
303         return css::uno::makeAny(
304             css::beans::Optional< css::uno::Any >(
305                 true, css::uno::makeAny(getLocale())));
306     } else if (PropertyName.equalsAsciiL(
307                    RTL_CONSTASCII_STRINGPARAM("SystemLocale")))
308     {
309         return css::uno::makeAny(
310             css::beans::Optional< css::uno::Any >(
311                 true, css::uno::makeAny(getSystemLocale())));
312     } else if (PropertyName.equalsAsciiL(
313                    RTL_CONSTASCII_STRINGPARAM("UILocale")))
314     {
315         return css::uno::makeAny(
316             css::beans::Optional< css::uno::Any >(
317                 true, css::uno::makeAny(getUILocale())));
318     } else {
319         throw css::beans::UnknownPropertyException(
320             PropertyName, static_cast< cppu::OWeakObject * >(this));
321     }
322 }
323 
324 //------------------------------------------------------------------------------
325 
326 rtl::OUString SAL_CALL LocaleBackend::getBackendName(void) {
327 	return rtl::OUString::createFromAscii("com.sun.star.comp.configuration.backend.LocaleBackend") ;
328 }
329 
330 //------------------------------------------------------------------------------
331 
332 rtl::OUString SAL_CALL LocaleBackend::getImplementationName(void)
333     throw (uno::RuntimeException)
334 {
335     return getBackendName() ;
336 }
337 
338 //------------------------------------------------------------------------------
339 
340 uno::Sequence<rtl::OUString> SAL_CALL LocaleBackend::getBackendServiceNames(void)
341 {
342     uno::Sequence<rtl::OUString> aServiceNameList(1);
343     aServiceNameList[0] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.configuration.backend.LocaleBackend")) ;
344     return aServiceNameList ;
345 }
346 
347 //------------------------------------------------------------------------------
348 
349 sal_Bool SAL_CALL LocaleBackend::supportsService(const rtl::OUString& aServiceName)
350     throw (uno::RuntimeException)
351 {
352     uno::Sequence< rtl::OUString > const svc = getBackendServiceNames();
353 
354     for(sal_Int32 i = 0; i < svc.getLength(); ++i )
355         if(svc[i] == aServiceName)
356             return true;
357 
358     return false;
359 }
360 
361 //------------------------------------------------------------------------------
362 
363 uno::Sequence<rtl::OUString> SAL_CALL LocaleBackend::getSupportedServiceNames(void)
364     throw (uno::RuntimeException)
365 {
366     return getBackendServiceNames() ;
367 }
368