Difference between revisions of "Formatted Numeric Data"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
 
 
(29 intermediate revisions by 6 users not shown)
Line 1: Line 1:
International Components for Unicode (ICU) is IBM's toolkit to internationalise applications. Initially developped for Java it has been ported to C/C++.
+
{{VersionBadge|0.6}} {{VersionBadge|0.5}}
  
==IBM's ICU==
+
International Components for Unicode (ICU) is a toolkit to internationalise applications. Initially developed for Java it has been ported to C/C++. There is a Java version of ICU called ICU4J, and there is a C/C++ version of ICU called ICU4C. Now ICU4C has been partially ported to CEGUI. Please use this [http://www.cegui.org.uk/phpBB2/viewtopic.php?p=8097#8097 thread] for discussion.
===Useful Links===
+
[http://www-306.ibm.com/software/globalization/icu/index.jsp Home page]
+
[http://icu.sourceforge.net/ Source Forge]
+
[http://www-306.ibm.com/software/globalization/icu/license.jsp Licence]
+
[http://icu.sourceforge.net/userguide/ Documentation]
+
[http://icu.sourceforge.net/apiref/icu4c/index.html Doxygen API]
+
[http://www.loc.gov/standards/iso639-2/ ISO Language Codes]
+
[http://www.iso.ch/iso/en/prods-services/iso3166ma/index.html ISO Country Codes]
+
[http://en.wikipedia.org/wiki/ISO_4217 ISO Currency Codes]
+
[http://icu.sourceforge.net/apiref/icu4c/classDecimalFormat.html#_details ICU Decimal Format Syntax]
+
[http://icu.sourceforge.net/userguide/formatDateTime.html ICU Date/Time Format Syntax]
+
  
===Getting started===
+
== International Components for Unicode ==
[http://www-306.ibm.com/software/globalization/icu/downloads.jsp Download from IBM]
+
=== Useful Links ===
*Source: icu-3.4.1.zip OR icu-3.4.1.tgz
+
* [http://www.icu-project.org/ Home page]
*Documentation: icu-3.4-docs.zip
+
* [http://source.icu-project.org/repos/icu/icu/trunk/license.html Licence]
*User Guide: icu-3.4-userguide.zip
+
* [http://www.icu-project.org/userguide/ Documentation]
 +
* [http://www.icu-project.org/apiref/icu4c/ ICU4C API Reference]
 +
* [http://www.loc.gov/standards/iso639-2/ ISO 639 Language Codes]
 +
* [http://unicode.org/iso15924/iso15924-codes.html ISO 15924 Script Codes]
 +
* [http://www.iso.org/iso/country_codes ISO 3166 Country Codes]
 +
* [http://www.iso.org/iso/currency_codes ISO 4217 Currency Codes]
 +
* [http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details ICU Decimal Format Syntax]
 +
* [http://www.icu-project.org/userguide/formatDateTime.html ICU Date/Time Format Syntax]
  
Compile:
+
=== Getting started ===
*icu/source/allinone/allinone.sln
+
Download these [http://www.icu-project.org/download/ files from the ICU web site]
*icu/source/samples/all/all.sln
+
* Source: icu-3.4.1.zip for Windows OR icu-3.4.1.tgz for Unix
 +
* Documentation: icu-3.4-docs.zip
 +
* User Guide: icu-3.4-userguide.zip
  
Errors:
+
Files of interest:
*For projects strsrch and coll
+
* Solution to compile: icu/source/allinone/allinone.sln
**Move:<pre>
+
* Instructions to compile and use ICU: icu/readme.html#HowToBuildWindows
        if (pOpt->name == 0)
+
* ICU4C samples: icu/source/samples/
        {
+
            fprintf(stderr, "Unrecognized option \"%s\"\n", pArgName);
+
            return FALSE;
+
        }</pre>
+
**Upward just after the line:<pre>
+
        for (OptSpec *pOpt = opts;  pOpt->name != 0; pOpt ++) {</pre>
+
  
*For project legacy
+
The instructions for building and setting up ICU are contained in the readme.html. If you have any problems, contact the icu-support mailing list or submit a bug report.
**remove dependencies to:
+
* [http://www.icu-project.org/contacts.html icu-support mailing list]
***../../../../icu-1-8-1/lib/icuucd.lib
+
* [http://www.icu-project.org/bugs.html ICU bug tracking tool]
***../../../../icu-1-8-1/lib/icuind.lib
+
  
*For project GDIFontInstance
+
=== Incorporating ICU4C within an existing project ===
**cast 6th parameter:  (LPCWSTR) &ttGlyphs[dyStart]
+
  
Please note that samples will NOT run in their compilation directories.  In order to run any of the samples you must place the executable within the icu/bin directory, where the required DLLs are situated.
+
Additional include directories:
 +
* $(ICU)\include
  
===Incorporating ICU within an existing project===
+
Additional library directories:
 
+
* $(ICU)\lib
Additional include directories: $(ICU)\include
+
Additional library directories:  $(ICU)\lib
+
  
 
Link with:
 
Link with:
  debug: icuucd.lib icuind.lib
+
* debug: icuucd.lib icuind.lib
  release: icuuc.lib icuin.lib
+
* release: icuuc.lib icuin.lib
  
Make certain these DLLs are present with your executable
+
Make certain these DLLs are present with your executable or in your PATH:
  debug: icuuc34d.dll icuin34d.dll icudt34.dll
+
* debug: icuuc34d.dll icuin34d.dll icudt34.dll
  release: icuuc34.dll icuin34.dll icudt34.dll
+
* release: icuuc34.dll icuin34.dll icudt34.dll
  
==CEGUI's ICU==
+
== CEGUI's ICU ==
IBM's ICU provides many features to support internationalisation. The class I've created encapsultes some (not all) of these features.
+
ICU4C provides many features to support internationalisation. The class I've created encapsulates some (not all) of these features.
  
===Locale===
+
=== Locale ===
A Locale describes the rules in effect within a country for a specific language. These rules specify the formatting of currencies, numeric values, dates, and others. Unfortunately these rules do not take into consideration the modifications that may have been applied within the Regional Settings of Window's Control Panel. However most functions accept a formatting mask. This allows applications to recreate some of the features of the control panel, allowing users to specify their desired formats. The setLocale() and setCurrency() functions will configure the class to adopt the rules specified by the specified language, country, and currency.
+
A Locale is an identifier that describes the rules in effect within a country for a specific language. These rules specify the formatting of currencies, numeric values, dates, and others. Unfortunately these rules do not take into consideration the modifications that may have been applied within the Regional Settings of Window's Control Panel, unless the "@compat=host" locale is used, which is available in ICU 3.6 and later. However most functions accept a formatting mask. This allows applications to recreate some of the features of the control panel, allowing users to specify their desired formats. The setLocale() and setCurrency() functions will configure the class to adopt the rules specified by the specified language, country, and currency.
  
===formatNumber==
+
=== formatNumber ===
The formatNumber() function formats a numeric value given the specified mask. Three versions are available. The versions accepting a CEGUI::String has been customised to allow large numbers to be formatted: numbers having up to 18 integers and 7 decimals. The current implementation of this function is limited to accepting a single format for positive numbers. If the numeric value to be formatted is negative then a negative sign will precede the formatted value. However it is impossible to format a negative value within parentheses: formatting -1234.56 to (1,234.56).
+
The formatNumber() function formats a numeric value given the specified mask. Three versions are available. The versions accepting a CEGUI::String has been customised to allow large numbers to be formatted: numbers having up to 18 integers and 7 decimals. The current implementation of this function is limited to accepting a single format for positive numbers. If the numeric value to be formatted is negative then a negative sign will precede the formatted value. However it is impossible to format a negative value within parentheses: formatting -1234.56 to (1,234.56).
  
===numberToText===
+
=== numberToText ===
The numberToText() function converts a numeric value into a textual representation. For example the numeric value 123 is converted into "one hundred twenty-three" in english and "cent vingt-trois" in french.
+
The numberToText() function converts a numeric value into a textual representation. For example the numeric value 123 is converted into "one hundred twenty-three" in English and "cent vingt-trois" in French.
  
===numberToOrdinal===
+
=== numberToOrdinal ===
The numberToOrdinal() function converts a numeric value into an ordinal representation. Form example the numeric value 1 is converted into "1st" in english. Support for other languages is either buggy or lacking (or I have improperly coded this feature).
+
The numberToOrdinal() function converts a numeric value into an ordinal representation. For example the numeric value 1 is converted into "1st" in english. Support for other languages is either buggy or lacking (or I have improperly coded this feature).
  
===formatText===
+
=== formatText ===
The formatText() function will parse a numeric value and sprinkle digits into the slots specified by the formatting mask. A North American telephone number of "12223334444" can be formatted into "1 (222) 333-4444" with the mask "0 (000) 000-0000". This non-localised function accepts three format specifier. A "0" represents a forced digit. If the numerical value possesses a digit at that position then the digit will be displayed, otherwise the place holder character(s) will be used. A "#" represents a potential digit. If the numerical value possesses a digit at that position the the digit will be displayed, otherwise nothing is displayed. Finally the apostrophe "'" allows the mask to specify the characters "0", "#", or "'", rather than using them as format specifiers.
+
The formatText() function will parse a numeric value and sprinkle digits into the slots specified by the formatting mask. A North American telephone number of "12223334444" can be formatted into "1 (222) 333-4444" with the mask "0 (000) 000-0000". This non-localised function accepts three format specifier. A "0" represents a forced digit. If the numerical value possesses a digit at that position then the digit will be displayed, otherwise the place holder character(s) will be used. A "#" represents a potential digit. If the numerical value possesses a digit at that position the the digit will be displayed, otherwise nothing is displayed. Finally the apostrophe "'" allows the mask to specify the characters "0", "#", or "'", rather than using them as format specifiers.
  
===formatCurrency===
+
=== formatCurrency ===
The formatCurrency() function will format a numerical value according to the currently configured locale and currency. It will NOT convert the monetary value of one currency to another.
+
The formatCurrency() function will format a numerical value according to the currently configured locale and currency. It will NOT convert the monetary value of one currency to another.
  
===formatDateTime===
+
=== formatDateTime ===
The formatDateTime() function will format a date, time, or date/time given the specified mask. The UDate variable type is a double. According to IBM's documentation "A UDate value is stored as UTC time in milliseconds, which means it is calendar and time zone independent. UDate is the most compact and portable way to store and transmit a date and time."  However I have found that attempting to specify a date AND time within the UDate data type results in imprecisions of up to 1 minute 47 seconds.  My solution is to use two UDate data variables, one to hold a date and another to hold a time.
+
The formatDateTime() function will format a date, time, or date/time given the specified mask. The UDate variable type is a double. According to [http://www.icu-project.org/userguide/dateCalendar.html ICU's documentation] ''A UDate value is stored as UTC time in milliseconds, which means it is calendar and time zone independent. UDate is the most compact and portable way to store and transmit a date and time.''
  
===localToGmt and gmtToLocal===
+
=== localToGmt and gmtToLocal ===
 
The localToGmt() and the gmtToLocal() functions convert a date and a time between a local and a GMT value.
 
The localToGmt() and the gmtToLocal() functions convert a date and a time between a local and a GMT value.
  
===CeguiStringToDateTime===
+
=== CeguiStringToDateTime ===
The CeguiStringToDateTime() function will parse a string specifying a date or a time into it's corresponding UDate value. Although it is possible to parse a string containing both a date and a time the resulting UDate value will be inaccurate, varying from its intended value by up to 1 minute 47 seconds. A better approach is to keep the date and time separated.
+
The CeguiStringToDateTime() function will parse a string specifying a date or a time into its corresponding UDate value. Although it is possible to parse a string containing both a date and a time the resulting UDate value will be inaccurate, varying from its intended value by up to 1 minute 47 seconds. A better approach is to keep the date and time separated.
 +
 
 +
== Source Code ==
 +
 
 +
=== icu.h ===
 +
<source lang="cpp">
 +
#ifndef _ICU_h_
 +
#define _ICU_h_
 +
 
 +
#include "unicode/utypes.h"
 +
#include "unicode/unistr.h"
 +
#include "unicode/numfmt.h"
 +
#include "unicode/dcfmtsym.h"
 +
#include "unicode/decimfmt.h"
 +
#include "unicode/locid.h"
 +
#include "unicode/uclean.h"
 +
#include "unicode/calendar.h"
 +
#include "unicode/datefmt.h"
 +
#include "unicode/smpdtfmt.h"
 +
#include "unicode/rbnf.h"
 +
#include "CEGUI.h"
 +
 
 +
class ICU
 +
{
 +
public:
 +
ICU(bool cleanupICU = true);
 +
~ICU();
 +
bool setLocale(const CEGUI::String& language, const CEGUI::String& country);
 +
const Locale& getLocale();
 +
bool setCurrency(const char *currency);
 +
 
 +
bool formatNumber(const CEGUI::String& rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
 +
bool formatNumber(const double rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
 +
bool formatNumber(const int32_t rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
 +
 
 +
bool numberToText(const double rawValue, CEGUI::String& formattedValue);
 +
bool numberToText(const int32_t rawValue, CEGUI::String& formattedValue);
 +
 
 +
bool numberToOrdinal(const double rawValue, CEGUI::String& formattedValue);
 +
bool numberToOrdinal(const int32_t rawValue, CEGUI::String& formattedValue);
 +
 
 +
bool formatText(const CEGUI::String& rawValue, const CEGUI::String& mask, const CEGUI::String& zeroPlaceHolder, CEGUI::String& formattedValue);
 +
 
 +
bool formatCurrency(const double& currency, CEGUI::String& formattedValue);
 +
 
 +
bool formatDateTime(UDate rawDateTime, const CEGUI::String& mask, CEGUI::String& formattedDateTime);
 +
bool localToGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime);
 +
bool gmtToLocal(const UDate& gmtDate, const UDate& gmtTime, UDate& localDate, UDate& localTime);
 +
 
 +
bool CeguiStringToDateTime(const CEGUI::String& stringDateTime, const CEGUI::String& mask, UDate& unicodeDateTime);
 +
bool CeguiStringToInt32(const CEGUI::String& stringValue, int32_t& int32Value);
 +
void UnicodeToCeguiString(const UnicodeString& unicodeString, CEGUI::String& ceguiString);
 +
private:
 +
void _splitIntegerDecimal(const CEGUI::String& combined, CEGUI::String& integerPart, CEGUI::String& decimalPart);
 +
bool _convertLocalAndGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime, bool fromLocalToGMT);
 +
bool _ruleBasedNumberFormat(const double rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue);
 +
bool _ruleBasedNumberFormat(const int32_t rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue);
 +
Locale m_locale; // Current locale of the computer
 +
UChar m_currency[4]; // Currency code
 +
bool m_cleanupICU; // Whether to call ICU's cleanup function
 +
};
 +
 
 +
#endif // _ICU_h_
 +
</source>
 +
 
 +
=== icu.cpp ===
 +
 
 +
<source lang="cpp">
 +
#include "ICU.h"
 +
#include <vector>
 +
 
 +
ICU::ICU(bool cleanupICU)
 +
{
 +
m_cleanupICU = cleanupICU;
 +
}
 +
 
 +
ICU::~ICU()
 +
{
 +
// http://www.icu-project.org/userguide/design.html#Init_and_Termination
 +
// The paragraph "ICU Initialization and Termination" mentions that this function
 +
//  can be called to force the ICU library to release any allocated heap storage
 +
//  otherwise memory leak checking tools will falsely report leaks.
 +
// My thinking with this m_cleanupICU variable is that the library should not release
 +
//  its memory allocation if it is also used outside of this class.  This
 +
// implementation grants you some control over when this cleanup occurs.
 +
// You are not required to call u_cleanup(). It's dangerous if called at the wrong time.
 +
if(m_cleanupICU)
 +
u_cleanup();
 +
}
 +
 
 +
bool ICU::setLocale(const CEGUI::String& language, const CEGUI::String& country)
 +
{
 +
// Set the locale
 +
Locale tempLocale = Locale::createFromName((language + "_" + country).c_str());
 +
if(tempLocale.isBogus())
 +
return false;
 +
 
 +
m_locale = tempLocale;
 +
return true;
 +
}
 +
 
 +
const Locale& ICU::getLocale()
 +
{
 +
// Retrive the locale
 +
return m_locale;
 +
}
 +
 
 +
bool ICU::setCurrency(const char *currency)
 +
{
 +
// Set the currency
 +
    if(currency==NULL || strlen(currency)!=3)
 +
        return false;
 +
 
 +
    // Invariant-character conversion to UChars
 +
    u_charsToUChars(currency, m_currency, 4);
 +
return true;
 +
}
 +
 
 +
bool ICU::formatNumber(const CEGUI::String& rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
 +
{
 +
// Format a unformatted number according to the rules specified within the mask
 +
// An unformatted number only contains numeric digits and a period as the decimal point
 +
// Note that a decimal value is broken down into two numbers, an
 +
//  integer and a decimal number for formatting and then reassembled
 +
//  into a single string.  This makes it possible to handle numbers
 +
//  as large as 999,999,999,999,999,999.9999999 (18 integers and 7 decimals).
 +
//  However it makes it impossible to specify a positive mask as well as a
 +
//  negative mask; only one mask is supported.  It is also impossible to
 +
//  format a negative value with a mask similar to "(#,##0.00)"
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
CEGUI::String formattedInteger, formattedDecimal;
 +
CEGUI::String::size_type idx;
 +
 
 +
// Separate the integer and the decimal parts from the value
 +
CEGUI::String maskInteger, maskDecimal, integerValue, decimalValue;
 +
idx = rawValue.find(".");
 +
if(idx == CEGUI::String::npos)
 +
{
 +
// If the value is empty then force the integer to 0, otherwise use the value
 +
// Since there is no decimal point force a decimal value of .0
 +
integerValue = rawValue.empty() ? "0" : rawValue;
 +
decimalValue = ".0";
 +
}
 +
else
 +
{
 +
// If there is no integer portion then force an integer value of 0
 +
integerValue = idx == 0 ? "0" : rawValue.substr(0, idx);
 +
decimalValue = rawValue.substr(idx);
 +
}
 +
 
 +
// Separate the integer and the decimal parts from the mask
 +
_splitIntegerDecimal(mask, maskInteger, maskDecimal);
 +
 
 +
// Prepare the numeric formatter
 +
DecimalFormatSymbols* decimalFormatSymbols = new DecimalFormatSymbols(m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
if(!maskInteger.empty())
 +
{
 +
// Prepare the integer parser
 +
UnicodeString patternInteger(maskInteger.c_str());
 +
DecimalFormat* fmt = new DecimalFormat(patternInteger, *decimalFormatSymbols, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete decimalFormatSymbols;
 +
return false;
 +
}
 +
 
 +
// Parse the string value into an integer value
 +
UnicodeString uIntegerValue(integerValue.c_str());
 +
Formattable fIntegerValue;
 +
fmt->parse(uIntegerValue, fIntegerValue, status);
 +
if( U_FAILURE(status) )
 +
{
 +
status = U_ZERO_ERROR;
 +
fIntegerValue.setInt64(0);
 +
}
 +
 
 +
// Format the integer value
 +
uIntegerValue = "";
 +
((NumberFormat*)fmt)->format(fIntegerValue.getInt64(), uIntegerValue);
 +
delete fmt;
 +
 
 +
UnicodeToCeguiString(uIntegerValue, formattedInteger);
 +
}
 +
 
 +
if(!maskDecimal.empty())
 +
{
 +
// Prepare the decimal parser
 +
DecimalFormat* fmtParse = new DecimalFormat(status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete decimalFormatSymbols;
 +
return false;
 +
}
 +
fmtParse->applyLocalizedPattern("0.0000003", status);
 +
 
 +
// Parse the string value into a decimal value
 +
UnicodeString uDecimalValue(decimalValue.c_str());
 +
Formattable fDecimalValue;
 +
fmtParse->parse(uDecimalValue, fDecimalValue, status);
 +
delete fmtParse;
 +
if( U_FAILURE(status) )
 +
{
 +
status = U_ZERO_ERROR;
 +
fDecimalValue.setDouble(0.0);
 +
}
 +
 
 +
// Prepare the decimal formatter
 +
UnicodeString patternDecimal(maskDecimal.c_str());
 +
DecimalFormat* fmt = new DecimalFormat(patternDecimal, *decimalFormatSymbols, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete decimalFormatSymbols;
 +
return false;
 +
}
 +
 
 +
// Format the decimal value
 +
uDecimalValue = "";
 +
fmt->format(fDecimalValue.getDouble(), uDecimalValue);
 +
delete fmt;
 +
 
 +
UnicodeToCeguiString(uDecimalValue, formattedDecimal);
 +
}
 +
 
 +
delete decimalFormatSymbols;
 +
 
 +
formattedValue = formattedInteger + formattedDecimal;
 +
return true;
 +
}
 +
 
 +
bool ICU::formatNumber(const double rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
 +
{
 +
// Format a number using the appropriate locale rules
 +
// Note that the use of a double limits the effective range
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
 
 +
// Prepare the numeric formatter
 +
DecimalFormatSymbols* symbols = new DecimalFormatSymbols(m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
    // Create a number formatter for the current locale
 +
UnicodeString pattern(mask.c_str());
 +
DecimalFormat* fmt = new DecimalFormat(pattern, *symbols, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete symbols;
 +
return false;
 +
}
 +
 
 +
// Format the number
 +
UnicodeString uValue;
 +
fmt->format(rawValue, uValue, status);
 +
delete symbols;
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
UnicodeToCeguiString(uValue, formattedValue);
 +
return true;
 +
}
 +
 
 +
bool ICU::formatNumber(const int32_t rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
 +
{
 +
// Format a number using the appropriate locale rules
 +
// Note that the use of a double limits the effective range
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
 
 +
// Prepare the numeric formatter
 +
DecimalFormatSymbols* symbols = new DecimalFormatSymbols(m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
    // Create a number formatter for the current locale
 +
UnicodeString pattern(mask.c_str());
 +
DecimalFormat* fmt = new DecimalFormat(pattern, *symbols, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete symbols;
 +
return false;
 +
}
 +
 
 +
// Format the number
 +
UnicodeString uValue;
 +
fmt->format(rawValue, uValue, status);
 +
delete symbols;
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
UnicodeToCeguiString(uValue, formattedValue);
 +
return true;
 +
}
 +
 
 +
bool ICU::numberToText(const double rawValue, CEGUI::String& formattedValue)
 +
{
 +
// Convert a number to a text
 +
// "1.2" to "one point two"
 +
return _ruleBasedNumberFormat(rawValue, URBNF_SPELLOUT, formattedValue);
 +
}
 +
 
 +
bool ICU::numberToText(const int32_t rawValue, CEGUI::String& formattedValue)
 +
{
 +
// Convert a number to a text
 +
// "12" to "twelve"
 +
return _ruleBasedNumberFormat(rawValue, URBNF_SPELLOUT, formattedValue);
 +
}
 +
 
 +
bool ICU::numberToOrdinal(const double rawValue, CEGUI::String& formattedValue)
 +
{
 +
// Convert a number to ordinal text
 +
// "1.5" to "2nd"
 +
return _ruleBasedNumberFormat(rawValue, URBNF_ORDINAL, formattedValue);
 +
}
 +
 
 +
bool ICU::numberToOrdinal(const int32_t rawValue, CEGUI::String& formattedValue)
 +
{
 +
// Convert a number to ordinal text
 +
// "1" to "1st"
 +
return _ruleBasedNumberFormat(rawValue, URBNF_ORDINAL, formattedValue);
 +
}
 +
 
 +
bool ICU::formatText(const CEGUI::String& rawValue, const CEGUI::String& mask, const CEGUI::String& zeroPlaceHolder, CEGUI::String& formattedValue)
 +
{
 +
/* Format a value according to a pattern
 +
* Format specifiers:
 +
* 0 forced digit: will be replaced by a digit or if there is no digit, by 'zeroPlaceHolder'
 +
* # potential digit: will be replaced by a digit if there is one otherwise nothing
 +
* ' literal character: the following character is to be treated as a character
 +
* rather than a format specifier
 +
* ex the value "1" with the format "'00" will result in the string "01"
 +
* Note that if the value is larger than the pattern then it is truncated to the left
 +
* ex the value "1234" with the format "#0" results in the string "34"
 +
*/
 +
formattedValue.clear();
 +
 
 +
if(!rawValue.length() || !mask.length())
 +
return false;
 +
 
 +
// Parse the mask
 +
std::vector<CEGUI::String> maskList;
 +
CEGUI::String maskDigit;
 +
for(CEGUI::String::size_type idxMask = 0; idxMask < mask.length(); ++idxMask)
 +
{
 +
if(!mask.compare(idxMask, 1, "0"))
 +
{
 +
maskDigit = "ForcedDigit";
 +
maskList.push_back(maskDigit);
 +
}
 +
else if(!mask.compare(idxMask, 1, "#"))
 +
{
 +
maskDigit = "PotentialDigit";
 +
maskList.push_back(maskDigit);
 +
}
 +
else
 +
{
 +
if(!mask.compare(idxMask, 1, "'") && idxMask < mask.length() - 1)
 +
++idxMask; // Literal specifier followed by a mask digit, so skip over the apostrophe
 +
maskDigit = mask.at(idxMask);
 +
maskList.push_back(maskDigit);
 +
}
 +
}
 +
 
 +
// Format the numeric value
 +
CEGUI::String::size_type idxValue;
 +
idxValue = rawValue.length();
 +
std::vector<CEGUI::String>::reverse_iterator itMask;
 +
for(itMask = maskList.rbegin(); itMask != maskList.rend(); ++itMask)
 +
{
 +
if(!(*itMask).compare("ForcedDigit"))
 +
{
 +
if(idxValue)
 +
{
 +
// We have a digit remaining
 +
--idxValue;
 +
formattedValue = rawValue.at(idxValue) + formattedValue;
 +
}
 +
else
 +
{
 +
// We're out of digits
 +
formattedValue = zeroPlaceHolder + formattedValue;
 +
}
 +
}
 +
else if(!(*itMask).compare("PotentialDigit"))
 +
{
 +
if(idxValue)
 +
{
 +
// We have a digit remaining
 +
--idxValue;
 +
formattedValue = rawValue.at(idxValue) + formattedValue;
 +
}
 +
}
 +
else
 +
{
 +
// Literal character
 +
formattedValue = (*itMask) + formattedValue;
 +
}
 +
}
 +
return true;
 +
}
 +
 
 +
bool ICU::formatCurrency(const double& currency, CEGUI::String& formattedValue)
 +
{
 +
// Format a number using the appropriate locale rules
 +
// Note that the use of a double limits the effective range
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
 
 +
    // Create a number formatter for the current locale
 +
    NumberFormat *fmt = NumberFormat::createCurrencyInstance(m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
// Format the currency
 +
UnicodeString uValue;
 +
fmt->format(currency, uValue);
 +
    delete fmt;
 +
 
 +
UnicodeToCeguiString(uValue, formattedValue);
 +
return true;
 +
}
 +
 
 +
bool ICU::formatDateTime(UDate rawDateTime, const CEGUI::String& mask, CEGUI::String& formattedDateTime)
 +
{
 +
// Format a date/time given the specified mask
 +
// Note that although a UDate can support both date and time in
 +
//  realite the resolution is insufficient.  In order to precisely
 +
//  represent a date/time value they should be processed separately
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedDateTime.clear();
 +
 
 +
// Create a Date/Time formatter
 +
DateFormat* fmt = DateFormat::createDateTimeInstance(DateFormat::kDefault, DateFormat::kDefault, m_locale); // kDefault is not really used
 +
 
 +
// Activate our pattern (overrides DateFormat::kDefault)
 +
    UnicodeString pattern(mask.c_str());
 +
((SimpleDateFormat*) fmt)->applyPattern(pattern);
 +
 
 +
// Format the date/time
 +
    UnicodeString uValue;
 +
fmt->format(rawDateTime, uValue, status);
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
UnicodeToCeguiString(uValue, formattedDateTime);
 +
return true;
 +
}
 +
 
 +
bool ICU::localToGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime)
 +
{
 +
// Convert a local date/time into a GMT date/time
 +
return _convertLocalAndGmt(localDate, localTime, gmtDate, gmtTime, true);
 +
}
 +
 
 +
bool ICU::gmtToLocal(const UDate& gmtDate, const UDate& gmtTime, UDate& localDate, UDate& localTime)
 +
{
 +
// Convert a GMT date/time into a local date/time string
 +
return _convertLocalAndGmt(gmtDate, gmtTime, localDate, localTime, false);
 +
}
 +
 
 +
bool ICU::CeguiStringToDateTime(const CEGUI::String& stringDateTime, const CEGUI::String& mask, UDate& unicodeDateTime)
 +
{
 +
// Convert a string date/time into a UDate
 +
    UErrorCode status = U_ZERO_ERROR;
 +
unicodeDateTime = 0.0;
 +
 
 +
// Create a Date/Time formatter
 +
DateFormat* fmt = DateFormat::createDateTimeInstance(DateFormat::kDefault, DateFormat::kDefault, m_locale);
 +
 
 +
// Activate our pattern
 +
    UnicodeString pattern(mask.c_str());
 +
((SimpleDateFormat*) fmt)->applyPattern(pattern);
 +
 
 +
// Parse the string into a date/time
 +
UnicodeString uValue(stringDateTime.c_str());
 +
unicodeDateTime = fmt->parse(uValue, status);
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
return true;
 +
}
 +
 
 +
bool ICU::CeguiStringToInt32(const CEGUI::String& stringValue, int32_t& int32Value)
 +
{
 +
// Convert a string into an int32_t
 +
    UErrorCode status = U_ZERO_ERROR;
 +
int32Value = 0;
 +
 
 +
// Create an integer formatter
 +
NumberFormat *fmt = NumberFormat::createInstance(Locale::getUS(), status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
// Activate our pattern
 +
UnicodeString pattern("#,##0");
 +
((DecimalFormat*) fmt)->applyPattern(pattern, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete fmt;
 +
return false;
 +
}
 +
 
 +
// Parse the string into a double
 +
UnicodeString uValue(stringValue.c_str());
 +
    Formattable result;
 +
    fmt->parse(uValue, result, status);
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
int32Value = result.getLong();
 +
 
 +
return true;
 +
}
 +
 
 +
void ICU::UnicodeToCeguiString(const UnicodeString& unicodeString, CEGUI::String& ceguiString)
 +
{
 +
// Convert a unicode string to a CEGUI string
 +
ceguiString.clear();
 +
 
 +
CEGUI::String digit;
 +
for(int32_t i = 0; i < unicodeString.length(); ++i)
 +
{
 +
digit = unicodeString.charAt(i);
 +
ceguiString.append(digit);
 +
}
 +
}
 +
 
 +
void ICU::_splitIntegerDecimal(const CEGUI::String& combined, CEGUI::String& integerPart, CEGUI::String& decimalPart)
 +
{
 +
// Split a decimal value into its constituent integer and decimal parts
 +
CEGUI::String::size_type idx = combined.find(".");
 +
if(idx == CEGUI::String::npos)
 +
{
 +
integerPart = combined;
 +
decimalPart.clear();
 +
}
 +
else
 +
{
 +
integerPart = combined.substr(0, idx);
 +
decimalPart = combined.substr(idx);
 +
}
 +
}
 +
 
 +
bool ICU::_convertLocalAndGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime, bool fromLocalToGMT)
 +
{
 +
// Helper function to convert between a local and a GMT date/time
 +
UErrorCode status = U_ZERO_ERROR;
 +
gmtDate = gmtTime = 0.0;
 +
 
 +
// Create a calendar for the local date
 +
Calendar* calLocalDate = Calendar::createInstance(m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
calLocalDate->clear();
 +
calLocalDate->setTime(localDate, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete calLocalDate;
 +
return false;
 +
}
 +
 
 +
// Create a calendar for the local time
 +
Calendar* calLocalTime = Calendar::createInstance(m_locale, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete calLocalDate;
 +
return false;
 +
}
 +
calLocalTime->clear();
 +
calLocalTime->setTime(localTime, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete calLocalDate;
 +
delete calLocalTime;
 +
return false;
 +
}
 +
 
 +
// Retrieve the offset between this time zone and the GMT time zone
 +
// The Daylight Saving Time offset is zero when DST is not in effect
 +
// Note that adding the local date and time together introduces an
 +
//  inaccuracy of up to 1 minute 47 seconds.  However the only impact
 +
//  of this inaccuracy is to advance/delay the activation or deactivation
 +
//  of daylight savings
 +
int32_t rawOffset, dstOffset, gmtOffset;
 +
calLocalDate->getTimeZone().getOffset(localDate + localTime, true, rawOffset, dstOffset, status);
 +
gmtOffset = (rawOffset + dstOffset) / 1000 / 60 / 60; // Convert from milliseconds to hours
 +
 
 +
// Converting from local to GMT requires "reversing" the time zone
 +
// EST is GMT-5 so it requires adding 5 hours to local time to obtain GMT
 +
// Converting from GMT to local requires the opposite
 +
int32_t hour = calLocalTime->get(UCAL_HOUR_OF_DAY, status);
 +
if(fromLocalToGMT)
 +
hour -= gmtOffset;
 +
else
 +
hour += gmtOffset;
 +
 
 +
// Adjust the time and date if necessary
 +
if(hour >= 24)
 +
{
 +
// We've moved to the next day
 +
hour -= 24;
 +
calLocalDate->add(UCAL_DAY_OF_MONTH, 1, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete calLocalDate;
 +
delete calLocalTime;
 +
return false;
 +
}
 +
}
 +
else if(hour < 0)
 +
{
 +
// We've moved to the previous day
 +
hour += 24;
 +
calLocalDate->add(UCAL_DAY_OF_MONTH, -1, status);
 +
if( U_FAILURE(status) )
 +
{
 +
delete calLocalDate;
 +
delete calLocalTime;
 +
return false;
 +
}
 +
}
 +
calLocalTime->set(UCAL_HOUR_OF_DAY, hour);
 +
 
 +
// Retrieve the converted date and time
 +
gmtDate = calLocalDate->getTime(status);
 +
gmtTime = calLocalTime->getTime(status);
 +
 
 +
delete calLocalDate;
 +
delete calLocalTime;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
return true;
 +
}
 +
 
 +
bool ICU::_ruleBasedNumberFormat(const double rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue)
 +
{
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
 
 +
// Create a rule based number formatter
 +
RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
    UnicodeString uValue;
 +
    fmt->format(rawValue, uValue);
 +
delete fmt;
 +
 
 +
formattedValue.clear();
 +
UnicodeToCeguiString(uValue, formattedValue);
 +
return true;
 +
}
 +
 
 +
bool ICU::_ruleBasedNumberFormat(const int32_t rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue)
 +
{
 +
    UErrorCode status = U_ZERO_ERROR;
 +
formattedValue.clear();
 +
 
 +
// Create a rule based number formatter
 +
RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, m_locale, status);
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
    UnicodeString uValue;
 +
Formattable fValue(rawValue);
 +
fmt->format(fValue, uValue, status);
 +
delete fmt;
 +
if( U_FAILURE(status) )
 +
return false;
 +
 
 +
formattedValue.clear();
 +
UnicodeToCeguiString(uValue, formattedValue);
 +
return true;
 +
}
 +
</source>
 +
 
 +
=== FormattedData.h ===
 +
<source lang="cpp">
 +
#ifndef _FormattedData_h_
 +
#define _FormattedData_h_
 +
 
 +
#include "CEGuiSample.h"
 +
#include "CEGUI.h"
 +
 
 +
#include "ICU.h"
 +
 
 +
class DemoSample : public CEGuiSample
 +
{
 +
public:
 +
bool initialiseSample()
 +
{
 +
using namespace CEGUI;
 +
try
 +
{
 +
// Retrieve the window manager
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
 
 +
// Load the TaharezLook scheme and set up the default mouse cursor and font
 +
SchemeManager::getSingleton().loadScheme("TaharezLook.scheme");
 +
System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow");
 +
if(!FontManager::getSingleton().isFontPresent("Commonwealth-10"))
 +
FontManager::getSingleton().createFont("Commonwealth-10.font");
 +
 
 +
// Set the GUI Sheet
 +
Window* sheet = winMgr.createWindow("DefaultWindow", "root_wnd");
 +
System::getSingleton().setGUISheet(sheet);
 +
 
 +
// Load a layout
 +
Window* guiLayout = winMgr.loadWindowLayout("FormattedData.layout");
 +
sheet->addChildWindow(guiLayout);
 +
 
 +
/******** ICU Stuff ********/
 +
 
 +
// Display one ISO country in a combo box
 +
Combobox* countries = static_cast<Combobox*>(winMgr.getWindow("Countries"));
 +
countries->setReadOnly(true);
 +
ListboxTextItem* listboxTextItem;
 +
listboxTextItem = new ListboxTextItem( Locale::getDefault().getCountry() );
 +
listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
 +
countries->addItem(listboxTextItem);
 +
countries->setText( Locale::getDefault().getCountry() );
 +
countries->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
 +
 
 +
// Display two ISO languages in a combo box
 +
Combobox* languages = static_cast<Combobox*>(winMgr.getWindow("Languages"));
 +
languages->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
 +
languages->setReadOnly(true);
 +
listboxTextItem = new ListboxTextItem("en");
 +
listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
 +
languages->addItem(listboxTextItem);
 +
listboxTextItem = new ListboxTextItem("fr");
 +
listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
 +
languages->addItem(listboxTextItem);
 +
languages->setText("en");
 +
languages->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
 +
PushButton* everyISO = static_cast<PushButton*>(winMgr.getWindow("EveryISO"));
 +
everyISO->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onLoadEveryISO, this));
 +
 
 +
// Configure the currency widgets
 +
Editbox* currencyValue = static_cast<Editbox*>(winMgr.getWindow("CurrencyValue"));
 +
currencyValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCurrencyChanged, this));
 +
RadioButton* radio;
 +
radio = static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"));
 +
radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
 +
radio->setID(1);
 +
radio->setSelected(true);
 +
radio = static_cast<RadioButton*>(winMgr.getWindow("RadioUSD"));
 +
radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
 +
radio->setID(2);
 +
radio = static_cast<RadioButton*>(winMgr.getWindow("RadioEUR"));
 +
radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
 +
radio->setID(3);
 +
radio = static_cast<RadioButton*>(winMgr.getWindow("RadioMRO"));
 +
radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
 +
radio->setID(4);
 +
 
 +
// Configure the numeric widgets
 +
Editbox* numericValue = static_cast<Editbox*>(winMgr.getWindow("NumericValue"));
 +
numericValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onNumericChanged, this));
 +
Editbox* numericFormat = static_cast<Editbox*>(winMgr.getWindow("NumericFormat"));
 +
numericFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onNumericChanged, this));
 +
PushButton* numericFormatButton = static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"));
 +
numericFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onNumericFormatClicked, this));
 +
 
 +
// Configure the date/time widgets
 +
Editbox* dateValue = static_cast<Editbox*>(winMgr.getWindow("DateValue"));
 +
dateValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
 +
Editbox* timeValue = static_cast<Editbox*>(winMgr.getWindow("TimeValue"));
 +
timeValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
 +
Editbox* dateFormat = static_cast<Editbox*>(winMgr.getWindow("DateFormat"));
 +
dateFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
 +
Editbox* timeFormat = static_cast<Editbox*>(winMgr.getWindow("TimeFormat"));
 +
timeFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
 +
PushButton* dateTimeFormatButton = static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"));
 +
dateTimeFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onDateTimeFormatClicked, this));
 +
 
 +
// Configure the spinner
 +
Spinner* spinner = static_cast<Spinner*>(winMgr.getWindow("Spinner"));
 +
spinner->subscribeEvent(Spinner::EventValueChanged, Event::Subscriber(&DemoSample::onSpinnerValueChanged, this));
 +
static_cast<Editbox*>(winMgr.getWindow(spinner->getName() + "__auto_editbox__"))->setReadOnly(true); // We cannot handle manually specified values yet
 +
spinner->setTextInputMode(Spinner::FloatingPoint); // FloatingPoint, Integer, Hexadecimal, Octal
 +
spinner->setMinimumValue(-10.0f);
 +
spinner->setMaximumValue(10.0f);
 +
spinner->setStepSize(0.02f);
 +
spinner->setCurrentValue(5.2f);
 +
 
 +
// Configure the character widgets
 +
Editbox* characterValue = static_cast<Editbox*>(winMgr.getWindow("CharacterValue"));
 +
characterValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCharacterChanged, this));
 +
Editbox* characterFormat = static_cast<Editbox*>(winMgr.getWindow("CharacterFormat"));
 +
characterFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCharacterChanged, this));
 +
PushButton* characterFormatButton = static_cast<PushButton*>(winMgr.getWindow("CharacterFormatButton"));
 +
characterFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onCharacterFormatClicked, this));
 +
EventArgs e;
 +
onCharacterFormatClicked(e);
 +
 
 +
// Initialize the localise values
 +
RefreshLocalisedValues();
 +
}
 +
catch(Exception &e)
 +
{
 +
#if defined( __WIN32__ ) || defined( _WIN32 )
 +
MessageBox(NULL, e.getMessage().c_str(), "Error initializing the demo", MB_OK | MB_ICONERROR | MB_TASKMODAL);
 +
#else
 +
std::cerr << "Error initializing the demo:" << e.getMessage().c_str() << "\n";
 +
#endif
 +
}
 +
 
 +
return true;
 +
}
 +
 
 +
    void cleanupSample(void)
 +
{
 +
CEGUI::FontManager::getSingleton().destroyAllFonts();
 +
}
 +
private:
 +
ICU icu; // International Components for Unicode
 +
 
 +
void RefreshLocalisedValues()
 +
{
 +
// Trigger changes in localised widgets to refresh their values accordingly
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
EventArgs eventArgs;
 +
static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->fireEvent(RadioButton::EventSelectStateChanged, eventArgs);
 +
static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 +
static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 +
static_cast<Spinner*>(winMgr.getWindow("Spinner"))->fireEvent(Spinner::EventValueChanged, eventArgs);
 +
}
 +
 
 +
bool onLocaleChanged(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
String country = static_cast<Combobox*>(winMgr.getWindow("Countries"))->getText();
 +
String language = static_cast<Combobox*>(winMgr.getWindow("Languages"))->getText();
 +
if(icu.setLocale(language.c_str(), country.c_str()))
 +
RefreshLocalisedValues();
 +
else
 +
MessageBox(NULL,
 +
("Error with setLocale(" + language + ", " + country + ")").c_str(),
 +
"Error",
 +
MB_OK | MB_ICONERROR | MB_TASKMODAL);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onLoadEveryISO(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
ListboxTextItem* listboxTextItem;
 +
int32_t count;
 +
 
 +
// Change the label of the button
 +
PushButton* everyISO = static_cast<PushButton*>(winMgr.getWindow("EveryISO"));
 +
everyISO->setText("This will take a few seconds...");
 +
 
 +
// Display every ISO countries in a combobox
 +
Combobox* countries = static_cast<Combobox*>(winMgr.getWindow("Countries"));
 +
const char * const * listCountries = Locale::getISOCountries();
 +
for(count = 0; listCountries[count]; count++)
 +
{
 +
listboxTextItem = new ListboxTextItem(listCountries[count]);
 +
listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
 +
countries->addItem(listboxTextItem);
 +
}
 +
countries->setText( Locale::getDefault().getCountry() );
 +
 
 +
// Display every ISO languages in a combobox
 +
Combobox* languages = static_cast<Combobox*>(winMgr.getWindow("Languages"));
 +
const char * const * listLanguages = Locale::getISOLanguages();
 +
for(count = 0; listLanguages[count]; count++)
 +
{
 +
listboxTextItem = new ListboxTextItem(listLanguages[count]);
 +
listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
 +
languages->addItem(listboxTextItem);
 +
}
 +
languages->setText( Locale::getDefault().getLanguage() );
 +
 
 +
// Remove this button since it is no longer useful
 +
everyISO->setEnabled(false);
 +
everyISO->setVisible(false);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onCurrencyChanged(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
EventArgs eventArgs;
 +
static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->fireEvent(RadioButton::EventSelectStateChanged, eventArgs);
 +
return true;
 +
}
 +
 
 +
bool onCurrencySelected(const CEGUI::EventArgs& e)
 +
{
 +
// Reformat the numeric value into a properly formatted currency
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
 
 +
// Set the currency
 +
CEGUI::uint id = static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->getSelectedButtonInGroup()->getID();
 +
switch(id)
 +
{
 +
case 1:
 +
icu.setCurrency("CAD");
 +
break;
 +
case 2:
 +
icu.setCurrency("USD");
 +
break;
 +
case 3:
 +
icu.setCurrency("EUR");
 +
break;
 +
case 4:
 +
// Mauritania does not use a decimal division of units
 +
// and yet ICU still displays decimal units??
 +
icu.setCurrency("MRO");
 +
break;
 +
}
 +
 
 +
CEGUI::String rawValue = static_cast<Editbox*>(winMgr.getWindow("CurrencyValue"))->getText();
 +
double currency = atof(rawValue.c_str());
 +
CEGUI::String formattedValue;
 +
if( icu.formatCurrency(currency, formattedValue) )
 +
static_cast<Editbox*>(winMgr.getWindow("CurrencyFormattedValue"))->setText(formattedValue);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onNumericChanged(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
EventArgs eventArgs;
 +
static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onNumericFormatClicked(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
String formattedText;
 +
if( icu.formatNumber(static_cast<Editbox*>(winMgr.getWindow("NumericValue"))->getText(),
 +
static_cast<Editbox*>(winMgr.getWindow("NumericFormat"))->getText(),
 +
formattedText) )
 +
static_cast<Editbox*>(winMgr.getWindow("NumericFormattedValue"))->setText(formattedText);
 +
 
 +
// Ordinal representation of an integer value
 +
double doubleValue = atof(static_cast<Editbox*>(winMgr.getWindow("NumericValue"))->getText().c_str());
 +
if( icu.numberToOrdinal((int) doubleValue, formattedText) )
 +
static_cast<Editbox*>(winMgr.getWindow("NumericFormattedOrdinal"))->setText(formattedText);
 +
 
 +
// Textual representation of the numeric value
 +
// Note that this value must be raw, containing only digits and a decimal point
 +
// Since atof() is used to convert from a string to a double this numeric value is not localised
 +
if( icu.numberToText(doubleValue, formattedText) )
 +
static_cast<Editbox*>(winMgr.getWindow("NumericFormattedText"))->setText(formattedText);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onDateTimeChanged(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
EventArgs eventArgs;
 +
static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onDateTimeFormatClicked(const CEGUI::EventArgs& e)
 +
{
 +
// Format the date and time according to their specified values and formats
 +
// In theory a format can include both the date and the time:  yyyy/MM/dd HH:mm:ss
 +
// However in practice the resulting time is not accurate (within 1 minute 47 seconds)
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
UDate localDate, localTime, gmtDate, gmtTime, gmtDateTime;
 +
if(icu.CeguiStringToDateTime( static_cast<Editbox*>(winMgr.getWindow("DateValue"))->getText(),
 +
"yyyy/MM/dd", // Internal format
 +
localDate)
 +
&& icu.CeguiStringToDateTime( static_cast<Editbox*>(winMgr.getWindow("TimeValue"))->getText(),
 +
"HH:mm:ss", // Internal format
 +
localTime) )
 +
{
 +
String formattedDate, formattedTime;
 +
if(icu.formatDateTime( localDate,
 +
static_cast<Editbox*>(winMgr.getWindow("DateFormat"))->getText(),
 +
formattedDate)
 +
&& icu.formatDateTime( localTime,
 +
static_cast<Editbox*>(winMgr.getWindow("TimeFormat"))->getText(),
 +
formattedTime)
 +
)
 +
static_cast<Editbox*>(winMgr.getWindow("LocalDateTimeFormattedValue"))->setText(formattedDate + " " + formattedTime);
 +
 
 +
// GMT time
 +
if( icu.CeguiStringToDateTime(static_cast<Editbox*>(winMgr.getWindow("LocalDateTimeFormattedValue"))->getText(),
 +
"yyyy/MM/dd HH:mm:ss",
 +
gmtDateTime)
 +
&& icu.localToGmt(localDate, localTime, gmtDate, gmtTime)
 +
&& icu.formatDateTime(gmtDate, "yyyy/MM/dd", formattedDate)
 +
&& icu.formatDateTime(gmtTime, "HH:mm:ss", formattedTime)
 +
)
 +
static_cast<Editbox*>(winMgr.getWindow("GmtDateTimeFormattedValue"))->setText(formattedDate + " " + formattedTime);
 +
}
 +
 
 +
return true;
 +
}
 +
 
 +
bool onSpinnerValueChanged(const CEGUI::EventArgs& e)
 +
{
 +
// Reformat the displayed value into a locale appropriate format
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
Spinner* spinner = static_cast<Spinner*>(winMgr.getWindow("Spinner"));
 +
 
 +
String formattedValue;
 +
if( icu.formatNumber(spinner->getCurrentValue(), "#0.00", formattedValue) )
 +
{
 +
// Disable the Editbox events, especially the EventTextChanged that
 +
// triggers Spinner::handleEditTextChange, which attempts to convert
 +
// the textual value into a non-localised numeric value
 +
Editbox* editbox = static_cast<Editbox*>(winMgr.getWindow(spinner->getName() + "__auto_editbox__"));
 +
bool muted = editbox->isMuted();
 +
editbox->setMutedState(true);
 +
spinner->setText(formattedValue);
 +
editbox->setMutedState(muted);
 +
}
 +
 
 +
return true;
 +
}
 +
 
 +
bool onCharacterChanged(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
EventArgs eventArgs;
 +
static_cast<PushButton*>(winMgr.getWindow("CharacterFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 +
 
 +
return true;
 +
}
 +
 
 +
bool onCharacterFormatClicked(const CEGUI::EventArgs& e)
 +
{
 +
using namespace CEGUI;
 +
WindowManager& winMgr = WindowManager::getSingleton();
 +
String formattedText;
 +
if( icu.formatText(static_cast<Editbox*>(winMgr.getWindow("CharacterValue"))->getText(),
 +
static_cast<Editbox*>(winMgr.getWindow("CharacterFormat"))->getText(),
 +
"*",
 +
formattedText) )
 +
static_cast<Editbox*>(winMgr.getWindow("CharacterFormattedValue"))->setText(formattedText);
 +
 
 +
return true;
 +
}
 +
 
 +
};
 +
 
 +
#endif // _FormattedData_h_
 +
</source>
 +
 
 +
=== main.cpp ===
 +
<source lang="cpp">
 +
#if defined( __WIN32__ ) || defined( _WIN32 )
 +
#define WIN32_LEAN_AND_MEAN
 +
#define NOMINMAX
 +
#include "windows.h"
 +
#endif
 +
 
 +
#include "FormattedNumericData.h"
 +
 
 +
#if defined( __WIN32__ ) || defined( _WIN32 )
 +
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
 +
#else
 +
int main(int argc, char *argv[])
 +
#endif
 +
{
 +
    DemoSample app;
 +
    return app.run();
 +
}
 +
</source>
 +
 
 +
=== FormattedData.layout ===
 +
<source lang="xml">
 +
<?xml version="1.0" encoding="UTF-8"?>
 +
 
 +
<GUILayout >
 +
    <Window Type="DefaultWindow" Name="Root" >
 +
        <Property Name="InheritsAlpha" Value="False" />
 +
        <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
        <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{1,0},{1,0}}" />
 +
        <Window Type="TaharezLook/FrameWindow" Name="FrameWindow" >
 +
            <Property Name="TitlebarFont" Value="Commonwealth-10" />
 +
            <Property Name="CaptionColour" Value="00FFFFFF" />
 +
            <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
            <Property Name="TitlebarEnabled" Value="True" />
 +
            <Property Name="UnifiedAreaRect" Value="{{0.07125,0},{0.013335,0},{0.910001,0},{0.873733,0}}" />
 +
            <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseTarget" />
 +
            <Window Type="TaharezLook/Spinner" Name="Spinner" >
 +
                <Property Name="Text" Value="10.00" />
 +
                <Property Name="StepSize" Value="1" />
 +
                <Property Name="CurrentValue" Value="10" />
 +
                <Property Name="MaximumValue" Value="32767" />
 +
                <Property Name="MinimumValue" Value="-32768" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.8473,0},{0.731682,0},{0.951964,0},{0.790123,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="SpinnerLabel" >
 +
                <Property Name="Text" Value="Localised Spinner widget with 2 decimals:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.395457,0},{0.731682,0},{0.836346,0},{0.790123,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="CurrencyFormattedValue" >
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.203821,0},{0.954642,0},{0.262262,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/RadioButton" Name="RadioCAD" >
 +
                <Property Name="Text" Value="Canadian (CAD)" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.158378,0},{0.614153,0},{0.187144,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/RadioButton" Name="RadioMRO" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Mauritania (MRO)" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.26902,0},{0.614153,0},{0.297787,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/RadioButton" Name="RadioUSD" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="US (USD)" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.195183,0},{0.5605,0},{0.22395,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/RadioButton" Name="RadioEUR" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Euro (EUR)" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.232215,0},{0.5605,0},{0.260983,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="CountriesLabel" >
 +
                <Property Name="Text" Value="Countries:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.022504,0},{0.069049,0},{0.132414,0},{0.117804,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="LanguagesLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Languages:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.268405,0},{0.069049,0},{0.385766,0},{0.117804,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Combobox" Name="Countries" >
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.142027,0},{0.069049,0},{0.245976,0},{0.902039,0}}" />
 +
                <Property Name="MaxEditTextLength" Value="1073741823" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Combobox" Name="Languages" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.395379,0},{0.069049,0},{0.499329,0},{0.90204,0}}" />
 +
                <Property Name="MaxEditTextLength" Value="1073741823" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="Separator1" >
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.141452,0},{0.995305,0},{0.150048,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="Separator2" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.309408,0},{0.995305,0},{0.318004,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="Separator3" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.553045,0},{0.995305,0},{0.561642,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="Separator5" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.007601,0},{0.814139,0},{0.989344,0},{0.822736,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="NumericFormatLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Format:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.403136,0},{0.141946,0},{0.461576,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="NumericFormat" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="#,##0.03" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.403136,0},{0.421111,0},{0.461576,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Button" Name="NumericFormatButton" >
 +
                <Property Name="Text" Value="Apply" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.370638,0},{0.603204,0},{0.429079,0}}" />
 +
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.339418,0},{0.954642,0},{0.397859,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="DateFormatLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Format:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.63711,0},{0.141946,0},{0.695551,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="DateFormat" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="yyyy/MM/dd" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.63711,0},{0.269099,0},{0.695551,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="DateValueLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Date:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.574987,0},{0.141946,0},{0.633428,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="DateValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="2006/05/14" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.574987,0},{0.267608,0},{0.633428,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Button" Name="DateTimeFormatButton" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Apply" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.610743,0},{0.603205,0},{0.669184,0}}" />
 +
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="LocalDateTimeFormattedValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.724067,0},{0.578862,0},{0.954642,0},{0.637303,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="TimeValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="12:00:01" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.351492,0},{0.574987,0},{0.441976,0},{0.633428,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="TimeValueLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Time:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.280703,0},{0.574987,0},{0.347609,0},{0.633428,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="TimeFormat" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="HH:mm:ss" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.281446,0},{0.63711,0},{0.441976,0},{0.695551,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="NumericValueLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Numeric:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.339418,0},{0.141946,0},{0.397857,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="NumericValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="123456.78" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.339418,0},{0.421111,0},{0.397858,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="CurrencyValueLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Currency:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.203821,0},{0.141946,0},{0.26226,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="CurrencyValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="123456.78" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.203821,0},{0.421111,0},{0.262262,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="Separator4" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.702202,0},{0.995305,0},{0.710798,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Button" Name="EveryISO" >
 +
                <Property Name="Text" Value="Load every country and languages" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.559611,0},{0.069049,0},{0.94523,0},{0.12749,0}}" />
 +
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedText" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.153278,0},{0.482989,0},{0.954642,0},{0.54143,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="CharacterFormattedValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.883872,0},{0.954642,0},{0.942313,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="CharacterFormat" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="000 000 000" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.908716,0},{0.421111,0},{0.967156,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Button" Name="CharacterFormatButton" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Apply" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.883872,0},{0.603205,0},{0.942313,0}}" />
 +
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="CharacterValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="123456" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.844998,0},{0.421111,0},{0.903438,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="LocalDateTimeFormattedLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Local:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.641358,0},{0.578862,0},{0.718697,0},{0.637302,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="GmtDateTimeFormattedLabel" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="GMT:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.641358,0},{0.63711,0},{0.718697,0},{0.69555,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="GmtDateTimeFormattedValue" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.725558,0},{0.63711,0},{0.954642,0},{0.695551,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="NumericValueLabel1" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Numeric:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.844998,0},{0.141946,0},{0.903437,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/StaticText" Name="NumericFormatLabel1" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="Text" Value="Format:" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.908716,0},{0.141946,0},{0.967156,0}}" />
 +
            </Window>
 +
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedOrdinal" >
 +
                <Property Name="Font" Value="Commonwealth-10" />
 +
                <Property Name="ReadOnly" Value="True" />
 +
                <Property Name="MaxTextLength" Value="1073741823" />
 +
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
 +
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.403136,0},{0.954642,0},{0.461577,0}}" />
 +
            </Window>
 +
        </Window>
 +
    </Window>
 +
</GUILayout>
 +
</source>
  
==Source Code==
+
[[Category:Tutorials]]

Latest revision as of 12:34, 28 February 2011

Written for CEGUI 0.6


Works with versions 0.6.x (obsolete)

Written for CEGUI 0.5


Works with versions 0.5.x (obsolete)

International Components for Unicode (ICU) is a toolkit to internationalise applications. Initially developed for Java it has been ported to C/C++. There is a Java version of ICU called ICU4J, and there is a C/C++ version of ICU called ICU4C. Now ICU4C has been partially ported to CEGUI. Please use this thread for discussion.

International Components for Unicode

Useful Links

Getting started

Download these files from the ICU web site

  • Source: icu-3.4.1.zip for Windows OR icu-3.4.1.tgz for Unix
  • Documentation: icu-3.4-docs.zip
  • User Guide: icu-3.4-userguide.zip

Files of interest:

  • Solution to compile: icu/source/allinone/allinone.sln
  • Instructions to compile and use ICU: icu/readme.html#HowToBuildWindows
  • ICU4C samples: icu/source/samples/

The instructions for building and setting up ICU are contained in the readme.html. If you have any problems, contact the icu-support mailing list or submit a bug report.

Incorporating ICU4C within an existing project

Additional include directories:

  • $(ICU)\include

Additional library directories:

  • $(ICU)\lib

Link with:

  • debug: icuucd.lib icuind.lib
  • release: icuuc.lib icuin.lib

Make certain these DLLs are present with your executable or in your PATH:

  • debug: icuuc34d.dll icuin34d.dll icudt34.dll
  • release: icuuc34.dll icuin34.dll icudt34.dll

CEGUI's ICU

ICU4C provides many features to support internationalisation. The class I've created encapsulates some (not all) of these features.

Locale

A Locale is an identifier that describes the rules in effect within a country for a specific language. These rules specify the formatting of currencies, numeric values, dates, and others. Unfortunately these rules do not take into consideration the modifications that may have been applied within the Regional Settings of Window's Control Panel, unless the "@compat=host" locale is used, which is available in ICU 3.6 and later. However most functions accept a formatting mask. This allows applications to recreate some of the features of the control panel, allowing users to specify their desired formats. The setLocale() and setCurrency() functions will configure the class to adopt the rules specified by the specified language, country, and currency.

formatNumber

The formatNumber() function formats a numeric value given the specified mask. Three versions are available. The versions accepting a CEGUI::String has been customised to allow large numbers to be formatted: numbers having up to 18 integers and 7 decimals. The current implementation of this function is limited to accepting a single format for positive numbers. If the numeric value to be formatted is negative then a negative sign will precede the formatted value. However it is impossible to format a negative value within parentheses: formatting -1234.56 to (1,234.56).

numberToText

The numberToText() function converts a numeric value into a textual representation. For example the numeric value 123 is converted into "one hundred twenty-three" in English and "cent vingt-trois" in French.

numberToOrdinal

The numberToOrdinal() function converts a numeric value into an ordinal representation. For example the numeric value 1 is converted into "1st" in english. Support for other languages is either buggy or lacking (or I have improperly coded this feature).

formatText

The formatText() function will parse a numeric value and sprinkle digits into the slots specified by the formatting mask. A North American telephone number of "12223334444" can be formatted into "1 (222) 333-4444" with the mask "0 (000) 000-0000". This non-localised function accepts three format specifier. A "0" represents a forced digit. If the numerical value possesses a digit at that position then the digit will be displayed, otherwise the place holder character(s) will be used. A "#" represents a potential digit. If the numerical value possesses a digit at that position the the digit will be displayed, otherwise nothing is displayed. Finally the apostrophe "'" allows the mask to specify the characters "0", "#", or "'", rather than using them as format specifiers.

formatCurrency

The formatCurrency() function will format a numerical value according to the currently configured locale and currency. It will NOT convert the monetary value of one currency to another.

formatDateTime

The formatDateTime() function will format a date, time, or date/time given the specified mask. The UDate variable type is a double. According to ICU's documentation A UDate value is stored as UTC time in milliseconds, which means it is calendar and time zone independent. UDate is the most compact and portable way to store and transmit a date and time.

localToGmt and gmtToLocal

The localToGmt() and the gmtToLocal() functions convert a date and a time between a local and a GMT value.

CeguiStringToDateTime

The CeguiStringToDateTime() function will parse a string specifying a date or a time into its corresponding UDate value. Although it is possible to parse a string containing both a date and a time the resulting UDate value will be inaccurate, varying from its intended value by up to 1 minute 47 seconds. A better approach is to keep the date and time separated.

Source Code

icu.h

#ifndef _ICU_h_
#define _ICU_h_
 
#include "unicode/utypes.h"
#include "unicode/unistr.h"
#include "unicode/numfmt.h"
#include "unicode/dcfmtsym.h"
#include "unicode/decimfmt.h"
#include "unicode/locid.h"
#include "unicode/uclean.h"
#include "unicode/calendar.h"
#include "unicode/datefmt.h"
#include "unicode/smpdtfmt.h"
#include "unicode/rbnf.h"
#include "CEGUI.h"
 
class ICU
{
public:
	ICU(bool cleanupICU = true);
	~ICU();
	bool setLocale(const CEGUI::String& language, const CEGUI::String& country);
	const Locale& getLocale();
	bool setCurrency(const char *currency);
 
	bool formatNumber(const CEGUI::String& rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
	bool formatNumber(const double rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
	bool formatNumber(const int32_t rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue);
 
	bool numberToText(const double rawValue, CEGUI::String& formattedValue);
	bool numberToText(const int32_t rawValue, CEGUI::String& formattedValue);
 
	bool numberToOrdinal(const double rawValue, CEGUI::String& formattedValue);
	bool numberToOrdinal(const int32_t rawValue, CEGUI::String& formattedValue);
 
	bool formatText(const CEGUI::String& rawValue, const CEGUI::String& mask, const CEGUI::String& zeroPlaceHolder, CEGUI::String& formattedValue);
 
	bool formatCurrency(const double& currency, CEGUI::String& formattedValue);
 
	bool formatDateTime(UDate rawDateTime, const CEGUI::String& mask, CEGUI::String& formattedDateTime);
	bool localToGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime);
	bool gmtToLocal(const UDate& gmtDate, const UDate& gmtTime, UDate& localDate, UDate& localTime);
 
	bool CeguiStringToDateTime(const CEGUI::String& stringDateTime, const CEGUI::String& mask, UDate& unicodeDateTime);
	bool CeguiStringToInt32(const CEGUI::String& stringValue, int32_t& int32Value);
	void UnicodeToCeguiString(const UnicodeString& unicodeString, CEGUI::String& ceguiString);
private:
	void _splitIntegerDecimal(const CEGUI::String& combined, CEGUI::String& integerPart, CEGUI::String& decimalPart);
	bool _convertLocalAndGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime, bool fromLocalToGMT);
	bool _ruleBasedNumberFormat(const double rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue);
	bool _ruleBasedNumberFormat(const int32_t rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue);
	Locale m_locale; // Current locale of the computer
	UChar m_currency[4]; // Currency code
	bool m_cleanupICU; // Whether to call ICU's cleanup function
};
 
#endif // _ICU_h_

icu.cpp

#include "ICU.h"
#include <vector>
 
ICU::ICU(bool cleanupICU)
{
	m_cleanupICU = cleanupICU;
}
 
ICU::~ICU()
{
	// http://www.icu-project.org/userguide/design.html#Init_and_Termination
	// The paragraph "ICU Initialization and Termination" mentions that this function
	//   can be called to force the ICU library to release any allocated heap storage
	//   otherwise memory leak checking tools will falsely report leaks.
	// My thinking with this m_cleanupICU variable is that the library should not release
	//   its memory allocation if it is also used outside of this class.  This
	//	 implementation grants you some control over when this cleanup occurs.
	// You are not required to call u_cleanup(). It's dangerous if called at the wrong time.
	if(m_cleanupICU)
		u_cleanup();
}
 
bool ICU::setLocale(const CEGUI::String& language, const CEGUI::String& country)
{
	// Set the locale
	Locale tempLocale = Locale::createFromName((language + "_" + country).c_str());
	if(tempLocale.isBogus())
		return false;
 
	m_locale = tempLocale;
	return true;
}
 
const Locale& ICU::getLocale()
{
	// Retrive the locale
	return m_locale;
}
 
bool ICU::setCurrency(const char *currency)
{
	// Set the currency
    if(currency==NULL || strlen(currency)!=3)
        return false;
 
    // Invariant-character conversion to UChars
    u_charsToUChars(currency, m_currency, 4);
	return true;
}
 
bool ICU::formatNumber(const CEGUI::String& rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
{
	// Format a unformatted number according to the rules specified within the mask
	// An unformatted number only contains numeric digits and a period as the decimal point
	// Note that a decimal value is broken down into two numbers, an
	//   integer and a decimal number for formatting and then reassembled
	//   into a single string.  This makes it possible to handle numbers
	//   as large as 999,999,999,999,999,999.9999999 (18 integers and 7 decimals).
	//   However it makes it impossible to specify a positive mask as well as a
	//   negative mask; only one mask is supported.  It is also impossible to
	//   format a negative value with a mask similar to "(#,##0.00)"
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
	CEGUI::String formattedInteger, formattedDecimal;
	CEGUI::String::size_type idx;
 
	// Separate the integer and the decimal parts from the value
	CEGUI::String maskInteger, maskDecimal, integerValue, decimalValue;
	idx = rawValue.find(".");
	if(idx == CEGUI::String::npos)
	{
		// If the value is empty then force the integer to 0, otherwise use the value
		// Since there is no decimal point force a decimal value of .0
		integerValue = rawValue.empty() ? "0" : rawValue;
		decimalValue = ".0";
	}
	else
	{
		// If there is no integer portion then force an integer value of 0
		integerValue = idx == 0 ? "0" : rawValue.substr(0, idx);
		decimalValue = rawValue.substr(idx);
	}
 
	// Separate the integer and the decimal parts from the mask
	_splitIntegerDecimal(mask, maskInteger, maskDecimal);
 
	// Prepare the numeric formatter
	DecimalFormatSymbols* decimalFormatSymbols = new DecimalFormatSymbols(m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
	if(!maskInteger.empty())
	{
		// Prepare the integer parser
		UnicodeString patternInteger(maskInteger.c_str());
		DecimalFormat* fmt = new DecimalFormat(patternInteger, *decimalFormatSymbols, status);
		if( U_FAILURE(status) )
		{
			delete decimalFormatSymbols;
			return false;
		}
 
		// Parse the string value into an integer value
		UnicodeString uIntegerValue(integerValue.c_str());
		Formattable fIntegerValue;
		fmt->parse(uIntegerValue, fIntegerValue, status);
		if( U_FAILURE(status) )
		{
			status = U_ZERO_ERROR;
			fIntegerValue.setInt64(0);
		}
 
		// Format the integer value
		uIntegerValue = "";
		((NumberFormat*)fmt)->format(fIntegerValue.getInt64(), uIntegerValue);
		delete fmt;
 
		UnicodeToCeguiString(uIntegerValue, formattedInteger);
	}
 
	if(!maskDecimal.empty())
	{
		// Prepare the decimal parser
		DecimalFormat* fmtParse = new DecimalFormat(status);
		if( U_FAILURE(status) )
		{
 			delete decimalFormatSymbols;
			return false;
		}
		fmtParse->applyLocalizedPattern("0.0000003", status);
 
		// Parse the string value into a decimal value
		UnicodeString uDecimalValue(decimalValue.c_str());
		Formattable fDecimalValue;
		fmtParse->parse(uDecimalValue, fDecimalValue, status);
		delete fmtParse;
		if( U_FAILURE(status) )
		{
			status = U_ZERO_ERROR;
			fDecimalValue.setDouble(0.0);
		}
 
		// Prepare the decimal formatter
		UnicodeString patternDecimal(maskDecimal.c_str());
		DecimalFormat* fmt = new DecimalFormat(patternDecimal, *decimalFormatSymbols, status);
		if( U_FAILURE(status) )
		{
 			delete decimalFormatSymbols;
			return false;
		}
 
		// Format the decimal value
		uDecimalValue = "";
		fmt->format(fDecimalValue.getDouble(), uDecimalValue);
		delete fmt;
 
		UnicodeToCeguiString(uDecimalValue, formattedDecimal);
	}
 
	delete decimalFormatSymbols;
 
	formattedValue = formattedInteger + formattedDecimal;
	return true;
}
 
bool ICU::formatNumber(const double rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
{
	// Format a number using the appropriate locale rules
	// Note that the use of a double limits the effective range
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
 
	// Prepare the numeric formatter
	DecimalFormatSymbols* symbols = new DecimalFormatSymbols(m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
    // Create a number formatter for the current locale
	UnicodeString pattern(mask.c_str());
	DecimalFormat* fmt = new DecimalFormat(pattern, *symbols, status);
	if( U_FAILURE(status) )
	{
		delete symbols;
		return false;
	}
 
	// Format the number
	UnicodeString uValue;
	fmt->format(rawValue, uValue, status);
	delete symbols;
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	UnicodeToCeguiString(uValue, formattedValue);
	return true;
}
 
bool ICU::formatNumber(const int32_t rawValue, const CEGUI::String& mask, CEGUI::String& formattedValue)
{
	// Format a number using the appropriate locale rules
	// Note that the use of a double limits the effective range
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
 
	// Prepare the numeric formatter
	DecimalFormatSymbols* symbols = new DecimalFormatSymbols(m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
    // Create a number formatter for the current locale
	UnicodeString pattern(mask.c_str());
	DecimalFormat* fmt = new DecimalFormat(pattern, *symbols, status);
	if( U_FAILURE(status) )
	{
		delete symbols;
		return false;
	}
 
	// Format the number
	UnicodeString uValue;
	fmt->format(rawValue, uValue, status);
	delete symbols;
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	UnicodeToCeguiString(uValue, formattedValue);
	return true;
}
 
bool ICU::numberToText(const double rawValue, CEGUI::String& formattedValue)
{
	// Convert a number to a text
	// "1.2" to "one point two"
	return _ruleBasedNumberFormat(rawValue, URBNF_SPELLOUT, formattedValue);
}
 
bool ICU::numberToText(const int32_t rawValue, CEGUI::String& formattedValue)
{
	// Convert a number to a text
	// "12" to "twelve"
	return _ruleBasedNumberFormat(rawValue, URBNF_SPELLOUT, formattedValue);
}
 
bool ICU::numberToOrdinal(const double rawValue, CEGUI::String& formattedValue)
{
	// Convert a number to ordinal text
	// "1.5" to "2nd"
	return _ruleBasedNumberFormat(rawValue, URBNF_ORDINAL, formattedValue);
}
 
bool ICU::numberToOrdinal(const int32_t rawValue, CEGUI::String& formattedValue)
{
	// Convert a number to ordinal text
	// "1" to "1st"
	return _ruleBasedNumberFormat(rawValue, URBNF_ORDINAL, formattedValue);
}
 
bool ICU::formatText(const CEGUI::String& rawValue, const CEGUI::String& mask, const CEGUI::String& zeroPlaceHolder, CEGUI::String& formattedValue)
{
	/* Format a value according to a pattern
	 * Format specifiers:
	 *		0 forced digit: will be replaced by a digit or if there is no digit, by 'zeroPlaceHolder'
	 *		# potential digit: will be replaced by a digit if there is one otherwise nothing
	 *		' literal character: the following character is to be treated as a character
	 *			rather than a format specifier
	 *			ex the value "1" with the format "'00" will result in the string "01"
	 * Note that if the value is larger than the pattern then it is truncated to the left
	 *		ex the value "1234" with the format "#0" results in the string "34"
	 */
	formattedValue.clear();
 
	if(!rawValue.length() || !mask.length())
		return false;
 
	// Parse the mask
	std::vector<CEGUI::String> maskList;
	CEGUI::String maskDigit;
	for(CEGUI::String::size_type idxMask = 0; idxMask < mask.length(); ++idxMask)
	{
		if(!mask.compare(idxMask, 1, "0"))
		{
			maskDigit = "ForcedDigit";
			maskList.push_back(maskDigit);
		}
		else if(!mask.compare(idxMask, 1, "#"))
		{
			maskDigit = "PotentialDigit";
			maskList.push_back(maskDigit);
		}
		else
		{
			if(!mask.compare(idxMask, 1, "'") && idxMask < mask.length() - 1)
				++idxMask; // Literal specifier followed by a mask digit, so skip over the apostrophe
			maskDigit = mask.at(idxMask);
			maskList.push_back(maskDigit);
		}
	}
 
	// Format the numeric value
	CEGUI::String::size_type idxValue;
	idxValue = rawValue.length();
	std::vector<CEGUI::String>::reverse_iterator itMask;
	for(itMask = maskList.rbegin(); itMask != maskList.rend(); ++itMask)
	{
		if(!(*itMask).compare("ForcedDigit"))
		{
			if(idxValue)
			{
				// We have a digit remaining
				--idxValue;
				formattedValue = rawValue.at(idxValue) + formattedValue;
			}
			else
			{
				// We're out of digits
				formattedValue = zeroPlaceHolder + formattedValue;
			}
		}
		else if(!(*itMask).compare("PotentialDigit"))
		{
			if(idxValue)
			{
				// We have a digit remaining
				--idxValue;
				formattedValue = rawValue.at(idxValue) + formattedValue;
			}
		}
		else
		{
			// Literal character
			formattedValue = (*itMask) + formattedValue;
		}
	}
	return true;
}
 
bool ICU::formatCurrency(const double& currency, CEGUI::String& formattedValue)
{
	// Format a number using the appropriate locale rules
	// Note that the use of a double limits the effective range
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
 
    // Create a number formatter for the current locale
    NumberFormat *fmt = NumberFormat::createCurrencyInstance(m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
	// Format the currency
	UnicodeString uValue;
	fmt->format(currency, uValue);
    delete fmt;
 
	UnicodeToCeguiString(uValue, formattedValue);
	return true;
}
 
bool ICU::formatDateTime(UDate rawDateTime, const CEGUI::String& mask, CEGUI::String& formattedDateTime)
{
	// Format a date/time given the specified mask
	// Note that although a UDate can support both date and time in
	//   realite the resolution is insufficient.  In order to precisely
	//   represent a date/time value they should be processed separately
    UErrorCode status = U_ZERO_ERROR;
	formattedDateTime.clear();
 
	// Create a Date/Time formatter
	DateFormat* fmt = DateFormat::createDateTimeInstance(DateFormat::kDefault, DateFormat::kDefault, m_locale); // kDefault is not really used
 
	// Activate our pattern (overrides DateFormat::kDefault)
    UnicodeString pattern(mask.c_str());
	((SimpleDateFormat*) fmt)->applyPattern(pattern);
 
	// Format the date/time
    UnicodeString uValue;
	fmt->format(rawDateTime, uValue, status);
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	UnicodeToCeguiString(uValue, formattedDateTime);
	return true;
}
 
bool ICU::localToGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime)
{
	// Convert a local date/time into a GMT date/time
	return _convertLocalAndGmt(localDate, localTime, gmtDate, gmtTime, true);
}
 
bool ICU::gmtToLocal(const UDate& gmtDate, const UDate& gmtTime, UDate& localDate, UDate& localTime)
{
	// Convert a GMT date/time into a local date/time string
	return _convertLocalAndGmt(gmtDate, gmtTime, localDate, localTime, false);
}
 
bool ICU::CeguiStringToDateTime(const CEGUI::String& stringDateTime, const CEGUI::String& mask, UDate& unicodeDateTime)
{
	// Convert a string date/time into a UDate
    UErrorCode status = U_ZERO_ERROR;
	unicodeDateTime = 0.0;
 
	// Create a Date/Time formatter
	DateFormat* fmt = DateFormat::createDateTimeInstance(DateFormat::kDefault, DateFormat::kDefault, m_locale);
 
	// Activate our pattern
    UnicodeString pattern(mask.c_str());
	((SimpleDateFormat*) fmt)->applyPattern(pattern);
 
	// Parse the string into a date/time
	UnicodeString uValue(stringDateTime.c_str());
	unicodeDateTime = fmt->parse(uValue, status);
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	return true;
}
 
bool ICU::CeguiStringToInt32(const CEGUI::String& stringValue, int32_t& int32Value)
{
	// Convert a string into an int32_t
    UErrorCode status = U_ZERO_ERROR;
	int32Value = 0;
 
	// Create an integer formatter
	NumberFormat *fmt = NumberFormat::createInstance(Locale::getUS(), status);
	if( U_FAILURE(status) )
		return false;
 
	// Activate our pattern
	UnicodeString pattern("#,##0");
	((DecimalFormat*) fmt)->applyPattern(pattern, status);
	if( U_FAILURE(status) )
	{
		delete fmt;
		return false;
	}
 
	// Parse the string into a double
	UnicodeString uValue(stringValue.c_str());
    Formattable result;
    fmt->parse(uValue, result, status);
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	int32Value = result.getLong();
 
	return true;
}
 
void ICU::UnicodeToCeguiString(const UnicodeString& unicodeString, CEGUI::String& ceguiString)
{
	// Convert a unicode string to a CEGUI string
	ceguiString.clear();
 
	CEGUI::String digit;
	for(int32_t i = 0; i < unicodeString.length(); ++i)
	{
		digit = unicodeString.charAt(i);
		ceguiString.append(digit);
	}
}
 
void ICU::_splitIntegerDecimal(const CEGUI::String& combined, CEGUI::String& integerPart, CEGUI::String& decimalPart)
{
	// Split a decimal value into its constituent integer and decimal parts
	CEGUI::String::size_type idx = combined.find(".");
	if(idx == CEGUI::String::npos)
	{
		integerPart = combined;
		decimalPart.clear();
	}
	else
	{
		integerPart = combined.substr(0, idx);
		decimalPart = combined.substr(idx);
	}
}
 
bool ICU::_convertLocalAndGmt(const UDate& localDate, const UDate& localTime, UDate& gmtDate, UDate& gmtTime, bool fromLocalToGMT)
{
	// Helper function to convert between a local and a GMT date/time
	UErrorCode status = U_ZERO_ERROR;
	gmtDate = gmtTime = 0.0;
 
	// Create a calendar for the local date
	Calendar* calLocalDate = Calendar::createInstance(m_locale, status);
	if( U_FAILURE(status) )
		return false;
	calLocalDate->clear();
	calLocalDate->setTime(localDate, status);
	if( U_FAILURE(status) )
	{
		delete calLocalDate;
		return false;
	}
 
	// Create a calendar for the local time
	Calendar* calLocalTime = Calendar::createInstance(m_locale, status);
	if( U_FAILURE(status) )
	{
		delete calLocalDate;
		return false;
	}
	calLocalTime->clear();
	calLocalTime->setTime(localTime, status);
	if( U_FAILURE(status) )
	{
		delete calLocalDate;
		delete calLocalTime;
		return false;
	}
 
	// Retrieve the offset between this time zone and the GMT time zone
	// The Daylight Saving Time offset is zero when DST is not in effect
	// Note that adding the local date and time together introduces an
	//   inaccuracy of up to 1 minute 47 seconds.  However the only impact
	//   of this inaccuracy is to advance/delay the activation or deactivation
	//   of daylight savings
	int32_t rawOffset, dstOffset, gmtOffset;
	calLocalDate->getTimeZone().getOffset(localDate + localTime, true, rawOffset, dstOffset, status);
	gmtOffset = (rawOffset + dstOffset) / 1000 / 60 / 60; // Convert from milliseconds to hours
 
	// Converting from local to GMT requires "reversing" the time zone
	// EST is GMT-5 so it requires adding 5 hours to local time to obtain GMT
	// Converting from GMT to local requires the opposite
	int32_t hour = calLocalTime->get(UCAL_HOUR_OF_DAY, status);
	if(fromLocalToGMT)
		hour -= gmtOffset;
	else
		hour += gmtOffset;
 
	// Adjust the time and date if necessary
	if(hour >= 24)
	{
		// We've moved to the next day
		hour -= 24;
		calLocalDate->add(UCAL_DAY_OF_MONTH, 1, status);
		if( U_FAILURE(status) )
		{
			delete calLocalDate;
			delete calLocalTime;
			return false;
		}
	}
	else if(hour < 0)
	{
		// We've moved to the previous day
		hour += 24;
		calLocalDate->add(UCAL_DAY_OF_MONTH, -1, status);
		if( U_FAILURE(status) )
		{
			delete calLocalDate;
			delete calLocalTime;
			return false;
		}
	}
	calLocalTime->set(UCAL_HOUR_OF_DAY, hour);
 
	// Retrieve the converted date and time
	gmtDate = calLocalDate->getTime(status);
	gmtTime = calLocalTime->getTime(status);
 
	delete calLocalDate;
	delete calLocalTime;
	if( U_FAILURE(status) )
		return false;
 
	return true;
}
 
bool ICU::_ruleBasedNumberFormat(const double rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue)
{
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
 
	// Create a rule based number formatter
	RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
    UnicodeString uValue;
    fmt->format(rawValue, uValue);
	delete fmt;
 
	formattedValue.clear();
	UnicodeToCeguiString(uValue, formattedValue);
	return true;
}
 
bool ICU::_ruleBasedNumberFormat(const int32_t rawValue, URBNFRuleSetTag tag, CEGUI::String& formattedValue)
{
    UErrorCode status = U_ZERO_ERROR;
	formattedValue.clear();
 
	// Create a rule based number formatter
	RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, m_locale, status);
	if( U_FAILURE(status) )
		return false;
 
    UnicodeString uValue;
	Formattable fValue(rawValue);
	fmt->format(fValue, uValue, status);
	delete fmt;
	if( U_FAILURE(status) )
		return false;
 
	formattedValue.clear();
	UnicodeToCeguiString(uValue, formattedValue);
	return true;
}

FormattedData.h

#ifndef _FormattedData_h_
#define _FormattedData_h_
 
#include "CEGuiSample.h"
#include "CEGUI.h"
 
#include "ICU.h"
 
class DemoSample : public CEGuiSample
{
public:
	bool initialiseSample()
	{
		using namespace CEGUI;
		try
		{
			// Retrieve the window manager
			WindowManager& winMgr = WindowManager::getSingleton();
 
			// Load the TaharezLook scheme and set up the default mouse cursor and font
			SchemeManager::getSingleton().loadScheme("TaharezLook.scheme");
			System::getSingleton().setDefaultMouseCursor("TaharezLook", "MouseArrow");
			if(!FontManager::getSingleton().isFontPresent("Commonwealth-10"))
				FontManager::getSingleton().createFont("Commonwealth-10.font");
 
			// Set the GUI Sheet
			Window* sheet = winMgr.createWindow("DefaultWindow", "root_wnd");
			System::getSingleton().setGUISheet(sheet);
 
			// Load a layout
			Window* guiLayout = winMgr.loadWindowLayout("FormattedData.layout");
			sheet->addChildWindow(guiLayout);
 
			/******** ICU Stuff ********/
 
			// Display one ISO country in a combo box
			Combobox* countries = static_cast<Combobox*>(winMgr.getWindow("Countries"));
			countries->setReadOnly(true);
			ListboxTextItem* listboxTextItem;
			listboxTextItem = new ListboxTextItem( Locale::getDefault().getCountry() );
			listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
			countries->addItem(listboxTextItem);
			countries->setText(	Locale::getDefault().getCountry() );
			countries->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
 
			// Display two ISO languages in a combo box
			Combobox* languages = static_cast<Combobox*>(winMgr.getWindow("Languages"));
			languages->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
			languages->setReadOnly(true);
			listboxTextItem = new ListboxTextItem("en");
			listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
			languages->addItem(listboxTextItem);
			listboxTextItem = new ListboxTextItem("fr");
			listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
			languages->addItem(listboxTextItem);
			languages->setText("en");
			languages->subscribeEvent(Combobox::EventListSelectionAccepted, Event::Subscriber(&DemoSample::onLocaleChanged, this));
			PushButton* everyISO = static_cast<PushButton*>(winMgr.getWindow("EveryISO"));
			everyISO->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onLoadEveryISO, this));
 
			// Configure the currency widgets
			Editbox* currencyValue = static_cast<Editbox*>(winMgr.getWindow("CurrencyValue"));
			currencyValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCurrencyChanged, this));
			RadioButton* radio;
			radio = static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"));
			radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
			radio->setID(1);
			radio->setSelected(true);
			radio = static_cast<RadioButton*>(winMgr.getWindow("RadioUSD"));
			radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
			radio->setID(2);
			radio = static_cast<RadioButton*>(winMgr.getWindow("RadioEUR"));
			radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
			radio->setID(3);
			radio = static_cast<RadioButton*>(winMgr.getWindow("RadioMRO"));
			radio->subscribeEvent(RadioButton::EventSelectStateChanged, Event::Subscriber(&DemoSample::onCurrencySelected, this));
			radio->setID(4);
 
			// Configure the numeric widgets
			Editbox* numericValue = static_cast<Editbox*>(winMgr.getWindow("NumericValue"));
			numericValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onNumericChanged, this));
			Editbox* numericFormat = static_cast<Editbox*>(winMgr.getWindow("NumericFormat"));
			numericFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onNumericChanged, this));
			PushButton* numericFormatButton = static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"));
			numericFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onNumericFormatClicked, this));
 
			// Configure the date/time widgets
			Editbox* dateValue = static_cast<Editbox*>(winMgr.getWindow("DateValue"));
			dateValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
			Editbox* timeValue = static_cast<Editbox*>(winMgr.getWindow("TimeValue"));
			timeValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
			Editbox* dateFormat = static_cast<Editbox*>(winMgr.getWindow("DateFormat"));
			dateFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
			Editbox* timeFormat = static_cast<Editbox*>(winMgr.getWindow("TimeFormat"));
			timeFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onDateTimeChanged, this));
			PushButton* dateTimeFormatButton = static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"));
			dateTimeFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onDateTimeFormatClicked, this));
 
			// Configure the spinner
			Spinner* spinner = static_cast<Spinner*>(winMgr.getWindow("Spinner"));
			spinner->subscribeEvent(Spinner::EventValueChanged, Event::Subscriber(&DemoSample::onSpinnerValueChanged, this));
			static_cast<Editbox*>(winMgr.getWindow(spinner->getName() + "__auto_editbox__"))->setReadOnly(true); // We cannot handle manually specified values yet
			spinner->setTextInputMode(Spinner::FloatingPoint); // FloatingPoint, Integer, Hexadecimal, Octal
			spinner->setMinimumValue(-10.0f);
			spinner->setMaximumValue(10.0f);
			spinner->setStepSize(0.02f);
			spinner->setCurrentValue(5.2f);
 
			// Configure the character widgets
			Editbox* characterValue = static_cast<Editbox*>(winMgr.getWindow("CharacterValue"));
			characterValue->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCharacterChanged, this));
			Editbox* characterFormat = static_cast<Editbox*>(winMgr.getWindow("CharacterFormat"));
			characterFormat->subscribeEvent(Editbox::EventTextChanged, Event::Subscriber(&DemoSample::onCharacterChanged, this));
			PushButton* characterFormatButton = static_cast<PushButton*>(winMgr.getWindow("CharacterFormatButton"));
			characterFormatButton->subscribeEvent(PushButton::EventClicked, Event::Subscriber(&DemoSample::onCharacterFormatClicked, this));
			EventArgs e;
			onCharacterFormatClicked(e);
 
			// Initialize the localise values
			RefreshLocalisedValues();
		}
		catch(Exception &e)
		{
			#if defined( __WIN32__ ) || defined( _WIN32 )
				MessageBox(NULL, e.getMessage().c_str(), "Error initializing the demo", MB_OK | MB_ICONERROR | MB_TASKMODAL);
			#else
				std::cerr << "Error initializing the demo:" << e.getMessage().c_str() << "\n";
			#endif
		}
 
		return true;
	}
 
    void cleanupSample(void)
	{
		CEGUI::FontManager::getSingleton().destroyAllFonts();
	}
