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 <MutableLocaleRoster.h> 12 13 #include <set> 14 15 #include <syslog.h> 16 17 #include <AppFileInfo.h> 18 #include <Autolock.h> 19 #include <Catalog.h> 20 #include <Collator.h> 21 #include <Country.h> 22 #include <DefaultCatalog.h> 23 #include <Directory.h> 24 #include <Entry.h> 25 #include <File.h> 26 #include <FindDirectory.h> 27 #include <Language.h> 28 #include <Locale.h> 29 #include <Node.h> 30 #include <Path.h> 31 #include <Roster.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 namespace BPrivate { 43 44 45 // #pragma mark - CatalogAddOnInfo 46 47 48 CatalogAddOnInfo::CatalogAddOnInfo(const BString& name, const BString& path, 49 uint8 priority) 50 : 51 fInstantiateFunc(NULL), 52 fInstantiateEmbeddedFunc(NULL), 53 fCreateFunc(NULL), 54 fLanguagesFunc(NULL), 55 fName(name), 56 fPath(path), 57 fAddOnImage(B_NO_INIT), 58 fPriority(priority), 59 fIsEmbedded(path.Length()==0) 60 { 61 } 62 63 64 CatalogAddOnInfo::~CatalogAddOnInfo() 65 { 66 int32 count = fLoadedCatalogs.CountItems(); 67 for (int32 i = 0; i < count; ++i) { 68 BCatalogAddOn* cat 69 = static_cast<BCatalogAddOn*>(fLoadedCatalogs.ItemAt(i)); 70 delete cat; 71 } 72 fLoadedCatalogs.MakeEmpty(); 73 UnloadIfPossible(); 74 } 75 76 77 bool 78 CatalogAddOnInfo::MakeSureItsLoaded() 79 { 80 if (!fIsEmbedded && fAddOnImage < B_OK) { 81 // add-on has not been loaded yet, so we try to load it: 82 BString fullAddOnPath(fPath); 83 fullAddOnPath << "/" << fName; 84 fAddOnImage = load_add_on(fullAddOnPath.String()); 85 if (fAddOnImage >= B_OK) { 86 get_image_symbol(fAddOnImage, "instantiate_catalog", 87 B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateFunc); 88 get_image_symbol(fAddOnImage, "instantiate_embedded_catalog", 89 B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateEmbeddedFunc); 90 get_image_symbol(fAddOnImage, "create_catalog", 91 B_SYMBOL_TYPE_TEXT, (void**)&fCreateFunc); 92 get_image_symbol(fAddOnImage, "get_available_languages", 93 B_SYMBOL_TYPE_TEXT, (void**)&fLanguagesFunc); 94 log_team(LOG_DEBUG, "catalog-add-on %s has been loaded", 95 fName.String()); 96 } else { 97 log_team(LOG_DEBUG, "could not load catalog-add-on %s (%s)", 98 fName.String(), strerror(fAddOnImage)); 99 return false; 100 } 101 } else if (fIsEmbedded) { 102 // The built-in catalog still has to provide this function 103 fLanguagesFunc = default_catalog_get_available_languages; 104 } 105 return true; 106 } 107 108 109 void 110 CatalogAddOnInfo::UnloadIfPossible() 111 { 112 if (!fIsEmbedded && fLoadedCatalogs.IsEmpty()) { 113 unload_add_on(fAddOnImage); 114 fAddOnImage = B_NO_INIT; 115 fInstantiateFunc = NULL; 116 fInstantiateEmbeddedFunc = NULL; 117 fCreateFunc = NULL; 118 fLanguagesFunc = NULL; 119 // log_team(LOG_DEBUG, "catalog-add-on %s has been unloaded", 120 // fName.String()); 121 } 122 } 123 124 125 // #pragma mark - RosterData 126 127 128 RosterData gRosterData; 129 130 static const char* kPriorityAttr = "ADDON:priority"; 131 132 133 RosterData::RosterData() 134 : 135 fLock("LocaleRosterData"), 136 fAreResourcesLoaded(false) 137 { 138 openlog_team("liblocale.so", LOG_PID, LOG_USER); 139 #ifndef DEBUG 140 setlogmask_team(LOG_UPTO(LOG_WARNING)); 141 #endif 142 143 InitializeCatalogAddOns(); 144 145 Refresh(); 146 } 147 148 149 RosterData::~RosterData() 150 { 151 BAutolock lock(fLock); 152 153 CleanupCatalogAddOns(); 154 closelog(); 155 } 156 157 158 int 159 RosterData::CompareInfos(const void* left, const void* right) 160 { 161 return ((CatalogAddOnInfo*)right)->fPriority 162 - ((CatalogAddOnInfo*)left)->fPriority; 163 } 164 165 166 status_t 167 RosterData::Refresh() 168 { 169 BAutolock lock(fLock); 170 if (!lock.IsLocked()) 171 return B_ERROR; 172 173 _LoadLocaleSettings(); 174 _LoadTimeSettings(); 175 176 return B_OK; 177 } 178 179 /* 180 iterate over add-on-folders and collect information about each 181 catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. 182 */ 183 void 184 RosterData::InitializeCatalogAddOns() 185 { 186 BAutolock lock(fLock); 187 if (!lock.IsLocked()) 188 return; 189 190 // add info about embedded default catalog: 191 CatalogAddOnInfo* defaultCatalogAddOnInfo 192 = new(std::nothrow) CatalogAddOnInfo("Default", "", 193 DefaultCatalog::kDefaultCatalogAddOnPriority); 194 if (!defaultCatalogAddOnInfo) 195 return; 196 197 defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate; 198 defaultCatalogAddOnInfo->fInstantiateEmbeddedFunc 199 = DefaultCatalog::InstantiateEmbedded; 200 defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create; 201 fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo); 202 203 directory_which folders[] = { 204 B_COMMON_ADDONS_DIRECTORY, 205 B_SYSTEM_ADDONS_DIRECTORY, 206 static_cast<directory_which>(-1) 207 }; 208 BPath addOnPath; 209 BDirectory addOnFolder; 210 char buf[4096]; 211 status_t err; 212 for (int f = 0; folders[f]>=0; ++f) { 213 find_directory(folders[f], &addOnPath); 214 BString addOnFolderName(addOnPath.Path()); 215 addOnFolderName << "/locale/catalogs"; 216 217 system_info info; 218 if (get_system_info(&info) == B_OK 219 && (info.abi & B_HAIKU_ABI_MAJOR) 220 != (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR)) { 221 switch (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) { 222 case B_HAIKU_ABI_GCC_2: 223 addOnFolderName << "/gcc2"; 224 break; 225 case B_HAIKU_ABI_GCC_4: 226 addOnFolderName << "/gcc4"; 227 break; 228 } 229 } 230 231 232 err = addOnFolder.SetTo(addOnFolderName.String()); 233 if (err != B_OK) 234 continue; 235 236 // scan through all the folder's entries for catalog add-ons: 237 int32 count; 238 int8 priority; 239 entry_ref eref; 240 BNode node; 241 BEntry entry; 242 dirent* dent; 243 while ((count = addOnFolder.GetNextDirents((dirent*)buf, 4096)) > 0) { 244 dent = (dirent*)buf; 245 while (count-- > 0) { 246 if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..") 247 && strcmp(dent->d_name, "gcc2") 248 && strcmp(dent->d_name, "gcc4")) { 249 // we have found (what should be) a catalog-add-on: 250 eref.device = dent->d_pdev; 251 eref.directory = dent->d_pino; 252 eref.set_name(dent->d_name); 253 entry.SetTo(&eref, true); 254 // traverse through any links to get to the real thang! 255 node.SetTo(&entry); 256 priority = -1; 257 if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0, 258 &priority, sizeof(int8)) <= 0) { 259 // add-on has no priority-attribute yet, so we load it 260 // to fetch the priority from the corresponding 261 // symbol... 262 BString fullAddOnPath(addOnFolderName); 263 fullAddOnPath << "/" << dent->d_name; 264 image_id image = load_add_on(fullAddOnPath.String()); 265 if (image >= B_OK) { 266 uint8* prioPtr; 267 if (get_image_symbol(image, "gCatalogAddOnPriority", 268 B_SYMBOL_TYPE_DATA, 269 (void**)&prioPtr) == B_OK) { 270 priority = *prioPtr; 271 node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0, 272 &priority, sizeof(int8)); 273 } else { 274 log_team(LOG_ERR, 275 "couldn't get priority for add-on %s\n", 276 fullAddOnPath.String()); 277 } 278 unload_add_on(image); 279 } else { 280 log_team(LOG_ERR, 281 "couldn't load add-on %s, error: %s\n", 282 fullAddOnPath.String(), strerror(image)); 283 } 284 } 285 286 if (priority >= 0) { 287 // add-ons with priority < 0 will be ignored 288 CatalogAddOnInfo* addOnInfo 289 = new(std::nothrow) CatalogAddOnInfo(dent->d_name, 290 addOnFolderName, priority); 291 if (addOnInfo) 292 fCatalogAddOnInfos.AddItem((void*)addOnInfo); 293 } 294 } 295 // Bump the dirent-pointer by length of the dirent just handled: 296 dent = (dirent*)((char*)dent + dent->d_reclen); 297 } 298 } 299 } 300 fCatalogAddOnInfos.SortItems(CompareInfos); 301 } 302 303 304 /* 305 * unloads all catalog-add-ons (which will throw away all loaded catalogs, too) 306 */ 307 void 308 RosterData::CleanupCatalogAddOns() 309 { 310 BAutolock lock(fLock); 311 if (!lock.IsLocked()) 312 return; 313 314 int32 count = fCatalogAddOnInfos.CountItems(); 315 for (int32 i = 0; i<count; ++i) { 316 CatalogAddOnInfo* info 317 = static_cast<CatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i)); 318 delete info; 319 } 320 fCatalogAddOnInfos.MakeEmpty(); 321 } 322 323 324 status_t 325 RosterData::SetDefaultCountry(const BCountry& newCountry) 326 { 327 status_t status = B_OK; 328 329 BAutolock lock(fLock); 330 if (!lock.IsLocked()) 331 return B_ERROR; 332 333 status = _SetDefaultCountry(newCountry); 334 335 if (status == B_OK) 336 status = _SaveLocaleSettings(); 337 338 if (status == B_OK) { 339 BMessage updateMessage(B_LOCALE_CHANGED); 340 status = _AddDefaultCountryToMessage(&updateMessage); 341 if (status == B_OK) 342 status = be_roster->Broadcast(&updateMessage); 343 } 344 345 return status; 346 } 347 348 349 status_t 350 RosterData::SetDefaultLocale(const BLocale& newLocale) 351 { 352 status_t status = B_OK; 353 354 BAutolock lock(fLock); 355 if (!lock.IsLocked()) 356 return B_ERROR; 357 358 status = _SetDefaultLocale(newLocale); 359 360 if (status == B_OK) 361 status = _SaveLocaleSettings(); 362 363 if (status == B_OK) { 364 BMessage updateMessage(B_LOCALE_CHANGED); 365 status = _AddDefaultCountryToMessage(&updateMessage); 366 // TODO : should be differenciated from country 367 if (status == B_OK) 368 status = be_roster->Broadcast(&updateMessage); 369 } 370 371 return status; 372 } 373 374 375 status_t 376 RosterData::SetDefaultTimeZone(const BTimeZone& newZone) 377 { 378 status_t status = B_OK; 379 380 BAutolock lock(fLock); 381 if (!lock.IsLocked()) 382 return B_ERROR; 383 384 status = _SetDefaultTimeZone(newZone); 385 386 if (status == B_OK) 387 status = _SaveTimeSettings(); 388 389 if (status == B_OK) { 390 BMessage updateMessage(B_LOCALE_CHANGED); 391 status = _AddDefaultTimeZoneToMessage(&updateMessage); 392 if (status == B_OK) 393 status = be_roster->Broadcast(&updateMessage); 394 } 395 396 return status; 397 } 398 399 400 status_t 401 RosterData::SetPreferredLanguages(const BMessage* languages) 402 { 403 status_t status = B_OK; 404 405 BAutolock lock(fLock); 406 if (!lock.IsLocked()) 407 return B_ERROR; 408 409 status = _SetPreferredLanguages(languages); 410 411 if (status == B_OK) 412 status = _SaveLocaleSettings(); 413 414 if (status == B_OK) { 415 BMessage updateMessage(B_LOCALE_CHANGED); 416 status = _AddPreferredLanguagesToMessage(&updateMessage); 417 if (status == B_OK) 418 status = be_roster->Broadcast(&updateMessage); 419 } 420 421 return status; 422 } 423 424 425 status_t 426 RosterData::_LoadLocaleSettings() 427 { 428 BPath path; 429 BFile file; 430 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 431 if (status == B_OK) { 432 path.Append("Locale settings"); 433 status = file.SetTo(path.Path(), B_READ_ONLY); 434 } 435 BMessage settings; 436 if (status == B_OK) 437 status = settings.Unflatten(&file); 438 439 if (status == B_OK) { 440 BString codeName; 441 BLocale newDefaultLocale; 442 443 if (settings.FindString("country", &codeName) == B_OK) { 444 BCountry country(codeName); 445 newDefaultLocale = BLocale(NULL, &country); 446 } 447 448 BString timeFormat; 449 if (settings.FindString("shortTimeFormat", &timeFormat) == B_OK) 450 newDefaultLocale.SetTimeFormat(timeFormat, false); 451 if (settings.FindString("longTimeFormat", &timeFormat) == B_OK) 452 newDefaultLocale.SetTimeFormat(timeFormat, true); 453 454 _SetDefaultLocale(newDefaultLocale); 455 456 _SetPreferredLanguages(&settings); 457 458 return B_OK; 459 } 460 461 462 // Something went wrong (no settings file or invalid BMessage), so we 463 // set everything to default values 464 465 fPreferredLanguages.MakeEmpty(); 466 fPreferredLanguages.AddString("language", "en"); 467 BLanguage defaultLanguage("en_US"); 468 _SetDefaultLocale(BLocale(&defaultLanguage)); 469 470 return status; 471 } 472 473 474 status_t 475 RosterData::_LoadTimeSettings() 476 { 477 BPath path; 478 BFile file; 479 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 480 if (status == B_OK) { 481 path.Append("Time settings"); 482 status = file.SetTo(path.Path(), B_READ_ONLY); 483 } 484 BMessage settings; 485 if (status == B_OK) 486 status = settings.Unflatten(&file); 487 if (status == B_OK) { 488 BString timeZoneID; 489 if (settings.FindString("timezone", &timeZoneID) == B_OK) 490 _SetDefaultTimeZone(BTimeZone(timeZoneID.String())); 491 else 492 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone)); 493 494 return B_OK; 495 } 496 497 // Something went wrong (no settings file or invalid BMessage), so we 498 // set everything to default values 499 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone)); 500 501 return status; 502 } 503 504 505 status_t 506 RosterData::_SaveLocaleSettings() 507 { 508 BMessage settings; 509 status_t status = _AddDefaultCountryToMessage(&settings); 510 if (status == B_OK) 511 _AddPreferredLanguagesToMessage(&settings); 512 513 BPath path; 514 if (status == B_OK) 515 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 516 517 BFile file; 518 if (status == B_OK) { 519 path.Append("Locale settings"); 520 status = file.SetTo(path.Path(), 521 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 522 } 523 if (status == B_OK) 524 status = settings.Flatten(&file); 525 if (status == B_OK) 526 status = file.Sync(); 527 528 return status; 529 } 530 531 532 status_t 533 RosterData::_SaveTimeSettings() 534 { 535 BMessage settings; 536 status_t status = _AddDefaultTimeZoneToMessage(&settings); 537 538 BPath path; 539 if (status == B_OK) 540 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path); 541 542 BFile file; 543 if (status == B_OK) { 544 path.Append("Time settings"); 545 status = file.SetTo(path.Path(), 546 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 547 } 548 if (status == B_OK) 549 status = settings.Flatten(&file); 550 if (status == B_OK) 551 status = file.Sync(); 552 553 return status; 554 } 555 556 557 status_t 558 RosterData::_SetDefaultCountry(const BCountry& newCountry) 559 { 560 fDefaultLocale.SetCountry(newCountry); 561 562 UErrorCode icuError = U_ZERO_ERROR; 563 Locale icuLocale = Locale::createCanonical(newCountry.Code()); 564 if (icuLocale.isBogus()) 565 return B_ERROR; 566 567 Locale::setDefault(icuLocale, icuError); 568 if (!U_SUCCESS(icuError)) 569 return B_ERROR; 570 571 return B_OK; 572 } 573 574 575 status_t 576 RosterData::_SetDefaultLocale(const BLocale& newLocale) 577 { 578 fDefaultLocale = newLocale; 579 580 UErrorCode icuError = U_ZERO_ERROR; 581 BCountry defaultCountry; 582 newLocale.GetCountry(&defaultCountry); 583 Locale icuLocale = Locale::createCanonical(defaultCountry.Code()); 584 if (icuLocale.isBogus()) 585 return B_ERROR; 586 587 Locale::setDefault(icuLocale, icuError); 588 if (!U_SUCCESS(icuError)) 589 return B_ERROR; 590 591 return B_OK; 592 } 593 594 595 status_t 596 RosterData::_SetDefaultTimeZone(const BTimeZone& newZone) 597 { 598 fDefaultTimeZone = newZone; 599 600 TimeZone* timeZone = TimeZone::createTimeZone(newZone.ID().String()); 601 if (timeZone == NULL) 602 return B_ERROR; 603 TimeZone::adoptDefault(timeZone); 604 605 return B_OK; 606 } 607 608 609 status_t 610 RosterData::_SetPreferredLanguages(const BMessage* languages) 611 { 612 BString langName; 613 if (languages != NULL 614 && languages->FindString("language", &langName) == B_OK) { 615 fDefaultLocale.SetCollator(BCollator(langName.String())); 616 fDefaultLocale.SetLanguage(BLanguage(langName.String())); 617 618 fPreferredLanguages.RemoveName("language"); 619 for (int i = 0; languages->FindString("language", i, &langName) == B_OK; 620 i++) { 621 fPreferredLanguages.AddString("language", langName); 622 } 623 } else { 624 fPreferredLanguages.MakeEmpty(); 625 fPreferredLanguages.AddString("language", "en"); 626 fDefaultLocale.SetCollator(BCollator("en")); 627 } 628 629 return B_OK; 630 } 631 632 633 status_t 634 RosterData::_AddDefaultCountryToMessage(BMessage* message) const 635 { 636 BCountry defaultCountry; 637 fDefaultLocale.GetCountry(&defaultCountry); 638 status_t status = message->AddString("country", defaultCountry.Code()); 639 BString timeFormat; 640 if (status == B_OK) 641 status = fDefaultLocale.GetTimeFormat(timeFormat, false); 642 if (status == B_OK) 643 status = message->AddString("shortTimeFormat", timeFormat.String()); 644 if (status == B_OK) 645 status = fDefaultLocale.GetTimeFormat(timeFormat, true); 646 if (status == B_OK) 647 status = message->AddString("longTimeFormat", timeFormat.String()); 648 649 return status; 650 } 651 652 653 status_t 654 RosterData::_AddDefaultTimeZoneToMessage(BMessage* message) const 655 { 656 status_t status = message->AddString("timezone", fDefaultTimeZone.ID()); 657 658 // add the offset, too, since that is used by clockconfig when setting 659 // up timezone state during boot 660 if (status == B_OK) 661 status = message->AddInt32("offset", fDefaultTimeZone.OffsetFromGMT()); 662 663 return status; 664 } 665 666 667 status_t 668 RosterData::_AddPreferredLanguagesToMessage(BMessage* message) const 669 { 670 status_t status = B_OK; 671 672 BString langName; 673 for (int i = 0; fPreferredLanguages.FindString("language", i, 674 &langName) == B_OK; i++) { 675 status = message->AddString("language", langName); 676 if (status != B_OK) 677 break; 678 } 679 680 return status; 681 } 682 683 684 // #pragma mark - MutableLocaleRoster 685 686 687 static MutableLocaleRoster gLocaleRoster; 688 MutableLocaleRoster* gMutableLocaleRoster = &gLocaleRoster; 689 690 691 MutableLocaleRoster::MutableLocaleRoster() 692 { 693 } 694 695 696 MutableLocaleRoster::~MutableLocaleRoster() 697 { 698 } 699 700 701 status_t 702 MutableLocaleRoster::SetDefaultCountry(const BCountry& newCountry) 703 { 704 return gRosterData.SetDefaultCountry(newCountry); 705 } 706 707 708 status_t 709 MutableLocaleRoster::SetDefaultLocale(const BLocale& newLocale) 710 { 711 return gRosterData.SetDefaultLocale(newLocale); 712 } 713 714 715 status_t 716 MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone) 717 { 718 return gRosterData.SetDefaultTimeZone(newZone); 719 } 720 721 722 status_t 723 MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages) 724 { 725 return gRosterData.SetPreferredLanguages(languages); 726 } 727 728 729 status_t 730 MutableLocaleRoster::GetDefaultLocale(BLocale* locale) const 731 { 732 if (!locale) 733 return B_BAD_VALUE; 734 735 BAutolock lock(gRosterData.fLock); 736 if (!lock.IsLocked()) 737 return B_ERROR; 738 739 *locale = gRosterData.fDefaultLocale; 740 741 return B_OK; 742 } 743 744 745 status_t 746 MutableLocaleRoster::GetSystemCatalog(BCatalogAddOn** catalog) const 747 { 748 if (!catalog) 749 return B_BAD_VALUE; 750 *catalog = LoadCatalog("system"); 751 return B_OK; 752 } 753 754 755 /* 756 * creates a new (empty) catalog of the given type (the request is dispatched 757 * to the appropriate add-on). 758 * If the add-on doesn't support catalog-creation or if the creation fails, 759 * NULL is returned, otherwise a pointer to the freshly created catalog. 760 * Any created catalog will be initialized with the given signature and 761 * language-name. 762 */ 763 BCatalogAddOn* 764 MutableLocaleRoster::CreateCatalog(const char* type, const char* signature, 765 const char* language) 766 { 767 if (!type || !signature || !language) 768 return NULL; 769 770 BAutolock lock(gRosterData.fLock); 771 if (!lock.IsLocked()) 772 return NULL; 773 774 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 775 for (int32 i = 0; i < count; ++i) { 776 CatalogAddOnInfo* info 777 = (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 778 if (info->fName.ICompare(type)!=0 || !info->MakeSureItsLoaded() 779 || !info->fCreateFunc) 780 continue; 781 782 BCatalogAddOn* catalog = info->fCreateFunc(signature, language); 783 if (catalog) { 784 info->fLoadedCatalogs.AddItem(catalog); 785 info->UnloadIfPossible(); 786 return catalog; 787 } 788 } 789 790 return NULL; 791 } 792 793 794 /* 795 * Loads a catalog for the given signature, language and fingerprint. 796 * The request to load this catalog is dispatched to all add-ons in turn, 797 * until an add-on reports success. 798 * If a catalog depends on another language (as 'english-british' depends 799 * on 'english') the dependant catalogs are automatically loaded, too. 800 * So it is perfectly possible that this method returns a catalog-chain 801 * instead of a single catalog. 802 * NULL is returned if no matching catalog could be found. 803 */ 804 BCatalogAddOn* 805 MutableLocaleRoster::LoadCatalog(const char* signature, const char* language, 806 int32 fingerprint) const 807 { 808 if (!signature) 809 return NULL; 810 811 BAutolock lock(gRosterData.fLock); 812 if (!lock.IsLocked()) 813 return NULL; 814 815 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 816 for (int32 i = 0; i < count; ++i) { 817 CatalogAddOnInfo* info 818 = (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 819 820 if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc) 821 continue; 822 BMessage languages; 823 if (language) 824 // try to load catalogs for the given language: 825 languages.AddString("language", language); 826 else 827 // try to load catalogs for one of the preferred languages: 828 GetPreferredLanguages(&languages); 829 830 BCatalogAddOn* catalog = NULL; 831 const char* lang; 832 for (int32 l=0; languages.FindString("language", l, &lang)==B_OK; ++l) { 833 catalog = info->fInstantiateFunc(signature, lang, fingerprint); 834 if (catalog) 835 info->fLoadedCatalogs.AddItem(catalog); 836 // Chain-load catalogs for languages that depend on 837 // other languages. 838 // The current implementation uses the filename in order to 839 // detect dependencies (parenthood) between languages (it 840 // traverses from "english_british_oxford" to "english_british" 841 // to "english"): 842 // TODO: use ICU facilities instead, so we can handle more 843 // complex things such as fr_FR@euro, or whatever, encodings 844 // and so on. 845 int32 pos; 846 BString langName(lang); 847 BCatalogAddOn* currCatalog = catalog; 848 BCatalogAddOn* nextCatalog; 849 while ((pos = langName.FindLast('_')) >= 0) { 850 // language is based on parent, so we load that, too: 851 // (even if the parent catalog was not found) 852 langName.Truncate(pos); 853 nextCatalog = info->fInstantiateFunc(signature, 854 langName.String(), fingerprint); 855 if (nextCatalog) { 856 info->fLoadedCatalogs.AddItem(nextCatalog); 857 if(currCatalog) 858 currCatalog->SetNext(nextCatalog); 859 else 860 catalog = nextCatalog; 861 currCatalog = nextCatalog; 862 } 863 } 864 return catalog; 865 } 866 info->UnloadIfPossible(); 867 } 868 869 return NULL; 870 } 871 872 873 /* 874 * Loads an embedded catalog from the given entry-ref (which is usually an 875 * app- or add-on-file. The request to load the catalog is dispatched to all 876 * add-ons in turn, until an add-on reports success. 877 * NULL is returned if no embedded catalog could be found. 878 */ 879 BCatalogAddOn* 880 MutableLocaleRoster::LoadEmbeddedCatalog(entry_ref* appOrAddOnRef) 881 { 882 if (!appOrAddOnRef) 883 return NULL; 884 885 BAutolock lock(gRosterData.fLock); 886 if (!lock.IsLocked()) 887 return NULL; 888 889 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 890 for (int32 i = 0; i < count; ++i) { 891 CatalogAddOnInfo* info 892 = (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); 893 894 if (!info->MakeSureItsLoaded() || !info->fInstantiateEmbeddedFunc) 895 continue; 896 897 BCatalogAddOn* catalog = NULL; 898 catalog = info->fInstantiateEmbeddedFunc(appOrAddOnRef); 899 if (catalog) { 900 info->fLoadedCatalogs.AddItem(catalog); 901 return catalog; 902 } 903 info->UnloadIfPossible(); 904 } 905 906 return NULL; 907 } 908 909 910 /* 911 * unloads the given catalog (or rather: catalog-chain). 912 * Every single catalog of the chain will be deleted automatically. 913 * Add-ons that have no more current catalogs are unloaded, too. 914 */ 915 status_t 916 MutableLocaleRoster::UnloadCatalog(BCatalogAddOn* catalog) 917 { 918 if (!catalog) 919 return B_BAD_VALUE; 920 921 BAutolock lock(gRosterData.fLock); 922 if (!lock.IsLocked()) 923 return B_ERROR; 924 925 status_t res = B_ERROR; 926 BCatalogAddOn* nextCatalog; 927 928 while (catalog) { 929 nextCatalog = catalog->Next(); 930 int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); 931 for (int32 i = 0; i < count; ++i) { 932 CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>( 933 gRosterData.fCatalogAddOnInfos.ItemAt(i)); 934 if (info->fLoadedCatalogs.HasItem(catalog)) { 935 info->fLoadedCatalogs.RemoveItem(catalog); 936 delete catalog; 937 info->UnloadIfPossible(); 938 res = B_OK; 939 } 940 } 941 catalog = nextCatalog; 942 } 943 return res; 944 } 945 946 947 } // namespace BPrivate 948 949 950 BLocaleRoster* be_locale_roster = &BPrivate::gLocaleRoster; 951 952 const BLocale* be_locale = &BPrivate::gRosterData.fDefaultLocale; 953