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