WinRT: Locale handling support

Provides locale-related codepaths for WinRT where existing Win32 API is
unsupported.

Change-Id: I35b83d6b208165b7660cac3c9b383cb6ba7e5cf9
Done-with: Maurice Kalinowski
Reviewed-by: Andrew Knight <andrew.knight@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
bb10
Kamil Trzcinski 2013-09-13 13:51:58 +03:00 committed by The Qt Project
parent 0a92295ca8
commit 8279a07c86
5 changed files with 209 additions and 29 deletions

View File

@ -125,9 +125,15 @@ int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) con
// comparing strings, the value 2 can be subtracted from a nonzero return value. Then, the
// meaning of <0, ==0, and >0 is consistent with the C runtime.
#ifndef Q_OS_WINRT
return CompareString(LOCALE_USER_DEFAULT, d->collator,
reinterpret_cast<const wchar_t*>(s1), len1,
reinterpret_cast<const wchar_t*>(s2), len2) - 2;
#else // !Q_OS_WINRT
return CompareStringEx(LOCALE_NAME_USER_DEFAULT, d->collator,
reinterpret_cast<LPCWSTR>(s1), len1,
reinterpret_cast<LPCWSTR>(s2), len2, NULL, NULL, 0) - 2;
#endif // Q_OS_WINRT
}
int QCollator::compare(const QString &str1, const QString &str2) const
@ -142,13 +148,31 @@ int QCollator::compare(const QStringRef &s1, const QStringRef &s2) const
QCollatorSortKey QCollator::sortKey(const QString &string) const
{
#ifndef Q_OS_WINRT
int size = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<const wchar_t*>(string.constData()), string.size(),
0, 0);
#elif defined(Q_OS_WINPHONE)
int size = 0;
Q_UNIMPLEMENTED();
#else // Q_OS_WINPHONE
int size = LCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<LPCWSTR>(string.constData()), string.size(),
0, 0, NULL, NULL, 0);
#endif // !Q_OS_WINPHONE
QString ret(size, Qt::Uninitialized);
#ifndef Q_OS_WINRT
int finalSize = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<const wchar_t*>(string.constData()), string.size(),
reinterpret_cast<wchar_t*>(ret.data()), ret.size());
#elif defined(Q_OS_WINPHONE)
int finalSize = 0;
#else // Q_OS_WINPHONE
int finalSize = LCMapStringEx(LOCALE_NAME_USER_DEFAULT, LCMAP_SORTKEY | d->collator,
reinterpret_cast<LPCWSTR>(string.constData()), string.size(),
reinterpret_cast<LPWSTR>(ret.data()), ret.size(),
NULL, NULL, 0);
#endif // !Q_OS_WINPHONE
if (finalSize == 0) {
qWarning() << "there were problems when generating the ::sortKey by LCMapStringW with error:" << GetLastError();
}

View File

@ -61,6 +61,9 @@
# ifdef Q_OS_WINCE
# include "qfunctions_wince.h"
# endif
# ifdef Q_OS_WINRT
# include "qfunctions_winrt.h"
# endif
#endif
#if defined(Q_OS_MAC)

View File

