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