2 * Copyright 2003-2012, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Oliver Tappe, zooey@hirschkaefer.de
11 #include <unicode/uversion.h>
12 #include <LocaleRosterData.h>
18 #include <DefaultCatalog.h>
19 #include <Directory.h>
22 #include <FindDirectory.h>
23 #include <FormattingConventions.h>
28 #include <PathFinder.h>
31 #include <StringList.h>
35 #include <unicode/locid.h>
36 #include <unicode/timezone.h>
42 // #pragma mark - CatalogAddOnInfo
45 CatalogAddOnInfo::CatalogAddOnInfo(const BString
& name
, const BString
& path
,
48 fInstantiateFunc(NULL
),
53 fAddOnImage(B_NO_INIT
),
55 fIsEmbedded(path
.Length()==0)
60 CatalogAddOnInfo::~CatalogAddOnInfo()
62 int32 count
= fLoadedCatalogs
.CountItems();
63 for (int32 i
= 0; i
< count
; ++i
) {
65 = static_cast<BCatalogData
*>(fLoadedCatalogs
.ItemAt(i
));
68 fLoadedCatalogs
.MakeEmpty();
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
);
90 } else if (fIsEmbedded
) {
91 // The built-in catalog still has to provide this function
92 fLanguagesFunc
= default_catalog_get_available_languages
;
99 CatalogAddOnInfo::UnloadIfPossible()
101 if (!fIsEmbedded
&& fLoadedCatalogs
.IsEmpty()) {
102 unload_add_on(fAddOnImage
);
103 fAddOnImage
= B_NO_INIT
;
104 fInstantiateFunc
= NULL
;
106 fLanguagesFunc
= NULL
;
111 // #pragma mark - LocaleRosterData
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();
148 LocaleRosterData::InitCheck() const
150 return fAreResourcesLoaded
? B_OK
: B_NO_INIT
;
155 LocaleRosterData::Refresh()
157 BAutolock
lock(fLock
);
158 if (!lock
.IsLocked())
161 _LoadLocaleSettings();
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
;
181 LocaleRosterData::SetDefaultFormattingConventions(
182 const BFormattingConventions
& newFormattingConventions
)
184 status_t status
= B_OK
;
186 BAutolock
lock(fLock
);
187 if (!lock
.IsLocked())
190 status
= _SetDefaultFormattingConventions(newFormattingConventions
);
193 status
= _SaveLocaleSettings();
195 if (status
== B_OK
) {
196 BMessage
updateMessage(B_LOCALE_CHANGED
);
197 status
= _AddDefaultFormattingConventionsToMessage(&updateMessage
);
199 status
= be_roster
->Broadcast(&updateMessage
);
207 LocaleRosterData::SetDefaultTimeZone(const BTimeZone
& newZone
)
209 status_t status
= B_OK
;
211 BAutolock
lock(fLock
);
212 if (!lock
.IsLocked())
215 status
= _SetDefaultTimeZone(newZone
);
218 status
= _SaveTimeSettings();
220 if (status
== B_OK
) {
221 BMessage
updateMessage(B_LOCALE_CHANGED
);
222 status
= _AddDefaultTimeZoneToMessage(&updateMessage
);
224 status
= be_roster
->Broadcast(&updateMessage
);
232 LocaleRosterData::SetPreferredLanguages(const BMessage
* languages
)
234 status_t status
= B_OK
;
236 BAutolock
lock(fLock
);
237 if (!lock
.IsLocked())
240 status
= _SetPreferredLanguages(languages
);
243 status
= _SaveLocaleSettings();
245 if (status
== B_OK
) {
246 BMessage
updateMessage(B_LOCALE_CHANGED
);
247 status
= _AddPreferredLanguagesToMessage(&updateMessage
);
249 status
= be_roster
->Broadcast(&updateMessage
);
257 LocaleRosterData::SetFilesystemTranslationPreferred(bool preferred
)
259 BAutolock
lock(fLock
);
260 if (!lock
.IsLocked())
263 _SetFilesystemTranslationPreferred(preferred
);
265 status_t status
= _SaveLocaleSettings();
267 if (status
== B_OK
) {
268 BMessage
updateMessage(B_LOCALE_CHANGED
);
269 status
= _AddFilesystemTranslationPreferenceToMessage(&updateMessage
);
271 status
= be_roster
->Broadcast(&updateMessage
);
279 LocaleRosterData::GetResources(BResources
** resources
)
281 if (resources
== NULL
)
284 if (!fAreResourcesLoaded
) {
286 = fResources
.SetToImage((const void*)&BLocaleRoster::Default
);
290 result
= fResources
.PreloadResourceType();
294 fAreResourcesLoaded
= true;
297 *resources
= &fResources
;
303 LocaleRosterData::_Initialize()
305 status_t result
= _InitializeCatalogAddOns();
309 if ((result
= Refresh()) != B_OK
)
318 iterate over add-on-folders and collect information about each
319 catalog-add-ons (types of catalogs) into fCatalogAddOnInfos.
322 LocaleRosterData::_InitializeCatalogAddOns()
324 BAutolock
lock(fLock
);
325 if (!lock
.IsLocked())
328 // add info about embedded default catalog:
329 CatalogAddOnInfo
* defaultCatalogAddOnInfo
330 = new(std::nothrow
) CatalogAddOnInfo("Default", "",
331 DefaultCatalog::kDefaultCatalogAddOnPriority
);
332 if (!defaultCatalogAddOnInfo
)
335 defaultCatalogAddOnInfo
->fInstantiateFunc
= DefaultCatalog::Instantiate
;
336 defaultCatalogAddOnInfo
->fCreateFunc
= DefaultCatalog::Create
;
337 fCatalogAddOnInfos
.AddItem((void*)defaultCatalogAddOnInfo
);
340 BPathFinder::FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY
, "locale/catalogs/",
341 B_FIND_PATH_EXISTING_ONLY
, folders
);
344 BDirectory addOnFolder
;
347 for (int32 f
= 0; f
< folders
.CountStrings(); f
++) {
348 BString addOnFolderName
= folders
.StringAt(f
);
349 err
= addOnFolder
.SetTo(addOnFolderName
.String());
353 // scan through all the folder's entries for catalog add-ons:
360 while ((count
= addOnFolder
.GetNextDirents((dirent
*)buf
, sizeof(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!
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
381 BString
fullAddOnPath(addOnFolderName
);
382 fullAddOnPath
<< "/" << dent
->d_name
;
383 image_id image
= load_add_on(fullAddOnPath
.String());
386 if (get_image_symbol(image
, "gCatalogAddOnPriority",
388 (void**)&prioPtr
) == B_OK
) {
390 node
.WriteAttr(kPriorityAttr
, B_INT8_TYPE
, 0,
391 &priority
, sizeof(int8
));
393 unload_add_on(image
);
398 // add-ons with priority < 0 will be ignored
399 CatalogAddOnInfo
* addOnInfo
400 = new(std::nothrow
) CatalogAddOnInfo(dent
->d_name
,
401 addOnFolderName
, priority
);
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
);
418 * unloads all catalog-add-ons (which will throw away all loaded catalogs, too)
421 LocaleRosterData::_CleanupCatalogAddOns()
423 BAutolock
lock(fLock
);
424 if (!lock
.IsLocked())
427 int32 count
= fCatalogAddOnInfos
.CountItems();
428 for (int32 i
= 0; i
<count
; ++i
) {
429 CatalogAddOnInfo
* info
430 = static_cast<CatalogAddOnInfo
*>(fCatalogAddOnInfos
.ItemAt(i
));
433 fCatalogAddOnInfos
.MakeEmpty();
438 LocaleRosterData::_LoadLocaleSettings()
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
);
449 status
= settings
.Unflatten(&file
);
451 if (status
== B_OK
) {
452 BFormattingConventions
conventions(&settings
);
453 fDefaultLocale
.SetFormattingConventions(conventions
);
455 _SetPreferredLanguages(&settings
);
458 if (settings
.FindBool(kTranslateFilesystemField
, &preferred
) == B_OK
)
459 _SetFilesystemTranslationPreferred(preferred
);
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
);
480 LocaleRosterData::_LoadTimeSettings()
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
);
491 status
= settings
.Unflatten(&file
);
492 if (status
== B_OK
) {
494 if (settings
.FindString(kTimezoneField
, &timeZoneID
) == B_OK
)
495 _SetDefaultTimeZone(BTimeZone(timeZoneID
.String()));
497 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone
));
502 // Something went wrong (no settings file or invalid BMessage), so we
503 // set everything to default values
504 _SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone
));
511 LocaleRosterData::_SaveLocaleSettings()
514 status_t status
= _AddDefaultFormattingConventionsToMessage(&settings
);
516 _AddPreferredLanguagesToMessage(&settings
);
518 _AddFilesystemTranslationPreferenceToMessage(&settings
);
522 status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
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
);
531 status
= settings
.Flatten(&file
);
533 status
= file
.Sync();
540 LocaleRosterData::_SaveTimeSettings()
543 status_t status
= _AddDefaultTimeZoneToMessage(&settings
);
547 status
= find_directory(B_USER_SETTINGS_DIRECTORY
, &path
);
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
);
556 status
= settings
.Flatten(&file
);
558 status
= file
.Sync();
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())
575 Locale::setDefault(icuLocale
, icuError
);
576 if (!U_SUCCESS(icuError
))
584 LocaleRosterData::_SetDefaultTimeZone(const BTimeZone
& newZone
)
586 fDefaultTimeZone
= newZone
;
588 TimeZone
* timeZone
= TimeZone::createTimeZone(newZone
.ID().String());
589 if (timeZone
== NULL
)
591 TimeZone::adoptDefault(timeZone
);
598 LocaleRosterData::_SetPreferredLanguages(const BMessage
* languages
)
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
)
609 fPreferredLanguages
.AddString(kLanguageField
, langName
);
612 fPreferredLanguages
.MakeEmpty();
613 fPreferredLanguages
.AddString(kLanguageField
, "en");
614 fDefaultLocale
.SetCollator(BCollator("en"));
622 LocaleRosterData::_SetFilesystemTranslationPreferred(bool preferred
)
624 fIsFilesystemTranslationPreferred
= preferred
;
629 LocaleRosterData::_AddDefaultFormattingConventionsToMessage(
630 BMessage
* message
) const
632 BFormattingConventions conventions
;
633 fDefaultLocale
.GetFormattingConventions(&conventions
);
635 return conventions
.Archive(message
);
640 LocaleRosterData::_AddDefaultTimeZoneToMessage(BMessage
* message
) const
642 return message
->AddString(kTimezoneField
, fDefaultTimeZone
.ID());
647 LocaleRosterData::_AddPreferredLanguagesToMessage(BMessage
* message
) const
649 status_t status
= B_OK
;
652 for (int i
= 0; fPreferredLanguages
.FindString("language", i
,
653 &langName
) == B_OK
; i
++) {
654 status
= message
->AddString(kLanguageField
, langName
);
664 LocaleRosterData::_AddFilesystemTranslationPreferenceToMessage(
665 BMessage
* message
) const
667 return message
->AddBool(kTranslateFilesystemField
,
668 fIsFilesystemTranslationPreferred
);
672 } // namespace BPrivate