diff --git a/configure b/configure index 3a748e2..77232dc 100755 --- a/configure +++ b/configure @@ -779,6 +779,7 @@ CFG_PULSEAUDIO=auto CFG_COREWLAN=auto CFG_ICD=auto CFG_NOPROCESS=no +CFG_ICU=no # initalize variables used for installation QT_INSTALL_PREFIX= @@ -940,7 +941,7 @@ while [ "$#" -gt 0 ]; do VAL=no ;; #Qt style yes options - -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-xinput2|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-declarative|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles) + -incremental|-qvfb|-profile|-shared|-static|-sm|-xinerama|-xshape|-xsync|-xinput|-xinput2|-egl|-reduce-exports|-pch|-separate-debug-info|-stl|-freetype|-xcursor|-xfixes|-xrandr|-xrender|-mitshm|-fontconfig|-xkb|-nis|-qdbus|-dbus|-dbus-linked|-glib|-gstreamer|-gtkstyle|-cups|-iconv|-largefile|-h|-help|-v|-verbose|-debug|-release|-fast|-accessibility|-confirm-license|-gnumake|-framework|-qt3support|-debug-and-release|-exceptions|-cocoa|-carbon|-universal|-prefix-install|-silent|-armfpa|-optimized-qmake|-dwarf2|-reduce-relocations|-sse|-openssl|-openssl-linked|-ptmalloc|-xmlpatterns|-phonon|-phonon-backend|-multimedia|-audio-backend|-svg|-declarative|-javascript-jit|-script|-scripttools|-rpath|-force-pkg-config|-s60|-usedeffiles|-icu) VAR=`echo $1 | sed "s,^-\(.*\),\1,"` VAL=yes ;; @@ -2242,6 +2243,13 @@ while [ "$#" -gt 0 ]; do QT_CFLAGS_FPU=$VAL fi ;; + icu) + if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then + CFG_ICU="$VAL" + else + UNKNOWN_OPT=yes + fi + ;; *) UNKNOWN_OPT=yes ;; @@ -7040,6 +7048,10 @@ if [ "$CFG_ICD" = "yes" ]; then QT_CONFIG="$QT_CONFIG icd" fi +if [ "$CFG_ICU" = "yes" ]; then + QT_CONFIG="$QT_CONFIG icu" +fi + # # Some Qt modules are too advanced in C++ for some old compilers # Detect here the platforms where they are known to work. diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index 7cdc256..11a85d5 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -57,6 +57,12 @@ QT_BEGIN_NAMESPACE +#ifdef QT_USE_ICU +// from qlocale_icu.cpp +extern uint qt_u_toUpper(uint c); +extern uint qt_u_toLower(uint c); +#endif + #ifndef QT_NO_CODEC_FOR_C_STRINGS # ifdef QT_NO_TEXTCODEC # define QT_NO_CODEC_FOR_C_STRINGS @@ -1076,6 +1082,12 @@ QChar::UnicodeVersion QChar::unicodeVersion(ushort ucs2) */ QChar QChar::toLower() const { +#ifdef QT_USE_ICU + uint res = qt_u_toLower(ucs); + if (res) + return QChar(res); + // else fall through +#endif const QUnicodeTables::Properties *p = qGetProp(ucs); if (!p->lowerCaseSpecial) return ucs + p->lowerCaseDiff; @@ -1090,6 +1102,12 @@ QChar QChar::toLower() const */ uint QChar::toLower(uint ucs4) { +#ifdef QT_USE_ICU + uint res = qt_u_toLower(ucs4); + if (res) + return res; + // else fall through +#endif if (ucs4 > UNICODE_LAST_CODEPOINT) return ucs4; const QUnicodeTables::Properties *p = qGetProp(ucs4); @@ -1106,6 +1124,12 @@ uint QChar::toLower(uint ucs4) */ ushort QChar::toLower(ushort ucs2) { +#ifdef QT_USE_ICU + uint res = qt_u_toLower(ucs2); + if (res) + return res & 0xffff; + // else fall through +#endif const QUnicodeTables::Properties *p = qGetProp(ucs2); if (!p->lowerCaseSpecial) return ucs2 + p->lowerCaseDiff; @@ -1118,6 +1142,12 @@ ushort QChar::toLower(ushort ucs2) */ QChar QChar::toUpper() const { +#ifdef QT_USE_ICU + uint res = qt_u_toUpper(ucs); + if (res) + return QChar(res); + // else fall through +#endif const QUnicodeTables::Properties *p = qGetProp(ucs); if (!p->upperCaseSpecial) return ucs + p->upperCaseDiff; @@ -1132,6 +1162,12 @@ QChar QChar::toUpper() const */ uint QChar::toUpper(uint ucs4) { +#ifdef QT_USE_ICU + uint res = qt_u_toUpper(ucs4); + if (res) + return res; + // else fall through +#endif if (ucs4 > UNICODE_LAST_CODEPOINT) return ucs4; const QUnicodeTables::Properties *p = qGetProp(ucs4); @@ -1148,6 +1184,12 @@ uint QChar::toUpper(uint ucs4) */ ushort QChar::toUpper(ushort ucs2) { +#ifdef QT_USE_ICU + uint res = qt_u_toUpper(ucs2); + if (res) + return res & 0xffff; + // else fall through +#endif const QUnicodeTables::Properties *p = qGetProp(ucs2); if (!p->upperCaseSpecial) return ucs2 + p->upperCaseDiff; diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 6515edb..5470048 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -134,6 +134,10 @@ void qt_symbianUpdateSystemPrivate(); void qt_symbianInitSystemLocale(); #endif +#ifdef QT_USE_ICU +extern bool qt_initIcu(const QString &localeName); +#endif + /****************************************************************************** ** Helpers for accessing Qt locale database */ @@ -2346,6 +2350,10 @@ void QLocale::setDefault(const QLocale &locale) { default_lp = locale.d(); default_number_options = locale.numberOptions(); + +#ifdef QT_USE_ICU + qt_initIcu(locale.name()); +#endif } /*! diff --git a/src/corelib/tools/qlocale_icu.cpp b/src/corelib/tools/qlocale_icu.cpp new file mode 100644 index 0000000..7fe3801 --- /dev/null +++ b/src/corelib/tools/qlocale_icu.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" +#include "qlibrary.h" +#include "qdebug.h" + +#include "unicode/uversion.h" +#include "unicode/ucol.h" + +QT_BEGIN_NAMESPACE + +typedef UCollator *(*Ptr_ucol_open)(const char *loc, UErrorCode *status); +typedef void (*Ptr_ucol_close)(UCollator *coll); +typedef UCollationResult (*Ptr_ucol_strcoll)(const UCollator *coll, const UChar *source, int32_t sourceLength, const UChar *target, int32_t targetLength); +typedef int32_t (*Ptr_u_strToCase)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); + +static Ptr_ucol_open ptr_ucol_open = 0; +static Ptr_ucol_strcoll ptr_ucol_strcoll = 0; +static Ptr_ucol_close ptr_ucol_close = 0; +static Ptr_u_strToCase ptr_u_strToUpper = 0; +static Ptr_u_strToCase ptr_u_strToLower = 0; + +enum LibLoadStatus +{ + ErrorLoading = -1, + NotLoaded = 0, + Loaded = 1 +}; + +static LibLoadStatus status = NotLoaded; + +static UCollator *icuCollator = 0; + +#define STRINGIFY2(x) #x +#define STRINGIFY(x) STRINGIFY2(x) + +bool qt_initIcu(const QString &localeString) +{ + if (status == ErrorLoading) + return false; + + if (status == NotLoaded) { + + // resolve libicui18n + QLibrary lib(QLatin1String("icui18n"), QLatin1String(U_ICU_VERSION_SHORT)); + if (!lib.load()) { + qWarning() << "Unable to load library icui18n" << lib.errorString(); + status = ErrorLoading; + return false; + } + + ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open"); + ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close"); + ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll"); + + if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { + // try again with decorated symbol names + ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open" STRINGIFY(U_ICU_VERSION_SUFFIX)); + ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close" STRINGIFY(U_ICU_VERSION_SUFFIX)); + ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll" STRINGIFY(U_ICU_VERSION_SUFFIX)); + } + + if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { + ptr_ucol_open = 0; + ptr_ucol_close = 0; + ptr_ucol_strcoll = 0; + + qWarning("Unable to find symbols in icui18n"); + status = ErrorLoading; + return false; + } + + // resolve libicuuc + QLibrary ucLib(QLatin1String("icuuc"), QLatin1String(U_ICU_VERSION_SHORT)); + if (!ucLib.load()) { + qWarning() << "Unable to load library icuuc" << ucLib.errorString(); + status = ErrorLoading; + return false; + } + + ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper"); + ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower"); + + if (!ptr_u_strToUpper || !ptr_u_strToLower) { + ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper" STRINGIFY(U_ICU_VERSION_SUFFIX)); + ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower" STRINGIFY(U_ICU_VERSION_SUFFIX)); + } + + if (!ptr_u_strToUpper || !ptr_u_strToLower) { + ptr_u_strToUpper = 0; + ptr_u_strToLower = 0; + + qWarning("Unable to find symbols in icuuc"); + status = ErrorLoading; + return false; + } + + // success :) + status = Loaded; + } + + if (icuCollator) { + ptr_ucol_close(icuCollator); + icuCollator = 0; + } + + UErrorCode icuStatus = U_ZERO_ERROR; + icuCollator = ptr_ucol_open(localeString.toLatin1().constData(), &icuStatus); + + if (!icuCollator) { + qWarning("Unable to open locale %s in ICU, error code %d", qPrintable(localeString), icuStatus); + return false; + } + + return true; +} + +bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result) +{ + Q_ASSERT(result); + Q_ASSERT(source); + Q_ASSERT(target); + + if (!icuCollator) + return false; + + *result = ptr_ucol_strcoll(icuCollator, reinterpret_cast(source), int32_t(sourceLength), + reinterpret_cast(target), int32_t(targetLength)); + + return true; +} + +// caseFunc can either be u_strToUpper or u_strToLower +static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &locale, Ptr_u_strToCase caseFunc) +{ + Q_ASSERT(out); + + if (!icuCollator) + return false; + + QString result(str.size(), Qt::Uninitialized); + + UErrorCode status = U_ZERO_ERROR; + + int32_t size = caseFunc(reinterpret_cast(result.data()), result.size(), + reinterpret_cast(str.constData()), str.size(), + locale.name().toLatin1().constData(), &status); + + if (U_FAILURE(status)) + return false; + + if (size < result.size()) { + result.resize(size); + } else if (size > result.size()) { + qDebug() << "RESULT SIZE MISMATCH"; + // the resulting string is larger than our source string + result.resize(size); + + status = U_ZERO_ERROR; + size = caseFunc(reinterpret_cast(result.data()), result.size(), + reinterpret_cast(str.constData()), str.size(), + locale.name().toLatin1().constData(), &status); + + if (U_FAILURE(status)) + return false; + + // if the sizes don't match now, we give up. + if (size != result.size()) + return false; + } + + *out = result; + return true; +} + +bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale) +{ + return qt_u_strToCase(str, out, locale, ptr_u_strToUpper); +} + +bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale) +{ + return qt_u_strToCase(str, out, locale, ptr_u_strToLower); +} + +uint qt_u_toUpper(uint c) +{ + if (c > 0xffff) + return 0; + + QString out; + if (qt_u_strToUpper(QChar(c), &out, QLocale()) && !out.isEmpty()) + return out.at(0).unicode(); + return 0; +} + +uint qt_u_toLower(uint c) +{ + if (c > 0xffff) + return 0; + + QString out; + if (qt_u_strToLower(QChar(c), &out, QLocale()) && !out.isEmpty()) + return out.at(0).unicode(); + return 0; +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index d641d74..0f5cee1 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -112,6 +112,13 @@ int qFindString(const QChar *haystack, int haystackLen, int from, int qFindStringBoyerMoore(const QChar *haystack, int haystackLen, int from, const QChar *needle, int needleLen, Qt::CaseSensitivity cs); +#ifdef QT_USE_ICU +// qlocale_icu.cpp +extern bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result); +extern bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale); +extern bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale); +#endif + // Unicode case-insensitive comparison static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be) @@ -4808,6 +4815,14 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, TPtrC p2 = TPtrC16(reinterpret_cast(data2), length2); return p1.CompareC(p2); #elif defined(Q_OS_UNIX) +# if defined(QT_USE_ICU) + int res; + if (qt_ucol_strcoll(data1, length1, data2, length2, &res)) { + if (res == 0) + res = ucstrcmp(data1, length1, data2, length2); + return res; + } // else fall through +# endif // declared in int delta = strcoll(toLocal8Bit_helper(data1, length1), toLocal8Bit_helper(data2, length2)); if (delta == 0) @@ -4943,6 +4958,15 @@ QString QString::toLower() const if (!d->size) return *this; +#ifdef QT_USE_ICU + { + QString result; + if (qt_u_strToLower(*this, &result, QLocale())) + return result; + // else fall through and use Qt's toUpper + } +#endif + const ushort *e = d->data + d->size; // this avoids one out of bounds check in the loop @@ -5034,6 +5058,15 @@ QString QString::toUpper() const if (!d->size) return *this; +#ifdef QT_USE_ICU + { + QString result; + if (qt_u_strToUpper(*this, &result, QLocale())) + return result; + // else fall through and use Qt's toUpper + } +#endif + const ushort *e = d->data + d->size; // this avoids one out of bounds check in the loop diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index d81ab04..46c39f3 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -92,6 +92,11 @@ else:SOURCES += tools/qelapsedtimer_generic.cpp contains(QT_CONFIG, zlib):include($$PWD/../../3rdparty/zlib.pri) else:include($$PWD/../../3rdparty/zlib_dependency.pri) +contains(QT_CONFIG,icu) { + SOURCES += tools/qlocale_icu.cpp + DEFINES += QT_USE_ICU +} + DEFINES += HB_EXPORT=Q_CORE_EXPORT INCLUDEPATH += ../3rdparty/harfbuzz/src HEADERS += ../3rdparty/harfbuzz/src/harfbuzz.h diff --git a/tests/auto/qchar/qchar.pro b/tests/auto/qchar/qchar.pro index 3813e4e..d991365 100644 --- a/tests/auto/qchar/qchar.pro +++ b/tests/auto/qchar/qchar.pro @@ -13,3 +13,5 @@ symbian: { } else { DEFINES += SRCDIR=\\\"$$PWD/\\\" } + +contains(QT_CONFIG,icu):DEFINES += QT_USE_ICU diff --git a/tests/auto/qchar/tst_qchar.cpp b/tests/auto/qchar/tst_qchar.cpp index 45dd7eb..514a836 100644 --- a/tests/auto/qchar/tst_qchar.cpp +++ b/tests/auto/qchar/tst_qchar.cpp @@ -103,7 +103,8 @@ tst_QChar::~tst_QChar() { } void tst_QChar::initTestCase() -{ } +{ +} void tst_QChar::cleanupTestCase() { } @@ -114,6 +115,7 @@ void tst_QChar::init() int argc = 0; app = new QCoreApplication(argc, NULL); #endif + QLocale::setDefault(QLocale::C); } void tst_QChar::cleanup() @@ -143,9 +145,36 @@ void tst_QChar::toUpper() QVERIFY(QChar::toUpper((uint)0x1c8) == 0x1c7); QVERIFY(QChar::toUpper((uint)0x1c9) == 0x1c7); - QVERIFY(QChar::toUpper((uint)0x10400) == 0x10400); - QVERIFY(QChar::toUpper((uint)0x10428) == 0x10400); + QCOMPARE(QChar::toUpper((uint)0x10400), 0x10400u); + QCOMPARE(QChar::toUpper((uint)0x10428), 0x10400u); + +#ifdef QT_USE_ICU + QCOMPARE(QChar('i').toUpper(), QChar('I')); + QCOMPARE(QChar::toUpper(ushort('i')), ushort('I')); + QCOMPARE(QChar::toUpper(uint('i')), uint('I')); + + QLocale::setDefault(QLocale(QLocale::Turkish, QLocale::Turkey)); + + // in Turkish locale, upper-caseing a 'i' will result in a capital I plus dot (0x0130) + QCOMPARE(QChar('i').toUpper(), QChar(0x0130u)); + QCOMPARE(QChar::toUpper(ushort('i')), ushort(0x0130u)); + QCOMPARE(QChar::toUpper(uint('i')), 0x0130u); + + // upper-casing a lower-case i without dot (0x0131) will result in 'I' + QCOMPARE(QChar(0x0131u).toUpper(), QChar('I')); + QCOMPARE(QChar::toUpper(ushort(0x0131u)), ushort('I')); + QCOMPARE(QChar::toUpper(uint(0x0131u)), uint('I')); + + // upper-casing an upper-case 0x0130 should do nothing + QCOMPARE(QChar(0x0130u).toUpper(), QChar(0x0130u)); + QCOMPARE(QChar::toUpper(ushort(0x0130u)), ushort(0x0130u)); + QCOMPARE(QChar::toUpper(uint(0x0130u)), uint(0x0130u)); + // upper-casing an upper-case I should do nothing + QCOMPARE(QChar('I').toUpper(), QChar('I')); + QCOMPARE(QChar::toUpper(ushort('I')), ushort('I')); + QCOMPARE(QChar::toUpper(uint('I')), uint('I')); +#endif } void tst_QChar::toLower() @@ -170,6 +199,34 @@ void tst_QChar::toLower() QVERIFY(QChar::toLower((uint)0x10400) == 0x10428); QVERIFY(QChar::toLower((uint)0x10428) == 0x10428); + +#ifdef QT_USE_ICU + QCOMPARE(QChar('I').toLower(), QChar('i')); + QCOMPARE(QChar::toLower(ushort('I')), ushort('i')); + QCOMPARE(QChar::toLower(uint('I')), uint('i')); + + QLocale::setDefault(QLocale(QLocale::Turkish, QLocale::Turkey)); + + // in turkish locale, lower-casing an 'I' will result with a lower-case i without dot (0x0131) + QCOMPARE(QChar('I').toLower(), QChar(0x0131u)); + QCOMPARE(QChar::toLower(ushort('I')), ushort(0x0131u)); + QCOMPARE(QChar::toLower(uint('I')), uint(0x0131u)); + + // lower-casing a captial I with dot (0x0130) will result in 'i' + QCOMPARE(QChar(0x0130u).toLower(), QChar('i')); + QCOMPARE(QChar::toLower(ushort(0x0130u)), ushort('i')); + QCOMPARE(QChar::toLower(uint(0x0130u)), uint('i')); + + // lower-casing a lower-case i should do nothing + QCOMPARE(QChar('i').toLower(), QChar('i')); + QCOMPARE(QChar::toLower(ushort('i')), ushort('i')); + QCOMPARE(QChar::toLower(uint('i')), uint('i')); + + // lower-casing a lower-case i without dot (0x0131) should do nothing + QCOMPARE(QChar(0x0131u).toLower(), QChar(0x0131u)); + QCOMPARE(QChar::toLower(ushort(0x0131u)), ushort(0x0131u)); + QCOMPARE(QChar::toLower(uint(0x0131u)), uint(0x0131u)); +#endif } void tst_QChar::toTitle() diff --git a/tests/auto/qstring/qstring.pro b/tests/auto/qstring/qstring.pro index ed758c6..92dd755 100644 --- a/tests/auto/qstring/qstring.pro +++ b/tests/auto/qstring/qstring.pro @@ -6,3 +6,5 @@ symbian:LIBS += -llibm QT = core DEFINES += QT_NO_CAST_TO_ASCII + +contains(QT_CONFIG,icu):DEFINES += QT_USE_ICU diff --git a/tests/auto/qstring/tst_qstring.cpp b/tests/auto/qstring/tst_qstring.cpp index ecae3d8..6baf9fc 100644 --- a/tests/auto/qstring/tst_qstring.cpp +++ b/tests/auto/qstring/tst_qstring.cpp @@ -210,6 +210,8 @@ private slots: void task262677remove(); void QTBUG10404_compareRef(); void QTBUG9281_arg_locale(); + + void toUpperLower_icu(); }; typedef QList IntList; @@ -1544,6 +1546,11 @@ void tst_QString::toUpper() QCOMPARE( lower.toUpper(), upper); +#ifdef QT_USE_ICU + // test doesn't work with ICU support, since QChar is unaware of any locale + QEXPECT_FAIL("", "test doesn't work with ICU support, since QChar is unaware of any locale", Continue); + QVERIFY(false); +#else for (int i = 0; i < 65536; ++i) { QString str(1, QChar(i)); QString upper = str.toUpper(); @@ -1551,6 +1558,7 @@ void tst_QString::toUpper() if (upper.length() == 1) QVERIFY(upper == QString(1, QChar(i).toUpper())); } +#endif } void tst_QString::toLower() @@ -1582,6 +1590,11 @@ void tst_QString::toLower() upper += QChar(QChar::lowSurrogate(0x10400)); QCOMPARE( upper.toLower(), lower); +#ifdef QT_USE_ICU + // test doesn't work with ICU support, since QChar is unaware of any locale + QEXPECT_FAIL("", "test doesn't work with ICU support, since QChar is unaware of any locale", Continue); + QVERIFY(false); +#else for (int i = 0; i < 65536; ++i) { QString str(1, QChar(i)); QString lower = str.toLower(); @@ -1589,6 +1602,7 @@ void tst_QString::toLower() if (lower.length() == 1) QVERIFY(str.toLower() == QString(1, QChar(i).toLower())); } +#endif } void tst_QString::trimmed() @@ -4200,6 +4214,8 @@ void tst_QString::localeAwareCompare() #elif defined (Q_WS_MAC) QSKIP("Setting the locale is not supported on OS X (you can set the C locale, but that won't affect CFStringCompare which is used to compare strings)", SkipAll); +#elif defined(QT_USE_ICU) + QLocale::setDefault(QLocale(locale)); #else if (!locale.isEmpty()) { const char *newLocale = setlocale(LC_ALL, locale.toLatin1()); @@ -4211,6 +4227,11 @@ void tst_QString::localeAwareCompare() } #endif +#ifdef QT_USE_ICU + // ### for c1, ICU disagrees with libc on how to compare + QEXPECT_FAIL("c1", "ICU disagrees with test", Abort); +#endif + int testres = QString::localeAwareCompare(s1, s2); if (result < 0) { QVERIFY(testres < 0); @@ -4912,6 +4933,40 @@ void tst_QString::QTBUG9281_arg_locale() QLocale::setDefault(QLocale::C); } +void tst_QString::toUpperLower_icu() +{ +#ifndef QT_USE_ICU + QSKIP("Qt was built without ICU support", QTest::SkipAll); +#endif + + QString s = QString::fromLatin1("i"); + + QCOMPARE(s.toUpper(), QString::fromLatin1("I")); + QCOMPARE(s.toLower(), QString::fromLatin1("i")); + + QLocale::setDefault(QLocale(QLocale::Turkish, QLocale::Turkey)); + + // turkish locale has a capital I with a dot (U+0130, utf8 c4b0) + + QCOMPARE(s.toUpper(), QString::fromUtf8("\xc4\xb0")); + QCOMPARE(QString::fromUtf8("\xc4\xb0").toLower(), s); + + // nothing should happen here + QCOMPARE(s.toLower(), s); + QCOMPARE(QString::fromLatin1("I").toUpper(), QString::fromLatin1("I")); + + // U+0131, utf8 c4b1 is the lower-case i without a dot + QString sup = QString::fromUtf8("\xc4\xb1"); + + QCOMPARE(sup.toUpper(), QString::fromLatin1("I")); + QCOMPARE(QString::fromLatin1("I").toLower(), sup); + + // nothing should happen here + QCOMPARE(sup.toLower(), sup); + QCOMPARE(QString::fromLatin1("i").toLower(), QString::fromLatin1("i")); + + // the cleanup function will restore the default locale +} QTEST_APPLESS_MAIN(tst_QString)