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