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