@ -55,12 +55,32 @@
# include <time.h>
#endif
#ifdef Q_OS_WINRT
#include <wrl.h>
#include <windows.foundation.h>
#include <windows.foundation.collections.h>
#ifndef Q_OS_WINPHONE
#include <windows.globalization.h>
#endif
#endif // Q_OS_WINRT
QT_BEGIN_NAMESPACE
#ifndef Q_OS_WINRT
static QByteArray getWinLocaleName(LCID id = LOCALE_USER_DEFAULT);
static const char *winLangCodeToIsoName(int code);
static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT);
static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT);
#else // !Q_OS_WINRT
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
static QByteArray getWinLocaleName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
static const char *winLangCodeToIsoName(int code);
static QString winIso639LangName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
static QString winIso3116CtryName(LPWSTR id = LOCALE_NAME_USER_DEFAULT);
#endif // Q_OS_WINRT
#ifndef QT_NO_SYSTEMLOCALE
@ -121,14 +141,23 @@ private:
};
// cached values:
#ifndef Q_OS_WINRT
LCID lcid;
#else
WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
#endif
SubstitutionType substitutionType;
QChar zero;
int getLocaleInfo(LCTYPE type, LPWSTR data, int size);
QString getLocaleInfo(LCTYPE type, int maxlen = 0);
int getLocaleInfo_int(LCTYPE type, int maxlen = 0);
QChar getLocaleInfo_qchar(LCTYPE type);
int getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size);
int getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size);
int getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size);
SubstitutionType substitution();
QString &substituteDigits(QString &string);
@ -140,20 +169,60 @@ Q_GLOBAL_STATIC(QSystemLocalePrivate, systemLocalePrivate)
QSystemLocalePrivate::QSystemLocalePrivate()
: substitutionType(SUnknown)
{
#ifndef Q_OS_WINRT
lcid = GetUserDefaultLCID();
#else
GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
#endif
}
inline int QSystemLocalePrivate::getCurrencyFormat(DWORD flags, LPCWSTR value, const CURRENCYFMTW *format, LPWSTR data, int size)
{
#ifndef Q_OS_WINRT
return GetCurrencyFormat(lcid, flags, value, format, data, size);
#else
return GetCurrencyFormatEx(lcName, flags, value, format, data, size);
#endif
}
inline int QSystemLocalePrivate::getDateFormat(DWORD flags, const SYSTEMTIME * date, LPCWSTR format, LPWSTR data, int size)
{
#ifndef Q_OS_WINRT
return GetDateFormat(lcid, flags, date, format, data, size);
#else
return GetDateFormatEx(lcName, flags, date, format, data, size, NULL);
#endif
}
inline int QSystemLocalePrivate::getTimeFormat(DWORD flags, const SYSTEMTIME *date, LPCWSTR format, LPWSTR data, int size)
{
#ifndef Q_OS_WINRT
return GetTimeFormat(lcid, flags, date, format, data, size);
#else
return GetTimeFormatEx(lcName, flags, date, format, data, size);
#endif
}
inline int QSystemLocalePrivate::getLocaleInfo(LCTYPE type, LPWSTR data, int size)
{
#ifndef Q_OS_WINRT
return GetLocaleInfo(lcid, type, data, size);
#else
return GetLocaleInfoEx(lcName, type, data, size);
#endif
}
QString QSystemLocalePrivate::getLocaleInfo(LCTYPE type, int maxlen)
{
QVarLengthArray<wchar_t, 64> buf(maxlen ? maxlen : 64);
if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
if (!getLocaleInfo(type, buf.data(), buf.size()))
return QString();
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
int cnt = GetLocaleInfo(lcid, type, 0, 0);
int cnt = getLocaleInfo(type, 0, 0);
if (cnt == 0)
return QString();
buf.resize(cnt);
if (!GetLocaleInfo(lcid, type, buf.data(), buf.size()))
if (!getLocaleInfo(type, buf.data(), buf.size()))
return QString();
}
return QString::fromWCharArray(buf.data());
@ -177,7 +246,7 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
{
if (substitutionType == SUnknown) {
wchar_t buf[8];
if (!GetLocaleInfo(lcid, LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
if (!getLocaleInfo(LOCALE_IDIGITSUBSTITUTION, buf, 8)) {
substitutionType = QSystemLocalePrivate::SNever;
return substitutionType;
}
@ -189,7 +258,7 @@ QSystemLocalePrivate::SubstitutionType QSystemLocalePrivate::substitution()
substitutionType = QSystemLocalePrivate::SAlways;
else {
wchar_t digits[11];
if (!GetLocaleInfo(lcid, LOCALE_SNATIVEDIGITS, digits, 11)) {
if (!getLocaleInfo(LOCALE_SNATIVEDIGITS, digits, 11)) {
substitutionType = QSystemLocalePrivate::SNever;
return substitutionType;
}
@ -332,7 +401,7 @@ QVariant QSystemLocalePrivate::toString(const QDate &date, QLocale::FormatType t
DWORD flags = (type == QLocale::LongFormat ? DATE_LONGDATE : DATE_SHORTDATE);
wchar_t buf[255];
if (GetDateFormat(lcid, flags, &st, NULL, buf, 255)) {
if (getDateFormat(flags, &st, NULL, buf, 255)) {
QString format = QString::fromWCharArray(buf);
if (substitution() == SAlways)
substituteDigits(format);
@ -353,7 +422,7 @@ QVariant QSystemLocalePrivate::toString(const QTime &time, QLocale::FormatType)
DWORD flags = 0;
wchar_t buf[255];
if (GetTimeFormat(lcid, flags, &st, NULL, buf, 255)) {
if (getTimeFormat(flags, &st, NULL, buf, 255)) {
QString format = QString::fromWCharArray(buf);
if (substitution() == SAlways)
substituteDigits(format);
@ -371,7 +440,7 @@ QVariant QSystemLocalePrivate::measurementSystem()
{
wchar_t output[2];
if (GetLocaleInfo(lcid, LOCALE_IMEASURE, output, 2)) {
if (getLocaleInfo(LOCALE_IMEASURE, output, 2)) {
QString iMeasure = QString::fromWCharArray(output);
if (iMeasure == QLatin1String("1")) {
return QLocale::ImperialSystem;
@ -385,7 +454,7 @@ QVariant QSystemLocalePrivate::amText()
{
wchar_t output[15]; // maximum length including terminating zero character for Win2003+
if (GetLocaleInfo(lcid, LOCALE_S1159, output, 15)) {
if (getLocaleInfo(LOCALE_S1159, output, 15)) {
return QString::fromWCharArray(output);
}
@ -396,7 +465,7 @@ QVariant QSystemLocalePrivate::pmText()
{
wchar_t output[15]; // maximum length including terminating zero character for Win2003+
if (GetLocaleInfo(lcid, LOCALE_S2359, output, 15)) {
if (getLocaleInfo(LOCALE_S2359, output, 15)) {
return QString::fromWCharArray(output);
}
@ -407,7 +476,7 @@ QVariant QSystemLocalePrivate::firstDayOfWeek()
{
wchar_t output[4]; // maximum length including terminating zero character for Win2003+
if (GetLocaleInfo(lcid, LOCALE_IFIRSTDAYOFWEEK, output, 4))
if (getLocaleInfo(LOCALE_IFIRSTDAYOFWEEK, output, 4))
return QString::fromWCharArray(output).toUInt()+1;
return 1;
@ -418,20 +487,20 @@ QVariant QSystemLocalePrivate::currencySymbol(QLocale::CurrencySymbolFormat form
wchar_t buf[13];
switch (format) {
case QLocale::CurrencySymbol:
if (GetLocaleInfo(lcid, LOCALE_SCURRENCY, buf, 13))
if (getLocaleInfo(LOCALE_SCURRENCY, buf, 13))
return QString::fromWCharArray(buf);
break;
case QLocale::CurrencyIsoCode:
if (GetLocaleInfo(lcid, LOCALE_SINTLSYMBOL, buf, 9))
if (getLocaleInfo(LOCALE_SINTLSYMBOL, buf, 9))
return QString::fromWCharArray(buf);
break;
case QLocale::CurrencyDisplayName: {
QVarLengthArray<wchar_t, 64> buf(64);
if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size())) {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break;
buf.resize(255); // should be large enough, right?
if (!GetLocaleInfo(lcid, LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
if (!getLocaleInfo(LOCALE_SNATIVECURRNAME, buf.data(), buf.size()))
break;
}
return QString::fromWCharArray(buf.data());
@ -478,14 +547,14 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
CURRENCYFMT format;
CURRENCYFMT *pformat = NULL;
if (!arg.symbol.isEmpty()) {
format.NumDigits = getLocaleInfo_int(lcid, LOCALE_ICURRDIGITS);
format.LeadingZero = getLocaleInfo_int(lcid, LOCALE_ILZERO);
decimalSep = getLocaleInfo(lcid, LOCALE_SMONDECIMALSEP);
format.NumDigits = getLocaleInfo_int(LOCALE_ICURRDIGITS);
format.LeadingZero = getLocaleInfo_int(LOCALE_ILZERO);
decimalSep = getLocaleInfo(LOCALE_SMONDECIMALSEP);
format.lpDecimalSep = (wchar_t *)decimalSep.utf16();
thousandSep = getLocaleInfo(lcid, LOCALE_SMONTHOUSANDSEP);
thousandSep = getLocaleInfo(LOCALE_SMONTHOUSANDSEP);
format.lpThousandSep = (wchar_t *)thousandSep.utf16();
format.NegativeOrder = getLocaleInfo_int(lcid, LOCALE_INEGCURR);
format.PositiveOrder = getLocaleInfo_int(lcid, LOCALE_ICURRENCY);
format.NegativeOrder = getLocaleInfo_int(LOCALE_INEGCURR);
format.PositiveOrder = getLocaleInfo_int(LOCALE_ICURRENCY);
format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16();
// grouping is complicated and ugly:
@ -494,7 +563,7 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
// int(30) == "123456,789.00" == string("3;0;0")
// int(32) == "12,34,56,789.00" == string("3;2;0")
// int(320)== "1234,56,789.00" == string("3;2")
QString groupingStr = getLocaleInfo(lcid, LOCALE_SMONGROUPING);
QString groupingStr = getLocaleInfo(LOCALE_SMONGROUPING);
format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt();
if (format.Grouping % 10 == 0) // magic
format.Grouping /= 10;
@ -503,13 +572,13 @@ QVariant QSystemLocalePrivate::toCurrencyString(const QSystemLocale::CurrencyToS
pformat = &format;
}
int ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
int ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), out.size());
if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
ret = getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), 0);
out.resize(ret);
::GetCurrencyFormat(lcid, 0, reinterpret_cast<const wchar_t *>(value.utf16()),
getCurrencyFormat(0, reinterpret_cast<const wchar_t *>(value.utf16()),
pformat, out.data(), out.size());
}
@ -528,11 +597,13 @@ QVariant QSystemLocalePrivate::uiLanguages()
PWSTR pwszLanguagesBuffer,
PULONG pcchLanguagesBuffer);
static GetUserPreferredUILanguagesFunc GetUserPreferredUILanguages_ptr = 0;
#ifndef Q_OS_WINRT
if (!GetUserPreferredUILanguages_ptr) {
QSystemLibrary lib(QLatin1String("kernel32"));
if (lib.load())
GetUserPreferredUILanguages_ptr = (GetUserPreferredUILanguagesFunc)lib.resolve("GetUserPreferredUILanguages");
}
#endif // !Q_OS_WINRT
if (GetUserPreferredUILanguages_ptr) {
unsigned long cnt = 0;
QVarLengthArray<wchar_t, 64> buf(64);
@ -560,8 +631,39 @@ QVariant QSystemLocalePrivate::uiLanguages()
}
}
#ifndef Q_OS_WINRT
// old Windows before Vista
return QStringList(QString::fromLatin1(winLangCodeToIsoName(GetUserDefaultUILanguage())));
#else // !Q_OS_WINRT
QStringList result;
#ifndef Q_OS_WINPHONE
ComPtr<ABI::Windows::Globalization::IApplicationLanguagesStatics> appLanguagesStatics;
if (FAILED(GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Globalization_ApplicationLanguages).Get(), &appLanguagesStatics))) {
qWarning("Could not obtain ApplicationLanguagesStatic");
return QStringList();
}
ComPtr<ABI::Windows::Foundation::Collections::IVectorView<HSTRING> > languageList;
appLanguagesStatics->get_ManifestLanguages(&languageList);
if (!languageList)
return QStringList();
unsigned int size;
languageList->get_Size(&size);
for (unsigned int i = 0; i < size; ++i) {
HSTRING language;
languageList->GetAt(i, &language);
UINT32 length;
PCWSTR rawString = WindowsGetStringRawBuffer(language, &length);
result << QString::fromWCharArray(rawString, length);
}
#else // !Q_OS_WINPHONE
result << QString::fromWCharArray(lcName);
#endif // Q_OS_WINPHONE
return result;
#endif // Q_OS_WINRT
}
QVariant QSystemLocalePrivate::nativeLanguageName()
@ -581,7 +683,11 @@ QVariant QSystemLocalePrivate::nativeCountryName()
void QSystemLocalePrivate::update()
{
#ifndef Q_OS_WINRT
lcid = GetUserDefaultLCID();
#else
GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
#endif
substitutionType = SUnknown;
zero = QChar();
}
@ -895,7 +1001,11 @@ static const char *winLangCodeToIsoName(int code)
}
#ifndef Q_OS_WINRT
static QString winIso639LangName(LCID id)
#else
static QString winIso639LangName(LPWSTR id)
#endif
{
QString result;
@ -903,7 +1013,11 @@ static QString winIso639LangName(LCID id)
// the language code
QString lang_code;
wchar_t out[256];
if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255)) // ### shouldn't use them according to msdn
#ifndef Q_OS_WINRT
if (GetLocaleInfo(id, LOCALE_ILANGUAGE, out, 255))
#else
if (GetLocaleInfoEx(id, LOCALE_ILANGUAGE, out, 255))
#endif
lang_code = QString::fromWCharArray(out);
if (!lang_code.isEmpty()) {
@ -926,27 +1040,47 @@ static QString winIso639LangName(LCID id)
return result;
// not one of the problematic languages - do the usual lookup
if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME , out, 255))
#ifndef Q_OS_WINRT
if (GetLocaleInfo(id, LOCALE_SISO639LANGNAME, out, 255))
#else
if (GetLocaleInfoEx(id, LOCALE_SISO639LANGNAME, out, 255))
#endif
result = QString::fromWCharArray(out);
return result;
}
#ifndef Q_OS_WINRT
static QString winIso3116CtryName(LCID id)
#else
static QString winIso3116CtryName(LPWSTR id)
#endif
{
QString result;
wchar_t out[256];
#ifndef Q_OS_WINRT
if (GetLocaleInfo(id, LOCALE_SISO3166CTRYNAME, out, 255))
#else
if (GetLocaleInfoEx(id, LOCALE_SISO3166CTRYNAME, out, 255))
#endif
result = QString::fromWCharArray(out);
return result;
}
#ifndef Q_OS_WINRT
static QByteArray getWinLocaleName(LCID id)
#else
static QByteArray getWinLocaleName(LPWSTR id)
#endif
{
QByteArray result;
#ifndef Q_OS_WINRT
if (id == LOCALE_USER_DEFAULT) {
#else
if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
#endif
static QByteArray langEnvVar = qgetenv("LANG");
result = langEnvVar;
QString lang, script, cntry;
@ -964,9 +1098,17 @@ static QByteArray getWinLocaleName(LCID id)
#if defined(Q_OS_WINCE)
result = winLangCodeToIsoName(id != LOCALE_USER_DEFAULT ? id : GetUserDefaultLCID());
#else
#else // !Q_OS_WINCE
# ifndef Q_OS_WINRT
if (id == LOCALE_USER_DEFAULT)
id = GetUserDefaultLCID();
# else // !Q_OS_WINRT
WCHAR lcName[LOCALE_NAME_MAX_LENGTH];
if (QString::fromWCharArray(id) == QString::fromWCharArray(LOCALE_NAME_USER_DEFAULT)) {
GetUserDefaultLocaleName(lcName, LOCALE_NAME_MAX_LENGTH);
id = lcName;
}
# endif // Q_OS_WINRT
QString resultuage = winIso639LangName(id);
QString country = winIso3116CtryName(id);
result = resultuage.toLatin1();
@ -974,14 +1116,20 @@ static QByteArray getWinLocaleName(LCID id)
result += '_';
result += country.toLatin1();
}
#endif
#endif // !Q_OS_WINCE
return result;
}
Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id)
{
#ifndef Q_OS_WINRT
return QLocale(QString::fromLatin1(getWinLocaleName(id)));
#else // !Q_OS_WINRT
WCHAR name[LOCALE_NAME_MAX_LENGTH];
LCIDToLocaleName(id, name, LOCALE_NAME_MAX_LENGTH, 0);
return QLocale(QString::fromLatin1(getWinLocaleName(name)));
#endif // Q_OS_WINRT
}
QT_END_NAMESPACE

View File

@ -5093,7 +5093,11 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1,
return ucstrcmp(data1, length1, data2, length2);
#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE)
#ifndef Q_OS_WINRT
int res = CompareString(GetUserDefaultLCID(), 0, (wchar_t*)data1, length1, (wchar_t*)data2, length2);
#else
int res = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, (LPCWSTR)data1, length1, (LPCWSTR)data2, length2, NULL, NULL, 0);
#endif
switch (res) {
case CSTR_LESS_THAN:

View File

@ -127,6 +127,7 @@ else:unix:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp tools/q
else:win32 {
SOURCES += tools/qelapsedtimer_win.cpp tools/qlocale_win.cpp
!winrt: SOURCES += tools/qtimezoneprivate_win.cpp
winphone: LIBS_PRIVATE += -lWindowsPhoneGlobalizationUtil
} else:integrity:SOURCES += tools/qelapsedtimer_unix.cpp tools/qlocale_unix.cpp
else:SOURCES += tools/qelapsedtimer_generic.cpp