xref: /haiku/src/kits/locale/LocaleRoster.cpp (revision 675ffabd70492a962f8c0288a32208c22ce5de18)
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 <set>
14 
15 #include <assert.h>
16 #include <syslog.h>
17 
18 #include <Autolock.h>
19 #include <AppFileInfo.h>
20 #include <Catalog.h>
21 #include <Collator.h>
22 #include <Country.h>
23 #include <DefaultCatalog.h>
24 #include <Directory.h>
25 #include <Entry.h>
26 #include <File.h>
27 #include <Language.h>
28 #include <Locale.h>
29 #include <MutableLocaleRoster.h>
30 #include <Node.h>
31 #include <Path.h>
32 #include <String.h>
33 #include <TimeZone.h>
34 
35 #include <ICUWrapper.h>
36 
37 // ICU includes
38 #include <unicode/locid.h>
39 #include <unicode/timezone.h>
40 
41 
42 using BPrivate::CatalogAddOnInfo;
43 
44 
45 /*
46  * several attributes/resource-IDs used within the Locale Kit:
47  */
48 const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
49 	// name of catalog language, lives in every catalog file
50 const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
51 	// catalog signature, lives in every catalog file
52 const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
53 	// catalog fingerprint, may live in catalog file
54 
55 const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG";
56 	// attribute which contains flattened data of embedded catalog
57 	// this may live in an app- or add-on-file
58 int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA;
59 	// a unique value used to identify the resource (=> embedded CAtalog DAta)
60 	// which contains flattened data of embedded catalog.
61 	// this may live in an app- or add-on-file
62 
63 
64 BLocaleRoster::BLocaleRoster()
65 {
66 }
67 
68 
69 BLocaleRoster::~BLocaleRoster()
70 {
71 }
72 
73 
74 status_t
75 BLocaleRoster::Refresh()
76 {
77 	return gRosterData.Refresh();
78 }
79 
80 
81 status_t
82 BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const
83 {
84 	if (!timezone)
85 		return B_BAD_VALUE;
86 
87 	BAutolock lock(gRosterData.fLock);
88 	if (!lock.IsLocked())
89 		return B_ERROR;
90 
91 	*timezone = gRosterData.fDefaultTimeZone;
92 
93 	return B_OK;
94 }
95 
96 
97 status_t
98 BLocaleRoster::GetLanguage(const char* languageCode,
99 	BLanguage** _language) const
100 {
101 	if (_language == NULL || languageCode == NULL || languageCode[0] == '\0')
102 		return B_BAD_VALUE;
103 
104 	BLanguage* language = new(std::nothrow) BLanguage(languageCode);
105 	if (language == NULL)
106 		return B_NO_MEMORY;
107 
108 	*_language = language;
109 	return B_OK;
110 }
111 
112 
113 status_t
114 BLocaleRoster::GetPreferredLanguages(BMessage* languages) const
115 {
116 	if (!languages)
117 		return B_BAD_VALUE;
118 
119 	BAutolock lock(gRosterData.fLock);
120 	if (!lock.IsLocked())
121 		return B_ERROR;
122 
123 	*languages = gRosterData.fPreferredLanguages;
124 
125 	return B_OK;
126 }
127 
128 
129 status_t
130 BLocaleRoster::GetInstalledLanguages(BMessage* languages) const
131 {
132 	if (!languages)
133 		return B_BAD_VALUE;
134 
135 	int32 i;
136 	UnicodeString icuLanguageName;
137 	BString languageName;
138 
139 	int32_t localeCount;
140 	const Locale* icuLocaleList
141 		= Locale::getAvailableLocales(localeCount);
142 
143 	// TODO: Loop over the strings and add them to a std::set to remove
144 	//       duplicates?
145 	for (i = 0; i < localeCount; i++)
146 		languages->AddString("langs", icuLocaleList[i].getName());
147 
148 	return B_OK;
149 }
150 
151 
152 status_t
153 BLocaleRoster::GetAvailableCountries(BMessage* countries) const
154 {
155 	if (!countries)
156 		return B_BAD_VALUE;
157 
158 	int32 i;
159 	const char* const* countryList = uloc_getISOCountries();
160 
161 	for (i = 0; countryList[i] != NULL; i++)
162 		countries->AddString("countries", countryList[i]);
163 
164 	return B_OK;
165 }
166 
167 
168 status_t
169 BLocaleRoster::GetAvailableTimeZones(BMessage* timeZones) const
170 {
171 	if (!timeZones)
172 		return B_BAD_VALUE;
173 
174 	status_t status = B_OK;
175 
176 	StringEnumeration* zoneList = TimeZone::createEnumeration();
177 
178 	UErrorCode icuStatus = U_ZERO_ERROR;
179 	int32 count = zoneList->count(icuStatus);
180 	if (U_SUCCESS(icuStatus)) {
181 		for (int i = 0; i < count; ++i) {
182 			const char* zoneID = zoneList->next(NULL, icuStatus);
183 			if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
184 				status = B_ERROR;
185 				break;
186 			}
187  			timeZones->AddString("timeZone", zoneID);
188 		}
189 	} else
190 		status = B_ERROR;
191 
192 	delete zoneList;
193 
194 	return status;
195 }
196 
197 
198 status_t
199 BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage* timeZones,
200 	const char* countryCode) const
201 {
202 	if (!timeZones)
203 		return B_BAD_VALUE;
204 
205 	status_t status = B_OK;
206 
207 	StringEnumeration* zoneList = TimeZone::createEnumeration(countryCode);
208 		// countryCode == NULL will yield all timezones not bound to a country
209 
210 	UErrorCode icuStatus = U_ZERO_ERROR;
211 	int32 count = zoneList->count(icuStatus);
212 	if (U_SUCCESS(icuStatus)) {
213 		for (int i = 0; i < count; ++i) {
214 			const char* zoneID = zoneList->next(NULL, icuStatus);
215 			if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
216 				status = B_ERROR;
217 				break;
218 			}
219  			timeZones->AddString("timeZone", zoneID);
220 		}
221 	} else
222 		status = B_ERROR;
223 
224 	delete zoneList;
225 
226 	return status;
227 }
228 
229 
230 status_t
231 BLocaleRoster::GetInstalledCatalogs(BMessage*  languageList,
232 		const char* sigPattern,	const char* langPattern, int32 fingerprint) const
233 {
234 	if (languageList == NULL)
235 		return B_BAD_VALUE;
236 
237 	BAutolock lock(gRosterData.fLock);
238 	if (!lock.IsLocked())
239 		return B_ERROR;
240 
241 	int32 count = gRosterData.fCatalogAddOnInfos.CountItems();
242 	for (int32 i = 0; i < count; ++i) {
243 		CatalogAddOnInfo* info
244 			= (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i);
245 
246 		if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc)
247 			continue;
248 
249 		info->fLanguagesFunc(languageList, sigPattern, langPattern,
250 			fingerprint);
251 	}
252 
253 	return B_OK;
254 }
255 
256 
257 BCatalog*
258 BLocaleRoster::_GetCatalog(BCatalog* catalog, vint32* catalogInitStatus)
259 {
260 	// This function is used in the translation macros, so it can't return a
261 	// status_t. Maybe it could throw exceptions ?
262 
263 	if (*catalogInitStatus == true) {
264 		// Catalog already loaded - nothing else to do
265 		return catalog;
266 	}
267 
268 	// figure out image (shared object) from catalog address
269 	image_info info;
270 	int32 cookie = 0;
271 	bool found = false;
272 
273 	while (get_next_image_info(0, &cookie, &info) == B_OK) {
274 		if ((char*)info.data < (char*)catalog && (char*)info.data
275 				+ info.data_size > (char*)catalog) {
276 			found = true;
277 			break;
278 		}
279 	}
280 
281 	if (!found) {
282 		log_team(LOG_DEBUG, "Catalog %x doesn't belong to any image !",
283 			catalog);
284 		return catalog;
285 	}
286 	// figure out mimetype from image
287 	BFile objectFile(info.name, B_READ_ONLY);
288 	BAppFileInfo objectInfo(&objectFile);
289 	char objectSignature[B_MIME_TYPE_LENGTH];
290 	if (objectInfo.GetSignature(objectSignature) != B_OK) {
291 		log_team(LOG_ERR, "File %s has no mimesignature, so it can't use"
292 			"localization.", info.name);
293 		return catalog;
294 	}
295 
296 	// drop supertype from mimetype (should be "application/"):
297 	char* stripSignature = objectSignature;
298 	while (*stripSignature != '/')
299 		stripSignature ++;
300 	stripSignature ++;
301 
302 	log_team(LOG_DEBUG,
303 		"Image %s (address %x) requested catalog with mimetype %s",
304 		info.name, catalog, stripSignature);
305 
306 	// load the catalog for this mimetype and return it to the app
307 	catalog->SetCatalog(stripSignature, 0);
308 	*catalogInitStatus = true;
309 
310 	return catalog;
311 }
312