private:
	ICU icu; // International Components for Unicode
 
	void RefreshLocalisedValues()
	{
		// Trigger changes in localised widgets to refresh their values accordingly
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		EventArgs eventArgs;
		static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->fireEvent(RadioButton::EventSelectStateChanged, eventArgs);
		static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
		static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
		static_cast<Spinner*>(winMgr.getWindow("Spinner"))->fireEvent(Spinner::EventValueChanged, eventArgs);
	}
 
	bool onLocaleChanged(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		String country = static_cast<Combobox*>(winMgr.getWindow("Countries"))->getText();
		String language = static_cast<Combobox*>(winMgr.getWindow("Languages"))->getText();
		if(icu.setLocale(language.c_str(), country.c_str()))
			RefreshLocalisedValues();
		else
			MessageBox(NULL,
						("Error with setLocale(" + language + ", " + country + ")").c_str(), 
						"Error",
						MB_OK | MB_ICONERROR | MB_TASKMODAL);
 
		return true;
	}
 
	bool onLoadEveryISO(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		ListboxTextItem* listboxTextItem;
		int32_t count;
 
		// Change the label of the button
		PushButton* everyISO = static_cast<PushButton*>(winMgr.getWindow("EveryISO"));
		everyISO->setText("This will take a few seconds...");
 
		// Display every ISO countries in a combobox
		Combobox* countries = static_cast<Combobox*>(winMgr.getWindow("Countries"));
		const char * const * listCountries = Locale::getISOCountries();
		for(count = 0; listCountries[count]; count++)
		{
			listboxTextItem = new ListboxTextItem(listCountries[count]);
			listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
			countries->addItem(listboxTextItem);
		}
		countries->setText(	Locale::getDefault().getCountry() );
 
		// Display every ISO languages in a combobox
		Combobox* languages = static_cast<Combobox*>(winMgr.getWindow("Languages"));
		const char * const * listLanguages = Locale::getISOLanguages();
		for(count = 0; listLanguages[count]; count++)
		{
			listboxTextItem = new ListboxTextItem(listLanguages[count]);
			listboxTextItem->setSelectionBrushImage("TaharezLook", "MultiListSelectionBrush");
			languages->addItem(listboxTextItem);
		}
		languages->setText(	Locale::getDefault().getLanguage() );
 
		// Remove this button since it is no longer useful
		everyISO->setEnabled(false);
		everyISO->setVisible(false);
 
		return true;
	}
 
	bool onCurrencyChanged(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		EventArgs eventArgs;
		static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->fireEvent(RadioButton::EventSelectStateChanged, eventArgs);
		return true;
	}
 
	bool onCurrencySelected(const CEGUI::EventArgs& e)
	{
		// Reformat the numeric value into a properly formatted currency
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
 
		// Set the currency
		CEGUI::uint id = static_cast<RadioButton*>(winMgr.getWindow("RadioCAD"))->getSelectedButtonInGroup()->getID();
		switch(id)
		{
		case 1:
			icu.setCurrency("CAD");
			break;
		case 2:
			icu.setCurrency("USD");
			break;
		case 3:
			icu.setCurrency("EUR");
			break;
		case 4:
			// Mauritania does not use a decimal division of units
			// and yet ICU still displays decimal units??
			icu.setCurrency("MRO");
			break;
		}
 
		CEGUI::String rawValue = static_cast<Editbox*>(winMgr.getWindow("CurrencyValue"))->getText();
		double currency = atof(rawValue.c_str());
		CEGUI::String formattedValue;
		if( icu.formatCurrency(currency, formattedValue) )
			static_cast<Editbox*>(winMgr.getWindow("CurrencyFormattedValue"))->setText(formattedValue);
 
		return true;
	}
 
	bool onNumericChanged(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		EventArgs eventArgs;
		static_cast<PushButton*>(winMgr.getWindow("NumericFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 
		return true;
	}
 
	bool onNumericFormatClicked(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		String formattedText;
		if( icu.formatNumber(static_cast<Editbox*>(winMgr.getWindow("NumericValue"))->getText(),
							static_cast<Editbox*>(winMgr.getWindow("NumericFormat"))->getText(),
							formattedText) )
			static_cast<Editbox*>(winMgr.getWindow("NumericFormattedValue"))->setText(formattedText);
 
		// Ordinal representation of an integer value
		double doubleValue = atof(static_cast<Editbox*>(winMgr.getWindow("NumericValue"))->getText().c_str());
		if( icu.numberToOrdinal((int) doubleValue, formattedText) )
			static_cast<Editbox*>(winMgr.getWindow("NumericFormattedOrdinal"))->setText(formattedText);
 
		// Textual representation of the numeric value
		// Note that this value must be raw, containing only digits and a decimal point
		// Since atof() is used to convert from a string to a double this numeric value is not localised
		if( icu.numberToText(doubleValue, formattedText) )
			static_cast<Editbox*>(winMgr.getWindow("NumericFormattedText"))->setText(formattedText);
 
		return true;
	}
 
	bool onDateTimeChanged(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		EventArgs eventArgs;
		static_cast<PushButton*>(winMgr.getWindow("DateTimeFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 
		return true;
	}
 
	bool onDateTimeFormatClicked(const CEGUI::EventArgs& e)
	{
		// Format the date and time according to their specified values and formats
		// In theory a format can include both the date and the time:  yyyy/MM/dd HH:mm:ss
		// However in practice the resulting time is not accurate (within 1 minute 47 seconds)
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		UDate localDate, localTime, gmtDate, gmtTime, gmtDateTime;
		if(icu.CeguiStringToDateTime(	static_cast<Editbox*>(winMgr.getWindow("DateValue"))->getText(),
										"yyyy/MM/dd", // Internal format
										localDate)
			&& icu.CeguiStringToDateTime(	static_cast<Editbox*>(winMgr.getWindow("TimeValue"))->getText(),
											"HH:mm:ss", // Internal format
											localTime) )
		{
			String formattedDate, formattedTime;
			if(icu.formatDateTime(	localDate,
									static_cast<Editbox*>(winMgr.getWindow("DateFormat"))->getText(),
									formattedDate)
				&& icu.formatDateTime(	localTime,
										static_cast<Editbox*>(winMgr.getWindow("TimeFormat"))->getText(),
										formattedTime)
				)
				static_cast<Editbox*>(winMgr.getWindow("LocalDateTimeFormattedValue"))->setText(formattedDate + " " + formattedTime);
 
			// GMT time
			if( icu.CeguiStringToDateTime(static_cast<Editbox*>(winMgr.getWindow("LocalDateTimeFormattedValue"))->getText(),
											"yyyy/MM/dd HH:mm:ss",
											gmtDateTime)
				&& icu.localToGmt(localDate, localTime, gmtDate, gmtTime)
				&& icu.formatDateTime(gmtDate, "yyyy/MM/dd", formattedDate)
				&& icu.formatDateTime(gmtTime, "HH:mm:ss", formattedTime)
				)
				static_cast<Editbox*>(winMgr.getWindow("GmtDateTimeFormattedValue"))->setText(formattedDate + " " + formattedTime);
		}
 
		return true;
	}
 
	bool onSpinnerValueChanged(const CEGUI::EventArgs& e)
	{
		// Reformat the displayed value into a locale appropriate format
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		Spinner* spinner = static_cast<Spinner*>(winMgr.getWindow("Spinner"));
 
		String formattedValue;
		if( icu.formatNumber(spinner->getCurrentValue(), "#0.00", formattedValue) )
		{
			// Disable the Editbox events, especially the EventTextChanged that
			// triggers Spinner::handleEditTextChange, which attempts to convert
			// the textual value into a non-localised numeric value
			Editbox* editbox = static_cast<Editbox*>(winMgr.getWindow(spinner->getName() + "__auto_editbox__"));
			bool muted = editbox->isMuted();
			editbox->setMutedState(true);
			spinner->setText(formattedValue);
			editbox->setMutedState(muted);
		}
 
		return true;
	}
 
	bool onCharacterChanged(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		EventArgs eventArgs;
		static_cast<PushButton*>(winMgr.getWindow("CharacterFormatButton"))->fireEvent(PushButton::EventClicked, eventArgs);
 
		return true;
	}
 
	bool onCharacterFormatClicked(const CEGUI::EventArgs& e)
	{
		using namespace CEGUI;
		WindowManager& winMgr = WindowManager::getSingleton();
		String formattedText;
		if( icu.formatText(static_cast<Editbox*>(winMgr.getWindow("CharacterValue"))->getText(),
							static_cast<Editbox*>(winMgr.getWindow("CharacterFormat"))->getText(),
							"*",
							formattedText) )
			static_cast<Editbox*>(winMgr.getWindow("CharacterFormattedValue"))->setText(formattedText);
 
		return true;
	}
 
};
 
#endif // _FormattedData_h_

main.cpp

#if defined( __WIN32__ ) || defined( _WIN32 )
	#define WIN32_LEAN_AND_MEAN
	#define NOMINMAX
	#include "windows.h"
#endif
 
#include "FormattedNumericData.h"
 
#if defined( __WIN32__ ) || defined( _WIN32 )
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nCmdShow)
#else
int main(int argc, char *argv[])
#endif
{
    DemoSample app;
    return app.run();
}

FormattedData.layout

<?xml version="1.0" encoding="UTF-8"?>
 
<GUILayout >
    <Window Type="DefaultWindow" Name="Root" >
        <Property Name="InheritsAlpha" Value="False" />
        <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
        <Property Name="UnifiedAreaRect" Value="{{0,0},{0,0},{1,0},{1,0}}" />
        <Window Type="TaharezLook/FrameWindow" Name="FrameWindow" >
            <Property Name="TitlebarFont" Value="Commonwealth-10" />
            <Property Name="CaptionColour" Value="00FFFFFF" />
            <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
            <Property Name="TitlebarEnabled" Value="True" />
            <Property Name="UnifiedAreaRect" Value="{{0.07125,0},{0.013335,0},{0.910001,0},{0.873733,0}}" />
            <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseTarget" />
            <Window Type="TaharezLook/Spinner" Name="Spinner" >
                <Property Name="Text" Value="10.00" />
                <Property Name="StepSize" Value="1" />
                <Property Name="CurrentValue" Value="10" />
                <Property Name="MaximumValue" Value="32767" />
                <Property Name="MinimumValue" Value="-32768" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.8473,0},{0.731682,0},{0.951964,0},{0.790123,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="SpinnerLabel" >
                <Property Name="Text" Value="Localised Spinner widget with 2 decimals:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.395457,0},{0.731682,0},{0.836346,0},{0.790123,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="CurrencyFormattedValue" >
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.203821,0},{0.954642,0},{0.262262,0}}" />
            </Window>
            <Window Type="TaharezLook/RadioButton" Name="RadioCAD" >
                <Property Name="Text" Value="Canadian (CAD)" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.158378,0},{0.614153,0},{0.187144,0}}" />
            </Window>
            <Window Type="TaharezLook/RadioButton" Name="RadioMRO" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Mauritania (MRO)" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.26902,0},{0.614153,0},{0.297787,0}}" />
            </Window>
            <Window Type="TaharezLook/RadioButton" Name="RadioUSD" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="US (USD)" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.195183,0},{0.5605,0},{0.22395,0}}" />
            </Window>
            <Window Type="TaharezLook/RadioButton" Name="RadioEUR" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Euro (EUR)" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.430655,0},{0.232215,0},{0.5605,0},{0.260983,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="CountriesLabel" >
                <Property Name="Text" Value="Countries:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.022504,0},{0.069049,0},{0.132414,0},{0.117804,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="LanguagesLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Languages:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.268405,0},{0.069049,0},{0.385766,0},{0.117804,0}}" />
            </Window>
            <Window Type="TaharezLook/Combobox" Name="Countries" >
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.142027,0},{0.069049,0},{0.245976,0},{0.902039,0}}" />
                <Property Name="MaxEditTextLength" Value="1073741823" />
            </Window>
            <Window Type="TaharezLook/Combobox" Name="Languages" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.395379,0},{0.069049,0},{0.499329,0},{0.90204,0}}" />
                <Property Name="MaxEditTextLength" Value="1073741823" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="Separator1" >
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.141452,0},{0.995305,0},{0.150048,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="Separator2" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.309408,0},{0.995305,0},{0.318004,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="Separator3" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.553045,0},{0.995305,0},{0.561642,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="Separator5" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.007601,0},{0.814139,0},{0.989344,0},{0.822736,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="NumericFormatLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Format:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.403136,0},{0.141946,0},{0.461576,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="NumericFormat" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="#,##0.03" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.403136,0},{0.421111,0},{0.461576,0}}" />
            </Window>
            <Window Type="TaharezLook/Button" Name="NumericFormatButton" >
                <Property Name="Text" Value="Apply" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.370638,0},{0.603204,0},{0.429079,0}}" />
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.339418,0},{0.954642,0},{0.397859,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="DateFormatLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Format:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.63711,0},{0.141946,0},{0.695551,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="DateFormat" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="yyyy/MM/dd" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.63711,0},{0.269099,0},{0.695551,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="DateValueLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Date:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.574987,0},{0.141946,0},{0.633428,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="DateValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="2006/05/14" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.574987,0},{0.267608,0},{0.633428,0}}" />
            </Window>
            <Window Type="TaharezLook/Button" Name="DateTimeFormatButton" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Apply" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.610743,0},{0.603205,0},{0.669184,0}}" />
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="LocalDateTimeFormattedValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.724067,0},{0.578862,0},{0.954642,0},{0.637303,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="TimeValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="12:00:01" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.351492,0},{0.574987,0},{0.441976,0},{0.633428,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="TimeValueLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Time:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.280703,0},{0.574987,0},{0.347609,0},{0.633428,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="TimeFormat" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="HH:mm:ss" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.281446,0},{0.63711,0},{0.441976,0},{0.695551,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="NumericValueLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Numeric:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.339418,0},{0.141946,0},{0.397857,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="NumericValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="123456.78" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.339418,0},{0.421111,0},{0.397858,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="CurrencyValueLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Currency:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.203821,0},{0.141946,0},{0.26226,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="CurrencyValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="123456.78" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.203821,0},{0.421111,0},{0.262262,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="Separator4" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.013562,0},{0.702202,0},{0.995305,0},{0.710798,0}}" />
            </Window>
            <Window Type="TaharezLook/Button" Name="EveryISO" >
                <Property Name="Text" Value="Load every country and languages" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.559611,0},{0.069049,0},{0.94523,0},{0.12749,0}}" />
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedText" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.153278,0},{0.482989,0},{0.954642,0},{0.54143,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="CharacterFormattedValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.883872,0},{0.954642,0},{0.942313,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="CharacterFormat" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="000 000 000" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.908716,0},{0.421111,0},{0.967156,0}}" />
            </Window>
            <Window Type="TaharezLook/Button" Name="CharacterFormatButton" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Apply" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.473919,0},{0.883872,0},{0.603205,0},{0.942313,0}}" />
                <Property Name="MouseCursorImage" Value="set:TaharezLook image:MouseArrow" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="CharacterValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="123456" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.150298,0},{0.844998,0},{0.421111,0},{0.903438,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="LocalDateTimeFormattedLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Local:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.641358,0},{0.578862,0},{0.718697,0},{0.637302,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="GmtDateTimeFormattedLabel" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="GMT:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.641358,0},{0.63711,0},{0.718697,0},{0.69555,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="GmtDateTimeFormattedValue" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.725558,0},{0.63711,0},{0.954642,0},{0.695551,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="NumericValueLabel1" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Numeric:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.844998,0},{0.141946,0},{0.903437,0}}" />
            </Window>
            <Window Type="TaharezLook/StaticText" Name="NumericFormatLabel1" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="Text" Value="Format:" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.036292,0},{0.908716,0},{0.141946,0},{0.967156,0}}" />
            </Window>
            <Window Type="TaharezLook/Editbox" Name="NumericFormattedOrdinal" >
                <Property Name="Font" Value="Commonwealth-10" />
                <Property Name="ReadOnly" Value="True" />
                <Property Name="MaxTextLength" Value="1073741823" />
                <Property Name="UnifiedMaxSize" Value="{{1,0},{1,0}}" />
                <Property Name="UnifiedAreaRect" Value="{{0.616765,0},{0.403136,0},{0.954642,0},{0.461577,0}}" />
            </Window>
        </Window>
    </Window>
</GUILayout>