using decimal point &c.
TODO: Currently only the character classification and transformation
- functions are implemented, we also need at least
- - numbers: atof_l(), strtod_l() &c
+ functions and number <-> string functions, are implemented,
+ we also need at least
- formatted IO: scanf_l(), printf_l() &c
- time: strftime_l(), strptime_l()
*/
#include <locale.h>
#include <xlocale.h>
#include <ctype.h>
+ #include <stdlib.h>
#if wxUSE_UNICODE
#include <wctype.h>
// Get the type
wxXLocale_t Get() const { return m_locale; }
+ bool operator== (const wxXLocale& loc) const
+ { return m_locale == loc.m_locale; }
+
private:
// Special ctor for the "C" locale, it's only used internally as the user
// code is supposed to use GetCLocale()
// A shorter synonym for the most commonly used locale object
#define wxCLocale (wxXLocale::GetCLocale())
-
+extern WXDLLIMPEXP_DATA_BASE(wxXLocale) wxNullXLocale;
// Wrappers for various functions:
#ifdef wxHAS_XLOCALE_SUPPORT
inline int wxToupper_l(char c, const wxXLocale& loc)
{ return wxCRT_Toupper_lA(static_cast<unsigned char>(c), loc.Get()); }
+
+ // stdlib functions for numeric <-> string conversion
+ // NOTE: GNU libc does not have ato[fil]_l functions;
+ // MSVC++8 does not have _strto[u]ll_l functions;
+ // thus we take the minimal set of functions provided in both environments:
+
+ #define wxCRT_Strtod_lA wxXLOCALE_IDENT(strtod_l)
+ #define wxCRT_Strtol_lA wxXLOCALE_IDENT(strtol_l)
+ #define wxCRT_Strtoul_lA wxXLOCALE_IDENT(strtoul_l)
+
+ inline double wxStrtod_lA(const char *c, char **endptr, const wxXLocale& loc)
+ { return wxCRT_Strtod_lA(c, endptr, loc.Get()); }
+ inline long wxStrtol_lA(const char *c, char **endptr, int base, const wxXLocale& loc)
+ { return wxCRT_Strtol_lA(c, endptr, base, loc.Get()); }
+ inline unsigned long wxStrtoul_lA(const char *c, char **endptr, int base, const wxXLocale& loc)
+ { return wxCRT_Strtoul_lA(c, endptr, base, loc.Get()); }
+
#if wxUSE_UNICODE
+
+ // ctype functions
#define wxCRT_Isalnum_lW wxXLOCALE_IDENT(iswalnum_l)
#define wxCRT_Isalpha_lW wxXLOCALE_IDENT(iswalpha_l)
#define wxCRT_Iscntrl_lW wxXLOCALE_IDENT(iswcntrl_l)
inline wchar_t wxToupper_l(wchar_t c, const wxXLocale& loc)
{ return wxCRT_Toupper_lW(c, loc.Get()); }
+
+ // stdlib functions for numeric <-> string conversion
+ // (see notes above about missing functions)
+ #define wxCRT_Strtod_lW wxXLOCALE_IDENT(wcstod_l)
+ #define wxCRT_Strtol_lW wxXLOCALE_IDENT(wcstol_l)
+ #define wxCRT_Strtoul_lW wxXLOCALE_IDENT(wcstoul_l)
+
+ inline double wxStrtod_l(const wchar_t *c, wchar_t **endptr, const wxXLocale& loc)
+ { return wxCRT_Strtod_lW(c, endptr, loc.Get()); }
+ inline long wxStrtol_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc)
+ { return wxCRT_Strtol_lW(c, endptr, base, loc.Get()); }
+ inline unsigned long wxStrtoul_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc)
+ { return wxCRT_Strtoul_lW(c, endptr, base, loc.Get()); }
+
#endif // wxUSE_UNICDE (ctype functions)
#else // !wxHAS_XLOCALE_SUPPORT
// ctype functions
number in a standard format it can use:
@code wxPrintf_l(wxXLocale::GetCLocale(), "%g", number) @endcode
to do it.
+
+ See @ref group_funcmacro_locale for a list of wxXLocale-enabled functions.
Conversely, if a program wanted to output the number in French locale, even if
the current locale is different, it could use wxXLocale(wxLANGUAGE_FRENCH).
@c wxUSE_XLOCALE was set to 0 during the library compilation.
- @section xlocale_func Locale-dependent functions
-
- Currently the following @c _l-functions are available:
- - Character classification functions:
- @c wxIsxxx_l(), e.g. @c wxIsalpha_l(), @c wxIslower_l() and all the others.
- - Character transformation functions:
- @c wxTolower_l() and @c wxToupper_l()
- We hope to provide many more functions (covering numbers, time and formatted
- IO) in the near future.
-
@library{wxbase}
@category{cfg}
+ @stdobjects
+ @li ::wxNullXLocale
+
@see wxLocale
*/
class wxXLocale
or @false otherwise.
*/
bool IsOk() const;
+
+ /**
+ Comparison operator.
+ */
+ bool operator==(const wxXLocale& loc) const;
};
+
+/**
+ An empty and invalid wxXLocale object.
+*/
+wxXLocale wxNullXLocale;
+
+
+
+// ============================================================================
+// Global functions/macros
+// ============================================================================
+
+/** @addtogroup group_funcmacro_locale */
+//@{
+
+int wxIsalnum_l(wchar_t c, const wxXLocale& loc);
+int wxIsalpha_l(wchar_t c, const wxXLocale& loc);
+int wxIscntrl_l(wchar_t c, const wxXLocale& loc);
+int wxIsdigit_l(wchar_t c, const wxXLocale& loc);
+int wxIsgraph_l(wchar_t c, const wxXLocale& loc);
+int wxIslower_l(wchar_t c, const wxXLocale& loc);
+int wxIsprint_l(wchar_t c, const wxXLocale& loc);
+int wxIspunct_l(wchar_t c, const wxXLocale& loc);
+int wxIsspace_l(wchar_t c, const wxXLocale& loc);
+int wxIsupper_l(wchar_t c, const wxXLocale& loc);
+int wxIsxdigit_l(wchar_t c, const wxXLocale& loc);
+wchar_t wxTolower_l(wchar_t c, const wxXLocale& loc);
+wchar_t wxToupper_l(wchar_t c, const wxXLocale& loc);
+
+double wxStrtod_l(const wchar_t *c, wchar_t **endptr, const wxXLocale& loc);
+long wxStrtol_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc);
+unsigned long wxStrtoul_l(const wchar_t *c, wchar_t **endptr, int base, const wxXLocale& loc);
+
+//@}
+
// This is the C locale object, it is created on demand
static wxXLocale *gs_cLocale = NULL;
+wxXLocale wxNullXLocale;
+
+
// ============================================================================
// implementation
// ============================================================================
void wxXLocale::Init(const char *loc)
{
+ if (!loc || *loc == '\0')
+ return;
+
m_locale = _create_locale(LC_ALL, loc);
}
void wxXLocale::Init(const char *loc)
{
+ if (!loc || *loc == '\0')
+ return;
+
m_locale = newlocale(LC_ALL_MASK, loc, NULL);
+ if (!m_locale)
+ {
+ // NOTE: here we do something similar to what wxSetLocaleTryUTF8() does
+ // in wxLocale code (but with newlocale() calls instead of wxSetlocale())
+ wxString buf(loc);
+ wxString buf2;
+ buf2 = buf + wxS(".UTF-8");
+ m_locale = newlocale(LC_ALL_MASK, buf2, NULL);
+ if ( !m_locale )
+ {
+ buf2 = buf + wxS(".utf-8");
+ m_locale = newlocale(LC_ALL_MASK, buf2, NULL);
+ }
+ if ( !m_locale )
+ {
+ buf2 = buf + wxS(".UTF8");
+ m_locale = newlocale(LC_ALL_MASK, buf2, NULL);
+ }
+ if ( !m_locale )
+ {
+ buf2 = buf + wxS(".utf8");
+ m_locale = newlocale(LC_ALL_MASK, buf2, NULL);
+ }
+ }
+
+ // TODO: wxLocale performs many more manipulations of the given locale
+ // string in the attempt to set a valid locale; reusing that code
+ // (changing it to take a generic wxTryLocale callback) would be nice
}
void wxXLocale::Free()
CPPUNIT_TEST_SUITE( XLocaleTestCase );
CPPUNIT_TEST( TestCtor );
CPPUNIT_TEST( TestCtypeFunctions );
+ CPPUNIT_TEST( TestStdlibFunctions );
CPPUNIT_TEST_SUITE_END();
void TestCtor();
void TestCtypeFunctions();
+ void TestStdlibFunctions();
void TestCtypeFunctionsWith(const wxXLocale& loc);
+ void TestStdlibFunctionsWith(const wxXLocale& loc);
DECLARE_NO_COPY_CLASS(XLocaleTestCase)
};
CPPUNIT_ASSERT( !wxXLocale().IsOk() );
CPPUNIT_ASSERT( wxCLocale.IsOk() );
CPPUNIT_ASSERT( wxXLocale("C").IsOk() );
+ CPPUNIT_ASSERT( !wxXLocale("bloordyblop").IsOk() );
+
+ if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH))
+ return; // you should have french support installed to continue this test!
+
#ifdef wxHAS_XLOCALE_SUPPORT
CPPUNIT_ASSERT( wxXLocale(wxLANGUAGE_FRENCH).IsOk() );
#ifdef __WXMSW__
CPPUNIT_ASSERT( wxXLocale("fr_FR").IsOk() );
#endif
#endif
- CPPUNIT_ASSERT( !wxXLocale("bloordyblop").IsOk() );
}
// test the ctype functions with the given locale
void XLocaleTestCase::TestCtypeFunctionsWith(const wxXLocale& loc)
{
+ // NOTE: here go the checks which must pass under _any_ locale "loc";
+ // checks for specific locales are in TestCtypeFunctions()
+
+
// isalnum
CPPUNIT_ASSERT( wxIsalnum_l('0', loc) );
CPPUNIT_ASSERT( wxIsalnum_l('9', loc) );
CPPUNIT_ASSERT_EQUAL( '9', (char)wxToupper_l('9', loc) );
}
+// test the stdlib functions with the given locale
+void XLocaleTestCase::TestStdlibFunctionsWith(const wxXLocale& loc)
+{
+ // NOTE: here go the checks which must pass under _any_ locale "loc";
+ // checks for specific locales are in TestStdlibFunctions()
+
+#if wxUSE_UNICODE
+ wchar_t* endptr;
+#else
+ char* endptr;
+#endif
+
+ // strtod (don't use decimal separator as it's locale-specific)
+ CPPUNIT_ASSERT_EQUAL( 0.0, wxStrtod_l(wxT("0"), NULL, loc) );
+ CPPUNIT_ASSERT_EQUAL( 1234.0, wxStrtod_l(wxT("1234"), NULL, loc) );
+
+ // strtol
+ endptr = NULL;
+ CPPUNIT_ASSERT_EQUAL( 100, wxStrtol_l(wxT("100"), NULL, 0, loc) );
+ CPPUNIT_ASSERT_EQUAL( 0xFF, wxStrtol_l(wxT("0xFF"), NULL, 0, loc) );
+ CPPUNIT_ASSERT_EQUAL( 2001, wxStrtol_l(wxT("2001 60c0c0 -1101110100110100100000 0x6fffff"), &endptr, 10, loc) );
+ CPPUNIT_ASSERT_EQUAL( 0x60c0c0, wxStrtol_l(endptr, &endptr, 16, loc) );
+ CPPUNIT_ASSERT_EQUAL( -0x374D20, wxStrtol_l(endptr, &endptr, 2, loc) );
+ CPPUNIT_ASSERT_EQUAL( 0x6fffff, wxStrtol_l(endptr, NULL, 0, loc) );
+
+ // strtoul
+ // NOTE: 3147483647 and 0x12A05F200 are greater than LONG_MAX (on 32bit machines) but
+ // smaller than ULONG_MAX
+ CPPUNIT_ASSERT_EQUAL( (unsigned long)3147483647, wxStrtoul_l(wxT("3147483647"), NULL, 0, loc) );
+ CPPUNIT_ASSERT_EQUAL( (unsigned long)0x12A05F200, wxStrtoul_l(wxT("0x12A05F200"), NULL, 0, loc) );
+
+ // TODO: test for "failure" behaviour of the functions above
+}
+
void XLocaleTestCase::TestCtypeFunctions()
{
TestCtypeFunctionsWith(wxCLocale);
#ifdef wxHAS_XLOCALE_SUPPORT
+
+ // french
+
+ if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH))
+ return; // you should have french support installed to continue this test!
+
wxXLocale locFR(wxLANGUAGE_FRENCH);
CPPUNIT_ASSERT( locFR.IsOk() ); // doesn't make sense to continue otherwise
TestCtypeFunctionsWith(locFR);
- CPPUNIT_ASSERT( wxIsalpha_l('\xe9', locFR) );
- CPPUNIT_ASSERT( wxIslower_l('\xe9', locFR) );
- CPPUNIT_ASSERT( !wxIslower_l('\xc9', locFR) );
- CPPUNIT_ASSERT( wxIsupper_l('\xc9', locFR) );
+ CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe9'), locFR) );
+ CPPUNIT_ASSERT( wxIslower_l(wxT('\xe9'), locFR) );
+ CPPUNIT_ASSERT( !wxIslower_l(wxT('\xc9'), locFR) );
+ CPPUNIT_ASSERT( wxIsupper_l(wxT('\xc9'), locFR) );
+ CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe7'), locFR) );
+ CPPUNIT_ASSERT( wxIslower_l(wxT('\xe7'), locFR) );
+ CPPUNIT_ASSERT( wxIsupper_l(wxT('\xc7'), locFR) );
+
+
+ // italian
+
+ if (!wxLocale::IsAvailable(wxLANGUAGE_ITALIAN))
+ return; // you should have italian support installed to continue this test!
+
+ wxXLocale locIT(wxLANGUAGE_ITALIAN);
+ CPPUNIT_ASSERT( locIT.IsOk() ); // doesn't make sense to continue otherwise
+
+ TestCtypeFunctionsWith(locIT);
+
+ CPPUNIT_ASSERT( wxIsalpha_l(wxT('\xe1'), locIT) );
+ CPPUNIT_ASSERT( wxIslower_l(wxT('\xe1'), locIT) );
+#endif
+}
+
+void XLocaleTestCase::TestStdlibFunctions()
+{
+ TestStdlibFunctionsWith(wxCLocale);
+
+#if wxUSE_UNICODE
+ wchar_t* endptr;
+#else
+ char* endptr;
+#endif
+
+ // strtod checks specific for C locale
+ endptr = NULL;
+ CPPUNIT_ASSERT_EQUAL( 0.0, wxStrtod_l(wxT("0.000"), NULL, wxCLocale) );
+ CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1.234"), NULL, wxCLocale) );
+ CPPUNIT_ASSERT_EQUAL( -1.234E-5, wxStrtod_l(wxT("-1.234E-5"), NULL, wxCLocale) );
+ CPPUNIT_ASSERT_EQUAL( 365.24, wxStrtod_l(wxT("365.24 29.53"), &endptr, wxCLocale) );
+ CPPUNIT_ASSERT_EQUAL( 29.53, wxStrtod_l(endptr, NULL, wxCLocale) );
+
+#ifdef wxHAS_XLOCALE_SUPPORT
+
+ // french
+
+ if (!wxLocale::IsAvailable(wxLANGUAGE_FRENCH))
+ return; // you should have french support installed to continue this test!
+
+ wxXLocale locFR(wxLANGUAGE_FRENCH);
+ CPPUNIT_ASSERT( locFR.IsOk() ); // doesn't make sense to continue otherwise
+
+ TestCtypeFunctionsWith(locFR);
+
+ // comma as decimal point:
+ CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1,234"), NULL, locFR) );
+
+ // space as thousands separator is not recognized by wxStrtod_l():
+ CPPUNIT_ASSERT( 1234.5 != wxStrtod_l(wxT("1 234,5"), NULL, locFR) );
+
+
+ // italian
+
+ if (!wxLocale::IsAvailable(wxLANGUAGE_ITALIAN))
+ return; // you should have italian support installed to continue this test!
+
+ wxXLocale locIT(wxLANGUAGE_ITALIAN);
+ CPPUNIT_ASSERT( locIT.IsOk() ); // doesn't make sense to continue otherwise
+
+ TestStdlibFunctionsWith(locIT);
+
+ // comma as decimal point:
+ CPPUNIT_ASSERT_EQUAL( 1.234, wxStrtod_l(wxT("1,234"), NULL, locIT) );
+
+ // dot as thousands separator is not recognized by wxStrtod_l():
+ CPPUNIT_ASSERT( 1234.5 != wxStrtod_l(wxT("1.234,5"), NULL, locIT) );
#endif
}