BPicture: Fix archive constructor.
[haiku.git] / src / kits / locale / LocaleRosterData.cpp
blobb1f9dfd7d1408f50b2438d3172dbc20a72630dcf
1 /*
2 * Copyright 2003-2012, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Oliver Tappe, zooey@hirschkaefer.de
8 */
11 #include <unicode/uversion.h>
12 #include <LocaleRosterData.h>
14 #include <Autolock.h>
15 #include <Catalog.h>
16 #include <Collator.h>
17 #include <Debug.h>
18 #include <DefaultCatalog.h>
19 #include <Directory.h>
20 #include <Entry.h>
21 #include <File.h>
22 #include <FindDirectory.h>
23 #include <FormattingConventions.h>
24 #include <Language.h>
25 #include <Locale.h>
26 #include <Node.h>
27 #include <Path.h>
28 #include <PathFinder.h>
29 #include <Roster.h>
30 #include <String.h>
31 #include <StringList.h>
32 #include <TimeZone.h>
34 // ICU includes
35 #include <unicode/locid.h>
36 #include <unicode/timezone.h>
39 namespace BPrivate {
42 // #pragma mark - CatalogAddOnInfo
45 CatalogAddOnInfo::CatalogAddOnInfo(const BString& name, const BString& path,
46 uint8 priority)
48 fInstantiateFunc(NULL),
49 fCreateFunc(NULL),
50 fLanguagesFunc(NULL),
51 fName(name),
52 fPath(path),
53 fAddOnImage(B_NO_INIT),
54 fPriority(priority),
55 fIsEmbedded(path.Length()==0)
60 CatalogAddOnInfo::~CatalogAddOnInfo()
62 int32 count = fLoadedCatalogs.CountItems();
63 for (int32 i = 0; i < count; ++i) {
64 BCatalogData* cat
65 = static_cast<BCatalogData*>(fLoadedCatalogs.ItemAt(i));
66 delete cat;
68 fLoadedCatalogs.MakeEmpty();
69 UnloadIfPossible();
73 bool
74 CatalogAddOnInfo::MakeSureItsLoaded()
76 if (!fIsEmbedded && fAddOnImage < B_OK) {
77 // add-on has not been loaded yet, so we try to load it:
78 BString fullAddOnPath(fPath);
79 fullAddOnPath << "/" << fName;
80 fAddOnImage = load_add_on(fullAddOnPath.String());
81 if (fAddOnImage >= B_OK) {
82 get_image_symbol(fAddOnImage, "instantiate_catalog",
83 B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateFunc);
84 get_image_symbol(fAddOnImage, "create_catalog",
85 B_SYMBOL_TYPE_TEXT, (void**)&fCreateFunc);
86 get_image_symbol(fAddOnImage, "get_available_languages",
87 B_SYMBOL_TYPE_TEXT, (void**)&fLanguagesFunc);
88 } else
89 return false;
90 } else if (fIsEmbedded) {
91 // The built-in catalog still has to provide this function
92 fLanguagesFunc = default_catalog_get_available_languages;
94 return true;
98 void
99 CatalogAddOnInfo::UnloadIfPossible()
101 if (!fIsEmbedded && fLoadedCatalogs.IsEmpty()) {
102 unload_add_on(fAddOnImage);
103 fAddOnImage = B_NO_INIT;
104 fInstantiateFunc = NULL;
105 fCreateFunc = NULL;
106 fLanguagesFunc = NULL;
111 // #pragma mark - LocaleRosterData
114 namespace {
117 static const char* kPriorityAttr = "ADDON:priority";
119 static const char* kLanguageField = "language";
120 static const char* kTimezoneField = "timezone";
121 static const char* kTranslateFilesystemField = "filesys";
124 } // anonymous namespace
127 LocaleRosterData::LocaleRosterData(const BLanguage& language,
128 const BFormattingConventions& conventions)
130 fLock("LocaleRosterData"),
131 fDefaultLocale(&language, &conventions),
132 fIsFilesystemTranslationPreferred(false),
133 fAreResourcesLoaded(false)
135 fInitStatus = _Initialize();
139 LocaleRosterData::~LocaleRosterData()
141 BAutolock lock(fLock);
143 _CleanupCatalogAddOns();
147 status_t
148 LocaleRosterData::InitCheck() const
150 return fAreResourcesLoaded ? B_OK : B_NO_INIT;
154 status_t
155 LocaleRosterData::Refresh()
157 BAutolock lock(fLock);
158 if (!lock.IsLocked())
159 return B_ERROR;
161 _LoadLocaleSettings();
162 _LoadTimeSettings();
164 return B_OK;
169 LocaleRosterData::CompareInfos(const void* left, const void* right)
171 const CatalogAddOnInfo* leftInfo
172 = * static_cast<const CatalogAddOnInfo* const *>(left);
173 const CatalogAddOnInfo* rightInfo
174 = * static_cast<const CatalogAddOnInfo* const *>(right);
176 return leftInfo->fPriority - rightInfo->fPriority;
180 status_t
181 LocaleRosterData::SetDefaultFormattingConventions(
182 const BFormattingConventions& newFormattingConventions)
184 status_t status = B_OK;
186 BAutolock lock(fLock);
187 if (!lock.IsLocked())
188 return B_ERROR;
190 status = _SetDefaultFormattingConventions(newFormattingConventions);
192 if (status == B_OK)
193 status = _SaveLocaleSettings();
195 if (status == B_OK) {
196 BMessage updateMessage(B_LOCALE_CHANGED);
197 status = _AddDefaultFormattingConventionsToMessage(&updateMessage);
198 if (status == B_OK)
199 status = be_roster->Broadcast(&updateMessage);
202 return status;
206 status_t
207 LocaleRosterData::SetDefaultTimeZone(const BTimeZone& newZone)
209 status_t status = B_OK;
211 BAutolock lock(fLock);
212 if (!lock.IsLocked())
213 return B_ERROR;
215 status = _SetDefaultTimeZone(newZone);
217 if (status == B_OK)
218 status = _SaveTimeSettings();
220 if (status == B_OK) {
221 BMessage updateMessage(B_LOCALE_CHANGED);
222 status = _AddDefaultTimeZoneToMessage(&updateMessage);
223 if (status == B_OK)
224 status = be_roster->Broadcast(&updateMessage);
227 return status;
231 status_t
232 LocaleRosterData::SetPreferredLanguages(const BMessage* languages)
234 status_t status = B_OK;
236 BAutolock lock(fLock);
237 if (!lock.IsLocked())
238 return B_ERROR;
240 status = _SetPreferredLanguages(languages);
242 if (status == B_OK)
243 status = _SaveLocaleSettings();
245 if (status == B_OK) {
246 BMessage updateMessage(B_LOCALE_CHANGED);
247 status = _AddPreferredLanguagesToMessage(&updateMessage);
248 if (status == B_OK)
249 status = be_roster->Broadcast(&updateMessage);
252 return status;
256 status_t
257 LocaleRosterData::SetFilesystemTranslationPreferred(bool preferred)
259 BAutolock lock(fLock);
260 if (!lock.IsLocked())
261 return B_ERROR;
263 _SetFilesystemTranslationPreferred(preferred);
265 status_t status = _SaveLocaleSettings();
267 if (status == B_OK) {
268 BMessage updateMessage(B_LOCALE_CHANGED);
269 status = _AddFilesystemTranslationPreferenceToMessage(&updateMessage);
270 if (status == B_OK)
271 status = be_roster->Broadcast(&updateMessage);
274 return status;
278 status_t
279 LocaleRosterData::GetResources(BResources** resources)
281 if (resources == NULL)
282 return B_BAD_VALUE;
284 if (!fAreResourcesLoaded) {
285 status_t result
286 = fResources.SetToImage((const void*)&BLocaleRoster::Default);
287 if (result != B_OK)
288 return result;
290 result = fResources.PreloadResourceType();
291 if (result != B_OK)
292 return result;
294 fAreResourcesLoaded = true;
297 *resources = &fResources;
298 return B_OK;
302 status_t
303 LocaleRosterData::_Initialize()
305 status_t result = _InitializeCatalogAddOns();
306 if (result != B_OK)
307 return result;
309 if ((result = Refresh()) != B_OK)
310 return result;
312 fInitStatus = B_OK;
313 return B_OK;
318 iterate over add-on-folders and collect information about each
319 catalog-add-ons (types of catalogs) into fCatalogAddOnInfos.
321 status_t
322 LocaleRosterData::_InitializeCatalogAddOns()
324 BAutolock lock(fLock);
325 if (!lock.IsLocked())
326 return B_ERROR;
328 // add info about embedded default catalog:
329 CatalogAddOnInfo* defaultCatalogAddOnInfo
330 = new(std::nothrow) CatalogAddOnInfo("Default", "",
331 DefaultCatalog::kDefaultCatalogAddOnPriority);
332 if (!defaultCatalogAddOnInfo)
333 return B_NO_MEMORY;
335 defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate;
336 defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create;
337 fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo);
339 BStringList folders;
340 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY, "locale/catalogs/",
341 B_FIND_PATH_EXISTING_ONLY, folders);
343 BPath addOnPath;
344 BDirectory addOnFolder;
345 char buf[4096];
346 status_t err;
347 for (int32 f = 0; f < folders.CountStrings(); f++) {
348 BString addOnFolderName = folders.StringAt(f);
349 err = addOnFolder.SetTo(addOnFolderName.String());
350 if (err != B_OK)
351 continue;
353 // scan through all the folder's entries for catalog add-ons:
354 int32 count;
355 int8 priority;
356 entry_ref eref;
357 BNode node;
358 BEntry entry;
359 dirent* dent;
360 while ((count = addOnFolder.GetNextDirents((dirent*)buf, sizeof(buf)))
361 > 0) {
362 dent = (dirent*)buf;
363 while (count-- > 0) {
364 if (strcmp(dent->d_name, ".") != 0
365 && strcmp(dent->d_name, "..") != 0
366 && strcmp(dent->d_name, "x86") != 0
367 && strcmp(dent->d_name, "x86_gcc2") != 0) {
368 // we have found (what should be) a catalog-add-on:
369 eref.device = dent->d_pdev;
370 eref.directory = dent->d_pino;
371 eref.set_name(dent->d_name);
372 entry.SetTo(&eref, true);
373 // traverse through any links to get to the real thang!
374 node.SetTo(&entry);
375 priority = -1;
376 if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0,
377 &priority, sizeof(int8)) <= 0) {
378 // add-on has no priority-attribute yet, so we load it
379 // to fetch the priority from the corresponding
380 // symbol...
381 BString fullAddOnPath(addOnFolderName);
382 fullAddOnPath << "/" << dent->d_name;
383 image_id image = load_add_on(fullAddOnPath.String());
384 if (image >= B_OK) {
385 uint8* prioPtr;
386 if (get_image_symbol(image, "gCatalogAddOnPriority",
387 B_SYMBOL_TYPE_DATA,
388 (void**)&prioPtr) == B_OK) {
389 priority = *prioPtr;
390 node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0,
391 &priority, sizeof(int8));
393 unload_add_on(image);
397 if (priority >= 0) {
398 // add-ons with priority < 0 will be ignored
399 CatalogAddOnInfo* addOnInfo
400 = new(std::nothrow) CatalogAddOnInfo(dent->d_name,
401 addOnFolderName, priority);
402 if (addOnInfo)
403 fCatalogAddOnInfos.AddItem((void*)addOnInfo);
406 // Bump the dirent-pointer by length of the dirent just handled:
407 dent = (dirent*)((char*)dent + dent->d_reclen);
411 fCatalogAddOnInfos.SortItems(CompareInfos);
413 return B_OK;
418 * unloads all catalog-add-ons (which will throw away all loaded catalogs, too)
420 void
421 LocaleRosterData::_CleanupCatalogAddOns()
423 BAutolock lock(fLock);
424 if (!lock.IsLocked())
425 return;
427 int32 count = fCatalogAddOnInfos.CountItems();
428 for (int32 i = 0; i<count; ++i) {
429 CatalogAddOnInfo* info
430 = static_cast<CatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i));
431 delete info;
433 fCatalogAddOnInfos.MakeEmpty();
437 status_t
438 LocaleRosterData::_LoadLocaleSettings()
440 BPath path;
441 BFile file;
442 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
443 if (status == B_OK) {
444 path.Append("Locale settings");
445 status = file.SetTo(path.Path(), B_READ_ONLY);
447 BMessage settings;
448 if (status == B_OK)
449 status = settings.Unflatten(&file);
451 if (status == B_OK) {
452 BFormattingConventions conventions(&settings);
453 fDefaultLocale.SetFormattingConventions(conventions);
455 _SetPreferredLanguages(&settings);
457 bool preferred;
458 if (settings.FindBool(kTranslateFilesystemField, &preferred) == B_OK)
459 _SetFilesystemTranslationPreferred(preferred);
461 return B_OK;
465 // Something went wrong (no settings file or invalid BMessage), so we
466 // set everything to default values
468 fPreferredLanguages.MakeEmpty();
469 fPreferredLanguages.AddString(kLanguageField, "en");
470 BLanguage defaultLanguage("en_US");
471 fDefaultLocale.SetLanguage(defaultLanguage);
472 BFormattingConventions conventions("en_US");
473 fDefaultLocale.SetFormattingConventions(conventions);
475 return status;
479 status_t
480 LocaleRosterData::_LoadTimeSettings()
482 BPath path;
483 BFile file;
484 status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
485 if (status == B_OK) {
486 path.Append("Time settings");
487 status = file.SetTo(path.Path(), B_READ_ONLY);
489 BMessage settings;
490 if (status == B_OK)
491 status = settings.Unflatten(&file);
492 if (status == B_OK) {
493 BString timeZoneID;
494 if (settings.FindString(kTimezoneField, &timeZoneID) == B_OK)
495 _SetDefaultTimeZone(BTimeZone(timeZoneID.String()));
496 else
497 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone));
499 return B_OK;
502 // Something went wrong (no settings file or invalid BMessage), so we
503 // set everything to default values
504 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone));
506 return status;
510 status_t
511 LocaleRosterData::_SaveLocaleSettings()
513 BMessage settings;
514 status_t status = _AddDefaultFormattingConventionsToMessage(&settings);
515 if (status == B_OK)
516 _AddPreferredLanguagesToMessage(&settings);
517 if (status == B_OK)
518 _AddFilesystemTranslationPreferenceToMessage(&settings);
520 BPath path;
521 if (status == B_OK)
522 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
524 BFile file;
525 if (status == B_OK) {
526 path.Append("Locale settings");
527 status = file.SetTo(path.Path(),
528 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
530 if (status == B_OK)
531 status = settings.Flatten(&file);
532 if (status == B_OK)
533 status = file.Sync();
535 return status;
539 status_t
540 LocaleRosterData::_SaveTimeSettings()
542 BMessage settings;
543 status_t status = _AddDefaultTimeZoneToMessage(&settings);
545 BPath path;
546 if (status == B_OK)
547 status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
549 BFile file;
550 if (status == B_OK) {
551 path.Append("Time settings");
552 status = file.SetTo(path.Path(),
553 B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
555 if (status == B_OK)
556 status = settings.Flatten(&file);
557 if (status == B_OK)
558 status = file.Sync();
560 return status;
564 status_t
565 LocaleRosterData::_SetDefaultFormattingConventions(
566 const BFormattingConventions& newFormattingConventions)
568 fDefaultLocale.SetFormattingConventions(newFormattingConventions);
570 UErrorCode icuError = U_ZERO_ERROR;
571 Locale icuLocale = Locale::createCanonical(newFormattingConventions.ID());
572 if (icuLocale.isBogus())
573 return B_ERROR;
575 Locale::setDefault(icuLocale, icuError);
576 if (!U_SUCCESS(icuError))
577 return B_ERROR;
579 return B_OK;
583 status_t
584 LocaleRosterData::_SetDefaultTimeZone(const BTimeZone& newZone)
586 fDefaultTimeZone = newZone;
588 TimeZone* timeZone = TimeZone::createTimeZone(newZone.ID().String());
589 if (timeZone == NULL)
590 return B_ERROR;
591 TimeZone::adoptDefault(timeZone);
593 return B_OK;
597 status_t
598 LocaleRosterData::_SetPreferredLanguages(const BMessage* languages)
600 BString langName;
601 if (languages != NULL
602 && languages->FindString(kLanguageField, &langName) == B_OK) {
603 fDefaultLocale.SetCollator(BCollator(langName.String()));
604 fDefaultLocale.SetLanguage(BLanguage(langName.String()));
606 fPreferredLanguages.RemoveName(kLanguageField);
607 for (int i = 0; languages->FindString(kLanguageField, i, &langName)
608 == B_OK; i++) {
609 fPreferredLanguages.AddString(kLanguageField, langName);
611 } else {
612 fPreferredLanguages.MakeEmpty();
613 fPreferredLanguages.AddString(kLanguageField, "en");
614 fDefaultLocale.SetCollator(BCollator("en"));
617 return B_OK;
621 void
622 LocaleRosterData::_SetFilesystemTranslationPreferred(bool preferred)
624 fIsFilesystemTranslationPreferred = preferred;
628 status_t
629 LocaleRosterData::_AddDefaultFormattingConventionsToMessage(
630 BMessage* message) const
632 BFormattingConventions conventions;
633 fDefaultLocale.GetFormattingConventions(&conventions);
635 return conventions.Archive(message);
639 status_t
640 LocaleRosterData::_AddDefaultTimeZoneToMessage(BMessage* message) const
642 return message->AddString(kTimezoneField, fDefaultTimeZone.ID());
646 status_t
647 LocaleRosterData::_AddPreferredLanguagesToMessage(BMessage* message) const
649 status_t status = B_OK;
651 BString langName;
652 for (int i = 0; fPreferredLanguages.FindString("language", i,
653 &langName) == B_OK; i++) {
654 status = message->AddString(kLanguageField, langName);
655 if (status != B_OK)
656 break;
659 return status;
663 status_t
664 LocaleRosterData::_AddFilesystemTranslationPreferenceToMessage(
665 BMessage* message) const
667 return message->AddBool(kTranslateFilesystemField,
668 fIsFilesystemTranslationPreferred);
672 } // namespace BPrivate