xref: /haiku/src/kits/locale/LocaleRoster.cpp (revision 70d5966963513ff47a456ba70b7d2eec0f99648d)
1 /*
2  * Copyright 2003-2010, Haiku. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Oliver Tappe, zooey@hirschkaefer.de
8  */
9 
10 
11 #include <LocaleRoster.h>
12 
13 #include <ctype.h>
14 #include <set>
15 
16 #include <assert.h>
17 #include <syslog.h>
18 
19 #include <Autolock.h>
20 #include <AppFileInfo.h>
21 #include <Bitmap.h>
22 #include <Catalog.h>
23 #include <Collator.h>
24 #include <DefaultCatalog.h>
25 #include <Directory.h>
26 #include <Entry.h>
27 #include <File.h>
28 #include <FormattingConventions.h>
29 #include <IconUtils.h>
30 #include <Language.h>
31 #include <Locale.h>
32 #include <MutableLocaleRoster.h>
33 #include <Node.h>
34 #include <Path.h>
35 #include <String.h>
36 #include <TimeZone.h>
37 
38 #include <ICUWrapper.h>
39 
40 // ICU includes
41 #include <unicode/locdspnm.h>
42 #include <unicode/locid.h>
43 #include <unicode/timezone.h>
44 
45 
46 using BPrivate::CatalogAddOnInfo;
47 
48 
49 /*
50  * several attributes/resource-IDs used within the Locale Kit:
51  */
52 const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
53 	// name of catalog language, lives in every catalog file
54 const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
55 	// catalog signature, lives in every catalog file
56 const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
57 	// catalog fingerprint, may live in catalog file
58 
59 const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG";
60 	// attribute which contains flattened data of embedded catalog
61 	// this may live in an app- or add-on-file
62 int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA;
63 	// a unique value used to identify the resource (=> embedded CAtalog DAta)
64 	// which contains flattened data of embedded catalog.
65 	// this may live in an app- or add-on-file
66 
67 
68 BLocaleRoster::BLocaleRoster()
69 {
70 }
71 
72 
73 BLocaleRoster::~BLocaleRoster()
74 {
75 }
76 
77 
78 /*static*/ BLocaleRoster*
79 BLocaleRoster::Default()
80 {
81 	return MutableLocaleRoster::Default();
82 }
83 
84 
85 status_t
86 BLocaleRoster::Refresh()
87 {
88 	return RosterData::Default()->Refresh();
89 }
90 
91 
92 status_t
93 BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const
94 {
95 	if (!timezone)
96 		return B_BAD_VALUE;
97 
98 	RosterData* rosterData = RosterData::Default();
99 	BAutolock lock(rosterData->fLock);
100 	if (!lock.IsLocked())
101 		return B_ERROR;
102 
103 	*timezone = rosterData->fDefaultTimeZone;
104 
105 	return B_OK;
106 }
107 
108 
109 status_t
110 BLocaleRoster::GetLanguage(const char* languageCode,
111 	BLanguage** _language) const
112 {
113 	if (_language == NULL || languageCode == NULL || languageCode[0] == '\0')
114 		return B_BAD_VALUE;
115 
116 	BLanguage* language = new(std::nothrow) BLanguage(languageCode);
117 	if (language == NULL)
118 		return B_NO_MEMORY;
119 
120 	*_language = language;
121 	return B_OK;
122 }
123 
124 
125 status_t
126 BLocaleRoster::GetPreferredLanguages(BMessage* languages) const
127 {
128 	if (!languages)
129 		return B_BAD_VALUE;
130 
131 	RosterData* rosterData = RosterData::Default();
132 	BAutolock lock(rosterData->fLock);
133 	if (!lock.IsLocked())
134 		return B_ERROR;
135 
136 	*languages = rosterData->fPreferredLanguages;
137 
138 	return B_OK;
139 }
140 
141 
142 /**
143  * \brief Fills \c message with 'language'-fields containing the language-
144  * ID(s) of all available languages.
145  */
146 status_t
147 BLocaleRoster::GetAvailableLanguages(BMessage* languages) const
148 {
149 	if (!languages)
150 		return B_BAD_VALUE;
151 
152 	int32_t localeCount;
153 	const Locale* icuLocaleList = Locale::getAvailableLocales(localeCount);
154 
155 	for (int i = 0; i < localeCount; i++)
156 		languages->AddString("language", icuLocaleList[i].getName());
157 
158 	return B_OK;
159 }
160 
161 
162 status_t
163 BLocaleRoster::GetAvailableCountries(BMessage* countries) const
164 {
165 	if (!countries)
166 		return B_BAD_VALUE;
167 
168 	int32 i;
169 	const char* const* countryList = uloc_getISOCountries();
170 
171 	for (i = 0; countryList[i] != NULL; i++)
172 		countries->AddString("country", countryList[i]);
173 
174 	return B_OK;
175 }
176 
177 
178 status_t
179 BLocaleRoster::GetAvailableTimeZones(BMessage* timeZones) const
180 {
181 	if (!timeZones)
182 		return B_BAD_VALUE;
183 
184 	status_t status = B_OK;
185 
186 	StringEnumeration* zoneList = TimeZone::createEnumeration();
187 
188 	UErrorCode icuStatus = U_ZERO_ERROR;
189 	int32 count = zoneList->count(icuStatus);
190 	if (U_SUCCESS(icuStatus)) {
191 		for (int i = 0; i < count; ++i) {
192 			const char* zoneID = zoneList->next(NULL, icuStatus);
193 			if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
194 				status = B_ERROR;
195 				break;
196 			}
197  			timeZones->AddString("timeZone", zoneID);
198 		}
199 	} else
200 		status = B_ERROR;
201 
202 	delete zoneList;
203 
204 	return status;
205 }
206 
207 
208 status_t
209 BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage* timeZones,
210 	const char* countryCode) const
211 {
212 	if (!timeZones)
213 		return B_BAD_VALUE;
214 
215 	status_t status = B_OK;
216 
217 	StringEnumeration* zoneList = TimeZone::createEnumeration(countryCode);
218 		// countryCode == NULL will yield all timezones not bound to a country
219 
220 	UErrorCode icuStatus = U_ZERO_ERROR;
221 	int32 count = zoneList->count(icuStatus);
222 	if (U_SUCCESS(icuStatus)) {
223 		for (int i = 0; i < count; ++i) {
224 			const char* zoneID = zoneList->next(NULL, icuStatus);
225 			if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
226 				status = B_ERROR;
227 				break;
228 			}
229 			timeZones->AddString("timeZone", zoneID);
230 		}
231 	} else
232 		status = B_ERROR;
233 
234 	delete zoneList;
235 
236 	return status;
237 }
238 
239 
240 status_t
241 BLocaleRoster::GetFlagIconForCountry(BBitmap* flagIcon, const char* countryCode)
242 {
243 	if (countryCode == NULL)
244 		return B_BAD_DATA;
245 
246 	RosterData* rosterData = RosterData::Default();
247 	BAutolock lock(rosterData->fLock);
248 	if (!lock.IsLocked())
249 		return B_ERROR;
250 
251 	if (!rosterData->fAreResourcesLoaded) {
252 		status_t result = rosterData->fResources.SetToImage(rosterData);
253 		if (result != B_OK)
254 			return result;
255 
256 		result = rosterData->fResources.PreloadResourceType();
257 		if (result != B_OK)
258 			return result;
259 
260 		rosterData->fAreResourcesLoaded = true;
261 	}
262 
263 	size_t size;
264 
265 	// normalize the country code : 2 letters uparcase
266 	// filter things out so that "pt_BR" gived the flag for brazil
267 	char normalizedCode[3];
268 	normalizedCode[2] = '\0';
269 
270 	int codeLength = strlen(countryCode);
271 
272 	normalizedCode[0] = toupper(countryCode[codeLength - 2]);
273 	normalizedCode[1] = toupper(countryCode[codeLength - 1]);
274 
275 	const void* buffer = rosterData->fResources.LoadResource(
276 		B_VECTOR_ICON_TYPE, normalizedCode, &size);
277 	if (buffer == NULL || size == 0)
278 		return B_NAME_NOT_FOUND;
279 
280 	return BIconUtils::GetVectorIcon(static_cast<const uint8*>(buffer), size,
281 		flagIcon);
282 }
283 
284 
285 status_t
286 BLocaleRoster::GetAvailableCatalogs(BMessage*  languageList,
287 	const char* sigPattern,	const char* langPattern, int32 fingerprint) const
288 {
289 	if (languageList == NULL)
290 		return B_BAD_VALUE;
291 
292 	RosterData* rosterData = RosterData::Default();
293 	BAutolock lock(rosterData->fLock);
294 	if (!lock.IsLocked())
295 		return B_ERROR;
296 
297 	int32 count = rosterData->fCatalogAddOnInfos.CountItems();
298 	for (int32 i = 0; i < count; ++i) {
299 		CatalogAddOnInfo* info
300 			= (CatalogAddOnInfo*)rosterData->fCatalogAddOnInfos.ItemAt(i);
301 
302 		if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc)
303 			continue;
304 
305 		info->fLanguagesFunc(languageList, sigPattern, langPattern,
306 			fingerprint);
307 	}
308 
309 	return B_OK;
310 }
311 
312 
313 BCatalog*
314 BLocaleRoster::_GetCatalog(BCatalog* catalog, vint32* catalogInitStatus)
315 {
316 	// This function is used in the translation macros, so it can't return a
317 	// status_t. Maybe it could throw exceptions ?
318 
319 	if (*catalogInitStatus == true) {
320 		// Catalog already loaded - nothing else to do
321 		return catalog;
322 	}
323 
324 	// figure out image (shared object) from catalog address
325 	image_info info;
326 	int32 cookie = 0;
327 	bool found = false;
328 
329 	while (get_next_image_info(0, &cookie, &info) == B_OK) {
330 		if ((char*)info.data < (char*)catalog && (char*)info.data
331 				+ info.data_size > (char*)catalog) {
332 			found = true;
333 			break;
334 		}
335 	}
336 
337 	if (!found) {
338 		log_team(LOG_DEBUG, "Catalog %x doesn't belong to any image!",
339 			catalog);
340 		return catalog;
341 	}
342 	// figure out mimetype from image
343 	BFile objectFile(info.name, B_READ_ONLY);
344 	BAppFileInfo objectInfo(&objectFile);
345 	char objectSignature[B_MIME_TYPE_LENGTH];
346 	if (objectInfo.GetSignature(objectSignature) != B_OK) {
347 		log_team(LOG_ERR, "File %s has no mimesignature, so it can't use"
348 			" localization.", info.name);
349 		return catalog;
350 	}
351 
352 	// drop supertype from mimetype (should be "application/"):
353 	char* stripSignature = objectSignature;
354 	while (*stripSignature != '/')
355 		stripSignature ++;
356 	stripSignature ++;
357 
358 	log_team(LOG_DEBUG,
359 		"Image %s (address %x) requested catalog with mimetype %s",
360 		info.name, catalog, stripSignature);
361 
362 	// load the catalog for this mimetype and return it to the app
363 	catalog->SetCatalog(stripSignature, 0);
364 	*catalogInitStatus = true;
365 
366 	return catalog;
367 }
368