* Copyright 2003-2012, 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 <unicode/uversion.h>
#include <LocaleRoster.h>
#include <assert.h>
#include <ctype.h>
#include <new>
#include <Autolock.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Entry.h>
#include <FormattingConventions.h>
#include <fs_attr.h>
#include <IconUtils.h>
#include <Language.h>
#include <Locale.h>
#include <LocaleRosterData.h>
#include <MutableLocaleRoster.h>
#include <Node.h>
#include <Roster.h>
#include <String.h>
#include <TimeZone.h>
#include <ICUWrapper.h>
#include <locks.h>
#include <unicode/locdspnm.h>
#include <unicode/locid.h>
#include <unicode/timezone.h>
using BPrivate::CatalogAddOnInfo;
using BPrivate::MutableLocaleRoster;
U_NAMESPACE_USE
* several attributes/resource-IDs used within the Locale Kit:
*/
const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE";
const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE";
const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT";
const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG";
int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA;
static const char*
country_code_for_language(const BLanguage& language)
{
if (language.IsCountrySpecific())
return language.CountryCode();
switch ((tolower(language.Code()[0]) << 8) | tolower(language.Code()[1])) {
case 'be':
return "BY";
case 'cs':
return "CZ";
case 'da':
return "DK";
case 'el':
return "GR";
case 'en':
return "GB";
case 'hi':
return "IN";
case 'ja':
return "JP";
case 'ko':
return "KR";
case 'nb':
return "NO";
case 'pa':
return "PK";
case 'sv':
return "SE";
case 'uk':
return "UA";
case 'zh':
return "CN";
case 'de':
case 'es':
case 'fi':
case 'fr':
case 'hr':
case 'hu':
case 'it':
case 'lt':
case 'nl':
case 'pl':
case 'pt':
case 'ro':
case 'ru':
case 'sk':
return language.Code();
}
return NULL;
}
BLocaleRoster::BLocaleRoster()
:
fData(new(std::nothrow) BPrivate::LocaleRosterData(BLanguage("en_US"),
BFormattingConventions("en_US")))
{
}
BLocaleRoster::~BLocaleRoster()
{
delete fData;
}
BLocaleRoster*
BLocaleRoster::Default()
{
return MutableLocaleRoster::Default();
}
status_t
BLocaleRoster::Refresh()
{
return fData->Refresh();
}
status_t
BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const
{
if (!timezone)
return B_BAD_VALUE;
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
*timezone = fData->fDefaultTimeZone;
return B_OK;
}
const BLocale*
BLocaleRoster::GetDefaultLocale() const
{
return &fData->fDefaultLocale;
}
status_t
BLocaleRoster::GetLanguage(const char* languageCode,
BLanguage** _language) const
{
if (_language == NULL || languageCode == NULL || languageCode[0] == '\0')
return B_BAD_VALUE;
BLanguage* language = new(std::nothrow) BLanguage(languageCode);
if (language == NULL)
return B_NO_MEMORY;
*_language = language;
return B_OK;
}
status_t
BLocaleRoster::GetPreferredLanguages(BMessage* languages) const
{
if (!languages)
return B_BAD_VALUE;
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
*languages = fData->fPreferredLanguages;
return B_OK;
}
* \brief Fills \c message with 'language'-fields containing the language-
* ID(s) of all available languages.
*/
status_t
BLocaleRoster::GetAvailableLanguages(BMessage* languages) const
{
if (!languages)
return B_BAD_VALUE;
int32_t localeCount;
const Locale* icuLocaleList = Locale::getAvailableLocales(localeCount);
for (int i = 0; i < localeCount; i++)
languages->AddString("language", icuLocaleList[i].getName());
return B_OK;
}
status_t
BLocaleRoster::GetAvailableCountries(BMessage* countries) const
{
if (!countries)
return B_BAD_VALUE;
int32 i;
const char* const* countryList = uloc_getISOCountries();
for (i = 0; countryList[i] != NULL; i++)
countries->AddString("country", countryList[i]);
return B_OK;
}
status_t
BLocaleRoster::GetAvailableTimeZones(BMessage* timeZones) const
{
if (!timeZones)
return B_BAD_VALUE;
status_t status = B_OK;
StringEnumeration* zoneList = TimeZone::createEnumeration();
UErrorCode icuStatus = U_ZERO_ERROR;
int32 count = zoneList->count(icuStatus);
if (U_SUCCESS(icuStatus)) {
for (int i = 0; i < count; ++i) {
const char* zoneID = zoneList->next(NULL, icuStatus);
if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
status = B_ERROR;
break;
}
timeZones->AddString("timeZone", zoneID);
}
} else
status = B_ERROR;
delete zoneList;
return status;
}
status_t
BLocaleRoster::GetAvailableTimeZonesWithRegionInfo(BMessage* timeZones) const
{
if (!timeZones)
return B_BAD_VALUE;
status_t status = B_OK;
UErrorCode icuStatus = U_ZERO_ERROR;
StringEnumeration* zoneList = TimeZone::createTimeZoneIDEnumeration(
UCAL_ZONE_TYPE_CANONICAL, NULL, NULL, icuStatus);
int32 count = zoneList->count(icuStatus);
if (U_SUCCESS(icuStatus)) {
for (int i = 0; i < count; ++i) {
const char* zoneID = zoneList->next(NULL, icuStatus);
if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
status = B_ERROR;
break;
}
timeZones->AddString("timeZone", zoneID);
char region[5];
icuStatus = U_ZERO_ERROR;
TimeZone::getRegion(zoneID, region, 5, icuStatus);
if (!U_SUCCESS(icuStatus)) {
status = B_ERROR;
break;
}
timeZones->AddString("region", region);
}
} else
status = B_ERROR;
delete zoneList;
return status;
}
status_t
BLocaleRoster::GetAvailableTimeZonesForCountry(BMessage* timeZones,
const char* countryCode) const
{
if (!timeZones)
return B_BAD_VALUE;
status_t status = B_OK;
StringEnumeration* zoneList = TimeZone::createEnumeration(countryCode);
UErrorCode icuStatus = U_ZERO_ERROR;
int32 count = zoneList->count(icuStatus);
if (U_SUCCESS(icuStatus)) {
for (int i = 0; i < count; ++i) {
const char* zoneID = zoneList->next(NULL, icuStatus);
if (zoneID == NULL || !U_SUCCESS(icuStatus)) {
status = B_ERROR;
break;
}
timeZones->AddString("timeZone", zoneID);
}
} else
status = B_ERROR;
delete zoneList;
return status;
}
status_t
BLocaleRoster::GetFlagIconForCountry(BBitmap* flagIcon, const char* countryCode)
{
if (countryCode == NULL)
return B_BAD_VALUE;
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
BResources* resources;
status_t status = fData->GetResources(&resources);
if (status != B_OK)
return status;
int codeLength = strlen(countryCode);
if (codeLength < 2)
return B_BAD_VALUE;
char normalizedCode[8];
strcpy(normalizedCode, "flag-");
normalizedCode[5] = tolower(countryCode[codeLength - 2]);
normalizedCode[6] = tolower(countryCode[codeLength - 1]);
normalizedCode[7] = '\0';
size_t size;
const void* buffer = resources->LoadResource(B_VECTOR_ICON_TYPE,
normalizedCode, &size);
if (buffer == NULL || size == 0)
return B_NAME_NOT_FOUND;
return BIconUtils::GetVectorIcon(static_cast<const uint8*>(buffer), size,
flagIcon);
}
status_t
BLocaleRoster::GetFlagIconForLanguage(BBitmap* flagIcon,
const char* languageCode)
{
if (languageCode == NULL || languageCode[0] == '\0'
|| languageCode[1] == '\0')
return B_BAD_VALUE;
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
BResources* resources;
status_t status = fData->GetResources(&resources);
if (status != B_OK)
return status;
char normalizedCode[3];
normalizedCode[0] = tolower(languageCode[0]);
normalizedCode[1] = tolower(languageCode[1]);
normalizedCode[2] = '\0';
size_t size;
const void* buffer = resources->LoadResource(B_VECTOR_ICON_TYPE,
normalizedCode, &size);
if (buffer != NULL && size != 0) {
return BIconUtils::GetVectorIcon(static_cast<const uint8*>(buffer),
size, flagIcon);
}
BLanguage language(languageCode);
const char* countryCode = country_code_for_language(language);
if (countryCode == NULL)
return B_NAME_NOT_FOUND;
return GetFlagIconForCountry(flagIcon, countryCode);
}
status_t
BLocaleRoster::GetAvailableCatalogs(BMessage* languageList,
const char* sigPattern, const char* langPattern, int32 fingerprint) const
{
if (languageList == NULL)
return B_BAD_VALUE;
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
int32 count = fData->fCatalogAddOnInfos.CountItems();
for (int32 i = 0; i < count; ++i) {
CatalogAddOnInfo* info
= (CatalogAddOnInfo*)fData->fCatalogAddOnInfos.ItemAt(i);
if (!info->fLanguagesFunc)
continue;
info->fLanguagesFunc(languageList, sigPattern, langPattern,
fingerprint);
}
return B_OK;
}
bool
BLocaleRoster::IsFilesystemTranslationPreferred() const
{
BAutolock lock(fData->fLock);
if (!lock.IsLocked())
return B_ERROR;
return fData->fIsFilesystemTranslationPreferred;
}
\param localizedFileName A pre-allocated BString object for the result
of the lookup.
\param ref An entry_ref with an attribute holding data for catalog lookup.
\param traverse A boolean to decide if symlinks are to be traversed.
\return
- \c B_OK: success
- \c B_ENTRY_NOT_FOUND: failure. Attribute not found, entry not found
in catalog, etc
- other error codes: failure
Attribute format: "signature:context:string"
(no colon in any of signature, context and string)
Lookup is done for the top preferred language, only.
Lookup fails if a comment is present in the catalog entry.
*/
status_t
BLocaleRoster::GetLocalizedFileName(BString& localizedFileName,
const entry_ref& ref, bool traverse)
{
BString signature;
BString context;
BString string;
status_t status = _PrepareCatalogEntry(ref, signature, context, string,
traverse);
if (status != B_OK)
return status;
BRoster roster;
entry_ref catalogRef;
signature.Prepend("application/");
status = roster.FindApp(signature, &catalogRef);
if (status != B_OK)
return status;
BCatalog catalog(catalogRef);
const char* temp = catalog.GetString(string, context);
if (temp == NULL)
return B_ENTRY_NOT_FOUND;
localizedFileName = temp;
return B_OK;
}
static status_t
_InitializeCatalog(void* param)
{
BCatalog* catalog = (BCatalog*)param;
image_info info;
int32 cookie = 0;
bool found = false;
while (get_next_image_info(0, &cookie, &info) == B_OK) {
if ((char*)info.data < (char*)catalog && (char*)info.data
+ info.data_size > (char*)catalog) {
found = true;
break;
}
}
if (!found)
return B_NAME_NOT_FOUND;
entry_ref ref;
if (BEntry(info.name).GetRef(&ref) == B_OK && catalog->SetTo(ref) == B_OK)
return B_OK;
return B_ERROR;
}
BCatalog*
BLocaleRoster::_GetCatalog(BCatalog* catalog, int32* catalogInitStatus)
{
__init_once(catalogInitStatus, _InitializeCatalog, catalog);
return catalog;
}
status_t
BLocaleRoster::_PrepareCatalogEntry(const entry_ref& ref, BString& signature,
BString& context, BString& string, bool traverse)
{
BEntry entry(&ref, traverse);
if (!entry.Exists())
return B_ENTRY_NOT_FOUND;
BNode node(&entry);
status_t status = node.InitCheck();
if (status != B_OK)
return status;
status = node.ReadAttrString("SYS:NAME", &signature);
if (status != B_OK)
return status;
int32 first = signature.FindFirst(':');
int32 last = signature.FindLast(':');
if (first == last)
return B_ENTRY_NOT_FOUND;
context = signature;
string = signature;
signature.Truncate(first);
context.Truncate(last);
context.Remove(0, first + 1);
string.Remove(0, last + 1);
if (signature.Length() == 0 || context.Length() == 0
|| string.Length() == 0)
return B_ENTRY_NOT_FOUND;
return B_OK;
}