6fd2f4a0d1
* extracted new class BFormattingConventions from BCountry, which manages the formatting conventions from a given locale and allows to get/set the four different date/time formats supported by ICU-locales as well as number and monetary formats * overhauled the Locale preflet: + drop editing features for all formats, since I don't think they do not make much sense to have in a prefs GUI - being able to select from the existing locales should be good enough. Please note that you can still change the formats programmatically in an application. + renamed the 'Countries' tab to 'Formatting' + the locale formatting conventions list in the 'Formatting' tab is now hierarchical for easier access (less scrolling) + fixed functionality of 'Revert' and 'Defaults' buttons + added support for using the month/day-names of your preferred language during date formatting * adjusted BLocale to ask BFormattingConventions for the current formats when formatting dates and times and to offer 4 different format styles (full, long, medium and short). * adjust all classes formatting dates/times to pick the appropriate format style * BLocaleRoster no longer directly archives/unarchives the individual formatting conventions but delegates that to BFormattingConventions git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39123 a95241bf-73f2-0310-859d-f6bbb57e9c96
872 lines
21 KiB
C++
872 lines
21 KiB
C++
/*
|
|
* Copyright 2003-2010, Haiku. All rights reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Axel Dörfler, axeld@pinc-software.de
|
|
* Oliver Tappe, zooey@hirschkaefer.de
|
|
*/
|
|
|
|
|
|
#include <MutableLocaleRoster.h>
|
|
|
|
#include <set>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <AppFileInfo.h>
|
|
#include <Autolock.h>
|
|
#include <Catalog.h>
|
|
#include <Collator.h>
|
|
#include <DefaultCatalog.h>
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <File.h>
|
|
#include <FindDirectory.h>
|
|
#include <FormattingConventions.h>
|
|
#include <Language.h>
|
|
#include <Locale.h>
|
|
#include <Node.h>
|
|
#include <Path.h>
|
|
#include <Roster.h>
|
|
#include <String.h>
|
|
#include <TimeZone.h>
|
|
|
|
#include <ICUWrapper.h>
|
|
|
|
// ICU includes
|
|
#include <unicode/locid.h>
|
|
#include <unicode/timezone.h>
|
|
|
|
|
|
namespace BPrivate {
|
|
|
|
|
|
// #pragma mark - CatalogAddOnInfo
|
|
|
|
|
|
CatalogAddOnInfo::CatalogAddOnInfo(const BString& name, const BString& path,
|
|
uint8 priority)
|
|
:
|
|
fInstantiateFunc(NULL),
|
|
fInstantiateEmbeddedFunc(NULL),
|
|
fCreateFunc(NULL),
|
|
fLanguagesFunc(NULL),
|
|
fName(name),
|
|
fPath(path),
|
|
fAddOnImage(B_NO_INIT),
|
|
fPriority(priority),
|
|
fIsEmbedded(path.Length()==0)
|
|
{
|
|
}
|
|
|
|
|
|
CatalogAddOnInfo::~CatalogAddOnInfo()
|
|
{
|
|
int32 count = fLoadedCatalogs.CountItems();
|
|
for (int32 i = 0; i < count; ++i) {
|
|
BCatalogAddOn* cat
|
|
= static_cast<BCatalogAddOn*>(fLoadedCatalogs.ItemAt(i));
|
|
delete cat;
|
|
}
|
|
fLoadedCatalogs.MakeEmpty();
|
|
UnloadIfPossible();
|
|
}
|
|
|
|
|
|
bool
|
|
CatalogAddOnInfo::MakeSureItsLoaded()
|
|
{
|
|
if (!fIsEmbedded && fAddOnImage < B_OK) {
|
|
// add-on has not been loaded yet, so we try to load it:
|
|
BString fullAddOnPath(fPath);
|
|
fullAddOnPath << "/" << fName;
|
|
fAddOnImage = load_add_on(fullAddOnPath.String());
|
|
if (fAddOnImage >= B_OK) {
|
|
get_image_symbol(fAddOnImage, "instantiate_catalog",
|
|
B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateFunc);
|
|
get_image_symbol(fAddOnImage, "instantiate_embedded_catalog",
|
|
B_SYMBOL_TYPE_TEXT, (void**)&fInstantiateEmbeddedFunc);
|
|
get_image_symbol(fAddOnImage, "create_catalog",
|
|
B_SYMBOL_TYPE_TEXT, (void**)&fCreateFunc);
|
|
get_image_symbol(fAddOnImage, "get_available_languages",
|
|
B_SYMBOL_TYPE_TEXT, (void**)&fLanguagesFunc);
|
|
log_team(LOG_DEBUG, "catalog-add-on %s has been loaded",
|
|
fName.String());
|
|
} else {
|
|
log_team(LOG_DEBUG, "could not load catalog-add-on %s (%s)",
|
|
fName.String(), strerror(fAddOnImage));
|
|
return false;
|
|
}
|
|
} else if (fIsEmbedded) {
|
|
// The built-in catalog still has to provide this function
|
|
fLanguagesFunc = default_catalog_get_available_languages;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
CatalogAddOnInfo::UnloadIfPossible()
|
|
{
|
|
if (!fIsEmbedded && fLoadedCatalogs.IsEmpty()) {
|
|
unload_add_on(fAddOnImage);
|
|
fAddOnImage = B_NO_INIT;
|
|
fInstantiateFunc = NULL;
|
|
fInstantiateEmbeddedFunc = NULL;
|
|
fCreateFunc = NULL;
|
|
fLanguagesFunc = NULL;
|
|
// log_team(LOG_DEBUG, "catalog-add-on %s has been unloaded",
|
|
// fName.String());
|
|
}
|
|
}
|
|
|
|
|
|
// #pragma mark - RosterData
|
|
|
|
|
|
RosterData gRosterData;
|
|
|
|
static const char* kPriorityAttr = "ADDON:priority";
|
|
|
|
static const char* kLanguageField = "language";
|
|
|
|
static const char* kTimezoneField = "timezone";
|
|
static const char* kOffsetField = "offset";
|
|
|
|
|
|
RosterData::RosterData()
|
|
:
|
|
fLock("LocaleRosterData"),
|
|
fAreResourcesLoaded(false)
|
|
{
|
|
openlog_team("liblocale.so", LOG_PID, LOG_USER);
|
|
#ifndef DEBUG
|
|
setlogmask_team(LOG_UPTO(LOG_WARNING));
|
|
#endif
|
|
|
|
InitializeCatalogAddOns();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
|
|
RosterData::~RosterData()
|
|
{
|
|
BAutolock lock(fLock);
|
|
|
|
CleanupCatalogAddOns();
|
|
closelog();
|
|
}
|
|
|
|
|
|
int
|
|
RosterData::CompareInfos(const void* left, const void* right)
|
|
{
|
|
return ((CatalogAddOnInfo*)right)->fPriority
|
|
- ((CatalogAddOnInfo*)left)->fPriority;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::Refresh()
|
|
{
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
_LoadLocaleSettings();
|
|
_LoadTimeSettings();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
/*
|
|
iterate over add-on-folders and collect information about each
|
|
catalog-add-ons (types of catalogs) into fCatalogAddOnInfos.
|
|
*/
|
|
void
|
|
RosterData::InitializeCatalogAddOns()
|
|
{
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return;
|
|
|
|
// add info about embedded default catalog:
|
|
CatalogAddOnInfo* defaultCatalogAddOnInfo
|
|
= new(std::nothrow) CatalogAddOnInfo("Default", "",
|
|
DefaultCatalog::kDefaultCatalogAddOnPriority);
|
|
if (!defaultCatalogAddOnInfo)
|
|
return;
|
|
|
|
defaultCatalogAddOnInfo->fInstantiateFunc = DefaultCatalog::Instantiate;
|
|
defaultCatalogAddOnInfo->fInstantiateEmbeddedFunc
|
|
= DefaultCatalog::InstantiateEmbedded;
|
|
defaultCatalogAddOnInfo->fCreateFunc = DefaultCatalog::Create;
|
|
fCatalogAddOnInfos.AddItem((void*)defaultCatalogAddOnInfo);
|
|
|
|
directory_which folders[] = {
|
|
B_COMMON_ADDONS_DIRECTORY,
|
|
B_SYSTEM_ADDONS_DIRECTORY,
|
|
static_cast<directory_which>(-1)
|
|
};
|
|
BPath addOnPath;
|
|
BDirectory addOnFolder;
|
|
char buf[4096];
|
|
status_t err;
|
|
for (int f = 0; folders[f]>=0; ++f) {
|
|
find_directory(folders[f], &addOnPath);
|
|
BString addOnFolderName(addOnPath.Path());
|
|
addOnFolderName << "/locale/catalogs";
|
|
|
|
system_info info;
|
|
if (get_system_info(&info) == B_OK
|
|
&& (info.abi & B_HAIKU_ABI_MAJOR)
|
|
!= (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR)) {
|
|
switch (B_HAIKU_ABI & B_HAIKU_ABI_MAJOR) {
|
|
case B_HAIKU_ABI_GCC_2:
|
|
addOnFolderName << "/gcc2";
|
|
break;
|
|
case B_HAIKU_ABI_GCC_4:
|
|
addOnFolderName << "/gcc4";
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
err = addOnFolder.SetTo(addOnFolderName.String());
|
|
if (err != B_OK)
|
|
continue;
|
|
|
|
// scan through all the folder's entries for catalog add-ons:
|
|
int32 count;
|
|
int8 priority;
|
|
entry_ref eref;
|
|
BNode node;
|
|
BEntry entry;
|
|
dirent* dent;
|
|
while ((count = addOnFolder.GetNextDirents((dirent*)buf, 4096)) > 0) {
|
|
dent = (dirent*)buf;
|
|
while (count-- > 0) {
|
|
if (strcmp(dent->d_name, ".") && strcmp(dent->d_name, "..")
|
|
&& strcmp(dent->d_name, "gcc2")
|
|
&& strcmp(dent->d_name, "gcc4")) {
|
|
// we have found (what should be) a catalog-add-on:
|
|
eref.device = dent->d_pdev;
|
|
eref.directory = dent->d_pino;
|
|
eref.set_name(dent->d_name);
|
|
entry.SetTo(&eref, true);
|
|
// traverse through any links to get to the real thang!
|
|
node.SetTo(&entry);
|
|
priority = -1;
|
|
if (node.ReadAttr(kPriorityAttr, B_INT8_TYPE, 0,
|
|
&priority, sizeof(int8)) <= 0) {
|
|
// add-on has no priority-attribute yet, so we load it
|
|
// to fetch the priority from the corresponding
|
|
// symbol...
|
|
BString fullAddOnPath(addOnFolderName);
|
|
fullAddOnPath << "/" << dent->d_name;
|
|
image_id image = load_add_on(fullAddOnPath.String());
|
|
if (image >= B_OK) {
|
|
uint8* prioPtr;
|
|
if (get_image_symbol(image, "gCatalogAddOnPriority",
|
|
B_SYMBOL_TYPE_DATA,
|
|
(void**)&prioPtr) == B_OK) {
|
|
priority = *prioPtr;
|
|
node.WriteAttr(kPriorityAttr, B_INT8_TYPE, 0,
|
|
&priority, sizeof(int8));
|
|
} else {
|
|
log_team(LOG_ERR,
|
|
"couldn't get priority for add-on %s\n",
|
|
fullAddOnPath.String());
|
|
}
|
|
unload_add_on(image);
|
|
} else {
|
|
log_team(LOG_ERR,
|
|
"couldn't load add-on %s, error: %s\n",
|
|
fullAddOnPath.String(), strerror(image));
|
|
}
|
|
}
|
|
|
|
if (priority >= 0) {
|
|
// add-ons with priority < 0 will be ignored
|
|
CatalogAddOnInfo* addOnInfo
|
|
= new(std::nothrow) CatalogAddOnInfo(dent->d_name,
|
|
addOnFolderName, priority);
|
|
if (addOnInfo)
|
|
fCatalogAddOnInfos.AddItem((void*)addOnInfo);
|
|
}
|
|
}
|
|
// Bump the dirent-pointer by length of the dirent just handled:
|
|
dent = (dirent*)((char*)dent + dent->d_reclen);
|
|
}
|
|
}
|
|
}
|
|
fCatalogAddOnInfos.SortItems(CompareInfos);
|
|
}
|
|
|
|
|
|
/*
|
|
* unloads all catalog-add-ons (which will throw away all loaded catalogs, too)
|
|
*/
|
|
void
|
|
RosterData::CleanupCatalogAddOns()
|
|
{
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return;
|
|
|
|
int32 count = fCatalogAddOnInfos.CountItems();
|
|
for (int32 i = 0; i<count; ++i) {
|
|
CatalogAddOnInfo* info
|
|
= static_cast<CatalogAddOnInfo*>(fCatalogAddOnInfos.ItemAt(i));
|
|
delete info;
|
|
}
|
|
fCatalogAddOnInfos.MakeEmpty();
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::SetDefaultFormattingConventions(
|
|
const BFormattingConventions& newFormattingConventions)
|
|
{
|
|
status_t status = B_OK;
|
|
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
status = _SetDefaultFormattingConventions(newFormattingConventions);
|
|
|
|
if (status == B_OK)
|
|
status = _SaveLocaleSettings();
|
|
|
|
if (status == B_OK) {
|
|
BMessage updateMessage(B_LOCALE_CHANGED);
|
|
status = _AddDefaultFormattingConventionsToMessage(&updateMessage);
|
|
if (status == B_OK)
|
|
status = be_roster->Broadcast(&updateMessage);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::SetDefaultTimeZone(const BTimeZone& newZone)
|
|
{
|
|
status_t status = B_OK;
|
|
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
status = _SetDefaultTimeZone(newZone);
|
|
|
|
if (status == B_OK)
|
|
status = _SaveTimeSettings();
|
|
|
|
if (status == B_OK) {
|
|
BMessage updateMessage(B_LOCALE_CHANGED);
|
|
status = _AddDefaultTimeZoneToMessage(&updateMessage);
|
|
if (status == B_OK)
|
|
status = be_roster->Broadcast(&updateMessage);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::SetPreferredLanguages(const BMessage* languages)
|
|
{
|
|
status_t status = B_OK;
|
|
|
|
BAutolock lock(fLock);
|
|
if (!lock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
status = _SetPreferredLanguages(languages);
|
|
|
|
if (status == B_OK)
|
|
status = _SaveLocaleSettings();
|
|
|
|
if (status == B_OK) {
|
|
BMessage updateMessage(B_LOCALE_CHANGED);
|
|
status = _AddPreferredLanguagesToMessage(&updateMessage);
|
|
if (status == B_OK)
|
|
status = be_roster->Broadcast(&updateMessage);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_LoadLocaleSettings()
|
|
{
|
|
BPath path;
|
|
BFile file;
|
|
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
|
|
if (status == B_OK) {
|
|
path.Append("Locale settings");
|
|
status = file.SetTo(path.Path(), B_READ_ONLY);
|
|
}
|
|
BMessage settings;
|
|
if (status == B_OK)
|
|
status = settings.Unflatten(&file);
|
|
|
|
if (status == B_OK) {
|
|
BFormattingConventions conventions(&settings);
|
|
fDefaultLocale.SetFormattingConventions(conventions);
|
|
|
|
_SetPreferredLanguages(&settings);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// Something went wrong (no settings file or invalid BMessage), so we
|
|
// set everything to default values
|
|
|
|
fPreferredLanguages.MakeEmpty();
|
|
fPreferredLanguages.AddString(kLanguageField, "en");
|
|
BLanguage defaultLanguage("en_US");
|
|
fDefaultLocale.SetLanguage(defaultLanguage);
|
|
BFormattingConventions conventions("en_US");
|
|
fDefaultLocale.SetFormattingConventions(conventions);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_LoadTimeSettings()
|
|
{
|
|
BPath path;
|
|
BFile file;
|
|
status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
|
|
if (status == B_OK) {
|
|
path.Append("Time settings");
|
|
status = file.SetTo(path.Path(), B_READ_ONLY);
|
|
}
|
|
BMessage settings;
|
|
if (status == B_OK)
|
|
status = settings.Unflatten(&file);
|
|
if (status == B_OK) {
|
|
BString timeZoneID;
|
|
if (settings.FindString(kTimezoneField, &timeZoneID) == B_OK)
|
|
_SetDefaultTimeZone(BTimeZone(timeZoneID.String()));
|
|
else
|
|
_SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone));
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
// Something went wrong (no settings file or invalid BMessage), so we
|
|
// set everything to default values
|
|
_SetDefaultTimeZone(BTimeZone(BTimeZone::kNameOfGmtZone));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_SaveLocaleSettings()
|
|
{
|
|
BMessage settings;
|
|
status_t status = _AddDefaultFormattingConventionsToMessage(&settings);
|
|
if (status == B_OK)
|
|
_AddPreferredLanguagesToMessage(&settings);
|
|
|
|
BPath path;
|
|
if (status == B_OK)
|
|
status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
|
|
|
|
BFile file;
|
|
if (status == B_OK) {
|
|
path.Append("Locale settings");
|
|
status = file.SetTo(path.Path(),
|
|
B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
|
|
}
|
|
if (status == B_OK)
|
|
status = settings.Flatten(&file);
|
|
if (status == B_OK)
|
|
status = file.Sync();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_SaveTimeSettings()
|
|
{
|
|
BMessage settings;
|
|
status_t status = _AddDefaultTimeZoneToMessage(&settings);
|
|
|
|
BPath path;
|
|
if (status == B_OK)
|
|
status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
|
|
|
|
BFile file;
|
|
if (status == B_OK) {
|
|
path.Append("Time settings");
|
|
status = file.SetTo(path.Path(),
|
|
B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
|
|
}
|
|
if (status == B_OK)
|
|
status = settings.Flatten(&file);
|
|
if (status == B_OK)
|
|
status = file.Sync();
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_SetDefaultFormattingConventions(
|
|
const BFormattingConventions& newFormattingConventions)
|
|
{
|
|
fDefaultLocale.SetFormattingConventions(newFormattingConventions);
|
|
|
|
UErrorCode icuError = U_ZERO_ERROR;
|
|
Locale icuLocale = Locale::createCanonical(newFormattingConventions.ID());
|
|
if (icuLocale.isBogus())
|
|
return B_ERROR;
|
|
|
|
Locale::setDefault(icuLocale, icuError);
|
|
if (!U_SUCCESS(icuError))
|
|
return B_ERROR;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_SetDefaultTimeZone(const BTimeZone& newZone)
|
|
{
|
|
fDefaultTimeZone = newZone;
|
|
|
|
TimeZone* timeZone = TimeZone::createTimeZone(newZone.ID().String());
|
|
if (timeZone == NULL)
|
|
return B_ERROR;
|
|
TimeZone::adoptDefault(timeZone);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_SetPreferredLanguages(const BMessage* languages)
|
|
{
|
|
BString langName;
|
|
if (languages != NULL
|
|
&& languages->FindString(kLanguageField, &langName) == B_OK) {
|
|
fDefaultLocale.SetCollator(BCollator(langName.String()));
|
|
fDefaultLocale.SetLanguage(BLanguage(langName.String()));
|
|
|
|
fPreferredLanguages.RemoveName(kLanguageField);
|
|
for (int i = 0; languages->FindString(kLanguageField, i, &langName)
|
|
== B_OK; i++) {
|
|
fPreferredLanguages.AddString(kLanguageField, langName);
|
|
}
|
|
} else {
|
|
fPreferredLanguages.MakeEmpty();
|
|
fPreferredLanguages.AddString(kLanguageField, "en");
|
|
fDefaultLocale.SetCollator(BCollator("en"));
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_AddDefaultFormattingConventionsToMessage(BMessage* message) const
|
|
{
|
|
BFormattingConventions conventions;
|
|
fDefaultLocale.GetFormattingConventions(&conventions);
|
|
|
|
return conventions.Archive(message);
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_AddDefaultTimeZoneToMessage(BMessage* message) const
|
|
{
|
|
status_t status = message->AddString(kTimezoneField, fDefaultTimeZone.ID());
|
|
|
|
// add the offset, too, since that is used by clockconfig when setting
|
|
// up timezone state during boot
|
|
if (status == B_OK) {
|
|
status = message->AddInt32(kOffsetField,
|
|
fDefaultTimeZone.OffsetFromGMT());
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
RosterData::_AddPreferredLanguagesToMessage(BMessage* message) const
|
|
{
|
|
status_t status = B_OK;
|
|
|
|
BString langName;
|
|
for (int i = 0; fPreferredLanguages.FindString("language", i,
|
|
&langName) == B_OK; i++) {
|
|
status = message->AddString(kLanguageField, langName);
|
|
if (status != B_OK)
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
// #pragma mark - MutableLocaleRoster
|
|
|
|
|
|
static MutableLocaleRoster gLocaleRoster;
|
|
MutableLocaleRoster* gMutableLocaleRoster = &gLocaleRoster;
|
|
|
|
|
|
MutableLocaleRoster::MutableLocaleRoster()
|
|
{
|
|
}
|
|
|
|
|
|
MutableLocaleRoster::~MutableLocaleRoster()
|
|
{
|
|
}
|
|
|
|
|
|
status_t
|
|
MutableLocaleRoster::SetDefaultFormattingConventions(const BFormattingConventions& newFormattingConventions)
|
|
{
|
|
return gRosterData.SetDefaultFormattingConventions(newFormattingConventions);
|
|
}
|
|
|
|
|
|
status_t
|
|
MutableLocaleRoster::SetDefaultTimeZone(const BTimeZone& newZone)
|
|
{
|
|
return gRosterData.SetDefaultTimeZone(newZone);
|
|
}
|
|
|
|
|
|
status_t
|
|
MutableLocaleRoster::SetPreferredLanguages(const BMessage* languages)
|
|
{
|
|
return gRosterData.SetPreferredLanguages(languages);
|
|
}
|
|
|
|
|
|
status_t
|
|
MutableLocaleRoster::GetSystemCatalog(BCatalogAddOn** catalog) const
|
|
{
|
|
if (!catalog)
|
|
return B_BAD_VALUE;
|
|
*catalog = LoadCatalog("system");
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/*
|
|
* creates a new (empty) catalog of the given type (the request is dispatched
|
|
* to the appropriate add-on).
|
|
* If the add-on doesn't support catalog-creation or if the creation fails,
|
|
* NULL is returned, otherwise a pointer to the freshly created catalog.
|
|
* Any created catalog will be initialized with the given signature and
|
|
* language-name.
|
|
*/
|
|
BCatalogAddOn*
|
|
MutableLocaleRoster::CreateCatalog(const char* type, const char* signature,
|
|
const char* language)
|
|
{
|
|
if (!type || !signature || !language)
|
|
return NULL;
|
|
|
|
BAutolock lock(gRosterData.fLock);
|
|
if (!lock.IsLocked())
|
|
return NULL;
|
|
|
|
int32 count = gRosterData.fCatalogAddOnInfos.CountItems();
|
|
for (int32 i = 0; i < count; ++i) {
|
|
CatalogAddOnInfo* info
|
|
= (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i);
|
|
if (info->fName.ICompare(type)!=0 || !info->MakeSureItsLoaded()
|
|
|| !info->fCreateFunc)
|
|
continue;
|
|
|
|
BCatalogAddOn* catalog = info->fCreateFunc(signature, language);
|
|
if (catalog) {
|
|
info->fLoadedCatalogs.AddItem(catalog);
|
|
info->UnloadIfPossible();
|
|
return catalog;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Loads a catalog for the given signature, language and fingerprint.
|
|
* The request to load this catalog is dispatched to all add-ons in turn,
|
|
* until an add-on reports success.
|
|
* If a catalog depends on another language (as 'english-british' depends
|
|
* on 'english') the dependant catalogs are automatically loaded, too.
|
|
* So it is perfectly possible that this method returns a catalog-chain
|
|
* instead of a single catalog.
|
|
* NULL is returned if no matching catalog could be found.
|
|
*/
|
|
BCatalogAddOn*
|
|
MutableLocaleRoster::LoadCatalog(const char* signature, const char* language,
|
|
int32 fingerprint) const
|
|
{
|
|
if (!signature)
|
|
return NULL;
|
|
|
|
BAutolock lock(gRosterData.fLock);
|
|
if (!lock.IsLocked())
|
|
return NULL;
|
|
|
|
int32 count = gRosterData.fCatalogAddOnInfos.CountItems();
|
|
for (int32 i = 0; i < count; ++i) {
|
|
CatalogAddOnInfo* info
|
|
= (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i);
|
|
|
|
if (!info->MakeSureItsLoaded() || !info->fInstantiateFunc)
|
|
continue;
|
|
BMessage languages;
|
|
if (language)
|
|
// try to load catalogs for the given language:
|
|
languages.AddString("language", language);
|
|
else
|
|
// try to load catalogs for one of the preferred languages:
|
|
GetPreferredLanguages(&languages);
|
|
|
|
BCatalogAddOn* catalog = NULL;
|
|
const char* lang;
|
|
for (int32 l=0; languages.FindString("language", l, &lang)==B_OK; ++l) {
|
|
catalog = info->fInstantiateFunc(signature, lang, fingerprint);
|
|
if (catalog)
|
|
info->fLoadedCatalogs.AddItem(catalog);
|
|
// Chain-load catalogs for languages that depend on
|
|
// other languages.
|
|
// The current implementation uses the filename in order to
|
|
// detect dependencies (parenthood) between languages (it
|
|
// traverses from "english_british_oxford" to "english_british"
|
|
// to "english"):
|
|
// TODO: use ICU facilities instead, so we can handle more
|
|
// complex things such as fr_FR@euro, or whatever, encodings
|
|
// and so on.
|
|
int32 pos;
|
|
BString langName(lang);
|
|
BCatalogAddOn* currCatalog = catalog;
|
|
BCatalogAddOn* nextCatalog;
|
|
while ((pos = langName.FindLast('_')) >= 0) {
|
|
// language is based on parent, so we load that, too:
|
|
// (even if the parent catalog was not found)
|
|
langName.Truncate(pos);
|
|
nextCatalog = info->fInstantiateFunc(signature,
|
|
langName.String(), fingerprint);
|
|
if (nextCatalog) {
|
|
info->fLoadedCatalogs.AddItem(nextCatalog);
|
|
if(currCatalog)
|
|
currCatalog->SetNext(nextCatalog);
|
|
else
|
|
catalog = nextCatalog;
|
|
currCatalog = nextCatalog;
|
|
}
|
|
}
|
|
return catalog;
|
|
}
|
|
info->UnloadIfPossible();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Loads an embedded catalog from the given entry-ref (which is usually an
|
|
* app- or add-on-file. The request to load the catalog is dispatched to all
|
|
* add-ons in turn, until an add-on reports success.
|
|
* NULL is returned if no embedded catalog could be found.
|
|
*/
|
|
BCatalogAddOn*
|
|
MutableLocaleRoster::LoadEmbeddedCatalog(entry_ref* appOrAddOnRef)
|
|
{
|
|
if (!appOrAddOnRef)
|
|
return NULL;
|
|
|
|
BAutolock lock(gRosterData.fLock);
|
|
if (!lock.IsLocked())
|
|
return NULL;
|
|
|
|
int32 count = gRosterData.fCatalogAddOnInfos.CountItems();
|
|
for (int32 i = 0; i < count; ++i) {
|
|
CatalogAddOnInfo* info
|
|
= (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i);
|
|
|
|
if (!info->MakeSureItsLoaded() || !info->fInstantiateEmbeddedFunc)
|
|
continue;
|
|
|
|
BCatalogAddOn* catalog = NULL;
|
|
catalog = info->fInstantiateEmbeddedFunc(appOrAddOnRef);
|
|
if (catalog) {
|
|
info->fLoadedCatalogs.AddItem(catalog);
|
|
return catalog;
|
|
}
|
|
info->UnloadIfPossible();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* unloads the given catalog (or rather: catalog-chain).
|
|
* Every single catalog of the chain will be deleted automatically.
|
|
* Add-ons that have no more current catalogs are unloaded, too.
|
|
*/
|
|
status_t
|
|
MutableLocaleRoster::UnloadCatalog(BCatalogAddOn* catalog)
|
|
{
|
|
if (!catalog)
|
|
return B_BAD_VALUE;
|
|
|
|
BAutolock lock(gRosterData.fLock);
|
|
if (!lock.IsLocked())
|
|
return B_ERROR;
|
|
|
|
status_t res = B_ERROR;
|
|
BCatalogAddOn* nextCatalog;
|
|
|
|
while (catalog) {
|
|
nextCatalog = catalog->Next();
|
|
int32 count = gRosterData.fCatalogAddOnInfos.CountItems();
|
|
for (int32 i = 0; i < count; ++i) {
|
|
CatalogAddOnInfo* info = static_cast<CatalogAddOnInfo*>(
|
|
gRosterData.fCatalogAddOnInfos.ItemAt(i));
|
|
if (info->fLoadedCatalogs.HasItem(catalog)) {
|
|
info->fLoadedCatalogs.RemoveItem(catalog);
|
|
delete catalog;
|
|
info->UnloadIfPossible();
|
|
res = B_OK;
|
|
}
|
|
}
|
|
catalog = nextCatalog;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
} // namespace BPrivate
|
|
|
|
|
|
BLocaleRoster* be_locale_roster = &BPrivate::gLocaleRoster;
|
|
|
|
const BLocale* be_locale = &BPrivate::gRosterData.fDefaultLocale;
|