From 38ac8def5a72de58d6ae4e82d8c4a6be1021e67e Mon Sep 17 00:00:00 2001 From: Oliver Tappe Date: Sun, 1 Aug 2010 20:28:19 +0000 Subject: [PATCH] Largish cleanup sweep concerning the Locale Kit (sorry it got so big): * refactored private/mutable stuff out of LocaleRoster into MutableLocaleRoster * moved management of Locale/Time settings file and broadcasting of any changes out of preflets and into MutableLocaleRoster * added proper sorting to the listviews of the Locale preflet * the Time preflet no longer overlaps long timezone names into the actual time * several fixes with respect to leaking ICU objects, esp. in BCountry * the locale roster no longer passes out references to its own BCountry object, but uses copies, instead - this makes locking superfluous, as the clients' BCountry objects can no longer be changed by the setting a new default country in the locale roster * removed pretty useless POSIX-style symbol fetching from BCountry - if we need that at all, it should live in the dedicated formatter classes * adjusted readonlybootprompt, dstcheck and Deskbar to the changed Locale API * refactored existing Time-formatter into TimeUnitFormat and DurationFormat (the latter of which is now used by AboutSystem) * added stubs for Date, DateTime and Time formatters * lots of coding style fixes throughout the Locale Kit and the Locale and Time preflets This will probably break most external apps making use of the Locale Kit - it does break WebPositive. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37831 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/locale/Catalog.h | 3 +- headers/os/locale/Collator.h | 82 +- headers/os/locale/Country.h | 146 ++- headers/os/locale/DateFormat.h | 30 + headers/os/locale/DateTimeFormat.h | 31 + headers/os/locale/DurationFormat.h | 24 + headers/os/locale/Format.h | 17 +- headers/os/locale/Language.h | 17 +- headers/os/locale/Locale.h | 105 +- headers/os/locale/LocaleRoster.h | 105 +- headers/os/locale/TimeFormat.h | 26 +- headers/os/locale/TimeUnitFormat.h | 44 + headers/os/locale/TimeZone.h | 6 +- headers/private/locale/MutableLocaleRoster.h | 153 +++ src/apps/aboutsystem/AboutSystem.cpp | 10 +- src/apps/deskbar/BarApp.cpp | 2 +- src/apps/deskbar/CalendarMenuWindow.cpp | 8 +- src/apps/deskbar/TimeView.cpp | 9 +- src/apps/deskbar/TimeView.h | 5 +- .../readonlybootprompt/BootPromptWindow.cpp | 8 +- src/apps/readonlybootprompt/Jamfile | 2 +- src/bin/dstcheck.cpp | 24 +- src/kits/locale/Catalog.cpp | 24 +- src/kits/locale/CatalogStub.cpp | 2 +- src/kits/locale/Collator.cpp | 168 ++-- src/kits/locale/Country.cpp | 742 ++++---------- src/kits/locale/DateFormat.cpp | 34 + src/kits/locale/DateTimeFormat.cpp | 34 + src/kits/locale/DefaultCatalog.cpp | 16 +- src/kits/locale/DurationFormat.cpp | 75 ++ src/kits/locale/Jamfile | 10 +- src/kits/locale/Language.cpp | 121 +-- src/kits/locale/LibbeLocaleBackend.cpp | 4 +- src/kits/locale/Locale.cpp | 4 +- src/kits/locale/LocaleRoster.cpp | 948 ++++-------------- src/kits/locale/MutableLocaleRoster.cpp | 861 ++++++++++++++++ src/kits/locale/TimeFormat.cpp | 102 +- src/kits/locale/TimeUnitFormat.cpp | 65 ++ src/kits/locale/TimeZone.cpp | 11 +- src/preferences/locale/LocaleSettings.cpp | 50 +- src/preferences/locale/LocaleSettings.h | 2 - src/preferences/locale/LocaleWindow.cpp | 33 +- .../locale/TimeFormatSettingsView.cpp | 39 +- .../locale/TimeFormatSettingsView.h | 4 +- src/preferences/time/DateTimeEdit.cpp | 52 +- src/preferences/time/DateTimeView.cpp | 28 +- src/preferences/time/Jamfile | 14 +- src/preferences/time/TZDisplay.cpp | 23 +- src/preferences/time/TZDisplay.h | 8 +- src/preferences/time/TimeZoneListItem.cpp | 19 +- src/preferences/time/TimeZoneListItem.h | 2 + src/preferences/time/ZoneView.cpp | 249 +++-- src/preferences/time/ZoneView.h | 22 +- 53 files changed, 2488 insertions(+), 2135 deletions(-) create mode 100644 headers/os/locale/DateFormat.h create mode 100644 headers/os/locale/DateTimeFormat.h create mode 100644 headers/os/locale/DurationFormat.h create mode 100644 headers/os/locale/TimeUnitFormat.h create mode 100644 headers/private/locale/MutableLocaleRoster.h create mode 100644 src/kits/locale/DateFormat.cpp create mode 100644 src/kits/locale/DateTimeFormat.cpp create mode 100644 src/kits/locale/DurationFormat.cpp create mode 100644 src/kits/locale/MutableLocaleRoster.cpp create mode 100644 src/kits/locale/TimeUnitFormat.cpp diff --git a/headers/os/locale/Catalog.h b/headers/os/locale/Catalog.h index d65c374490..7f2831bac8 100644 --- a/headers/os/locale/Catalog.h +++ b/headers/os/locale/Catalog.h @@ -220,7 +220,6 @@ class BCatalog { // For BCatalog add-on implementations: class BCatalogAddOn { - friend class BLocaleRoster; public: BCatalogAddOn(const char *signature, const char *language, uint32 fingerprint); @@ -269,6 +268,8 @@ class BCatalogAddOn { const char *cmt); static int32 MarkForTranslation(int32 id); + void SetNext(BCatalogAddOn *next); + protected: virtual void UpdateFingerprint(); diff --git a/headers/os/locale/Collator.h b/headers/os/locale/Collator.h index fe46a343d0..3b8c2fd8ad 100644 --- a/headers/os/locale/Collator.h +++ b/headers/os/locale/Collator.h @@ -1,13 +1,13 @@ /* * Copyright 2003-2010, Haiku, Inc. * Distributed under the terms of the MIT Licence. -*/ + */ #ifndef _COLLATOR_H_ #define _COLLATOR_H_ -#include #include +#include namespace icu_44 { @@ -33,62 +33,72 @@ enum collator_strengths { }; +// N.B.: This class is not multithread-safe, as Compare() and GetKey() change +// the ICUCollator (the strength). So if you want to use a BCollator from +// more than one thread, you need to protect it with a lock class BCollator : public BArchivable { - public: - BCollator(); - BCollator(const char *locale, int8 strength, bool ignorePunctuation); - BCollator(BMessage *archive); - ~BCollator(); +public: + BCollator(); + BCollator(const char* locale, + int8 strength = B_COLLATE_PRIMARY, + bool ignorePunctuation = false); + BCollator(BMessage* archive); - void SetDefaultStrength(int8 strength); - int8 DefaultStrength() const; + BCollator(const BCollator& other); - void SetIgnorePunctuation(bool ignore); - bool IgnorePunctuation() const; + ~BCollator(); - status_t GetSortKey(const char *string, BString *key, - int8 strength = B_COLLATE_DEFAULT); + BCollator& operator=(const BCollator& source); - int Compare(const char *, const char *, int32 len = -1, - int8 strength = B_COLLATE_DEFAULT); - bool Equal(const char *, const char *, int32 len = -1, - int8 strength = B_COLLATE_DEFAULT); - bool Greater(const char *, const char *, int32 len = -1, - int8 strength = B_COLLATE_DEFAULT); - bool GreaterOrEqual(const char *, const char *, int32 len = -1, - int8 strength = B_COLLATE_DEFAULT); + void SetDefaultStrength(int8 strength); + int8 DefaultStrength() const; - // (un-)archiving API - status_t Archive(BMessage *archive, bool deep) const; - static BArchivable *Instantiate(BMessage *archive); + void SetIgnorePunctuation(bool ignore); + bool IgnorePunctuation() const; - private: - icu_44::Collator* fICUCollator; - icu_44::RuleBasedCollator* fFallbackICUCollator; - int8 fStrength; - bool fIgnorePunctuation; + status_t GetSortKey(const char* string, BString* key, + int8 strength = B_COLLATE_DEFAULT) const; + + int Compare(const char* s1, const char* s2, + int8 strength = B_COLLATE_DEFAULT) const; + bool Equal(const char* s1, const char* s2, + int8 strength = B_COLLATE_DEFAULT) const; + bool Greater(const char* s1, const char* s2, + int8 strength = B_COLLATE_DEFAULT) const; + bool GreaterOrEqual(const char* s1, const char* s2, + int8 strength = B_COLLATE_DEFAULT) const; + + // (un-)archiving API + status_t Archive(BMessage* archive, bool deep) const; + static BArchivable* Instantiate(BMessage* archive); + +private: + status_t _SetStrength(int8 strength) const; + + mutable icu_44::Collator* fICUCollator; + int8 fDefaultStrength; + bool fIgnorePunctuation; }; inline bool -BCollator::Equal(const char *s1, const char *s2, int32 len, int8 strength) +BCollator::Equal(const char *s1, const char *s2, int8 strength) const { - return Compare(s1, s2, len, strength) == 0; + return Compare(s1, s2, strength) == 0; } inline bool -BCollator::Greater(const char *s1, const char *s2, int32 len, int8 strength) +BCollator::Greater(const char *s1, const char *s2, int8 strength) const { - return Compare(s1, s2, len, strength) > 0; + return Compare(s1, s2, strength) > 0; } inline bool -BCollator::GreaterOrEqual(const char *s1, const char *s2, int32 len, - int8 strength) +BCollator::GreaterOrEqual(const char *s1, const char *s2, int8 strength) const { - return Compare(s1, s2, len, strength) >= 0; + return Compare(s1, s2, strength) >= 0; } diff --git a/headers/os/locale/Country.h b/headers/os/locale/Country.h index a2081b35cf..45d050e649 100644 --- a/headers/os/locale/Country.h +++ b/headers/os/locale/Country.h @@ -1,10 +1,13 @@ +/* + * Copyright 2003-2010, Haiku, Inc. + * Distributed under the terms of the MIT Licence. + */ #ifndef _COUNTRY_H_ #define _COUNTRY_H_ #include #include -#include #include #include @@ -36,101 +39,86 @@ typedef enum { class BCountry { - public: - BCountry(const char* languageCode, const char* countryCode); - BCountry(const char* languageAndCountryCode = "en_US"); - BCountry(const BCountry& other); - BCountry& operator=(const BCountry& other); - virtual ~BCountry(); +public: + BCountry(const char* languageCode, + const char* countryCode); + BCountry(const char* languageAndCountryCode + = "en_US"); + BCountry(const BCountry& other); + BCountry& operator=(const BCountry& other); + ~BCountry(); - bool Name(BString& name) const; - bool LocaleName(BString& name) const; - const char* Code() const; - status_t GetIcon(BBitmap* result); + bool GetName(BString& name) const; + bool GetLocaleName(BString& name) const; + const char* Code() const; + status_t GetIcon(BBitmap* result) const; - const char* GetString(uint32 id) const; + const char* GetLocalizedString(uint32 id) const; - // Date + // Date - status_t FormatDate(char* string, size_t maxSize, time_t time, - bool longFormat); - status_t FormatDate(BString* string, time_t time, - bool longFormat); - status_t FormatDate(BString* string, int*& fieldPositions, - int& fieldCount, time_t time, bool longFormat); - status_t DateFields(BDateElement*& fields, int& fieldCount, - bool longFormat); - status_t DateFormat(BString&, bool longFormat); - status_t SetDateFormat(const char* formatString, - bool longFormat = true); + status_t FormatDate(char* string, size_t maxSize, + time_t time, bool longFormat); + status_t FormatDate(BString* string, time_t time, + bool longFormat); + status_t FormatDate(BString* string, + int*& fieldPositions, int& fieldCount, + time_t time, bool longFormat); + status_t GetDateFields(BDateElement*& fields, + int& fieldCount, bool longFormat) const; + status_t GetDateFormat(BString&, bool longFormat) const; + status_t SetDateFormat(const char* formatString, + bool longFormat = true); - int StartOfWeek(); + int StartOfWeek() const; - // Time + // Time - status_t FormatTime(char* string, size_t maxSize, time_t time, - bool longFormat); - status_t FormatTime(BString* string, time_t time, - bool longFormat); - status_t FormatTime(BString* string, int*& fieldPositions, - int& fieldCount, time_t time, bool longFormat); - status_t TimeFields(BDateElement*& fields, int& fieldCount, - bool longFormat); + status_t FormatTime(char* string, size_t maxSize, + time_t time, bool longFormat); + status_t FormatTime(BString* string, time_t time, + bool longFormat); + status_t FormatTime(BString* string, + int*& fieldPositions, int& fieldCount, + time_t time, bool longFormat); + status_t GetTimeFields(BDateElement*& fields, + int& fieldCount, bool longFormat) const; - status_t SetTimeFormat(const char* formatString, - bool longFormat = true); - status_t TimeFormat(BString&, bool longFormat); + status_t SetTimeFormat(const char* formatString, + bool longFormat = true); + status_t GetTimeFormat(BString& out, + bool longFormat) const; - // numbers + // numbers - virtual void FormatNumber(char* string, size_t maxSize, double value); - virtual status_t FormatNumber(BString* string, double value); - virtual void FormatNumber(char* string, size_t maxSize, int32 value); - virtual void FormatNumber(BString* string, int32 value); + status_t FormatNumber(char* string, size_t maxSize, + double value); + status_t FormatNumber(BString* string, double value); + status_t FormatNumber(char* string, size_t maxSize, + int32 value); + status_t FormatNumber(BString* string, int32 value); - bool DecimalPoint(BString&) const; - bool ThousandsSeparator(BString&) const; - bool Grouping(BString&) const; + // measurements - bool PositiveSign(BString&) const; - bool NegativeSign(BString&) const; + int8 Measurement() const; - // measurements + // monetary - virtual int8 Measurement() const; + ssize_t FormatMonetary(char* string, size_t maxSize, + double value); + ssize_t FormatMonetary(BString* string, double value); - // monetary + // timezones - virtual ssize_t FormatMonetary(char* string, size_t maxSize, - double value); - virtual ssize_t FormatMonetary(BString* string, double value); + int GetTimeZones(BList& timezones) const; - bool CurrencySymbol(BString&) const; - bool InternationalCurrencySymbol(BString&) const; - bool MonDecimalPoint(BString&) const; - bool MonThousandsSeparator(BString&) const; - bool MonGrouping(BString&) const; - virtual int32 MonFracDigits() const; - - // timezones - int GetTimeZones(BList& timezones); - - private: - icu_44::DateFormat* _LockDateFormatter(bool longFormat); - icu_44::DateFormat* _LockTimeFormatter(bool longFormat); - void _UnlockDateFormatter(bool longFormat); - void _UnlockTimeFormatter(bool longFormat); - - icu_44::DateFormat* fICULongDateFormatter; - icu_44::DateFormat* fICUShortDateFormatter; - icu_44::DateFormat* fICULongTimeFormatter; - icu_44::DateFormat* fICUShortTimeFormatter; - icu_44::Locale* fICULocale; - - BLocker fLongDateLock; - BLocker fShortDateLock; - BLocker fLongTimeLock; - BLocker fShortTimeLock; +private: + icu_44::Locale* fICULocale; + BString fLongDateFormat; + BString fShortDateFormat; + BString fLongTimeFormat; + BString fShortTimeFormat; }; + #endif /* _COUNTRY_H_ */ diff --git a/headers/os/locale/DateFormat.h b/headers/os/locale/DateFormat.h new file mode 100644 index 0000000000..2aee302a34 --- /dev/null +++ b/headers/os/locale/DateFormat.h @@ -0,0 +1,30 @@ +/* + * Copyright 2010, Haiku, Inc. + * Distributed under the terms of the MIT Licence. + */ +#ifndef _B_DATE_FORMAT_H_ +#define _B_DATE_FORMAT_H_ + +#include + + +class BString; + +class BDateFormat : public BDateTimeFormat { +public: + BDateFormat(); + BDateFormat(const BDateFormat &other); + virtual ~BDateFormat(); + + // formatting + + // no-frills version: Simply appends the + // formatted date to the string buffer. + // Can fail only with B_NO_MEMORY or B_BAD_VALUE. + virtual status_t Format(bigtime_t value, BString* buffer) const; + + // TODO: ... all, basically! +}; + + +#endif // _B_DATE_FORMAT_H_ diff --git a/headers/os/locale/DateTimeFormat.h b/headers/os/locale/DateTimeFormat.h new file mode 100644 index 0000000000..83aa3590b6 --- /dev/null +++ b/headers/os/locale/DateTimeFormat.h @@ -0,0 +1,31 @@ +/* + * Copyright 2010, Haiku, Inc. + * Distributed under the terms of the MIT Licence. + */ +#ifndef _B_DATE_TIME_FORMAT_H_ +#define _B_DATE_TIME_FORMAT_H_ + +#include +#include + + +class BString; + +class BDateTimeFormat : public BFormat { +public: + BDateTimeFormat(); + BDateTimeFormat(const BDateTimeFormat &other); + virtual ~BDateTimeFormat(); + + // formatting + + // no-frills version: Simply appends the + // formatted date to the string buffer. + // Can fail only with B_NO_MEMORY or B_BAD_VALUE. + virtual status_t Format(bigtime_t value, BString* buffer) const; + + // TODO: ... basically, all of it! +}; + + +#endif // _B_DATE_TIME_FORMAT_H_ diff --git a/headers/os/locale/DurationFormat.h b/headers/os/locale/DurationFormat.h new file mode 100644 index 0000000000..26b0fbe270 --- /dev/null +++ b/headers/os/locale/DurationFormat.h @@ -0,0 +1,24 @@ +/* + * Copyright 2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ +#ifndef _B_DURATION_FORMAT_H_ +#define _B_DURATION_FORMAT_H_ + + +#include +#include +#include + + +class BDurationFormat : public BFormat { +public: + status_t Format(bigtime_t startValue, + bigtime_t stopValue, BString* buffer, + time_unit_style style = B_TIME_UNIT_FULL, + const BString& separator = ", " + ) const; +}; + + +#endif diff --git a/headers/os/locale/Format.h b/headers/os/locale/Format.h index 05374c6460..fa015f175c 100644 --- a/headers/os/locale/Format.h +++ b/headers/os/locale/Format.h @@ -1,9 +1,14 @@ +/* + * Copyright 2003-2010, Haiku, Inc. + * Distributed under the terms of the MIT Licence. +*/ #ifndef _B_FORMAT_H_ #define _B_FORMAT_H_ #include #include + // types of fields contained in formatted strings enum { // number format fields @@ -30,16 +35,18 @@ struct format_field_position { int32 length; }; + class BFormatImpl; class BFormat { - protected: - BFormat(const BFormat &other); - ~BFormat(); +protected: + BFormat(const BFormat& other); + virtual ~BFormat(); - BFormat &operator=(const BFormat &other); + BFormat& operator=(const BFormat& other); - BFormat(); + BFormat(); }; + #endif // _B_FORMAT_H_ diff --git a/headers/os/locale/Language.h b/headers/os/locale/Language.h index ac150e248a..ec03c43886 100644 --- a/headers/os/locale/Language.h +++ b/headers/os/locale/Language.h @@ -27,8 +27,13 @@ enum script_direction { class BLanguage { public: + BLanguage(); + BLanguage(const char* language); + BLanguage(const BLanguage& other); ~BLanguage(); + BLanguage& operator=(const BLanguage& source); + status_t GetName(BString& name) const; status_t GetTranslatedName(BString& name) const; @@ -43,19 +48,15 @@ public: uint8 Direction() const; + status_t SetTo(const char* language); + // see definitions below const char* GetString(uint32 id) const; private: - friend class BLocaleRoster; - - BLanguage(const char *language); - void Default(); - -private: - char* fStrings[B_NUM_LANGUAGE_STRINGS]; +// BString fStrings[B_NUM_LANGUAGE_STRINGS]; uint8 fDirection; - icu_44::Locale* fICULocale; + icu_44::Locale* fICULocale; }; diff --git a/headers/os/locale/Locale.h b/headers/os/locale/Locale.h index 1ff9ab0ad5..300515c30a 100644 --- a/headers/os/locale/Locale.h +++ b/headers/os/locale/Locale.h @@ -7,86 +7,92 @@ #include -#include #include +#include + class BCatalog; -class BLocaleRoster; class BString; class BLocale { - public: - BLocale(); - ~BLocale(); +public: + BLocale(); + ~BLocale(); - BCollator *Collator() const { return fCollator; } - BCountry *Country() const { return fCountry; } - BLanguage *Language() const { return fLanguage; } + const BCollator* Collator() const { return &fCollator; } + const BCountry* Country() const { return &fCountry; } + const BLanguage* Language() const { return &fLanguage; } - // see definitions in LocaleStrings.h - const char *GetString(uint32 id); + // see definitions in LocaleStrings.h + const char* GetString(uint32 id); - void FormatString(char *target, size_t maxSize, char *fmt, ...); - void FormatString(BString *, char *fmt, ...); - void FormatDateTime(char *target, size_t maxSize, const char *fmt, - time_t); - void FormatDateTime(BString *, const char *fmt, time_t); + void FormatString(char* target, size_t maxSize, + char* fmt, ...); + void FormatString(BString* buffer, char* fmt, ...); + void FormatDateTime(char* target, size_t maxSize, + const char* fmt, time_t value); + void FormatDateTime(BString* buffer, const char* fmt, + time_t value); - // Country short-hands + // Country short-hands, TODO: all these should go, once the + // Date...Format classes are done + void FormatDate(char* target, size_t maxSize, + time_t value, bool longFormat); + void FormatDate(BString* target, time_t value, + bool longFormat); + void FormatTime(char* target, size_t maxSize, + time_t value, bool longFormat); + void FormatTime(BString* target, time_t value, + bool longFormat); - void FormatDate(char *target, size_t maxSize, time_t, bool longFormat); - void FormatDate(BString *target, time_t, bool longFormat); - void FormatTime(char *target, size_t maxSize, time_t, bool longFormat); - void FormatTime(BString *target, time_t, bool longFormat); + // Collator short-hands + int StringCompare(const char* s1, + const char* s2) const; + int StringCompare(const BString* s1, + const BString* s2) const; - // Collator short-hands + void GetSortKey(const char* string, + BString* key) const; - int StringCompare(const char *, const char *, int32 len = -1, - int8 strength = B_COLLATE_DEFAULT) const; - int StringCompare(const BString *, const BString *, - int32 len = -1, int8 strength = B_COLLATE_DEFAULT) const; - - void GetSortKey(const char *string, BString *key); - - protected: - BCollator *fCollator; - BLanguage *fLanguage; - BCountry *fCountry; +protected: + BCollator fCollator; + BCountry fCountry; + BLanguage fLanguage; }; + // global objects -extern BLocale *be_locale; -extern BLocaleRoster *be_locale_roster; +extern BLocale* be_locale; + //---------------------------------------------------------------------- //--- country short-hands inlines --- - inline void -BLocale::FormatDate(char *target, size_t maxSize, time_t timer, bool longFormat) +BLocale::FormatDate(char* target, size_t maxSize, time_t timer, bool longFormat) { - fCountry->FormatDate(target, maxSize, timer, longFormat); + fCountry.FormatDate(target, maxSize, timer, longFormat); } inline void -BLocale::FormatDate(BString *target, time_t timer, bool longFormat) +BLocale::FormatDate(BString* target, time_t timer, bool longFormat) { - fCountry->FormatDate(target, timer, longFormat); + fCountry.FormatDate(target, timer, longFormat); } inline void -BLocale::FormatTime(char *target, size_t maxSize, time_t timer, bool longFormat) +BLocale::FormatTime(char* target, size_t maxSize, time_t timer, bool longFormat) { - fCountry->FormatTime(target, maxSize, timer, longFormat); + fCountry.FormatTime(target, maxSize, timer, longFormat); } inline void -BLocale::FormatTime(BString *target, time_t timer, bool longFormat) +BLocale::FormatTime(BString* target, time_t timer, bool longFormat) { - fCountry->FormatTime(target, timer, longFormat); + fCountry.FormatTime(target, timer, longFormat); } @@ -94,23 +100,24 @@ BLocale::FormatTime(BString *target, time_t timer, bool longFormat) // #pragma mark - inline int -BLocale::StringCompare(const char *string1, const char *string2, int32 length, int8 strength) const +BLocale::StringCompare(const char* s1, const char* s2) const { - return fCollator->Compare(string1, string2, length, strength); + return fCollator.Compare(s1, s2); } inline int -BLocale::StringCompare(const BString *string1, const BString *string2, int32 length, int8 strength) const +BLocale::StringCompare(const BString* s1, const BString* s2) const { - return fCollator->Compare(string1->String(), string2->String(), length, strength); + return fCollator.Compare(s1->String(), s2->String()); } inline void -BLocale::GetSortKey(const char *string, BString *key) +BLocale::GetSortKey(const char* string, BString* key) const { - fCollator->GetSortKey(string, key); + fCollator.GetSortKey(string, key); } + #endif /* _B_LOCALE_H_ */ diff --git a/headers/os/locale/LocaleRoster.h b/headers/os/locale/LocaleRoster.h index b4b2d379a7..65aa00fb26 100644 --- a/headers/os/locale/LocaleRoster.h +++ b/headers/os/locale/LocaleRoster.h @@ -9,89 +9,70 @@ #include -class BLanguage; -class BLocale; +class BCatalog; class BCollator; class BCountry; -class BCatalog; -class BCatalogAddOn; +class BLanguage; +class BLocale; class BMessage; class BTimeZone; -struct entry_ref; - -namespace BPrivate { - class EditableCatalog; -} - enum { - B_LOCALE_CHANGED = '_LCC', + B_LOCALE_CHANGED = '_LCC', }; class BLocaleRoster { - public: - BLocaleRoster(); - ~BLocaleRoster(); +public: + BLocaleRoster(); + ~BLocaleRoster(); - status_t GetSystemCatalog(BCatalogAddOn **catalog) const; - status_t GetDefaultCollator(BCollator **collator) const; - status_t GetDefaultLanguage(BLanguage **language) const; - status_t GetDefaultCountry(BCountry **contry) const; - status_t GetDefaultTimeZone(BTimeZone**timezone) const; + status_t GetDefaultCollator(BCollator* collator) const; + status_t GetDefaultLanguage(BLanguage* language) const; + status_t GetDefaultCountry(BCountry* country) const; + status_t GetDefaultTimeZone(BTimeZone* timezone) const; - void SetDefaultCountry(BCountry *) const; - void SetDefaultTimeZone(const char* zone) const; - void UpdateSettings(BMessage* newSettings); + status_t GetLanguage(const char* languageCode, + BLanguage** _language) const; - status_t GetLanguage(const char* languageCode, BLanguage** _language) - const; + status_t GetPreferredLanguages(BMessage* message) const; - status_t GetPreferredLanguages(BMessage *) const; - status_t SetPreferredLanguages(BMessage *); - // the message contains one or more 'language'-string-fields - // which contain the language-name(s) + status_t GetInstalledLanguages(BMessage* message) const; + // the message contains one or more + // 'language'-string-fields which + // contain the language-name(s) - status_t GetInstalledLanguages(BMessage *) const; - // the message contains one or more 'language'-string-fields - // which contain the language-name(s) + status_t GetAvailableCountries(BMessage* message) const; - status_t GetAvailableCountries(BMessage *) const; + status_t GetInstalledCatalogs(BMessage* message, + const char* sigPattern = NULL, + const char* langPattern = NULL, + int32 fingerprint = 0) const; + // the message contains... - status_t GetInstalledCatalogs(BMessage *, const char* sigPattern = NULL, - const char* langPattern = NULL, int32 fingerprint = 0) const; - // the message contains... + status_t Refresh(); + // Refresh the internal data from the + // settings file(s) - BCatalog* GetCatalog(); - // Get the catalog for the calling image (that needs to link with - // liblocalestub.a) + BCatalog* GetCatalog(); + // Get the catalog for the calling image + // (that needs to link with liblocalestub.a) - static const char *kCatLangAttr; - static const char *kCatSigAttr; - static const char *kCatFingerprintAttr; + static const char* kCatLangAttr; + static const char* kCatSigAttr; + static const char* kCatFingerprintAttr; - static const char *kCatManagerMimeType; - static const char *kCatEditorMimeType; + static const char* kEmbeddedCatAttr; + static int32 kEmbeddedCatResId; - static const char *kEmbeddedCatAttr; - static int32 kEmbeddedCatResId; - - private: - BCatalog* GetCatalog(BCatalog* catalog, vint32* catalogInitStatus); - - BCatalogAddOn* LoadCatalog(const char *signature, - const char *language = NULL, int32 fingerprint = 0); - BCatalogAddOn* LoadEmbeddedCatalog(entry_ref *appOrAddOnRef); - status_t UnloadCatalog(BCatalogAddOn *addOn); - - BCatalogAddOn* CreateCatalog(const char *type, - const char *signature, const char *language); - - friend class BCatalog; - friend class BCatalogStub; - friend class BPrivate::EditableCatalog; - friend status_t get_add_on_catalog(BCatalog*, const char *); +private: + static BCatalog* _GetCatalog(BCatalog* catalog, + vint32* catalogInitStatus); }; -#endif /* _LOCALE_ROSTER_H_ */ + +extern BLocaleRoster* be_locale_roster; + + +#endif // _LOCALE_ROSTER_H_ diff --git a/headers/os/locale/TimeFormat.h b/headers/os/locale/TimeFormat.h index 5b906c7c44..1556aa837b 100644 --- a/headers/os/locale/TimeFormat.h +++ b/headers/os/locale/TimeFormat.h @@ -1,25 +1,29 @@ -/* +/* * Copyright 2010, Haiku, Inc. - * Distributed under the terms of the MIT License. + * Distributed under the terms of the MIT Licence. */ #ifndef _B_TIME_FORMAT_H_ #define _B_TIME_FORMAT_H_ - -#include -#include +#include class BString; +class BTimeFormat : public BDateTimeFormat { +public: + BTimeFormat(const BTimeFormat &other); + virtual ~BTimeFormat(); -class BTimeFormat : public BNumberFormat { - public: - status_t Format(int64 number, BString *buffer) const; + // formatting - // TODO : version for char* buffer, size_t bufferSize - // TODO : parsing ? + // no-frills version: Simply appends the + // formatted date to the string buffer. + // Can fail only with B_NO_MEMORY or B_BAD_VALUE. + virtual status_t Format(bigtime_t value, BString* buffer) const; + + // TODO: ... basically, all of it! }; -#endif +#endif // _B_TIME_FORMAT_H_ diff --git a/headers/os/locale/TimeUnitFormat.h b/headers/os/locale/TimeUnitFormat.h new file mode 100644 index 0000000000..faea760ab9 --- /dev/null +++ b/headers/os/locale/TimeUnitFormat.h @@ -0,0 +1,44 @@ +/* + * Copyright 2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + */ +#ifndef _B_TIME_UNIT_FORMAT_H_ +#define _B_TIME_UNIT_FORMAT_H_ + + +#include +#include + + +class BString; + + +enum time_unit_style { + B_TIME_UNIT_ABBREVIATED, // e.g. '5 hrs.' + B_TIME_UNIT_FULL, // e.g. '5 hours' +}; + + +enum time_unit_element { + B_TIME_UNIT_YEAR, + B_TIME_UNIT_MONTH, + B_TIME_UNIT_WEEK, + B_TIME_UNIT_DAY, + B_TIME_UNIT_HOUR, + B_TIME_UNIT_MINUTE, + B_TIME_UNIT_SECOND, + + B_TIME_UNIT_LAST = B_TIME_UNIT_SECOND +}; + + +class BTimeUnitFormat : public BFormat { +public: + status_t Format(int32 value, time_unit_element unit, + BString* buffer, + time_unit_style style = B_TIME_UNIT_FULL + ) const; +}; + + +#endif diff --git a/headers/os/locale/TimeZone.h b/headers/os/locale/TimeZone.h index 9758716171..daf154bae7 100644 --- a/headers/os/locale/TimeZone.h +++ b/headers/os/locale/TimeZone.h @@ -20,9 +20,11 @@ public: status_t InitCheck() const; -private: - void _Init(const char* zoneCode); + status_t SetTo(const char* zoneCode); + static const char* kNameOfGmtZone; + +private: BString fCode; BString fName; int fOffsetFromGMT; diff --git a/headers/private/locale/MutableLocaleRoster.h b/headers/private/locale/MutableLocaleRoster.h new file mode 100644 index 0000000000..402c15f1fd --- /dev/null +++ b/headers/private/locale/MutableLocaleRoster.h @@ -0,0 +1,153 @@ +/* + * Copyright 2010, Haiku. All rights reserved. + * Distributed under the terms of the MIT license. + */ +#ifndef _MUTABLE_LOCALE_ROSTER_H_ +#define _MUTABLE_LOCALE_ROSTER_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class BLocale; +class BCatalog; +class BCatalogAddOn; + +struct entry_ref; + + +namespace BPrivate { + + +class MutableLocaleRoster : public BLocaleRoster { +public: + MutableLocaleRoster(); + ~MutableLocaleRoster(); + + status_t SetDefaultCountry(const BCountry& country); + status_t SetDefaultTimeZone(const BTimeZone& zone); + + status_t SetPreferredLanguages(const BMessage* message); + // the message contains one or more + // 'language'-string-fields which + // contain the language-name(s) + + status_t GetSystemCatalog(BCatalogAddOn** catalog) const; + + BCatalogAddOn* LoadCatalog(const char* signature, + const char* language = NULL, + int32 fingerprint = 0) const; + BCatalogAddOn* LoadEmbeddedCatalog(entry_ref* appOrAddOnRef); + status_t UnloadCatalog(BCatalogAddOn* addOn); + + BCatalogAddOn* CreateCatalog(const char* type, + const char* signature, + const char* language); +}; + + +extern MutableLocaleRoster* mutable_locale_roster; + + +typedef BCatalogAddOn* (*InstantiateCatalogFunc)(const char* name, + const char* language, uint32 fingerprint); + +typedef BCatalogAddOn* (*CreateCatalogFunc)(const char* name, + const char* language); + +typedef BCatalogAddOn* (*InstantiateEmbeddedCatalogFunc)( + entry_ref* appOrAddOnRef); + +typedef status_t (*GetAvailableLanguagesFunc)(BMessage*, const char*, + const char*, int32); + +/* + * info about a single catalog-add-on (representing a catalog type): + */ +struct CatalogAddOnInfo { + InstantiateCatalogFunc fInstantiateFunc; + InstantiateEmbeddedCatalogFunc fInstantiateEmbeddedFunc; + CreateCatalogFunc fCreateFunc; + GetAvailableLanguagesFunc fLanguagesFunc; + + BString fName; + BString fPath; + image_id fAddOnImage; + uint8 fPriority; + BList fLoadedCatalogs; + bool fIsEmbedded; + // an embedded add-on actually isn't an + // add-on, it is included as part of the + // library. + // The DefaultCatalog is such a beast! + + CatalogAddOnInfo(const BString& name, + const BString& path, uint8 priority); + ~CatalogAddOnInfo(); + + bool MakeSureItsLoaded(); + void UnloadIfPossible(); +}; + + +/* + * The global data that is shared between all roster-objects of a process. + */ +struct RosterData { + BLocker fLock; + BList fCatalogAddOnInfos; + BMessage fPreferredLanguages; + + BCollator fDefaultCollator; + BCountry fDefaultCountry; + BLanguage fDefaultLanguage; + BTimeZone fDefaultTimeZone; + + RosterData(); + ~RosterData(); + + void InitializeCatalogAddOns(); + void CleanupCatalogAddOns(); + status_t Refresh(); + + static int CompareInfos(const void* left, + const void* right); + + status_t SetDefaultCountry(const BCountry& country); + status_t SetDefaultTimeZone(const BTimeZone& zone); + status_t SetPreferredLanguages(const BMessage* msg); +private: + status_t _LoadLocaleSettings(); + status_t _SaveLocaleSettings(); + + status_t _LoadTimeSettings(); + status_t _SaveTimeSettings(); + + status_t _SetDefaultCountry(const BCountry& country); + status_t _SetDefaultTimeZone(const BTimeZone& zone); + status_t _SetPreferredLanguages(const BMessage* msg); + + status_t _AddDefaultCountryToMessage( + BMessage* message) const; + status_t _AddDefaultTimeZoneToMessage( + BMessage* message) const; + status_t _AddPreferredLanguagesToMessage( + BMessage* message) const; +}; + + +extern RosterData gRosterData; + + +} // namespace BPrivate + + +#endif // _MUTABLE_LOCALE_ROSTER_H_ diff --git a/src/apps/aboutsystem/AboutSystem.cpp b/src/apps/aboutsystem/AboutSystem.cpp index 6d5a81868a..b9e5ed4706 100644 --- a/src/apps/aboutsystem/AboutSystem.cpp +++ b/src/apps/aboutsystem/AboutSystem.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -1724,12 +1724,14 @@ MemUsageToString(char string[], size_t size, system_info* info) static const char* UptimeToString(char string[], size_t size) { - BTimeFormat formatter; + BDurationFormat formatter; BString str; - formatter.Format(system_time() / 1000000, &str); + bigtime_t uptime = system_time(); + bigtime_t now = (bigtime_t)time(NULL) * 1000000; + formatter.Format(now - uptime, now, &str); str.CopyInto(string, 0, size); - string[str.Length()] = '\0'; + string[std::min((size_t)str.Length(), size)] = '\0'; return string; } diff --git a/src/apps/deskbar/BarApp.cpp b/src/apps/deskbar/BarApp.cpp index 21600fc64e..e36716678c 100644 --- a/src/apps/deskbar/BarApp.cpp +++ b/src/apps/deskbar/BarApp.cpp @@ -515,7 +515,7 @@ TBarApp::MessageReceived(BMessage* message) case B_LOCALE_CHANGED: { - be_locale_roster->UpdateSettings(message); + be_locale_roster->Refresh(); BMessenger(fBarWindow->FindView("_deskbar_tv_")).SendMessage( message); diff --git a/src/apps/deskbar/CalendarMenuWindow.cpp b/src/apps/deskbar/CalendarMenuWindow.cpp index 0fe62fe152..c5bfb3465c 100644 --- a/src/apps/deskbar/CalendarMenuWindow.cpp +++ b/src/apps/deskbar/CalendarMenuWindow.cpp @@ -89,10 +89,10 @@ CalendarMenuWindow::CalendarMenuWindow(BPoint where) fCalendarView(NULL), fSuppressFirstClose(true) { - BCountry* here; - be_locale_roster->GetDefaultCountry(&here); - BPrivate::week_start startOfWeek - = (BPrivate::week_start)here->StartOfWeek(); + BCountry country; + be_locale_roster->GetDefaultCountry(&country); + BPrivate::week_start startOfWeek + = (BPrivate::week_start)country.StartOfWeek(); RemoveShortcut('H', B_COMMAND_KEY | B_CONTROL_KEY); AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); diff --git a/src/apps/deskbar/TimeView.cpp b/src/apps/deskbar/TimeView.cpp index 361ee58c95..d6116fe23b 100644 --- a/src/apps/deskbar/TimeView.cpp +++ b/src/apps/deskbar/TimeView.cpp @@ -91,7 +91,7 @@ TTimeView::TTimeView(float maxWidth, float height, bool showSeconds, fLastDateStr[0] = 0; fNeedToUpdate = true; - be_locale_roster->GetDefaultCountry(&fHere); + be_locale_roster->GetDefaultCountry(&fCountry); } @@ -105,7 +105,7 @@ TTimeView::TTimeView(BMessage* data) data->FindBool("interval", &fInterval); fShowingDate = false; - be_locale_roster->GetDefaultCountry(&fHere); + be_locale_roster->GetDefaultCountry(&fCountry); } #endif @@ -313,7 +313,7 @@ TTimeView::GetCurrentTime() fMinute = time.tm_min; fHour = time.tm_hour; - fHere->FormatTime(fTimeStr, 64, fTime, fShowSeconds); + fCountry.FormatTime(fTimeStr, 64, fTime, fShowSeconds); } @@ -322,7 +322,7 @@ TTimeView::GetCurrentDate() { char tmp[64]; - fHere->FormatDate(tmp, 64, fTime, fFullDate && CanShowFullDate()); + fCountry.FormatDate(tmp, 64, fTime, fFullDate && CanShowFullDate()); // remove leading 0 from date when month is less than 10 (MM/DD/YY) // or remove leading 0 from date when day is less than 10 (DD/MM/YY) @@ -465,6 +465,7 @@ TTimeView::AllowFullDate(bool allow) void TTimeView::Update() { + be_locale_roster->GetDefaultCountry(&fCountry); GetCurrentTime(); GetCurrentDate(); diff --git a/src/apps/deskbar/TimeView.h b/src/apps/deskbar/TimeView.h index 1190348a1f..06584f9566 100644 --- a/src/apps/deskbar/TimeView.h +++ b/src/apps/deskbar/TimeView.h @@ -36,6 +36,7 @@ All rights reserved. #include +#include #include #include @@ -127,8 +128,8 @@ class TTimeView : public BView { BMessenger fCalendarWindow; BMessageRunner* fLongClickMessageRunner; - - BCountry* fHere; // For date and time localizing purposes + + BCountry fCountry; // For date and time localizing purposes }; diff --git a/src/apps/readonlybootprompt/BootPromptWindow.cpp b/src/apps/readonlybootprompt/BootPromptWindow.cpp index 609ccd3d93..2f0611d9ef 100644 --- a/src/apps/readonlybootprompt/BootPromptWindow.cpp +++ b/src/apps/readonlybootprompt/BootPromptWindow.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -32,6 +32,9 @@ #include "KeymapListItem.h" +using BPrivate::mutable_locale_roster; + + enum { MSG_LANGUAGE_SELECTED = 'lngs', MSG_KEYMAP_SELECTED = 'kmps' @@ -156,7 +159,8 @@ BootPromptWindow::MessageReceived(BMessage* message) BMessage preferredLanguages; preferredLanguages.AddString("language", languageItem->Language()); - be_locale_roster->SetPreferredLanguages(&preferredLanguages); + mutable_locale_roster->SetPreferredLanguages( + &preferredLanguages); _InitCatalog(true); } // Calling it here is a cheap way of preventing the user to have diff --git a/src/apps/readonlybootprompt/Jamfile b/src/apps/readonlybootprompt/Jamfile index 80b6e0c377..6ed1cb3890 100644 --- a/src/apps/readonlybootprompt/Jamfile +++ b/src/apps/readonlybootprompt/Jamfile @@ -1,6 +1,6 @@ SubDir HAIKU_TOP src apps readonlybootprompt ; -UsePrivateHeaders interface shared ; +UsePrivateHeaders interface locale shared ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src preferences keymap ] ; diff --git a/src/bin/dstcheck.cpp b/src/bin/dstcheck.cpp index b7e53d82a7..5c2c40bd34 100644 --- a/src/bin/dstcheck.cpp +++ b/src/bin/dstcheck.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright 2004, Jérôme Duval, jerome.duval@free.fr. * Distributed under the terms of the MIT License. */ @@ -29,7 +29,7 @@ const uint32 TIMEDALERT_UPDATE = 'taup'; class TimedAlert : public BAlert { public: TimedAlert(const char *title, const char *text, const char *button1, - const char *button2 = NULL, const char *button3 = NULL, + const char *button2 = NULL, const char *button3 = NULL, button_width width = B_WIDTH_AS_USUAL, alert_type type = B_INFO_ALERT); void MessageReceived(BMessage *); void Show(); @@ -59,12 +59,12 @@ TimedAlert::Show() } -void +void TimedAlert::MessageReceived(BMessage *msg) { if (msg->what == TIMEDALERT_UPDATE) { BString string; - GetLabel(string); + GetLabel(string); TextView()->SetText(string.String()); } else BAlert::MessageReceived(msg); @@ -83,12 +83,12 @@ TimedAlert::GetLabel(BString &string) char timestring[15]; time(&t); localtime_r(&t, &tm); - - BCountry* here; + + BCountry here; be_locale_roster->GetDefaultCountry(&here); - - here->FormatTime(timestring, 15, t, false); - + + here.FormatTime(timestring, 15, t, false); + string += " "; string += timestring; @@ -106,9 +106,9 @@ main(int argc, char **argv) struct tm tm; tzset(); time(&t); - localtime_r(&t, &tm); + localtime_r(&t, &tm); - char path[B_PATH_NAME_LENGTH]; + char path[B_PATH_NAME_LENGTH]; if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, true, path, B_PATH_NAME_LENGTH) != B_OK) { fprintf(stderr, "%s: can't find settings directory\n", argv[0]); exit(1); @@ -148,7 +148,7 @@ main(int argc, char **argv) if (index == 2) { index = (new BAlert("dstcheck", - B_TRANSLATE("Would you like to set the clock?"), + B_TRANSLATE("Would you like to set the clock?"), B_TRANSLATE("No"), B_TRANSLATE("Yes")))->Go(); if (index == 1) diff --git a/src/kits/locale/Catalog.cpp b/src/kits/locale/Catalog.cpp index 2e9fe2035c..ed9aa50a9b 100644 --- a/src/kits/locale/Catalog.cpp +++ b/src/kits/locale/Catalog.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright 2003-2004, Axel Dörfler, axeld@pinc-software.de * Copyright 2003-2004, Oliver Tappe, zooey@hirschkaefer.de * Distributed under the terms of the MIT License. @@ -10,11 +10,14 @@ #include #include -#include +#include #include #include +using BPrivate::mutable_locale_roster; + + //#pragma mark - BCatalog BCatalog::BCatalog() : @@ -26,13 +29,14 @@ BCatalog::BCatalog() BCatalog::BCatalog(const char *signature, const char *language, uint32 fingerprint) { - fCatalog = be_locale_roster->LoadCatalog(signature, language, fingerprint); + fCatalog + = mutable_locale_roster->LoadCatalog(signature, language, fingerprint); } BCatalog::~BCatalog() { - be_locale_roster->UnloadCatalog(fCatalog); + mutable_locale_roster->UnloadCatalog(fCatalog); } @@ -101,8 +105,7 @@ BCatalog::SetCatalog(const char* signature, uint32 fingerprint) // properly maintained. // No other method should touch fCatalog directly, either (constructor for // example) - fCatalog - = be_locale_roster->LoadCatalog(signature, NULL, fingerprint); + fCatalog = mutable_locale_roster->LoadCatalog(signature, NULL, fingerprint); return B_OK; } @@ -255,12 +258,19 @@ BCatalogAddOn::CountItems() const } +void +BCatalogAddOn::SetNext(BCatalogAddOn *next) +{ + fNext = next; +} + + //#pragma mark - EditableCatalog namespace BPrivate { EditableCatalog::EditableCatalog(const char *type, const char *signature, const char *language) { - fCatalog = be_locale_roster->CreateCatalog(type, signature, language); + fCatalog = mutable_locale_roster->CreateCatalog(type, signature, language); } diff --git a/src/kits/locale/CatalogStub.cpp b/src/kits/locale/CatalogStub.cpp index f28d53af05..0646e9fdad 100644 --- a/src/kits/locale/CatalogStub.cpp +++ b/src/kits/locale/CatalogStub.cpp @@ -21,6 +21,6 @@ BLocaleRoster::GetCatalog() asm volatile(".hidden _ZN13BLocaleRoster10GetCatalogEv"); #endif - return GetCatalog(&sCatalog, &sCatalogInitOnce); + return _GetCatalog(&sCatalog, &sCatalogInitOnce); } diff --git a/src/kits/locale/Collator.cpp b/src/kits/locale/Collator.cpp index 7af0b91b50..ac669264c9 100644 --- a/src/kits/locale/Collator.cpp +++ b/src/kits/locale/Collator.cpp @@ -5,23 +5,24 @@ */ +#include +#include + +#include +#include + #include #include #include #include -#include -#include -#include - #include #include BCollator::BCollator() : - fFallbackICUCollator(NULL), - fStrength(B_COLLATE_PRIMARY), + fDefaultStrength(B_COLLATE_PRIMARY), fIgnorePunctuation(true) { // TODO: the collator construction will have to change; the default @@ -33,11 +34,9 @@ BCollator::BCollator() } -BCollator::BCollator(const char *locale, int8 strength, - bool ignorePunctuation) +BCollator::BCollator(const char* locale, int8 strength, bool ignorePunctuation) : - fFallbackICUCollator(NULL), - fStrength(strength), + fDefaultStrength(strength), fIgnorePunctuation(ignorePunctuation) { UErrorCode error = U_ZERO_ERROR; @@ -45,57 +44,80 @@ BCollator::BCollator(const char *locale, int8 strength, } -BCollator::BCollator(BMessage *archive) - : BArchivable(archive), +BCollator::BCollator(BMessage* archive) + : + BArchivable(archive), fICUCollator(NULL), - fFallbackICUCollator(NULL), + fDefaultStrength(B_COLLATE_PRIMARY), fIgnorePunctuation(true) { int32 data; if (archive->FindInt32("loc:strength", &data) == B_OK) - fStrength = (uint8)data; + fDefaultStrength = (uint8)data; else - fStrength = B_COLLATE_PRIMARY; + fDefaultStrength = B_COLLATE_PRIMARY; - if (archive->FindBool("loc:punctuation", &fIgnorePunctuation) != B_OK) - fIgnorePunctuation = true; + archive->FindBool("loc:punctuation", &fIgnorePunctuation); UErrorCode error = U_ZERO_ERROR; - fFallbackICUCollator = static_cast - (Collator::createInstance(error)); + RuleBasedCollator* fallbackICUCollator + = static_cast(Collator::createInstance(error)); ssize_t size; const void* buffer = NULL; if (archive->FindData("loc:collator", B_RAW_TYPE, &buffer, &size) == B_OK) { fICUCollator = new RuleBasedCollator((const uint8_t*)buffer, (int)size, - fFallbackICUCollator, error); + fallbackICUCollator, error); if (fICUCollator == NULL) { - fICUCollator = fFallbackICUCollator; + fICUCollator = fallbackICUCollator; // Unarchiving failed, so we revert to the fallback collator + // TODO: when can this happen, can it be avoided? } } } +BCollator::BCollator(const BCollator& other) + : + fICUCollator(NULL) +{ + *this = other; +} + + BCollator::~BCollator() { delete fICUCollator; - fICUCollator = NULL; - delete fFallbackICUCollator; +} + + +BCollator& BCollator::operator=(const BCollator& source) +{ + if (&source != this) { + delete fICUCollator; + + fICUCollator = source.fICUCollator != NULL + ? source.fICUCollator->clone() + : NULL; + fDefaultStrength = source.fDefaultStrength; + fIgnorePunctuation = source.fIgnorePunctuation; + } + + return *this; } void BCollator::SetDefaultStrength(int8 strength) { - fStrength = strength; + fDefaultStrength = strength; } int8 BCollator::DefaultStrength() const { - return fStrength; + return fDefaultStrength; } @@ -114,31 +136,11 @@ BCollator::IgnorePunctuation() const status_t -BCollator::GetSortKey(const char *string, BString *key, int8 strength) +BCollator::GetSortKey(const char* string, BString* key, int8 strength) const { + _SetStrength(strength); + // TODO : handle fIgnorePunctuation - if (strength == B_COLLATE_DEFAULT) - strength = fStrength; - Collator::ECollationStrength icuStrength; - switch (strength) { - case B_COLLATE_PRIMARY: - icuStrength = Collator::PRIMARY; - break; - case B_COLLATE_SECONDARY: - icuStrength = Collator::SECONDARY; - break; - case B_COLLATE_TERTIARY: - default: - icuStrength = Collator::TERTIARY; - break; - case B_COLLATE_QUATERNARY: - icuStrength = Collator::QUATERNARY; - break; - case B_COLLATE_IDENTICAL: - icuStrength = Collator::IDENTICAL; - break; - } - fICUCollator->setStrength(icuStrength); int length = strlen(string); @@ -154,6 +156,7 @@ BCollator::GetSortKey(const char *string, BString *key, int8 strength) buffer = (uint8_t*)realloc(buffer, requiredSize); if (buffer == NULL) return B_NO_MEMORY; + error = U_ZERO_ERROR; fICUCollator->getSortKey(UnicodeString(string, length, NULL, error), buffer, requiredSize); @@ -164,50 +167,32 @@ BCollator::GetSortKey(const char *string, BString *key, int8 strength) if (error == U_ZERO_ERROR) return B_OK; + return B_ERROR; } int -BCollator::Compare(const char *a, const char *b, int32 length, int8 strength) +BCollator::Compare(const char* s1, const char* s2, int8 strength) const { + _SetStrength(strength); + // TODO : handle fIgnorePunctuation - if (strength == B_COLLATE_DEFAULT) - strength = fStrength; - Collator::ECollationStrength icuStrength; - switch (strength) { - case B_COLLATE_PRIMARY: - icuStrength = Collator::PRIMARY; - break; - case B_COLLATE_SECONDARY: - icuStrength = Collator::SECONDARY; - break; - case B_COLLATE_TERTIARY: - default: - icuStrength = Collator::TERTIARY; - break; - case B_COLLATE_QUATERNARY: - icuStrength = Collator::QUATERNARY; - break; - case B_COLLATE_IDENTICAL: - icuStrength = Collator::IDENTICAL; - break; - } - fICUCollator->setStrength(icuStrength); + UErrorCode error = U_ZERO_ERROR; - return fICUCollator->compare(a, b, error); + return fICUCollator->compare(s1, s2, error); } status_t -BCollator::Archive(BMessage *archive, bool deep) const +BCollator::Archive(BMessage* archive, bool deep) const { status_t status = BArchivable::Archive(archive, deep); if (status < B_OK) return status; if (status == B_OK) - status = archive->AddInt32("loc:strength", fStrength); + status = archive->AddInt32("loc:strength", fDefaultStrength); if (status == B_OK) status = archive->AddBool("loc:punctuation", fIgnorePunctuation); @@ -231,13 +216,42 @@ BCollator::Archive(BMessage *archive, bool deep) const } -BArchivable * -BCollator::Instantiate(BMessage *archive) +BArchivable* +BCollator::Instantiate(BMessage* archive) { if (validate_instantiation(archive, "BCollator")) - return new BCollator(archive); + return new(std::nothrow) BCollator(archive); return NULL; } +status_t +BCollator::_SetStrength(int8 strength) const +{ + if (strength == B_COLLATE_DEFAULT) + strength = fDefaultStrength; + + Collator::ECollationStrength icuStrength; + switch (strength) { + case B_COLLATE_PRIMARY: + icuStrength = Collator::PRIMARY; + break; + case B_COLLATE_SECONDARY: + icuStrength = Collator::SECONDARY; + break; + case B_COLLATE_TERTIARY: + default: + icuStrength = Collator::TERTIARY; + break; + case B_COLLATE_QUATERNARY: + icuStrength = Collator::QUATERNARY; + break; + case B_COLLATE_IDENTICAL: + icuStrength = Collator::IDENTICAL; + break; + } + fICUCollator->setStrength(icuStrength); + + return B_OK; +} diff --git a/src/kits/locale/Country.cpp b/src/kits/locale/Country.cpp index 080ffd437b..37a1995a69 100644 --- a/src/kits/locale/Country.cpp +++ b/src/kits/locale/Country.cpp @@ -7,6 +7,7 @@ #include +#include #include #include #include @@ -21,10 +22,10 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -33,66 +34,39 @@ #define ICU_VERSION icu_44 +using BPrivate::ObjectDeleter; using BPrivate::B_WEEK_START_MONDAY; using BPrivate::B_WEEK_START_SUNDAY; +static DateFormat* CreateDateFormat(bool longFormat, const Locale& locale, + const BString& format); +static DateFormat* CreateTimeFormat(bool longFormat, const Locale& locale, + const BString& format); + + BCountry::BCountry(const char* languageCode, const char* countryCode) + : + fICULocale(new ICU_VERSION::Locale(languageCode, countryCode)) { - fICULocale = new ICU_VERSION::Locale(languageCode, countryCode); - fICULongDateFormatter = NULL; - fICUShortDateFormatter = NULL; - fICULongTimeFormatter = NULL; - fICUShortTimeFormatter = NULL; } BCountry::BCountry(const char* languageAndCountryCode) + : + fICULocale(new ICU_VERSION::Locale(languageAndCountryCode)) { - fICULocale = new ICU_VERSION::Locale(languageAndCountryCode); - - // These will be initialized the first time they are actually needed - // this way the class is more lightweight when you just want the country - // name or some other information - fICULongDateFormatter = NULL; - fICUShortDateFormatter = NULL; - fICULongTimeFormatter = NULL; - fICUShortTimeFormatter = NULL; } BCountry::BCountry(const BCountry& other) + : + fICULocale(new ICU_VERSION::Locale(*other.fICULocale)), + fLongDateFormat(other.fLongDateFormat), + fShortDateFormat(other.fShortDateFormat), + fLongTimeFormat(other.fLongTimeFormat), + fShortTimeFormat(other.fShortTimeFormat) { - fICULocale = new ICU_VERSION::Locale(*other.fICULocale); - - // We could copy these, but we can recreate them when needed from the locale - // anyway - fICULongDateFormatter = NULL; - fICUShortDateFormatter = NULL; - fICULongTimeFormatter = NULL; - fICUShortTimeFormatter = NULL; - - /* - if (other.fICULongDateFormatter) { - fICULongDateFormatter = new ICU_VERSION::SimpleDateFormat( - *static_cast(other.fICULongDateFormatter)); - } - - if (other.fICUShortDateFormatter) { - fICUShortDateFormatter = new ICU_VERSION::SimpleDateFormat( - *static_cast(other.fICUShortDateFormatter)); - } - - if (other.fICULongTimeFormatter) { - fICULongTimeFormatter = new ICU_VERSION::SimpleDateFormat( - *static_cast(other.fICULongTimeFormatter)); - } - - if (other.fICUShortTimeFormatter) { - fICUShortTimeFormatter = new ICU_VERSION::SimpleDateFormat( - *static_cast(other.fICUShortTimeFormatter)); - } - */ } @@ -102,30 +76,13 @@ BCountry::operator=(const BCountry& other) if (this == &other) return *this; - fLongDateLock.Lock(); - fShortDateLock.Lock(); - fLongTimeLock.Lock(); - fShortTimeLock.Lock(); - - delete fICULongTimeFormatter; - delete fICUShortTimeFormatter; - delete fICULongDateFormatter; - delete fICUShortDateFormatter; delete fICULocale; - fICULocale = new ICU_VERSION::Locale(*other.fICULocale); - // We could copy these, but we can recreate them when needed from the locale - // anyway - fICULongDateFormatter = NULL; - fICUShortDateFormatter = NULL; - fICULongTimeFormatter = NULL; - fICUShortTimeFormatter = NULL; - - fShortTimeLock.Unlock(); - fLongTimeLock.Unlock(); - fShortDateLock.Unlock(); - fLongDateLock.Unlock(); + fLongDateFormat = other.fLongDateFormat; + fShortDateFormat = other.fShortDateFormat; + fLongTimeFormat = other.fLongTimeFormat; + fShortTimeFormat = other.fShortTimeFormat; return *this; } @@ -133,16 +90,12 @@ BCountry::operator=(const BCountry& other) BCountry::~BCountry() { - delete fICULongTimeFormatter; - delete fICUShortTimeFormatter; - delete fICULongDateFormatter; - delete fICUShortDateFormatter; delete fICULocale; } bool -BCountry::Name(BString& name) const +BCountry::GetName(BString& name) const { UnicodeString uString; fICULocale->getDisplayCountry(uString); @@ -153,7 +106,7 @@ BCountry::Name(BString& name) const bool -BCountry::LocaleName(BString& name) const +BCountry::GetLocaleName(BString& name) const { UnicodeString uString; fICULocale->getDisplayName(uString); @@ -171,7 +124,7 @@ BCountry::Code() const status_t -BCountry::GetIcon(BBitmap* result) +BCountry::GetIcon(BBitmap* result) const { if (result == NULL) return B_BAD_DATA; @@ -196,96 +149,25 @@ BCountry::GetIcon(BBitmap* result) // #pragma mark - Date -DateFormat* -BCountry::_LockDateFormatter(bool longFormat) -{ - // TODO: ICU allows for 4 different levels of expansion : - // short, medium, long, and full. Our bool parameter is not enough... - if (longFormat) { - fLongDateLock.Lock(); - if (fICULongDateFormatter == NULL) { - fICULongDateFormatter = DateFormat::createDateInstance( - DateFormat::FULL, *fICULocale); - } - return fICULongDateFormatter; - } else { - fShortDateLock.Lock(); - if (fICUShortDateFormatter == NULL) { - fICUShortDateFormatter = DateFormat::createDateInstance( - DateFormat::SHORT, *fICULocale); - } - return fICUShortDateFormatter; - } -} - - -void -BCountry::_UnlockDateFormatter(bool longFormat) -{ - if (longFormat) { - fLongDateLock.Unlock(); - } else { - fShortDateLock.Unlock(); - } -} - - -DateFormat* -BCountry::_LockTimeFormatter(bool longFormat) -{ - // TODO: ICU allows for 4 different levels of expansion : - // short, medium, long, and full. Our bool parameter is not enough... - if (longFormat) { - fLongTimeLock.Lock(); - if (fICULongTimeFormatter == NULL) { - fICULongTimeFormatter = DateFormat::createTimeInstance( - DateFormat::MEDIUM, *fICULocale); - } - return fICULongTimeFormatter; - } else { - fShortTimeLock.Lock(); - if (fICUShortTimeFormatter == NULL) { - fICUShortTimeFormatter = DateFormat::createTimeInstance( - DateFormat::SHORT, *fICULocale); - } - return fICUShortTimeFormatter; - } -} - - -void -BCountry::_UnlockTimeFormatter(bool longFormat) -{ - if (longFormat) { - fLongTimeLock.Unlock(); - } else { - fShortTimeLock.Unlock(); - } -} - - status_t BCountry::FormatDate(char* string, size_t maxSize, time_t time, bool longFormat) { - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); + ObjectDeleter dateFormatter = CreateDateFormat(longFormat, + *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); + if (dateFormatter.Get() == NULL) return B_NO_MEMORY; - } UnicodeString ICUString; ICUString = dateFormatter->format((UDate)time * 1000, ICUString); - _UnlockDateFormatter(longFormat); - CheckedArrayByteSink stringConverter(string, maxSize); ICUString.toUTF8(stringConverter); if (stringConverter.Overflowed()) return B_BAD_VALUE; - else - return B_OK; + + return B_OK; } @@ -295,15 +177,13 @@ BCountry::FormatDate(BString *string, time_t time, bool longFormat) string->Truncate(0); // We make the string empty, this way even in cases where ICU fail we at // least return something sane - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); + ObjectDeleter dateFormatter = CreateDateFormat(longFormat, + *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); + if (dateFormatter.Get() == NULL) return B_NO_MEMORY; - } UnicodeString ICUString; ICUString = dateFormatter->format((UDate)time * 1000, ICUString); - _UnlockDateFormatter(longFormat); BStringByteSink stringConverter(string); @@ -319,11 +199,10 @@ BCountry::FormatDate(BString* string, int*& fieldPositions, int& fieldCount, { string->Truncate(0); - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); + ObjectDeleter dateFormatter = CreateDateFormat(longFormat, + *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); + if (dateFormatter.Get() == NULL) return B_NO_MEMORY; - } fieldPositions = NULL; UErrorCode error = U_ZERO_ERROR; @@ -331,7 +210,6 @@ BCountry::FormatDate(BString* string, int*& fieldPositions, int& fieldCount, UnicodeString ICUString; ICUString = dateFormatter->format((UDate)time * 1000, ICUString, &positionIterator, error); - _UnlockDateFormatter(longFormat); if (error != U_ZERO_ERROR) return B_ERROR; @@ -359,27 +237,31 @@ BCountry::FormatDate(BString* string, int*& fieldPositions, int& fieldCount, status_t -BCountry::DateFormat(BString& format, bool longFormat) +BCountry::GetDateFormat(BString& format, bool longFormat) const { - format.Truncate(0); + if (longFormat && fLongDateFormat.Length() > 0) + format = fLongDateFormat; + else if (!longFormat && fShortDateFormat.Length() > 0) + format = fShortDateFormat; + else { + format.Truncate(0); - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); - return B_NO_MEMORY; + ObjectDeleter dateFormatter = CreateDateFormat(longFormat, + *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); + if (dateFormatter.Get() == NULL) + return B_NO_MEMORY; + + SimpleDateFormat* dateFormatterImpl + = static_cast(dateFormatter.Get()); + + UnicodeString ICUString; + ICUString = dateFormatterImpl->toPattern(ICUString); + + BStringByteSink stringConverter(&format); + + ICUString.toUTF8(stringConverter); } - SimpleDateFormat* dateFormatterImpl - = static_cast(dateFormatter); - - UnicodeString ICUString; - ICUString = dateFormatterImpl->toPattern(ICUString); - _UnlockDateFormatter(longFormat); - - BStringByteSink stringConverter(&format); - - ICUString.toUTF8(stringConverter); - return B_OK; } @@ -387,31 +269,24 @@ BCountry::DateFormat(BString& format, bool longFormat) status_t BCountry::SetDateFormat(const char* formatString, bool longFormat) { - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); - return B_NO_MEMORY; - } - - SimpleDateFormat* dateFormatterImpl - = static_cast(dateFormatter); - - UnicodeString pattern(formatString); - dateFormatterImpl->applyPattern(pattern); - _UnlockDateFormatter(longFormat); +printf("FV::SetDateFormat: df='%s'\n", formatString); + if (longFormat) + fLongDateFormat = formatString; + else + fShortDateFormat = formatString; return B_OK; } status_t -BCountry::DateFields(BDateElement*& fields, int& fieldCount, bool longFormat) +BCountry::GetDateFields(BDateElement*& fields, int& fieldCount, + bool longFormat) const { - ICU_VERSION::DateFormat* dateFormatter = _LockDateFormatter(longFormat); - if (dateFormatter == NULL) { - _UnlockDateFormatter(longFormat); + ObjectDeleter dateFormatter = CreateDateFormat(longFormat, + *fICULocale, longFormat ? fLongDateFormat : fShortDateFormat); + if (dateFormatter.Get() == NULL) return B_NO_MEMORY; - } fields = NULL; UErrorCode error = U_ZERO_ERROR; @@ -420,9 +295,8 @@ BCountry::DateFields(BDateElement*& fields, int& fieldCount, bool longFormat) time_t now; ICUString = dateFormatter->format((UDate)time(&now) * 1000, ICUString, &positionIterator, error); - _UnlockDateFormatter(longFormat); - if (error != U_ZERO_ERROR) + if (U_FAILURE(error)) return B_ERROR; ICU_VERSION::FieldPosition field; @@ -457,7 +331,7 @@ BCountry::DateFields(BDateElement*& fields, int& fieldCount, bool longFormat) int -BCountry::StartOfWeek() +BCountry::StartOfWeek() const { UErrorCode err = U_ZERO_ERROR; Calendar* c = Calendar::createInstance(*fICULocale, err); @@ -479,15 +353,13 @@ BCountry::StartOfWeek() status_t BCountry::FormatTime(char* string, size_t maxSize, time_t time, bool longFormat) { - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); + ObjectDeleter timeFormatter = CreateTimeFormat(longFormat, + *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); + if (timeFormatter.Get() == NULL) return B_NO_MEMORY; - } UnicodeString ICUString; ICUString = timeFormatter->format((UDate)time * 1000, ICUString); - _UnlockTimeFormatter(longFormat); CheckedArrayByteSink stringConverter(string, maxSize); @@ -495,8 +367,8 @@ BCountry::FormatTime(char* string, size_t maxSize, time_t time, bool longFormat) if (stringConverter.Overflowed()) return B_BAD_VALUE; - else - return B_OK; + + return B_OK; } @@ -505,15 +377,13 @@ BCountry::FormatTime(BString* string, time_t time, bool longFormat) { string->Truncate(0); - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); + ObjectDeleter timeFormatter = CreateTimeFormat(longFormat, + *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); + if (timeFormatter.Get() == NULL) return B_NO_MEMORY; - } UnicodeString ICUString; ICUString = timeFormatter->format((UDate)time * 1000, ICUString); - _UnlockTimeFormatter(longFormat); BStringByteSink stringConverter(string); @@ -529,11 +399,10 @@ BCountry::FormatTime(BString* string, int*& fieldPositions, int& fieldCount, { string->Truncate(0); - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); + ObjectDeleter timeFormatter = CreateTimeFormat(longFormat, + *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); + if (timeFormatter.Get() == NULL) return B_NO_MEMORY; - } fieldPositions = NULL; UErrorCode error = U_ZERO_ERROR; @@ -541,7 +410,6 @@ BCountry::FormatTime(BString* string, int*& fieldPositions, int& fieldCount, UnicodeString ICUString; ICUString = timeFormatter->format((UDate)time * 1000, ICUString, &positionIterator, error); - _UnlockTimeFormatter(longFormat); if (error != U_ZERO_ERROR) return B_ERROR; @@ -569,13 +437,13 @@ BCountry::FormatTime(BString* string, int*& fieldPositions, int& fieldCount, status_t -BCountry::TimeFields(BDateElement*& fields, int& fieldCount, bool longFormat) +BCountry::GetTimeFields(BDateElement*& fields, int& fieldCount, + bool longFormat) const { - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); + ObjectDeleter timeFormatter = CreateTimeFormat(longFormat, + *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); + if (timeFormatter.Get() == NULL) return B_NO_MEMORY; - } fields = NULL; UErrorCode error = U_ZERO_ERROR; @@ -584,7 +452,6 @@ BCountry::TimeFields(BDateElement*& fields, int& fieldCount, bool longFormat) time_t now; ICUString = timeFormatter->format((UDate)time(&now) * 1000, ICUString, &positionIterator, error); - _UnlockTimeFormatter(longFormat); if (error != U_ZERO_ERROR) return B_ERROR; @@ -629,43 +496,40 @@ BCountry::TimeFields(BDateElement*& fields, int& fieldCount, bool longFormat) status_t BCountry::SetTimeFormat(const char* formatString, bool longFormat) { - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); - return B_NO_MEMORY; - } - - SimpleDateFormat* dateFormatterImpl - = static_cast(timeFormatter); - - UnicodeString pattern(formatString); - dateFormatterImpl->applyPattern(pattern); - _UnlockTimeFormatter(longFormat); + if (longFormat) + fLongTimeFormat = formatString; + else + fShortTimeFormat = formatString; return B_OK; } status_t -BCountry::TimeFormat(BString& format, bool longFormat) +BCountry::GetTimeFormat(BString& format, bool longFormat) const { - ICU_VERSION::DateFormat* timeFormatter = _LockTimeFormatter(longFormat); - if (timeFormatter == NULL) { - _UnlockTimeFormatter(longFormat); - return B_NO_MEMORY; + if (longFormat && fLongTimeFormat.Length() > 0) + format = fLongTimeFormat; + else if (!longFormat && fShortTimeFormat.Length() > 0) + format = fShortTimeFormat; + else { + format.Truncate(0); + + ObjectDeleter timeFormatter = CreateTimeFormat(longFormat, + *fICULocale, longFormat ? fLongTimeFormat : fShortTimeFormat); + if (timeFormatter.Get() == NULL) + return B_NO_MEMORY; + + SimpleDateFormat* timeFormatterImpl + = static_cast(timeFormatter.Get()); + + UnicodeString ICUString; + ICUString = timeFormatterImpl->toPattern(ICUString); + + BStringByteSink stringConverter(&format); + ICUString.toUTF8(stringConverter); } - SimpleDateFormat* dateFormatterImpl - = static_cast(timeFormatter); - - UnicodeString ICUString; - ICUString = dateFormatterImpl->toPattern(ICUString); - _UnlockTimeFormatter(longFormat); - - BStringByteSink stringConverter(&format); - - ICUString.toUTF8(stringConverter); - return B_OK; } @@ -673,12 +537,15 @@ BCountry::TimeFormat(BString& format, bool longFormat) // #pragma mark - Numbers -void +status_t BCountry::FormatNumber(char* string, size_t maxSize, double value) { BString fullString; - FormatNumber(&fullString, value); - strncpy(string, fullString.String(), maxSize); + status_t status = FormatNumber(&fullString, value); + if (status == B_OK) + strlcpy(string, fullString.String(), maxSize); + + return status; } @@ -686,10 +553,11 @@ status_t BCountry::FormatNumber(BString* string, double value) { UErrorCode err = U_ZERO_ERROR; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, NumberFormat::kNumberStyle, - err); + ObjectDeleter numberFormatter = NumberFormat::createInstance( + *fICULocale, NumberFormat::kNumberStyle, err); + if (numberFormatter.Get() == NULL) + return B_NO_MEMORY; if (U_FAILURE(err)) return B_ERROR; @@ -698,171 +566,44 @@ BCountry::FormatNumber(BString* string, double value) string->Truncate(0); BStringByteSink stringConverter(string); - ICUString.toUTF8(stringConverter); return B_OK; } -void +status_t BCountry::FormatNumber(char* string, size_t maxSize, int32 value) { BString fullString; - FormatNumber(&fullString, value); - strncpy(string, fullString.String(), maxSize); + status_t status = FormatNumber(&fullString, value); + if (status == B_OK) + strlcpy(string, fullString.String(), maxSize); + + return status; } -void +status_t BCountry::FormatNumber(BString* string, int32 value) { - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); + UErrorCode err = U_ZERO_ERROR; + ObjectDeleter numberFormatter = NumberFormat::createInstance( + *fICULocale, NumberFormat::kNumberStyle, err); - assert(err == U_ZERO_ERROR); + if (numberFormatter.Get() == NULL) + return B_NO_MEMORY; + if (U_FAILURE(err)) + return B_ERROR; UnicodeString ICUString; ICUString = numberFormatter->format((int32_t)value, ICUString); string->Truncate(0); BStringByteSink stringConverter(string); - - ICUString.toUTF8(stringConverter); -} - - -// This will only work for locales using the decimal system... -bool -BCountry::DecimalPoint(BString& format) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); - - assert(err == U_ZERO_ERROR); - - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol); - - BStringByteSink stringConverter(&format); - ICUString.toUTF8(stringConverter); - return true; -} - - -bool -BCountry::ThousandsSeparator(BString& separator) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); - - BStringByteSink stringConverter(&separator); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::Grouping(BString& grouping) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol); - - BStringByteSink stringConverter(&grouping); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::PositiveSign(BString& sign) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kPlusSignSymbol); - - BStringByteSink stringConverter(&sign); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::NegativeSign(BString& sign) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kMinusSignSymbol); - - BStringByteSink stringConverter(&sign); - - ICUString.toUTF8(stringConverter); - - return true; + return B_OK; } @@ -893,170 +634,36 @@ BCountry::FormatMonetary(BString* string, double value) return B_BAD_VALUE; UErrorCode err; - NumberFormat* numberFormatter + ObjectDeleter numberFormatter = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); + if (numberFormatter.Get() == NULL) + return B_NO_MEMORY; + if (U_FAILURE(err)) + return B_ERROR; UnicodeString ICUString; ICUString = numberFormatter->format(value, ICUString); string->Truncate(0); BStringByteSink stringConverter(string); - ICUString.toUTF8(stringConverter); return string->Length(); } -bool -BCountry::CurrencySymbol(BString& symbol) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kCurrencySymbol); - - BStringByteSink stringConverter(&symbol); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::InternationalCurrencySymbol(BString& symbol) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kIntlCurrencySymbol); - - BStringByteSink stringConverter(&symbol); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::MonDecimalPoint(BString& decimal) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kMonetarySeparatorSymbol); - - BStringByteSink stringConverter(&decimal); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::MonThousandsSeparator(BString& separator) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol(DecimalFormatSymbols::kPatternSeparatorSymbol); - - BStringByteSink stringConverter(&separator); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -bool -BCountry::MonGrouping(BString& grouping) const -{ - UErrorCode err; - NumberFormat* numberFormatter - = NumberFormat::createCurrencyInstance(*fICULocale, err); - assert(err == U_ZERO_ERROR); - DecimalFormat* decimalFormatter - = dynamic_cast(numberFormatter); - - assert(decimalFormatter != NULL); - - const DecimalFormatSymbols* syms - = decimalFormatter->getDecimalFormatSymbols(); - - UnicodeString ICUString; - ICUString = syms->getSymbol( - DecimalFormatSymbols::kMonetaryGroupingSeparatorSymbol); - - BStringByteSink stringConverter(&grouping); - - ICUString.toUTF8(stringConverter); - - return true; -} - - -// TODO: is this possible to get from ICU ? -int32 -BCountry::MonFracDigits() const -{ - return 2; -} - - // #pragma mark - Timezones int -BCountry::GetTimeZones(BList& timezones) +BCountry::GetTimeZones(BList& timezones) const { - StringEnumeration* icuTimeZoneList = TimeZone::createEnumeration( - fICULocale->getCountry()); + ObjectDeleter icuTimeZoneList + = TimeZone::createEnumeration(fICULocale->getCountry()); + if (icuTimeZoneList.Get() == NULL) + return 0; + UErrorCode error = U_ZERO_ERROR; const char* tzName; @@ -1065,20 +672,57 @@ BCountry::GetTimeZones(BList& timezones) // remaining zones after that while ((tzName = icuTimeZoneList->next(NULL, error)) != NULL) { if (error == U_ZERO_ERROR) { - BTimeZone* timeZone = new BTimeZone(tzName); + BTimeZone* timeZone = new(std::nothrow) BTimeZone(tzName); timeZoneMap.insert(std::pair(timeZone->Name(), timeZone)); } else error = U_ZERO_ERROR; } - delete icuTimeZoneList; + std::map::const_iterator tzIter; + for (tzIter = timeZoneMap.begin(); tzIter != timeZoneMap.end(); ++tzIter) + timezones.AddItem(tzIter->second); - - for (std::map::const_iterator timeZoneIterator - = timeZoneMap.begin(); timeZoneIterator != timeZoneMap.end(); - timeZoneIterator++) - timezones.AddItem((*timeZoneIterator).second); - - return timeZoneMap.size(); + return timezones.CountItems(); +} + + +// #pragma mark - Helpers + + +static DateFormat* +CreateDateFormat(bool longFormat, const Locale& locale, + const BString& format) +{ + DateFormat* dateFormatter = DateFormat::createDateInstance( + longFormat ? DateFormat::FULL : DateFormat::SHORT, locale); + + if (format.Length() > 0) { + SimpleDateFormat* dateFormatterImpl + = static_cast(dateFormatter); + + UnicodeString pattern(format.String()); + dateFormatterImpl->applyPattern(pattern); + } + + return dateFormatter; +} + + +static DateFormat* +CreateTimeFormat(bool longFormat, const Locale& locale, + const BString& format) +{ + DateFormat* timeFormatter = DateFormat::createTimeInstance( + longFormat ? DateFormat::MEDIUM : DateFormat::SHORT, locale); + + if (format.Length() > 0) { + SimpleDateFormat* timeFormatterImpl + = static_cast(timeFormatter); + + UnicodeString pattern(format.String()); + timeFormatterImpl->applyPattern(pattern); + } + + return timeFormatter; } diff --git a/src/kits/locale/DateFormat.cpp b/src/kits/locale/DateFormat.cpp new file mode 100644 index 0000000000..3377049362 --- /dev/null +++ b/src/kits/locale/DateFormat.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + +#include + + +// default constructor +BDateFormat::BDateFormat() + : BDateTimeFormat() +{ +} + +// copy constructor +BDateFormat::BDateFormat(const BDateFormat &other) + : BDateTimeFormat(other) +{ +} + +// destructor +BDateFormat::~BDateFormat() +{ +} + +// Format +status_t +BDateFormat::Format(bigtime_t value, BString* buffer) const +{ + return B_ERROR; +} diff --git a/src/kits/locale/DateTimeFormat.cpp b/src/kits/locale/DateTimeFormat.cpp new file mode 100644 index 0000000000..4ddc7df3a1 --- /dev/null +++ b/src/kits/locale/DateTimeFormat.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + +#include + + +// default constructor +BDateTimeFormat::BDateTimeFormat() + : BFormat() +{ +} + +// copy constructor +BDateTimeFormat::BDateTimeFormat(const BDateTimeFormat &other) + : BFormat(other) +{ +} + +// destructor +BDateTimeFormat::~BDateTimeFormat() +{ +} + +// Format +status_t +BDateTimeFormat::Format(bigtime_t value, BString* buffer) const +{ + return B_ERROR; +} diff --git a/src/kits/locale/DefaultCatalog.cpp b/src/kits/locale/DefaultCatalog.cpp index ab415664ba..2a811e22f9 100644 --- a/src/kits/locale/DefaultCatalog.cpp +++ b/src/kits/locale/DefaultCatalog.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include @@ -596,9 +596,9 @@ default_catalog_get_available_languages(BMessage* availableLanguages, BPath catalogPath(&appDir, catalogName.String()); BEntry file(catalogPath.Path()); BDirectory dir(&file); - + char fileName[B_FILE_NAME_LENGTH]; - while(dir.GetNextEntry(&file) == B_OK) { + while(dir.GetNextEntry(&file) == B_OK) { file.GetName(fileName); BString langName(fileName); langName.Replace(kCatExtension,"",1); @@ -616,16 +616,16 @@ default_catalog_get_available_languages(BMessage* availableLanguages, for (size_t i = 0; i < sizeof(which) / sizeof(which[0]); i++) { BPath path; if (find_directory(which[i], &path) == B_OK) { - catalogName = BString("locale/") + catalogName = BString("locale/") << kCatFolder << "/" << sigPattern; - + BPath catalogPath(path.Path(), catalogName.String()); BEntry file(catalogPath.Path()); BDirectory dir(&file); - + char fileName[B_FILE_NAME_LENGTH]; - while(dir.GetNextEntry(&file) == B_OK) { + while(dir.GetNextEntry(&file) == B_OK) { file.GetName(fileName); BString langName(fileName); langName.Replace(kCatExtension,"",1); @@ -633,6 +633,6 @@ default_catalog_get_available_languages(BMessage* availableLanguages, } } } - + return B_OK; } diff --git a/src/kits/locale/DurationFormat.cpp b/src/kits/locale/DurationFormat.cpp new file mode 100644 index 0000000000..c3e5f94ad1 --- /dev/null +++ b/src/kits/locale/DurationFormat.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe + */ + + +#include + +#include + +#include +#include +#include + +#include +#include + + +using BPrivate::ObjectDeleter; + + +// maps our unit element to the corresponding ICU unit +static const UCalendarDateFields skUnitMap[] = { + UCAL_YEAR, + UCAL_MONTH, + UCAL_WEEK_OF_MONTH, + UCAL_DAY_OF_WEEK, + UCAL_HOUR_OF_DAY, + UCAL_MINUTE, + UCAL_SECOND, +}; + + +status_t BDurationFormat::Format(bigtime_t startValue, bigtime_t stopValue, + BString* buffer, time_unit_style style, const BString& separator) const +{ + if (buffer == NULL) + return B_BAD_VALUE; + + UErrorCode icuStatus = U_ZERO_ERROR; + ObjectDeleter calendar = Calendar::createInstance(icuStatus); + if (calendar.Get() == NULL) + return B_NO_MEMORY; + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + calendar->setTime((UDate)startValue / 1000, icuStatus); + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + BTimeUnitFormat timeUnitFormat; + UDate stop = (UDate)stopValue / 1000; + bool needSeparator = false; + for (int unit = 0; unit <= B_TIME_UNIT_LAST; ++unit) { + int delta = calendar->fieldDifference(stop, skUnitMap[unit], icuStatus); + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + if (delta != 0) { + if (needSeparator) + buffer->Append(separator); + else + needSeparator = true; + status_t status = timeUnitFormat.Format(delta, + (time_unit_element)unit, buffer, style); + if (status != B_OK) + return status; + } + } + + return B_OK; +} diff --git a/src/kits/locale/Jamfile b/src/kits/locale/Jamfile index f10af4c4e8..a6c2826a20 100644 --- a/src/kits/locale/Jamfile +++ b/src/kits/locale/Jamfile @@ -18,8 +18,17 @@ local sources = LibraryInit.cpp Locale.cpp LocaleRoster.cpp + MutableLocaleRoster.cpp TimeZone.cpp + # in progress + DateFormat.cpp + DateTimeFormat.cpp + DurationFormat.cpp + TimeFormat.cpp + TimeUnitFormat.cpp + + # old, needs investigation Currency.cpp FloatFormat.cpp FloatFormatImpl.cpp @@ -36,7 +45,6 @@ local sources = NumberFormatImpl.cpp NumberFormatParameters.cpp PropertyFile.cpp - TimeFormat.cpp UnicodeChar.cpp ; diff --git a/src/kits/locale/Language.cpp b/src/kits/locale/Language.cpp index 5534436fcc..180e6e7e19 100644 --- a/src/kits/locale/Language.cpp +++ b/src/kits/locale/Language.cpp @@ -6,6 +6,11 @@ #include +#include +#include +#include +#include + #include #include @@ -15,11 +20,6 @@ #include #include -#include -#include -#include -#include - #include #include @@ -28,91 +28,62 @@ #define ICU_VERSION icu_44 -static const char *gBuiltInStrings[] = { - "Yesterday", - "Today", - "Tomorrow", - "Future", - - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - - "Sun", - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat", - - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec", - - "^[yY]", - "^[nN]", - "yes", - "no" -}; +BLanguage::BLanguage() + : + fDirection(B_LEFT_TO_RIGHT), + fICULocale(NULL) +{ + SetTo(NULL); +} BLanguage::BLanguage(const char* language) : - fDirection(B_LEFT_TO_RIGHT) + fDirection(B_LEFT_TO_RIGHT), + fICULocale(NULL) { - fICULocale = new ICU_VERSION::Locale(language); + SetTo(language); +} - for (int32 i = B_NUM_LANGUAGE_STRINGS;i-- > 0;) - fStrings[i] = NULL; + +BLanguage::BLanguage(const BLanguage& other) + : + fICULocale(NULL) +{ + *this = other; } BLanguage::~BLanguage() { delete fICULocale; - - for (int32 i = B_NUM_LANGUAGE_STRINGS;i-- > 0;) - free(fStrings[i]); } -void -BLanguage::Default() +status_t +BLanguage::SetTo(const char* language) { - fICULocale = new ICU_VERSION::Locale("en"); - fDirection = B_LEFT_TO_RIGHT; + delete fICULocale; + fICULocale = new ICU_VERSION::Locale(language); + if (fICULocale == NULL) + return B_NO_MEMORY; - for (int32 i = B_NUM_LANGUAGE_STRINGS;i-- > 0;) { - free(fStrings[i]); - fStrings[i] = strdup(gBuiltInStrings[i]); + return B_OK; +} + + +BLanguage& BLanguage::operator=(const BLanguage& source) +{ + if (&source != this) { + delete fICULocale; + + fICULocale = source.fICULocale != NULL + ? source.fICULocale->clone() + : NULL; + fDirection = source.fDirection; } + + return *this; } @@ -130,7 +101,11 @@ BLanguage::GetString(uint32 id) const || id > B_LANGUAGE_STRINGS_BASE + B_NUM_LANGUAGE_STRINGS) return NULL; - return fStrings[id - B_LANGUAGE_STRINGS_BASE]; + return NULL; + + // TODO: fetch string from ICU + +// return fStrings[id - B_LANGUAGE_STRINGS_BASE]; } diff --git a/src/kits/locale/LibbeLocaleBackend.cpp b/src/kits/locale/LibbeLocaleBackend.cpp index e4b97a2ff0..65c62281b2 100644 --- a/src/kits/locale/LibbeLocaleBackend.cpp +++ b/src/kits/locale/LibbeLocaleBackend.cpp @@ -8,7 +8,7 @@ #include "Catalog.h" #include "Locale.h" -#include "LocaleRoster.h" +#include "MutableLocaleRoster.h" #include @@ -25,7 +25,7 @@ CreateLocaleBackendInstance() LibbeLocaleBackend::LibbeLocaleBackend() { - be_locale_roster->GetSystemCatalog(&systemCatalog); + mutable_locale_roster->GetSystemCatalog(&systemCatalog); } diff --git a/src/kits/locale/Locale.cpp b/src/kits/locale/Locale.cpp index 9ccf91f873..3d680cab30 100644 --- a/src/kits/locale/Locale.cpp +++ b/src/kits/locale/Locale.cpp @@ -1,4 +1,4 @@ -/* +/* ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved. ** Distributed under the terms of the OpenBeOS License. */ @@ -38,5 +38,5 @@ BLocale::GetString(uint32 id) return ""; } - return fLanguage->GetString(id); + return fLanguage.GetString(id); } diff --git a/src/kits/locale/LocaleRoster.cpp b/src/kits/locale/LocaleRoster.cpp index 63ac377385..e15b0e4abe 100644 --- a/src/kits/locale/LocaleRoster.cpp +++ b/src/kits/locale/LocaleRoster.cpp @@ -13,11 +13,10 @@ #include #include -#include // for debug only #include -#include #include +#include #include #include #include @@ -25,9 +24,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -40,19 +39,20 @@ #include +using BPrivate::CatalogAddOnInfo; + + /* * several attributes/resource-IDs used within the Locale Kit: */ -static const char *kPriorityAttr = "ADDON:priority"; - -const char *BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; +const char* BLocaleRoster::kCatLangAttr = "BEOS:LOCALE_LANGUAGE"; // name of catalog language, lives in every catalog file -const char *BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; +const char* BLocaleRoster::kCatSigAttr = "BEOS:LOCALE_SIGNATURE"; // catalog signature, lives in every catalog file -const char *BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; +const char* BLocaleRoster::kCatFingerprintAttr = "BEOS:LOCALE_FINGERPRINT"; // catalog fingerprint, may live in catalog file -const char *BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG"; +const char* BLocaleRoster::kEmbeddedCatAttr = "BEOS:LOCALE_EMBEDDED_CATALOG"; // attribute which contains flattened data of embedded catalog // this may live in an app- or add-on-file int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA; @@ -61,340 +61,6 @@ int32 BLocaleRoster::kEmbeddedCatResId = 0xCADA; // this may live in an app- or add-on-file - -typedef BCatalogAddOn *(*InstantiateCatalogFunc)(const char *name, - const char *language, uint32 fingerprint); - -typedef BCatalogAddOn *(*CreateCatalogFunc)(const char *name, - const char *language); - -typedef BCatalogAddOn *(*InstantiateEmbeddedCatalogFunc)( - entry_ref *appOrAddOnRef); - -typedef status_t (*GetAvailableLanguagesFunc)(BMessage*, const char*, - const char*, int32); - -static BLocaleRoster gLocaleRoster; -BLocaleRoster *be_locale_roster = &gLocaleRoster; - - -/* - * info about a single catalog-add-on (representing a catalog type): - */ -struct BCatalogAddOnInfo { - BString fName; - BString fPath; - image_id fAddOnImage; - InstantiateCatalogFunc fInstantiateFunc; - InstantiateEmbeddedCatalogFunc fInstantiateEmbeddedFunc; - CreateCatalogFunc fCreateFunc; - GetAvailableLanguagesFunc fLanguagesFunc; - uint8 fPriority; - BList fLoadedCatalogs; - bool fIsEmbedded; - // an embedded add-on actually isn't an add-on, it is included - // as part of the library. The DefaultCatalog is such a beast! - - BCatalogAddOnInfo(const BString& name, const BString& path, uint8 priority); - ~BCatalogAddOnInfo(); - bool MakeSureItsLoaded(); - void UnloadIfPossible(); -}; - - -BCatalogAddOnInfo::BCatalogAddOnInfo(const BString& name, const BString& path, - uint8 priority) - : - fName(name), - fPath(path), - fAddOnImage(B_NO_INIT), - fInstantiateFunc(NULL), - fInstantiateEmbeddedFunc(NULL), - fCreateFunc(NULL), - fLanguagesFunc(NULL), - fPriority(priority), - fIsEmbedded(path.Length()==0) -{ -} - - -BCatalogAddOnInfo::~BCatalogAddOnInfo() -{ - int32 count = fLoadedCatalogs.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOn* cat - = static_cast(fLoadedCatalogs.ItemAt(i)); - delete cat; - } - fLoadedCatalogs.MakeEmpty(); - UnloadIfPossible(); -} - - -bool -BCatalogAddOnInfo::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 -BCatalogAddOnInfo::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()); - } -} - - -/* - * the global data that is shared between all roster-objects: - */ -struct RosterData { - BLocker fLock; - BList fCatalogAddOnInfos; - BMessage fPreferredLanguages; - // BString fCountryCodeName; - // BString fCountryDateFormat; - BCountry fDefaultCountry; - - RosterData(); - ~RosterData(); - void InitializeCatalogAddOns(); - void CleanupCatalogAddOns(); - static int CompareInfos(const void *left, const void *right); - void UpdateSettings(BMessage* newSettings); -}; -static RosterData gRosterData; - - -RosterData::RosterData() - : - fLock("LocaleRosterData") -{ - - openlog_team("liblocale.so", LOG_PID, LOG_USER); -#ifndef DEBUG - setlogmask_team(LOG_UPTO(LOG_WARNING)); -#endif - - InitializeCatalogAddOns(); - - // Load preferences to get the preferred languages - BPath path; - BFile file; - if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { - path.Append("Locale settings"); - BMessage settingsMessage; - if (file.SetTo(path.Path(), B_READ_ONLY) == B_OK - && settingsMessage.Unflatten(&file) == B_OK) { - UpdateSettings(&settingsMessage); - return; - } - } - - // Something went wrong (no settings file or invalid BMessage - // set everything to default values - fPreferredLanguages.AddString("language", "en"); - fDefaultCountry = BCountry("en_US"); - log_team(LOG_ERR, "*** No language preference found!\n"); -} - - -RosterData::~RosterData() -{ - BAutolock lock(fLock); - assert(lock.IsLocked()); - CleanupCatalogAddOns(); - closelog(); -} - - -int -RosterData::CompareInfos(const void *left, const void *right) -{ - return ((BCatalogAddOnInfo*)right)->fPriority - - ((BCatalogAddOnInfo*)left)->fPriority; -} - - -/* -iterate over add-on-folders and collect information about each -catalog-add-ons (types of catalogs) into fCatalogAddOnInfos. -*/ -void -RosterData::InitializeCatalogAddOns() -{ - BAutolock lock(fLock); - assert(lock.IsLocked()); - - // add info about embedded default catalog: - BCatalogAddOnInfo *defaultCatalogAddOnInfo - = new(std::nothrow) BCatalogAddOnInfo("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(-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 - BCatalogAddOnInfo* addOnInfo - = new(std::nothrow) BCatalogAddOnInfo(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); - assert(lock.IsLocked()); - int32 count = fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i(fCatalogAddOnInfos.ItemAt(i)); - delete info; - } - fCatalogAddOnInfos.MakeEmpty(); -} - - -// #pragma mark - BLocaleRoster - - BLocaleRoster::BLocaleRoster() { } @@ -405,8 +71,177 @@ BLocaleRoster::~BLocaleRoster() } +status_t +BLocaleRoster::Refresh() +{ + return gRosterData.Refresh(); +} + + +status_t +BLocaleRoster::GetDefaultCollator(BCollator* collator) const +{ + if (!collator) + return B_BAD_VALUE; + + BAutolock lock(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + *collator = gRosterData.fDefaultCollator; + + return B_OK; +} + + +status_t +BLocaleRoster::GetDefaultLanguage(BLanguage* language) const +{ + if (!language) + return B_BAD_VALUE; + + BAutolock lock(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + *language = gRosterData.fDefaultLanguage; + + return B_OK; +} + + +status_t +BLocaleRoster::GetDefaultCountry(BCountry* country) const +{ + if (!country) + return B_BAD_VALUE; + + BAutolock lock(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + *country = gRosterData.fDefaultCountry; + + return B_OK; +} + + +status_t +BLocaleRoster::GetDefaultTimeZone(BTimeZone* timezone) const +{ + if (!timezone) + return B_BAD_VALUE; + + BAutolock lock(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + *timezone = gRosterData.fDefaultTimeZone; + + return B_OK; +} + + +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(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + *languages = gRosterData.fPreferredLanguages; + + return B_OK; +} + + +status_t +BLocaleRoster::GetInstalledLanguages(BMessage* languages) const +{ + if (!languages) + return B_BAD_VALUE; + + int32 i; + UnicodeString icuLanguageName; + BString languageName; + + int32_t localeCount; + const Locale* icuLocaleList + = Locale::getAvailableLocales(localeCount); + + // TODO: Loop over the strings and add them to a std::set to remove + // duplicates? + for (i = 0; i < localeCount; i++) + languages->AddString("langs", 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("countries", countryList[i]); + + return B_OK; +} + + +status_t +BLocaleRoster::GetInstalledCatalogs(BMessage* languageList, + const char* sigPattern, const char* langPattern, int32 fingerprint) const +{ + if (languageList == NULL) + return B_BAD_VALUE; + + BAutolock lock(gRosterData.fLock); + if (!lock.IsLocked()) + return B_ERROR; + + int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); + for (int32 i = 0; i < count; ++i) { + CatalogAddOnInfo* info + = (CatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); + + if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc) + continue; + + info->fLanguagesFunc(languageList, sigPattern, langPattern, + fingerprint); + } + + return B_OK; +} + + BCatalog* -BLocaleRoster::GetCatalog(BCatalog* catalog, vint32* catalogInitStatus) +BLocaleRoster::_GetCatalog(BCatalog* catalog, vint32* catalogInitStatus) { // This function is used in the translation macros, so it can't return a // status_t. Maybe it could throw exceptions ? @@ -460,428 +295,3 @@ BLocaleRoster::GetCatalog(BCatalog* catalog, vint32* catalogInitStatus) return catalog; } - - -status_t -BLocaleRoster::GetSystemCatalog(BCatalogAddOn **catalog) const -{ - if (!catalog) - return B_BAD_VALUE; - *catalog = be_locale_roster->LoadCatalog("system"); - return B_OK; -} - - -status_t -BLocaleRoster::GetDefaultCollator(BCollator **collator) const -{ - // It should just use the archived collator from the locale settings; - // if that is not available, just return the standard collator - if (!collator) - return B_BAD_VALUE; - *collator = new(std::nothrow) BCollator(); - return B_OK; -} - - -status_t -BLocaleRoster::GetDefaultLanguage(BLanguage **language) const -{ - if (!language) - return B_BAD_VALUE; - *language = new(std::nothrow) BLanguage(NULL); - return B_OK; -} - - -status_t -BLocaleRoster::GetDefaultCountry(BCountry **country) const -{ - if (!country) - return B_BAD_VALUE; - - *country = &gRosterData.fDefaultCountry; - return B_OK; -} - - -status_t -BLocaleRoster::GetDefaultTimeZone(BTimeZone **timezone) const -{ - if (!timezone) - return B_BAD_VALUE; - - *timezone = new(std::nothrow) BTimeZone(); - return B_OK; -} - - -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; -} - - -void -BLocaleRoster::SetDefaultCountry(BCountry* newDefault) const -{ - if (newDefault == NULL) - return; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - gRosterData.fDefaultCountry = *newDefault; -} - - -void -BLocaleRoster::SetDefaultTimeZone(const char* zone) const -{ - TimeZone::adoptDefault(TimeZone::createTimeZone(zone)); -} - - -void -BLocaleRoster::UpdateSettings(BMessage* newSettings) -{ - gRosterData.UpdateSettings(newSettings); -} - - -void -RosterData::UpdateSettings(BMessage* newSettings) -{ - BAutolock lock(fLock); - assert(lock.IsLocked()); - - BString langName; - if (newSettings->FindString("language", &langName) == B_OK) { - UErrorCode icuError = U_ZERO_ERROR; - Locale icuLocale = Locale::createCanonical(langName.String()); - assert(!icuLocale.isBogus()); - UnicodeString ustr; - BString bstr; - BStringByteSink bbs(&bstr); - icuLocale.getDisplayName(ustr); - ustr.toUTF8(bbs); - - Locale::setDefault(icuLocale, icuError); - assert(icuError == U_ZERO_ERROR); - fPreferredLanguages.RemoveName("language"); - for (int i = 0; newSettings->FindString("language", i, - &langName) == B_OK; i++) { - fPreferredLanguages.AddString("language", langName); - } - } else - fPreferredLanguages.AddString("language", "en"); - - BString codeName; - - if (newSettings->FindString("country", &codeName) - == B_OK) - fDefaultCountry = BCountry(codeName); - else - fDefaultCountry = BCountry("en_US"); - - BString timeFormat; - if (newSettings->FindString("shortTimeFormat", &timeFormat) - == B_OK) - fDefaultCountry.SetTimeFormat(timeFormat, false); - if (newSettings->FindString("longTimeFormat", &timeFormat) - == B_OK) - fDefaultCountry.SetTimeFormat(timeFormat, true); -} - - -status_t -BLocaleRoster::GetPreferredLanguages(BMessage* languages) const -{ - if (!languages) - return B_BAD_VALUE; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - *languages = gRosterData.fPreferredLanguages; - return B_OK; -} - - - status_t -BLocaleRoster::SetPreferredLanguages(BMessage *languages) -{ - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - if (languages) - gRosterData.fPreferredLanguages = *languages; - else - gRosterData.fPreferredLanguages.MakeEmpty(); - return B_OK; -} - - -// Get all the available languages from ICU -status_t -BLocaleRoster::GetInstalledLanguages(BMessage *languages) const -{ - if (!languages) - return B_BAD_VALUE; - - int32 i; - UnicodeString icuLanguageName; - BString languageName; - - int32_t localeCount; - const Locale* icuLocaleList - = Locale::getAvailableLocales(localeCount); - - // Loop over the strings and add them to an std::set to remove duplicates - for (i = 0; i < localeCount; i++) { - languages->AddString("langs", icuLocaleList[i].getName()); - } - - return B_OK; -} - - -// Get all the available countries from ICU -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("countries", countryList[i]); - } - - return B_OK; -} - - -status_t -BLocaleRoster::GetInstalledCatalogs(BMessage * languageList, - const char* sigPattern, const char* langPattern, int32 fingerprint) const -{ - if (languageList == NULL) - return B_BAD_VALUE; - - int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOnInfo *info - = (BCatalogAddOnInfo*)gRosterData.fCatalogAddOnInfos.ItemAt(i); - - if (!info->MakeSureItsLoaded() || !info->fLanguagesFunc) - continue; - - info->fLanguagesFunc(languageList, sigPattern, langPattern, - fingerprint); - } - - 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* -BLocaleRoster::CreateCatalog(const char *type, const char *signature, - const char *language) -{ - if (!type || !signature || !language) - return NULL; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOnInfo *info - = (BCatalogAddOnInfo*)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* -BLocaleRoster::LoadCatalog(const char *signature, const char *language, - int32 fingerprint) -{ - if (!signature) - return NULL; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOnInfo *info - = (BCatalogAddOnInfo*)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, *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->fNext = 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* -BLocaleRoster::LoadEmbeddedCatalog(entry_ref *appOrAddOnRef) -{ - if (!appOrAddOnRef) - return NULL; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOnInfo *info - = (BCatalogAddOnInfo*)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 -BLocaleRoster::UnloadCatalog(BCatalogAddOn *catalog) -{ - if (!catalog) - return B_BAD_VALUE; - - BAutolock lock(gRosterData.fLock); - assert(lock.IsLocked()); - - status_t res = B_ERROR; - BCatalogAddOn *nextCatalog; - // note: as we currently aren't chainloading catalogs, there is only - // one catalog to unload... - while (catalog) { - nextCatalog = catalog->fNext; - int32 count = gRosterData.fCatalogAddOnInfos.CountItems(); - for (int32 i = 0; i < count; ++i) { - BCatalogAddOnInfo *info - = static_cast( - gRosterData.fCatalogAddOnInfos.ItemAt(i) - ); - if (info->fLoadedCatalogs.HasItem(catalog)) { - info->fLoadedCatalogs.RemoveItem(catalog); - delete catalog; - info->UnloadIfPossible(); - res = B_OK; - } - } - catalog = nextCatalog; - } - return res; -} diff --git a/src/kits/locale/MutableLocaleRoster.cpp b/src/kits/locale/MutableLocaleRoster.cpp new file mode 100644 index 0000000000..1d59e2c233 --- /dev/null +++ b/src/kits/locale/MutableLocaleRoster.cpp @@ -0,0 +1,861 @@ +/* + * 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 + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// ICU includes +#include +#include + + +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(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 - MutableLocaleRoster + + +RosterData gRosterData; + +static const char* kPriorityAttr = "ADDON:priority"; + + +RosterData::RosterData() + : + fLock("LocaleRosterData") +{ + 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; + + status_t status = _LoadLocaleSettings(); + if (status == B_OK) + status = _LoadTimeSettings(); + + return status; +} + +/* +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(-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(fCatalogAddOnInfos.ItemAt(i)); + delete info; + } + fCatalogAddOnInfos.MakeEmpty(); +} + + +status_t +RosterData::SetDefaultCountry(const BCountry& newCountry) +{ + status_t status = B_OK; + + BAutolock lock(fLock); + if (!lock.IsLocked()) + return B_ERROR; + + status = _SetDefaultCountry(newCountry); + + if (status == B_OK) + status = _SaveLocaleSettings(); + + if (status == B_OK) { + BMessage updateMessage(B_LOCALE_CHANGED); + status = _AddDefaultCountryToMessage(&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) + status = _SetPreferredLanguages(&settings); + + if (status == B_OK) { + BString codeName; + BCountry newDefaultCountry; + + if (settings.FindString("country", &codeName) == B_OK) + newDefaultCountry = BCountry(codeName); + + BString timeFormat; + if (settings.FindString("shortTimeFormat", &timeFormat) == B_OK) + newDefaultCountry.SetTimeFormat(timeFormat, false); + if (settings.FindString("longTimeFormat", &timeFormat) == B_OK) + newDefaultCountry.SetTimeFormat(timeFormat, true); + + _SetDefaultCountry(newDefaultCountry); + + return B_OK; + } + + // Something went wrong (no settings file or invalid BMessage), so we + // set everything to default values + fPreferredLanguages.AddString("language", "en"); + log_team(LOG_ERR, "*** No locale settings found!\n"); + + 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 timeZoneCode; + if (settings.FindString("timezone", &timeZoneCode) == B_OK) + _SetDefaultTimeZone(BTimeZone(timeZoneCode.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)); + log_team(LOG_ERR, "*** No time settings found!\n"); + + return status; +} + + +status_t +RosterData::_SaveLocaleSettings() +{ + BMessage settings; + status_t status = _AddDefaultCountryToMessage(&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::_SetDefaultCountry(const BCountry& newCountry) +{ + fDefaultCountry = newCountry; + + return B_OK; +} + + +status_t +RosterData::_SetDefaultTimeZone(const BTimeZone& newZone) +{ + fDefaultTimeZone = newZone; + + return B_OK; +} + + +status_t +RosterData::_SetPreferredLanguages(const BMessage* languages) +{ + BString langName; + if (languages != NULL + && languages->FindString("language", &langName) == B_OK) { + UErrorCode icuError = U_ZERO_ERROR; + Locale icuLocale = Locale::createCanonical(langName.String()); + if (icuLocale.isBogus()) + return B_ERROR; + + Locale::setDefault(icuLocale, icuError); + if (!U_SUCCESS(icuError)) + return B_ERROR; + + fDefaultCollator = BCollator(langName.String()); + fDefaultLanguage.SetTo(langName.String()); + + fPreferredLanguages.RemoveName("language"); + for (int i = 0; languages->FindString("language", i, &langName) == B_OK; + i++) { + fPreferredLanguages.AddString("language", langName); + } + } else { + fPreferredLanguages.MakeEmpty(); + fPreferredLanguages.AddString("language", "en"); + } + + return B_OK; +} + + +status_t +RosterData::_AddDefaultCountryToMessage(BMessage* message) const +{ + status_t status = message->AddString("country", fDefaultCountry.Code()); + BString timeFormat; + if (status == B_OK) + status = fDefaultCountry.GetTimeFormat(timeFormat, false); + if (status == B_OK) + status = message->AddString("shortTimeFormat", timeFormat.String()); + if (status == B_OK) + status = fDefaultCountry.GetTimeFormat(timeFormat, true); + if (status == B_OK) + status = message->AddString("longTimeFormat", timeFormat.String()); + + return status; +} + + +status_t +RosterData::_AddDefaultTimeZoneToMessage(BMessage* message) const +{ + return message->AddString("timezone", fDefaultTimeZone.Code()); +} + + +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("language", langName); + if (status != B_OK) + break; + } + + return status; +} + + +// #pragma mark - MutableLocaleRoster + + +static MutableLocaleRoster gLocaleRoster; +MutableLocaleRoster* mutable_locale_roster = &gLocaleRoster; + + +MutableLocaleRoster::MutableLocaleRoster() +{ +} + + +MutableLocaleRoster::~MutableLocaleRoster() +{ +} + + +status_t +MutableLocaleRoster::SetDefaultCountry(const BCountry& newCountry) +{ + return gRosterData.SetDefaultCountry(newCountry); +} + + +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( + 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; diff --git a/src/kits/locale/TimeFormat.cpp b/src/kits/locale/TimeFormat.cpp index 3d4ddbccf3..c99c5c063a 100644 --- a/src/kits/locale/TimeFormat.cpp +++ b/src/kits/locale/TimeFormat.cpp @@ -1,90 +1,28 @@ -/* - * Copyright 2010, Adrien Destugues, pulkomandy@gmail.com +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. + * + * Authors: + * Oliver Tappe */ #include -#include -#include -#include -#include -#define ICU_VERSION icu_44 - -status_t BTimeFormat::Format(int64 number, BString* buffer) const +// copy constructor +BTimeFormat::BTimeFormat(const BTimeFormat &other) + : BDateTimeFormat(other) { - // create time unit amount instance - a combination of Number and time unit - UErrorCode status = U_ZERO_ERROR; - - int64 days, hours, minutes, seconds, remainder; - - days = number / (24 * 3600); - remainder = number % (24 * 3600); - - hours = remainder / 3600; - remainder %= 3600; - - minutes = remainder / 60; - remainder %= 60; - - seconds = remainder; - - TimeUnitFormat* format = new TimeUnitFormat(status); - UnicodeString formatted; - Formattable formattable; - BStringByteSink bbs(buffer); - - if (!U_SUCCESS(status)) { - delete format; - return B_ERROR; - } - - if (days) { - TimeUnitAmount* daysAmount = new TimeUnitAmount(days, - TimeUnit::UTIMEUNIT_DAY, status); - - formattable.adoptObject(daysAmount); - formatted = ((ICU_VERSION::Format*)format)->format(formattable, formatted, - status); - } - - if (hours) { - TimeUnitAmount* hoursAmount = new TimeUnitAmount(hours, - TimeUnit::UTIMEUNIT_HOUR, status); - - formattable.adoptObject(hoursAmount); - if (days) - formatted.append(", "); - formatted = ((ICU_VERSION::Format*)format)->format(formattable, formatted, - status); - } - - if (minutes) { - TimeUnitAmount* minutesAmount = new TimeUnitAmount(minutes, - TimeUnit::UTIMEUNIT_MINUTE, status); - - formattable.adoptObject(minutesAmount); - if (days || hours) - formatted.append(", "); - formatted = ((ICU_VERSION::Format*)format)->format(formattable, formatted, - status); - } - - - if (seconds || (minutes == 0 && hours == 0 && days == 0)) { - TimeUnitAmount* secondsAmount = new TimeUnitAmount(seconds, - TimeUnit::UTIMEUNIT_SECOND, status); - - formattable.adoptObject(secondsAmount); - if (days || hours || minutes) - formatted.append(", "); - formatted = ((ICU_VERSION::Format*)format)->format(formattable, formatted, - status); - } - formatted.toUTF8(bbs); - - - delete format; - return B_OK; +} + +// destructor +BTimeFormat::~BTimeFormat() +{ +} + +// Format +status_t +BTimeFormat::Format(bigtime_t value, BString* buffer) const +{ + return B_ERROR; } diff --git a/src/kits/locale/TimeUnitFormat.cpp b/src/kits/locale/TimeUnitFormat.cpp new file mode 100644 index 0000000000..a9cbad945c --- /dev/null +++ b/src/kits/locale/TimeUnitFormat.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2010, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Adrien Destugues + * Oliver Tappe + */ + + +#include + +#include + +#include +#include +#include +#include + + +// maps our unit element to the corresponding ICU unit +static const TimeUnit::UTimeUnitFields skUnitMap[] = { + TimeUnit::UTIMEUNIT_YEAR, + TimeUnit::UTIMEUNIT_MONTH, + TimeUnit::UTIMEUNIT_WEEK, + TimeUnit::UTIMEUNIT_DAY, + TimeUnit::UTIMEUNIT_HOUR, + TimeUnit::UTIMEUNIT_MINUTE, + TimeUnit::UTIMEUNIT_SECOND, +}; + + +status_t BTimeUnitFormat::Format(int32 value, time_unit_element unit, + BString* buffer, time_unit_style style) const +{ + if (buffer == NULL || unit < 0 || unit > B_TIME_UNIT_LAST + || (style != B_TIME_UNIT_ABBREVIATED && style != B_TIME_UNIT_FULL)) + return B_BAD_VALUE; + + UErrorCode icuStatus = U_ZERO_ERROR; + TimeUnitFormat timeUnitFormatter(icuStatus); + + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + TimeUnitAmount* timeUnitAmount + = new TimeUnitAmount((double)value, skUnitMap[unit], icuStatus); + if (timeUnitAmount == NULL) + return B_NO_MEMORY; + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + Formattable formattable; + formattable.adoptObject(timeUnitAmount); + FieldPosition pos(FieldPosition::DONT_CARE); + UnicodeString unicodeResult; + timeUnitFormatter.format(formattable, unicodeResult, pos, icuStatus); + if (!U_SUCCESS(icuStatus)) + return B_ERROR; + + BStringByteSink byteSink(buffer); + unicodeResult.toUTF8(byteSink); + + return B_OK; +} diff --git a/src/kits/locale/TimeZone.cpp b/src/kits/locale/TimeZone.cpp index 22968fe28c..dfb4066f63 100644 --- a/src/kits/locale/TimeZone.cpp +++ b/src/kits/locale/TimeZone.cpp @@ -14,9 +14,12 @@ #include +const char* BTimeZone::kNameOfGmtZone = "GMT"; + + BTimeZone::BTimeZone(const char* zoneCode) { - _Init(zoneCode); + SetTo(zoneCode); } @@ -53,8 +56,8 @@ BTimeZone::InitCheck() const } -void -BTimeZone::_Init(const char* zoneCode) +status_t +BTimeZone::SetTo(const char* zoneCode) { TimeZone* icuTimeZone; if (zoneCode == NULL || zoneCode[0] == '\0') @@ -86,4 +89,6 @@ BTimeZone::_Init(const char* zoneCode) // we want seconds, not ms (which ICU gives us) fInitStatus = B_OK; } + + return fInitStatus; } diff --git a/src/preferences/locale/LocaleSettings.cpp b/src/preferences/locale/LocaleSettings.cpp index 31b0d5e5bd..8db0b2969b 100644 --- a/src/preferences/locale/LocaleSettings.cpp +++ b/src/preferences/locale/LocaleSettings.cpp @@ -7,20 +7,21 @@ #include "LocaleSettings.h" #include -#include +#include #include -#include #include #include +using BPrivate::mutable_locale_roster; + + static const uint32 kMsgLocaleSettings = 'LCst'; LocaleSettings::LocaleSettings() : - fMessage(kMsgLocaleSettings), - fSaved(false) + fMessage(kMsgLocaleSettings) { // Set default preferences fMessage.AddString("language", "en"); @@ -36,27 +37,14 @@ LocaleSettings::Load() err = _Open(&file, B_READ_ONLY); if (err != B_OK) return err; - err = fMessage.Unflatten(&file); - if (err == B_OK) - fSaved = true; - return err; + return fMessage.Unflatten(&file); } status_t LocaleSettings::Save() { - // Save on disk for next time we reboot - BFile file; - status_t err; - err = _Open(&file, B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); - if (err != B_OK) - return err; - - err = fMessage.Flatten(&file); - if (err == B_OK) - fSaved = true; - return err; + return B_OK; } @@ -86,33 +74,35 @@ LocaleSettings::UpdateFrom(BMessage* message) break; fMessage.AddString("language", messageContent); } - fSaved = false; + mutable_locale_roster->SetPreferredLanguages(message); } + BCountry defaultCountry; + mutable_locale_roster->GetDefaultCountry(&defaultCountry); + + bool countryChanged = false; if (message->FindString("country", &messageContent) == B_OK) { fMessage.ReplaceString("country", messageContent); fMessage.RemoveName("shortTimeFormat"); fMessage.RemoveName("longTimeFormat"); - fSaved = false; + defaultCountry = BCountry(messageContent.String()); + countryChanged = true; } if (message->FindString("shortTimeFormat", &messageContent) == B_OK) { fMessage.RemoveName("shortTimeFormat"); fMessage.AddString("shortTimeFormat", messageContent); - fSaved = false; + defaultCountry.SetTimeFormat(messageContent, false); + countryChanged = true; } if (message->FindString("longTimeFormat", &messageContent) == B_OK) { fMessage.RemoveName("longTimeFormat"); fMessage.AddString("longTimeFormat", messageContent); - fSaved = false; + defaultCountry.SetTimeFormat(messageContent, true); + countryChanged = true; } - if (fSaved == false) { - // Send to all running apps to notify them they should update their - // settings - fMessage.what = B_LOCALE_CHANGED; - be_roster->Broadcast(&fMessage); - } + if (countryChanged) + mutable_locale_roster->SetDefaultCountry(defaultCountry); } - diff --git a/src/preferences/locale/LocaleSettings.h b/src/preferences/locale/LocaleSettings.h index d374c708f4..c456a72aab 100644 --- a/src/preferences/locale/LocaleSettings.h +++ b/src/preferences/locale/LocaleSettings.h @@ -19,7 +19,6 @@ class LocaleSettings { status_t Load(); status_t Save(); - bool Saved(); bool operator==(const LocaleSettings& other); void UpdateFrom(BMessage* message); @@ -44,7 +43,6 @@ class LocaleSettings { status_t _Open(BFile* file, int32 mode); BMessage fMessage; - bool fSaved; }; diff --git a/src/preferences/locale/LocaleWindow.cpp b/src/preferences/locale/LocaleWindow.cpp index dc4f4c90e3..68f7b97b69 100644 --- a/src/preferences/locale/LocaleWindow.cpp +++ b/src/preferences/locale/LocaleWindow.cpp @@ -47,10 +47,24 @@ static const uint32 kMsgPreferredLanguagesChanged = 'lang'; static int compare_typed_list_items(const BListItem* _a, const BListItem* _b) { - // TODO: sort them using collators. + static BCollator collator; + LanguageListItem* a = (LanguageListItem*)_a; LanguageListItem* b = (LanguageListItem*)_b; - return strcasecmp(a->Text(), b->Text()); + + return collator.Compare(a->Text(), b->Text()); +} + + +static int +compare_void_list_items(const void* _a, const void* _b) +{ + static BCollator collator; + + LanguageListItem* a = *(LanguageListItem**)_a; + LanguageListItem* b = *(LanguageListItem**)_b; + + return collator.Compare(a->Text(), b->Text()); } @@ -62,7 +76,7 @@ LocaleWindow::LocaleWindow() BWindow(BRect(0, 0, 0, 0), "Locale", B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS) { - BCountry* defaultCountry; + BCountry defaultCountry; be_locale_roster->GetDefaultCountry(&defaultCountry); SetLayout(new BGroupLayout(B_HORIZONTAL)); @@ -173,21 +187,26 @@ LocaleWindow::LocaleWindow() be_locale_roster->GetInstalledLanguages(&countryList); BString countryCode; + LanguageListItem* currentItem = NULL; for (int i = 0; countryList.FindString("langs", i, &countryCode) == B_OK; i++) { BCountry country(countryCode); BString countryName; - country.LocaleName(countryName); + country.GetLocaleName(countryName); LanguageListItem* item = new LanguageListItem(countryName, countryCode, NULL); listView->AddItem(item); - if (!strcmp(countryCode, defaultCountry->Code())) - listView->Select(listView->CountItems() - 1); + if (!strcmp(countryCode, defaultCountry.Code())) + currentItem = item; } + listView->SortItems(compare_void_list_items); + if (currentItem != NULL) + listView->Select(listView->IndexOf(currentItem)); + // TODO: find a real solution intead of this hack listView->SetExplicitMinSize( BSize(25 * be_plain_font->Size(), B_SIZE_UNSET)); @@ -341,7 +360,7 @@ LocaleWindow::MessageReceived(BMessage* message) be_app_messenger.SendMessage(&newMessage); SettingsChanged(); - BCountry* country = new BCountry(item->ID()); + BCountry country(item->ID()); fFormatView->SetCountry(country); break; } diff --git a/src/preferences/locale/TimeFormatSettingsView.cpp b/src/preferences/locale/TimeFormatSettingsView.cpp index 8f7d8ef14f..8f7149401e 100644 --- a/src/preferences/locale/TimeFormatSettingsView.cpp +++ b/src/preferences/locale/TimeFormatSettingsView.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -34,6 +34,10 @@ #include +#include + +using BPrivate::mutable_locale_roster; + #undef B_TRANSLATE_CONTEXT #define B_TRANSLATE_CONTEXT "TimeFormatSettings" @@ -144,10 +148,10 @@ IsSpecialDateChar(char charToTest) // #pragma mark - -FormatView::FormatView(BCountry* country) +FormatView::FormatView(const BCountry& country) : BView("WindowsSettingsView", B_FRAME_EVENTS), - fCountry(*country) + fCountry(country) { SetLayout(new BGroupLayout(B_HORIZONTAL)); @@ -185,8 +189,8 @@ FormatView::FormatView(BCountry* country) f12HrRadioButton = new BRadioButton("", B_TRANSLATE("12 hour"), new BMessage(kClockFormatChange)); - fCountry.TimeFormat(fOriginalTimeFormat, false); - fCountry.TimeFormat(fOriginalLongTimeFormat, true); + fCountry.GetTimeFormat(fOriginalTimeFormat, false); + fCountry.GetTimeFormat(fOriginalLongTimeFormat, true); if (fOriginalTimeFormat.FindFirst("a") != B_ERROR) { f12HrRadioButton->SetValue(B_CONTROL_ON); fCountryIs24Hr = false; @@ -356,7 +360,7 @@ FormatView::FormatView(BCountry* country) FormatView::~FormatView() { - be_locale_roster->SetDefaultCountry(&fCountry); + mutable_locale_roster->SetDefaultCountry(fCountry); } @@ -510,9 +514,9 @@ FormatView::SetDefaults() settings.SetClockTo24Hr(false); */ - BCountry* defaultCountry; + BCountry defaultCountry; be_locale_roster->GetDefaultCountry(&defaultCountry); - fCountry = *defaultCountry; + fCountry = defaultCountry; // We work on a copy of the default country and set the changes when // closing the preflet _UpdateExamples(); @@ -551,14 +555,12 @@ FormatView::Revert() void -FormatView::SetCountry(BCountry* country) +FormatView::SetCountry(const BCountry& country) { - fCountry = *country; + fCountry = country; - fOriginalTimeFormat.Truncate(0); - fCountry.TimeFormat(fOriginalTimeFormat, false); - fOriginalLongTimeFormat.Truncate(0); - fCountry.TimeFormat(fOriginalLongTimeFormat, true); + fCountry.GetTimeFormat(fOriginalTimeFormat, false); + fCountry.GetTimeFormat(fOriginalLongTimeFormat, true); if (fOriginalTimeFormat.FindFirst("a") != B_ERROR) { f12HrRadioButton->SetValue(B_CONTROL_ON); @@ -666,10 +668,12 @@ FormatView::_ParseDateFormat() { // TODO parse the short date too BString dateFormatString; - fCountry.DateFormat(dateFormatString, true); + fCountry.GetDateFormat(dateFormatString, true); const char* dateFormat = dateFormatString.String(); - // Travel trough the string and parse it +printf("FV::_ParseDateFormat: df='%s'\n", dateFormat); + + // Travel through the string and parse it const char* parsePointer = dateFormat; const char* fieldBegin = dateFormat; @@ -717,8 +721,7 @@ FormatView::_ParseDateFormat() } // Short date is a bit more tricky, we want to extract the separator - dateFormatString.Truncate(0); - fCountry.DateFormat(dateFormatString, false); + fCountry.GetDateFormat(dateFormatString, false); dateFormat = dateFormatString.String(); // Travel trough the string and parse it diff --git a/src/preferences/locale/TimeFormatSettingsView.h b/src/preferences/locale/TimeFormatSettingsView.h index 4a0df9b3a0..eb892cb8f1 100644 --- a/src/preferences/locale/TimeFormatSettingsView.h +++ b/src/preferences/locale/TimeFormatSettingsView.h @@ -37,7 +37,7 @@ const uint32 kMenuMessage = 'FRMT'; class FormatView : public BView { public: - FormatView(BCountry* country); + FormatView(const BCountry& country); ~FormatView(); virtual void MessageReceived(BMessage* message); @@ -46,7 +46,7 @@ public: virtual void SetDefaults(); virtual bool IsDefaultable() const; virtual void Revert(); - virtual void SetCountry(BCountry* country); + virtual void SetCountry(const BCountry& country); virtual void RecordRevertSettings(); virtual bool IsRevertable() const; diff --git a/src/preferences/time/DateTimeEdit.cpp b/src/preferences/time/DateTimeEdit.cpp index 78b05ac010..b2b9691b45 100644 --- a/src/preferences/time/DateTimeEdit.cpp +++ b/src/preferences/time/DateTimeEdit.cpp @@ -105,9 +105,9 @@ TTimeEdit::DrawSection(uint32 index, bool hasFocus) int* fieldPositions; int fieldCount; - BCountry* country; + BCountry country; be_locale_roster->GetDefaultCountry(&country); - country->FormatTime(&text, fieldPositions, fieldCount, time, true); + country.FormatTime(&text, fieldPositions, fieldCount, time, true); // TODO : this should be cached somehow to not redo it for each field if (index * 2 + 1 > (uint32)fieldCount) { @@ -148,10 +148,10 @@ TTimeEdit::DrawSeparator(uint32 index) int* fieldPositions; int fieldCount; - BCountry* country; + BCountry country; be_locale_roster->GetDefaultCountry(&country); time_t time = fTime.Time_t(); - country->FormatTime(&text, fieldPositions, fieldCount, time, true); + country.FormatTime(&text, fieldPositions, fieldCount, time, true); // TODO : this should be cached somehow to not redo it for each field if (index * 2 + 2 > (uint32)fieldCount) { @@ -265,9 +265,9 @@ TTimeEdit::BuildDispatch(BMessage* message) BDateElement* dateFormat; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->TimeFields(dateFormat, fieldCount, true); + here.GetTimeFields(dateFormat, fieldCount, true); if (fFocus > fieldCount) { free(dateFormat); return; @@ -303,9 +303,9 @@ TTimeEdit::_CheckRange() int32 value = fHoldValue; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->TimeFields(fields, fieldCount, true); + here.GetTimeFields(fields, fieldCount, true); if (fFocus > fieldCount) { free(fields); return; @@ -373,9 +373,9 @@ TTimeEdit::_IsValidDoubleDigi(int32 value) bool isInRange = false; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->TimeFields(fields, fieldCount, true); + here.GetTimeFields(fields, fieldCount, true); if (fFocus > fieldCount) { free(fields); return false; @@ -412,9 +412,9 @@ TTimeEdit::_SectionValue(int32 index) const int32 value; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->TimeFields(fields, fieldCount, true); + here.GetTimeFields(fields, fieldCount, true); if (index > fieldCount) { free(fields); return 0; @@ -487,9 +487,9 @@ TDateEdit::KeyDown(const char* bytes, int32 numBytes) BDateElement* dateFormat; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->DateFields(dateFormat, fieldCount, false); + here.GetDateFields(dateFormat, fieldCount, false); if (dateFormat[section] == B_DATE_ELEMENT_YEAR) { int32 oldCentury = int32(fHoldValue / 100) * 100; @@ -541,9 +541,9 @@ TDateEdit::DrawSection(uint32 index, bool hasFocus) int* fieldPositions; int fieldCount; - BCountry* country; + BCountry country; be_locale_roster->GetDefaultCountry(&country); - country->FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), + country.FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), false); // TODO : this should be cached somehow to not redo it for each field @@ -587,10 +587,10 @@ TDateEdit::DrawSeparator(uint32 index) int* fieldPositions; int fieldCount; - BCountry* country; + BCountry country; be_locale_roster->GetDefaultCountry(&country); BDateTime dateTime(fDate, BTime()); - country->FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), + country.FormatDate(&text, fieldPositions, fieldCount, dateTime.Time_t(), false); // TODO : this should be cached somehow to not redo it for each field @@ -716,9 +716,9 @@ TDateEdit::BuildDispatch(BMessage* message) BDateElement* dateFormat; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->DateFields(dateFormat, fieldCount, false); + here.GetDateFields(dateFormat, fieldCount, false); if (fFocus > fieldCount) { free(dateFormat); return; @@ -754,9 +754,9 @@ TDateEdit::_CheckRange() int32 value = fHoldValue; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->DateFields(fields, fieldCount, false); + here.GetDateFields(fields, fieldCount, false); if (fFocus > fieldCount) { free(fields); return; @@ -811,9 +811,9 @@ TDateEdit::_IsValidDoubleDigi(int32 value) bool isInRange = false; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->DateFields(fields, fieldCount, false); + here.GetDateFields(fields, fieldCount, false); if (fFocus > fieldCount) { free(fields); return false; @@ -852,9 +852,9 @@ TDateEdit::_SectionValue(int32 index) const int32 value = 0; BDateElement* fields; int fieldCount; - BCountry* here; + BCountry here; be_locale_roster->GetDefaultCountry(&here); - here->DateFields(fields, fieldCount, false); + here.GetDateFields(fields, fieldCount, false); if (index > fieldCount) { free(fields); return 0; diff --git a/src/preferences/time/DateTimeView.cpp b/src/preferences/time/DateTimeView.cpp index 1a9ad42431..e8d3cfae11 100644 --- a/src/preferences/time/DateTimeView.cpp +++ b/src/preferences/time/DateTimeView.cpp @@ -15,7 +15,6 @@ #include "TimeMessages.h" #include "TimeWindow.h" - #include #include #include @@ -28,22 +27,18 @@ #include #include #include + +#include #include +#include + using BPrivate::BCalendarView; using BPrivate::BDateTime; using BPrivate::B_LOCAL_TIME; -#ifdef HAIKU_TARGET_PLATFORM_HAIKU -# include -#else -extern "C" void _kset_tzfilename_(const char* name, size_t length, bool isGMT); -# define _kern_set_tzfilename _kset_tzfilename_ -#endif - - DateTimeView::DateTimeView(BRect frame) : BView(frame, "dateTimeView", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE_JUMP), @@ -266,6 +261,7 @@ DateTimeView::_InitView() AddChild(fGmtTime); fGmtTime->ResizeToPreferred(); +printf("fUseGmtTime=%d\n", fUseGmtTime); if (fUseGmtTime) fGmtTime->SetValue(B_CONTROL_ON); else @@ -323,20 +319,6 @@ DateTimeView::_UpdateGmtSettings() { _WriteRTCSettings(); - BPath path; - if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { - path.Append("timezone"); - BEntry entry(path.Path(), true); - if (entry.Exists()) { - entry.GetPath(&path); - - // take the existing timezone and set it's gmt use - _kern_set_tzfilename(path.Path(), B_PATH_NAME_LENGTH, fUseGmtTime); - return; - } - } - - // Only update GMT _kern_set_tzfilename(NULL, 0, fUseGmtTime); } diff --git a/src/preferences/time/Jamfile b/src/preferences/time/Jamfile index 4d44071149..e0840bd8e5 100644 --- a/src/preferences/time/Jamfile +++ b/src/preferences/time/Jamfile @@ -2,10 +2,10 @@ SubDir HAIKU_TOP src preferences time ; SetSubDirSupportedPlatformsBeOSCompatible ; -UsePrivateHeaders shared ; +UsePrivateHeaders locale shared ; UsePrivateSystemHeaders ; -Preference Time : +local sources = AnalogClock.cpp BaseView.cpp Bitmaps.cpp @@ -18,6 +18,16 @@ Preference Time : TimeZoneListItem.cpp TZDisplay.cpp ZoneView.cpp + ; + + +SubDirSysHdrs $(HAIKU_ICU_HEADERS) ; +Includes [ FGristFiles $(sources) ] : $(HAIKU_ICU_HEADERS_DEPENDENCY) ; + # Dependency needed to trigger downloading/unzipping the package before + # compiling the files. + +Preference Time + : $(sources) : be libshared.a $(TARGET_LIBSUPC++) $(HAIKU_LOCALE_LIBS) : Time.rdef ; diff --git a/src/preferences/time/TZDisplay.cpp b/src/preferences/time/TZDisplay.cpp index b2c6cd1f1b..926d36fd0f 100644 --- a/src/preferences/time/TZDisplay.cpp +++ b/src/preferences/time/TZDisplay.cpp @@ -88,7 +88,7 @@ void TTZDisplay::SetLabel(const char* label) { fLabel.SetTo(label); - Draw(Bounds()); + Invalidate(); } @@ -103,7 +103,7 @@ void TTZDisplay::SetText(const char* text) { fText.SetTo(text); - Draw(Bounds()); + Invalidate(); } @@ -115,24 +115,9 @@ TTZDisplay::Time() const void -TTZDisplay::SetTime(int32 hour, int32 minute) +TTZDisplay::SetTime(const char* time) { - int32 ahour = hour; - if (hour > 12) - ahour = hour -12; - - if (ahour == 0) - ahour = 12; - - const char* ap = "AM"; - if (hour > 11) - ap = "PM"; - - char buffer[32]; - snprintf(buffer, sizeof(buffer), "%ld:%02ld %s", ahour, minute, ap); - - fTime.SetTo(buffer); - + fTime.SetTo(time); Invalidate(); } diff --git a/src/preferences/time/TZDisplay.h b/src/preferences/time/TZDisplay.h index 09336e0001..ddbf0bb2a1 100644 --- a/src/preferences/time/TZDisplay.h +++ b/src/preferences/time/TZDisplay.h @@ -33,12 +33,12 @@ public: void SetText(const char* text); const char* Time() const; - void SetTime(int32 hour, int32 minute); + void SetTime(const char* time); private: - BString fLabel; - BString fText; - BString fTime; + BString fLabel; + BString fText; + BString fTime; }; diff --git a/src/preferences/time/TimeZoneListItem.cpp b/src/preferences/time/TimeZoneListItem.cpp index 86cfdda189..f76721c50f 100644 --- a/src/preferences/time/TimeZoneListItem.cpp +++ b/src/preferences/time/TimeZoneListItem.cpp @@ -28,7 +28,8 @@ TimeZoneListItem::TimeZoneListItem(const char* text, BCountry* country, BTimeZone* timeZone) : BStringItem(text), - fIcon(NULL) + fIcon(NULL), + fTimeZone(timeZone) { if (country != NULL) { fIcon = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_RGBA32); @@ -37,8 +38,6 @@ TimeZoneListItem::TimeZoneListItem(const char* text, BCountry* country, fIcon = NULL; } } - - fTimeZone = timeZone; } @@ -95,6 +94,20 @@ TimeZoneListItem::DrawItem(BView* owner, BRect frame, bool complete) } +bool +TimeZoneListItem::HasTimeZone() const +{ + return fTimeZone != NULL; +} + + +const BTimeZone& +TimeZoneListItem::TimeZone() const +{ + return *fTimeZone; +} + + const BString& TimeZoneListItem::Code() const { diff --git a/src/preferences/time/TimeZoneListItem.h b/src/preferences/time/TimeZoneListItem.h index 463ad2f945..d387aebe78 100644 --- a/src/preferences/time/TimeZoneListItem.h +++ b/src/preferences/time/TimeZoneListItem.h @@ -27,6 +27,8 @@ public: void DrawItem(BView* owner, BRect frame, bool complete = false); + bool HasTimeZone() const; + const BTimeZone& TimeZone() const; const BString& Code() const; const BString& Name() const; int OffsetFromGMT() const; diff --git a/src/preferences/time/ZoneView.cpp b/src/preferences/time/ZoneView.cpp index 4e7a02830a..f5319da5b9 100644 --- a/src/preferences/time/ZoneView.cpp +++ b/src/preferences/time/ZoneView.cpp @@ -10,16 +10,14 @@ * Oliver Tappe */ -/* - Exceptions: - doesn't calc "Time in" time. -*/ #include "ZoneView.h" -#include #include +#include + +#include #include #include #include @@ -27,7 +25,7 @@ #include #include #include -#include +#include #include #include #include @@ -38,21 +36,25 @@ #include +#include +#include +#include + #include "TimeMessages.h" #include "TimeZoneListItem.h" #include "TZDisplay.h" #include "TimeWindow.h" -static BCollator sCollator; - // used to sort the timezone list +using BPrivate::mutable_locale_roster; +using BPrivate::ObjectDeleter; TimeZoneView::TimeZoneView(BRect frame) : BView(frame, "timeZoneView", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE_JUMP), - fCurrentZone(NULL), - fOldZone(NULL), + fCurrentZoneItem(NULL), + fOldZoneItem(NULL), fInitialized(false) { _InitView(); @@ -62,33 +64,30 @@ TimeZoneView::TimeZoneView(BRect frame) bool TimeZoneView::CheckCanRevert() { - return fCurrentZone != fOldZone; + return fCurrentZoneItem != fOldZoneItem; } void TimeZoneView::_Revert() { - fCurrentZone = fOldZone; - int32 czone = 0; + fCurrentZoneItem = fOldZoneItem; - if (fCurrentZone != NULL) { - czone = fCityList->IndexOf(fCurrentZone); - fCityList->Select(czone); - } - // TODO : else, select ??! + if (fCurrentZoneItem != NULL) { + int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem); + fZoneList->Select(currentZoneIndex); + } else + fZoneList->DeselectAll(); + fZoneList->ScrollToSelection(); - fCityList->ScrollToSelection(); - fCurrent->SetText(((TimeZoneListItem*)fCityList->ItemAt(czone))->Text()); - _SetPreview(); - _SetTimeZone(); + _SetSystemTimeZone(); + _UpdatePreview(); + _UpdateCurrent(); } TimeZoneView::~TimeZoneView() { - delete fCurrentZone; - delete fOldZone; } @@ -102,23 +101,23 @@ TimeZoneView::AttachedToWindow() fInitialized = true; fSetZone->SetTarget(this); - fCityList->SetTarget(this); + fZoneList->SetTarget(this); // update displays int32 czone = 0; - if (fCurrentZone != NULL) { - czone = fCityList->IndexOf(fCurrentZone); + if (fCurrentZoneItem != NULL) { + czone = fZoneList->IndexOf(fCurrentZoneItem); } else { // TODO : else, select ??! - fCurrentZone = (TimeZoneListItem*)fCityList->ItemAt(0); + fCurrentZoneItem = (TimeZoneListItem*)fZoneList->ItemAt(0); } - fCityList->Select(czone); + fZoneList->Select(czone); - fCityList->ScrollToSelection(); - fCurrent->SetText(fCurrentZone->Text()); + fZoneList->ScrollToSelection(); + fCurrent->SetText(fCurrentZoneItem->Text()); } - fCityList->ScrollToSelection(); + fZoneList->ScrollToSelection(); } @@ -144,7 +143,7 @@ TimeZoneView::MessageReceived(BMessage* message) case H_SET_TIME_ZONE: { - _SetTimeZone(); + _SetSystemTimeZone(); ((TTimeWindow*)Window())->SetRevertStatus(); break; } @@ -154,7 +153,7 @@ TimeZoneView::MessageReceived(BMessage* message) break; case H_CITY_CHANGED: - _SetPreview(); + _UpdatePreview(); break; default: @@ -167,20 +166,14 @@ TimeZoneView::MessageReceived(BMessage* message) void TimeZoneView::_UpdateDateTime(BMessage* message) { - int32 hour; + // only need to update once every minute int32 minute; + if (message->FindInt32("minute", &minute) == B_OK) { + if (fLastUpdateMinute != minute) { + _UpdateCurrent(); + _UpdatePreview(); - // only need hour and minute - if (message->FindInt32("hour", &hour) == B_OK - && message->FindInt32("minute", &minute) == B_OK) { - if (fHour != hour || fMinute != minute) { - fHour = hour; - fMinute = minute; - fCurrent->SetTime(hour, minute); - - // do calc to get other zone time - if (fCityList->CurrentSelection() > -1) - _SetPreview(); + fLastUpdateMinute = minute; } } } @@ -195,14 +188,14 @@ TimeZoneView::_InitView() frameLeft.InsetBy(10.0f, 10.0f); // City Listing - fCityList = new BOutlineListView(frameLeft, "cityList", + fZoneList = new BOutlineListView(frameLeft, "cityList", B_SINGLE_SELECTION_LIST); - fCityList->SetSelectionMessage(new BMessage(H_CITY_CHANGED)); - fCityList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE)); + fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED)); + fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE)); _BuildRegionMenu(); - BScrollView* scrollList = new BScrollView("scrollList", fCityList, + BScrollView* scrollList = new BScrollView("scrollList", fZoneList, B_FOLLOW_ALL, 0, false, true); AddChild(scrollList); @@ -237,7 +230,7 @@ TimeZoneView::_InitView() void TimeZoneView::_BuildRegionMenu() { - BTimeZone* defaultTimeZone = NULL; + BTimeZone defaultTimeZone = NULL; be_locale_roster->GetDefaultTimeZone(&defaultTimeZone); // Get a list of countries and, for each country, get all the timezones and @@ -252,7 +245,7 @@ TimeZoneView::_BuildRegionMenu() == B_OK; i++) { BCountry country("", countryCode); BString fullName; - country.Name(fullName); + country.GetName(fullName); // Now list the timezones for this country BList tzList; @@ -269,24 +262,24 @@ TimeZoneView::_BuildRegionMenu() timeZone = (BTimeZone*)tzList.ItemAt(0); countryItem = new TimeZoneListItem(fullName, &country, timeZone); - fCityList->AddItem(countryItem); - if (timeZone->Code() == defaultTimeZone->Code()) - fCurrentZone = countryItem; + fZoneList->AddItem(countryItem); + if (timeZone->Code() == defaultTimeZone.Code()) + fCurrentZoneItem = countryItem; break; default: countryItem = new TimeZoneListItem(fullName, &country, NULL); countryItem->SetExpanded(false); - fCityList->AddItem(countryItem); + fZoneList->AddItem(countryItem); for (int j = 0; (timeZone = (BTimeZone*)tzList.ItemAt(j)) != NULL; j++) { TimeZoneListItem* tzItem = new TimeZoneListItem( timeZone->Name(), NULL, timeZone); - fCityList->AddUnder(tzItem, countryItem); - if (timeZone->Code() == defaultTimeZone->Code()) + fZoneList->AddUnder(tzItem, countryItem); + if (timeZone->Code() == defaultTimeZone.Code()) { - fCurrentZone = tzItem; + fCurrentZoneItem = tzItem; countryItem->SetExpanded(true); } } @@ -294,103 +287,93 @@ TimeZoneView::_BuildRegionMenu() } } - fOldZone = fCurrentZone; + // add an artifical (i.e. belongs to no country) entry for GMT/UTC + BTimeZone* gmtZone = new(std::nothrow) BTimeZone(BTimeZone::kNameOfGmtZone); + TimeZoneListItem* gmtItem + = new TimeZoneListItem("- GMT/UTC (Universal Time) -", NULL, gmtZone); + fZoneList->AddItem(gmtItem); + if (gmtZone->Code() == defaultTimeZone.Code()) + fCurrentZoneItem = gmtItem; - delete defaultTimeZone; + fOldZoneItem = fCurrentZoneItem; struct ListSorter { static int compare(const BListItem* first, const BListItem* second) { - return sCollator.Compare(((BStringItem*)first)->Text(), + static BCollator collator; + return collator.Compare(((BStringItem*)first)->Text(), ((BStringItem*)second)->Text()); } }; - fCityList->SortItemsUnder(NULL, false, ListSorter::compare); - + fZoneList->SortItemsUnder(NULL, false, ListSorter::compare); } void -TimeZoneView::_SetPreview() +TimeZoneView::_UpdatePreview() { - int32 selection = fCityList->CurrentSelection(); - if (selection >= 0) { - TimeZoneListItem* item - = (TimeZoneListItem*)fCityList->ItemAt(selection); - - // calc preview time - time_t current = time(NULL) + item->OffsetFromGMT(); - struct tm localTime; - gmtime_r(¤t, &localTime); - - // update prview - fPreview->SetText(item->Text()); - fPreview->SetTime(localTime.tm_hour, localTime.tm_min); - - fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0)); - } -} - - -void -TimeZoneView::_SetCurrent(const char* text) -{ - _SetTimeZone(fCurrentZone->Code().String()); - - time_t current = time(NULL); - struct tm localTime; - localtime_r(¤t, &localTime); - - fCurrent->SetText(text); - fCurrent->SetTime(localTime.tm_hour, localTime.tm_min); -} - - -void -TimeZoneView::_SetTimeZone() -{ - /* set time based on supplied timezone. How to do this? - 1) replace symlink "timezone" in B_USER_SETTINGS_DIR with a link to the - new timezone - 2) set TZ environment var - 3) call settz() - 4) call set_timezone from OS.h passing path to timezone file - */ - - int32 selection = fCityList->CurrentSelection(); + int32 selection = fZoneList->CurrentSelection(); if (selection < 0) return; - const BString& code - = ((TimeZoneListItem*)fCityList->ItemAt(selection))->Code(); - _SetTimeZone(code.String()); + TimeZoneListItem* item = (TimeZoneListItem*)fZoneList->ItemAt(selection); + if (!item->HasTimeZone()) + return; - // update display - time_t current = time(NULL); - struct tm localTime; - localtime_r(¤t, &localTime); + BString timeString = _FormatTime(item); + fPreview->SetText(item->Text()); + fPreview->SetTime(timeString.String()); - set_timezone(code.String()); - // disable button - fSetZone->SetEnabled(false); - - time_t newTime = mktime(&localTime); - localtime_r(&newTime, &localTime); - stime(&newTime); - - fHour = localTime.tm_hour; - fMinute = localTime.tm_min; - fCurrentZone = (TimeZoneListItem*)(fCityList->ItemAt(selection)); - _SetCurrent(((TimeZoneListItem*)fCityList->ItemAt(selection))->Text()); + fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0)); } void -TimeZoneView::_SetTimeZone(const char* zone) +TimeZoneView::_UpdateCurrent() { - putenv(BString("TZ=").Append(zone).String()); - tzset(); + if (fCurrentZoneItem == NULL) + return; - be_locale_roster->SetDefaultTimeZone(zone); + BString timeString = _FormatTime(fCurrentZoneItem); + fCurrent->SetText(fCurrentZoneItem->Text()); + fCurrent->SetTime(timeString.String()); } + +void +TimeZoneView::_SetSystemTimeZone() +{ + /* Set sytem timezone for all different API levels. How to do this? + * 1) tell locale-roster about new default timezone + * 2) write new POSIX-timezone file + */ + + int32 selection = fZoneList->CurrentSelection(); + if (selection < 0) + return; + + fCurrentZoneItem = (TimeZoneListItem*)(fZoneList->ItemAt(selection)); + + mutable_locale_roster->SetDefaultTimeZone(fCurrentZoneItem->TimeZone()); + + set_timezone(fCurrentZoneItem->Code()); + + fSetZone->SetEnabled(false); + fLastUpdateMinute = -1; + // just to trigger updating immediately +} + + +BString +TimeZoneView::_FormatTime(TimeZoneListItem* zoneItem) +{ + BString result; + + if (zoneItem == NULL) + return result; + + time_t nowInTimeZone = time(NULL) + zoneItem->OffsetFromGMT(); + be_locale->FormatTime(&result, nowInTimeZone, false); + + return result; +} diff --git a/src/preferences/time/ZoneView.h b/src/preferences/time/ZoneView.h index 86d3229917..b29c1abdb8 100644 --- a/src/preferences/time/ZoneView.h +++ b/src/preferences/time/ZoneView.h @@ -33,24 +33,28 @@ public: private: void _UpdateDateTime(BMessage* message); - void _SetTimeZone(); - void _SetTimeZone(const char* zone); - void _SetPreview(); - void _SetCurrent(const char* text); + + void _SetSystemTimeZone(); + + void _UpdatePreview(); + void _UpdateCurrent(); + BString _FormatTime(TimeZoneListItem* zoneItem); + void _InitView(); void _BuildRegionMenu(); + void _Revert(); private: - BOutlineListView* fCityList; + BOutlineListView* fZoneList; BButton* fSetZone; TTZDisplay* fCurrent; TTZDisplay* fPreview; - int32 fHour; - int32 fMinute; - TimeZoneListItem* fCurrentZone; - TimeZoneListItem* fOldZone; + int32 fLastUpdateMinute; + + TimeZoneListItem* fCurrentZoneItem; + TimeZoneListItem* fOldZoneItem; bool fInitialized; };