From 0c2ec3cef9f0a91b08d5103ac0bafd173f2a20df Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Fri, 25 Feb 2011 16:08:57 +0100 Subject: Improved currency value to string conversion in QLocale. Added a second, optional, argument to QLocale::toCurrencyString() that represents a currency symbol that is supposed to be added to the formatted string. Task-number: QTBUG-17100 Reviewed-by: Zeno Albisser Reviewed-by: Olivier Goffart --- src/corelib/tools/qlocale.cpp | 58 ++++++++++++++++++------------- src/corelib/tools/qlocale.h | 50 ++++++++++++++++----------- src/corelib/tools/qlocale_mac.mm | 16 +++++---- src/corelib/tools/qlocale_win.cpp | 70 ++++++++++++++++++++++++++++++++------ tests/auto/qlocale/tst_qlocale.cpp | 6 ++++ tests/manual/qlocale/currency.cpp | 29 +++++++++++----- tests/manual/qlocale/currency.h | 4 ++- 7 files changed, 163 insertions(+), 70 deletions(-) diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 5027d00..4925129 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -2802,7 +2802,7 @@ qulonglong QLocalePrivate::bytearrayToUnsLongLong(const char *num, int base, boo /*! \since 4.8 - \enum QLocale::CurrencyFormat + \enum QLocale::CurrencySymbolFormat Specifies the format of the currency symbol. @@ -2847,30 +2847,30 @@ QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const } /*! - \fn QString QLocale::toCurrencyString(short) const + \fn QString QLocale::toCurrencyString(short, const QString &) const \since 4.8 \overload */ /*! - \fn QString QLocale::toCurrencyString(ushort) const + \fn QString QLocale::toCurrencyString(ushort, const QString &) const \since 4.8 \overload */ /*! - \fn QString QLocale::toCurrencyString(int) const + \fn QString QLocale::toCurrencyString(int, const QString &) const \since 4.8 \overload */ /*! - \fn QString QLocale::toCurrencyString(uint) const + \fn QString QLocale::toCurrencyString(uint, const QString &) const \since 4.8 \overload */ /*! - \fn QString QLocale::toCurrencyString(float) const + \fn QString QLocale::toCurrencyString(float, const QString &) const \since 4.8 \overload */ @@ -2879,12 +2879,16 @@ QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const \since 4.8 Returns a localized string representation of \a value as a currency. + If the \a symbol is provided it is used instead of the default currency symbol. + + \sa currencySymbol */ -QString QLocale::toCurrencyString(qlonglong value) const +QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const { #ifndef QT_NO_SYSTEMLOCALE if (d() == systemPrivate()) { - QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, value); + QSystemLocale::CurrencyToStringArgument arg(value, symbol); + QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg)); if (!res.isNull()) return res.toString(); } @@ -2898,22 +2902,23 @@ QString QLocale::toCurrencyString(qlonglong value) const value = -value; } QString str = d->longLongToString(value); - QString symbol = currencySymbol(); - if (symbol.isEmpty()) - symbol = currencySymbol(QLocale::CurrencyIsoCode); + QString sym = symbol.isEmpty() ? currencySymbol() : symbol; + if (sym.isEmpty()) + sym = currencySymbol(QLocale::CurrencyIsoCode); QString format = getLocaleData(currency_format_data + idx, size); - return format.arg(str, symbol); + return format.arg(str, sym); } /*! \since 4.8 \overload */ -QString QLocale::toCurrencyString(qulonglong value) const +QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const { #ifndef QT_NO_SYSTEMLOCALE if (d() == systemPrivate()) { - QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, value); + QSystemLocale::CurrencyToStringArgument arg(value, symbol); + QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg)); if (!res.isNull()) return res.toString(); } @@ -2922,18 +2927,23 @@ QString QLocale::toCurrencyString(qulonglong value) const quint8 idx = d->m_currency_format_idx; quint8 size = d->m_currency_format_size; QString str = d->unsLongLongToString(value); - QString symbol = currencySymbol(); - if (symbol.isEmpty()) - symbol = currencySymbol(QLocale::CurrencyIsoCode); + QString sym = symbol.isEmpty() ? currencySymbol() : symbol; + if (sym.isEmpty()) + sym = currencySymbol(QLocale::CurrencyIsoCode); QString format = getLocaleData(currency_format_data + idx, size); - return format.arg(str, symbol); + return format.arg(str, sym); } -QString QLocale::toCurrencyString(double value) const +/*! + \since 4.8 + \overload +*/ +QString QLocale::toCurrencyString(double value, const QString &symbol) const { #ifndef QT_NO_SYSTEMLOCALE if (d() == systemPrivate()) { - QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, value); + QSystemLocale::CurrencyToStringArgument arg(value, symbol); + QVariant res = systemLocale()->query(QSystemLocale::CurrencyToString, QVariant::fromValue(arg)); if (!res.isNull()) return res.toString(); } @@ -2948,11 +2958,11 @@ QString QLocale::toCurrencyString(double value) const } QString str = d->doubleToString(value, d->m_currency_digits, QLocalePrivate::DFDecimal); - QString symbol = currencySymbol(); - if (symbol.isEmpty()) - symbol = currencySymbol(QLocale::CurrencyIsoCode); + QString sym = symbol.isEmpty() ? currencySymbol() : symbol; + if (sym.isEmpty()) + sym = currencySymbol(QLocale::CurrencyIsoCode); QString format = getLocaleData(currency_format_data + idx, size); - return format.arg(str, symbol); + return format.arg(str, sym); } /*! diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 7900c57..a5d5a17 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -42,6 +42,7 @@ #ifndef QLOCALE_H #define QLOCALE_H +#include #include #include @@ -68,6 +69,15 @@ public: QSystemLocale(); virtual ~QSystemLocale(); + struct CurrencyToStringArgument + { + CurrencyToStringArgument() { } + CurrencyToStringArgument(const QVariant &v, const QString &s) + : value(v), symbol(s) { } + QVariant value; + QString symbol; + }; + enum QueryType { LanguageId, // uint CountryId, // uint @@ -98,7 +108,7 @@ public: FirstDayOfWeek, // Qt::DayOfWeek WeekendStart, // Qt::DayOfWeek WeekendEnd, // Qt::DayOfWeek - CurrencySymbol, // QString in: format + CurrencySymbol, // QString in: CurrencyToStringArgument CurrencyToString, // QString in: qlonglong, qulonglong or double UILanguages, // QStringList StringToStandardQuotation, // QString in: QStringRef to quote @@ -690,14 +700,14 @@ public: Qt::LayoutDirection textDirection() const; QString currencySymbol(CurrencySymbolFormat = CurrencySymbol) const; - QString toCurrencyString(qlonglong) const; - QString toCurrencyString(qulonglong) const; - inline QString toCurrencyString(short) const; - inline QString toCurrencyString(ushort) const; - inline QString toCurrencyString(int) const; - inline QString toCurrencyString(uint) const; - QString toCurrencyString(double) const; - inline QString toCurrencyString(float) const; + QString toCurrencyString(qlonglong, const QString &symbol = QString()) const; + QString toCurrencyString(qulonglong, const QString &symbol = QString()) const; + inline QString toCurrencyString(short, const QString &symbol = QString()) const; + inline QString toCurrencyString(ushort, const QString &symbol = QString()) const; + inline QString toCurrencyString(int, const QString &symbol = QString()) const; + inline QString toCurrencyString(uint, const QString &symbol = QString()) const; + QString toCurrencyString(double, const QString &symbol = QString()) const; + inline QString toCurrencyString(float, const QString &symbol = QString()) const; QStringList uiLanguages() const; @@ -753,16 +763,16 @@ inline bool QLocale::operator==(const QLocale &other) const inline bool QLocale::operator!=(const QLocale &other) const { return d() != other.d() || numberOptions() != other.numberOptions(); } -inline QString QLocale::toCurrencyString(short i) const - { return toCurrencyString(qlonglong(i)); } -inline QString QLocale::toCurrencyString(ushort i) const - { return toCurrencyString(qulonglong(i)); } -inline QString QLocale::toCurrencyString(int i) const -{ return toCurrencyString(qlonglong(i)); } -inline QString QLocale::toCurrencyString(uint i) const -{ return toCurrencyString(qulonglong(i)); } -inline QString QLocale::toCurrencyString(float i) const -{ return toCurrencyString(double(i)); } +inline QString QLocale::toCurrencyString(short i, const QString &symbol) const + { return toCurrencyString(qlonglong(i), symbol); } +inline QString QLocale::toCurrencyString(ushort i, const QString &symbol) const + { return toCurrencyString(qulonglong(i), symbol); } +inline QString QLocale::toCurrencyString(int i, const QString &symbol) const +{ return toCurrencyString(qlonglong(i), symbol); } +inline QString QLocale::toCurrencyString(uint i, const QString &symbol) const +{ return toCurrencyString(qulonglong(i), symbol); } +inline QString QLocale::toCurrencyString(float i, const QString &symbol) const +{ return toCurrencyString(double(i), symbol); } #ifndef QT_NO_DATASTREAM Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QLocale &); @@ -771,6 +781,8 @@ Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QLocale &); QT_END_NAMESPACE +Q_DECLARE_METATYPE(QSystemLocale::CurrencyToStringArgument) + QT_END_HEADER #endif // QLOCALE_H diff --git a/src/corelib/tools/qlocale_mac.mm b/src/corelib/tools/qlocale_mac.mm index 6c4829b..68bb7c5 100644 --- a/src/corelib/tools/qlocale_mac.mm +++ b/src/corelib/tools/qlocale_mac.mm @@ -307,24 +307,24 @@ static QString macCurrencySymbol(QLocale::CurrencySymbolFormat format) return QString(); } -static QString macFormatCurrency(const QVariant &in) +static QString macFormatCurrency(const QSystemLocale::CurrencyToStringArgument &arg) { QCFType value; - switch (in.type()) { + switch (arg.value.type()) { case QVariant::Int: case QVariant::UInt: { - int v = in.toInt(); + int v = arg.value.toInt(); value = CFNumberCreate(NULL, kCFNumberIntType, &v); break; } case QVariant::Double: { - double v = in.toInt(); + double v = arg.value.toInt(); value = CFNumberCreate(NULL, kCFNumberDoubleType, &v); break; } case QVariant::LongLong: case QVariant::ULongLong: { - qint64 v = in.toLongLong(); + qint64 v = arg.value.toLongLong(); value = CFNumberCreate(NULL, kCFNumberLongLongType, &v); break; } @@ -335,6 +335,10 @@ static QString macFormatCurrency(const QVariant &in) QCFType locale = CFLocaleCopyCurrent(); QCFType currencyFormatter = CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterCurrencyStyle); + if (!arg.symbol.isEmpty()) { + CFNumberFormatterSetProperty(currencyFormatter, kCFNumberFormatterCurrencySymbol, + QCFString::toCFStringRef(arg.symbol)); + } QCFType result = CFNumberFormatterCreateStringWithNumber(NULL, currencyFormatter, value); return QCFString::toQString(result); } @@ -458,7 +462,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const case CurrencySymbol: return QVariant(macCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()))); case CurrencyToString: - return macFormatCurrency(in); + return macFormatCurrency(in.value()); case UILanguages: { QCFType languages = (CFArrayRef)CFPreferencesCopyValue( CFSTR("AppleLanguages"), diff --git a/src/corelib/tools/qlocale_win.cpp b/src/corelib/tools/qlocale_win.cpp index cf84094..cb191f2 100644 --- a/src/corelib/tools/qlocale_win.cpp +++ b/src/corelib/tools/qlocale_win.cpp @@ -48,6 +48,8 @@ #include "private/qsystemlibrary_p.h" +#include "qdebug.h" + #if defined(Q_WS_WIN) # include "qt_windows.h" # include @@ -63,6 +65,21 @@ static const char *winLangCodeToIsoName(int code); static QString winIso639LangName(LCID id = LOCALE_USER_DEFAULT); static QString winIso3116CtryName(LCID id = LOCALE_USER_DEFAULT); +static QString qt_getLocaleInfo(LCID lcid, LCTYPE type, int maxlen = 0) +{ + QVarLengthArray buf(maxlen ? maxlen : 64); + if (!GetLocaleInfo(lcid, type, buf.data(), buf.size())) + return QString(); + return QString::fromWCharArray(buf.data()); +} +static int qt_getLocaleInfo_int(LCID lcid, LCTYPE type, int maxlen = 0) +{ + QString str = qt_getLocaleInfo(lcid, type, maxlen); + bool ok = false; + int v = str.toInt(&ok); + return ok ? v : 0; +} + static QString getWinLocaleInfo(LCTYPE type) { LCID id = GetUserDefaultLCID(); @@ -355,30 +372,30 @@ QString winCurrencySymbol(QLocale::CurrencySymbolFormat format) return QString(); } -static QString winFormatCurrency(const QVariant &in) +static QString winFormatCurrency(const QSystemLocale::CurrencyToStringArgument &arg) { QString value; - switch (in.type()) { + switch (arg.value.type()) { case QVariant::Int: value = QLocalePrivate::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'), - in.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator); + arg.value.toInt(), -1, 10, -1, QLocale::OmitGroupSeparator); break; case QVariant::UInt: value = QLocalePrivate::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), - in.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator); + arg.value.toUInt(), -1, 10, -1, QLocale::OmitGroupSeparator); break; case QVariant::Double: value = QLocalePrivate::doubleToString(QLatin1Char('0'), QLatin1Char('+'), QLatin1Char('-'), QLatin1Char(' '), QLatin1Char(','), QLatin1Char('.'), - in.toDouble(), -1, QLocalePrivate::DFDecimal, -1, QLocale::OmitGroupSeparator); + arg.value.toDouble(), -1, QLocalePrivate::DFDecimal, -1, QLocale::OmitGroupSeparator); break; case QVariant::LongLong: value = QLocalePrivate::longLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), QLatin1Char('-'), - in.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator); + arg.value.toLongLong(), -1, 10, -1, QLocale::OmitGroupSeparator); break; case QVariant::ULongLong: value = QLocalePrivate::unsLongLongToString(QLatin1Char('0'), QLatin1Char(','), QLatin1Char('+'), - in.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator); + arg.value.toULongLong(), -1, 10, -1, QLocale::OmitGroupSeparator); break; default: return QString(); @@ -386,14 +403,45 @@ static QString winFormatCurrency(const QVariant &in) QVarLengthArray out(64); LCID lcid = GetUserDefaultLCID(); + + QString decimalSep; + QString thousandSep; + CURRENCYFMT format; + CURRENCYFMT *pformat = NULL; + if (!arg.symbol.isEmpty()) { + format.NumDigits = qt_getLocaleInfo_int(lcid, LOCALE_ICURRDIGITS); + format.LeadingZero = qt_getLocaleInfo_int(lcid, LOCALE_ILZERO); + decimalSep = qt_getLocaleInfo(lcid, LOCALE_SMONDECIMALSEP); + format.lpDecimalSep = (wchar_t *)decimalSep.utf16(); + thousandSep = qt_getLocaleInfo(lcid, LOCALE_SMONTHOUSANDSEP); + format.lpThousandSep = (wchar_t *)thousandSep.utf16(); + format.NegativeOrder = qt_getLocaleInfo_int(lcid, LOCALE_INEGCURR); + format.PositiveOrder = qt_getLocaleInfo_int(lcid, LOCALE_ICURRENCY); + format.lpCurrencySymbol = (wchar_t *)arg.symbol.utf16(); + + // grouping is complicated and ugly: + // int(0) == "123456789.00" == string("0") + // int(3) == "123,456,789.00" == string("3;0") + // 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 = qt_getLocaleInfo(lcid, LOCALE_SMONGROUPING); + format.Grouping = groupingStr.remove(QLatin1Char(';')).toInt(); + if (format.Grouping % 10 == 0) // magic + format.Grouping /= 10; + else + format.Grouping *= 10; + pformat = &format; + } + int ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast(value.utf16()), - NULL, out.data(), out.size()); + pformat, out.data(), out.size()); if (ret == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { ret = ::GetCurrencyFormat(lcid, 0, reinterpret_cast(value.utf16()), - NULL, out.data(), 0); + pformat, out.data(), 0); out.resize(ret); ::GetCurrencyFormat(lcid, 0, reinterpret_cast(value.utf16()), - NULL, out.data(), out.size()); + pformat, out.data(), out.size()); } return QString::fromWCharArray(out.data()); @@ -540,7 +588,7 @@ QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const case CurrencySymbol: return QVariant(winCurrencySymbol(QLocale::CurrencySymbolFormat(in.toUInt()))); case CurrencyToString: - return QVariant(winFormatCurrency(in)); + return QVariant(winFormatCurrency(in.value())); case UILanguages: return QVariant(winUILanguages()); default: diff --git a/tests/auto/qlocale/tst_qlocale.cpp b/tests/auto/qlocale/tst_qlocale.cpp index d1b7193..7a3e339 100644 --- a/tests/auto/qlocale/tst_qlocale.cpp +++ b/tests/auto/qlocale/tst_qlocale.cpp @@ -2159,9 +2159,15 @@ void tst_QLocale::currency() const QLocale de_DE("de_DE"); QCOMPARE(de_DE.toCurrencyString(qulonglong(1234)), QString::fromUtf8("1234\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(qulonglong(1234), QLatin1String("BAZ")), QString::fromUtf8("1234\xc2\xa0" "BAZ")); QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234)), QString::fromUtf8("-1234\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(qlonglong(-1234), QLatin1String("BAZ")), QString::fromUtf8("-1234\xc2\xa0" "BAZ")); QCOMPARE(de_DE.toCurrencyString(double(1234.56)), QString::fromUtf8("1234,56\xc2\xa0\xe2\x82\xac")); QCOMPARE(de_DE.toCurrencyString(double(-1234.56)), QString::fromUtf8("-1234,56\xc2\xa0\xe2\x82\xac")); + QCOMPARE(de_DE.toCurrencyString(double(-1234.56), QLatin1String("BAZ")), QString::fromUtf8("-1234,56\xc2\xa0" "BAZ")); + + const QLocale system = QLocale::system(); + QVERIFY(system.toCurrencyString(1, QLatin1String("FOO")).contains(QLatin1String("FOO"))); } void tst_QLocale::quoteString() diff --git a/tests/manual/qlocale/currency.cpp b/tests/manual/qlocale/currency.cpp index c55df3d..4ef157c 100644 --- a/tests/manual/qlocale/currency.cpp +++ b/tests/manual/qlocale/currency.cpp @@ -52,37 +52,48 @@ CurrencyWidget::CurrencyWidget() currencyName = new QLineEdit; currencyFormattingLabel = new QLabel("Currency formatting:"); currencyFormattingValue = new QLineEdit(QString::number(1234.56, 'f', 2)); + currencyFormattingSymbolLabel = new QLabel("currency:"); + currencyFormattingSymbol = new QLineEdit; currencyFormatting = new QLineEdit; + currencyFormattingValue->setFixedWidth(150); + currencyFormattingSymbol->setFixedWidth(50); + l->addWidget(currencySymbolLabel, 0, 0); - l->addWidget(currencySymbol, 0, 1, 1, 2); + l->addWidget(currencySymbol, 0, 1, 1, 4); l->addWidget(currencyISOLabel, 1, 0); - l->addWidget(currencyISO, 1, 1, 1, 2); + l->addWidget(currencyISO, 1, 1, 1, 4); l->addWidget(currencyNameLabel, 2, 0); - l->addWidget(currencyName, 2, 1, 1, 2); + l->addWidget(currencyName, 2, 1, 1, 4); l->addWidget(currencyFormattingLabel, 3, 0); l->addWidget(currencyFormattingValue, 3, 1); - l->addWidget(currencyFormatting, 3, 2); + l->addWidget(currencyFormattingSymbolLabel, 3, 2); + l->addWidget(currencyFormattingSymbol, 3, 3); + l->addWidget(currencyFormatting, 3, 4); QVBoxLayout *v = new QVBoxLayout(this); v->addLayout(l); v->addStretch(); + connect(currencyFormattingSymbol, SIGNAL(textChanged(QString)), + this, SLOT(updateCurrencyFormatting())); connect(currencyFormattingValue, SIGNAL(textChanged(QString)), - this, SLOT(updateCurrencyFormatting(QString))); + this, SLOT(updateCurrencyFormatting())); } -void CurrencyWidget::updateCurrencyFormatting(QString value) +void CurrencyWidget::updateCurrencyFormatting() { QString result; bool ok; + QString symbol = currencyFormattingSymbol->text(); + QString value = currencyFormattingValue->text(); int i = value.toInt(&ok); if (ok) { - result = locale().toCurrencyString(i); + result = locale().toCurrencyString(i, symbol); } else { double d = value.toDouble(&ok); if (ok) - result = locale().toCurrencyString(d); + result = locale().toCurrencyString(d, symbol); } currencyFormatting->setText(result); } @@ -93,6 +104,6 @@ void CurrencyWidget::localeChanged(QLocale locale) currencySymbol->setText(locale.currencySymbol()); currencyISO->setText(locale.currencySymbol(QLocale::CurrencyIsoCode)); currencyName->setText(locale.currencySymbol(QLocale::CurrencyDisplayName)); - updateCurrencyFormatting(currencyFormattingValue->text()); + updateCurrencyFormatting(); } diff --git a/tests/manual/qlocale/currency.h b/tests/manual/qlocale/currency.h index 5e43689..3a12553 100644 --- a/tests/manual/qlocale/currency.h +++ b/tests/manual/qlocale/currency.h @@ -58,11 +58,13 @@ private: QLineEdit *currencyName; QLabel *currencyFormattingLabel; QLineEdit *currencyFormattingValue; + QLabel *currencyFormattingSymbolLabel; + QLineEdit *currencyFormattingSymbol; QLineEdit *currencyFormatting; private slots: void localeChanged(QLocale locale); - void updateCurrencyFormatting(QString); + void updateCurrencyFormatting(); }; #endif -- cgit v0.12