From 176dd0bee83abe742ca8d91c97f8adb063520213 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Tue, 16 Jun 2015 01:23:33 +0800 Subject: [PATCH] Sort labels in RSS Downloader dialog, closes #3140. --- src/core/misc.cpp | 79 +++++++++++++++++++++++++- src/core/misc.h | 17 +++++- src/gui/rss/automatedrssdownloader.cpp | 9 +-- 3 files changed, 99 insertions(+), 6 deletions(-) diff --git a/src/core/misc.cpp b/src/core/misc.cpp index 42414d8f6..ea6989706 100644 --- a/src/core/misc.cpp +++ b/src/core/misc.cpp @@ -534,7 +534,7 @@ QString misc::toQString(time_t t) } #ifndef DISABLE_GUI -bool misc::naturalSort(QString left, QString right, bool &result) // uses lessThan comparison +bool misc::naturalSort(const QString &left, const QString &right, bool &result) // uses lessThan comparison { // Return value indicates if functions was successful // result argument will contain actual comparison result if function was successful int posL = 0; @@ -593,6 +593,83 @@ bool misc::naturalSort(QString left, QString right, bool &result) // uses less return false; } + +misc::NaturalCompare::NaturalCompare() +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) +#if defined(Q_OS_WIN) + // Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7 + if(SysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7) + return; +#endif + m_collator.setNumericMode(true); + m_collator.setCaseSensitivity(Qt::CaseInsensitive); +#endif +} + +bool misc::NaturalCompare::operator()(const QString &l, const QString &r) +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) +#if defined(Q_OS_WIN) + // Without ICU library, QCollator doesn't support `setNumericMode(true)` on OS older than Win7 + if(SysInfo::windowsVersion() < QSysInfo::WV_WINDOWS7) + return lessThan(l, r); +#endif + return (m_collator.compare(l, r) < 0); +#else + return lessThan(l, r); +#endif +} + +bool misc::NaturalCompare::lessThan(const QString &left, const QString &right) +{ + // Return value `false` indicates `right` should go before `left`, otherwise, after + int posL = 0; + int posR = 0; + while (true) { + while (true) { + if (posL == left.size() || posR == right.size()) + return (left.size() < right.size()); // when a shorter string is another string's prefix, shorter string place before longer string + + QChar leftChar = left[posL].toLower(); + QChar rightChar = right[posR].toLower(); + if (leftChar == rightChar) + ; // compare next character + else if (leftChar.isDigit() && rightChar.isDigit()) + break; // Both are digits, break this loop and compare numbers + else + return leftChar < rightChar; + + ++posL; + ++posR; + } + + int startL = posL; + while ((posL < left.size()) && left[posL].isDigit()) + ++posL; +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + int numL = left.midRef(startL, posL - startL).toInt(); +#else + int numL = left.mid(startL, posL - startL).toInt(); +#endif + + int startR = posR; + while ((posR < right.size()) && right[posR].isDigit()) + ++posR; +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + int numR = right.midRef(startR, posR - startR).toInt(); +#else + int numR = right.mid(startR, posR - startR).toInt(); +#endif + + if (numL != numR) + return (numL < numR); + + // Strings + digits do match and we haven't hit string end + // Do another round + } + return false; +} #endif // to send numbers instead of strings with suffixes diff --git a/src/core/misc.h b/src/core/misc.h index b99d58ae7..b4502ee31 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -41,6 +41,9 @@ #include #ifndef DISABLE_GUI #include +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) +#include +#endif #endif #include @@ -104,7 +107,19 @@ namespace misc QString accurateDoubleToString(const double &n, const int &precision); #ifndef DISABLE_GUI - bool naturalSort(QString left, QString right, bool& result); + bool naturalSort(const QString &left, const QString &right, bool &result); + + class NaturalCompare + { + public: + NaturalCompare(); + bool operator()(const QString &l, const QString &r); + bool lessThan(const QString &left, const QString &right); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) + private: + QCollator m_collator; +#endif + }; #endif // Implements constant-time comparison to protect against timing attacks diff --git a/src/gui/rss/automatedrssdownloader.cpp b/src/gui/rss/automatedrssdownloader.cpp index df4b1167b..615db93d3 100644 --- a/src/gui/rss/automatedrssdownloader.cpp +++ b/src/gui/rss/automatedrssdownloader.cpp @@ -43,6 +43,7 @@ #include "iconprovider.h" #include "autoexpandabledialog.h" #include "fs_utils.h" +#include "misc.h" AutomatedRssDownloader::AutomatedRssDownloader(const QWeakPointer& manager, QWidget *parent) : QDialog(parent), @@ -308,10 +309,10 @@ RssDownloadRulePtr AutomatedRssDownloader::getCurrentRule() const void AutomatedRssDownloader::initLabelCombobox() { // Load custom labels - const QStringList customLabels = Preferences::instance()->getTorrentLabels(); - foreach (const QString& label, customLabels) { - ui->comboLabel->addItem(label); - } + QStringList customLabels = Preferences::instance()->getTorrentLabels(); + std::sort(customLabels.begin(), customLabels.end(), misc::NaturalCompare()); + foreach (const QString& l, customLabels) + ui->comboLabel->addItem(l); } void AutomatedRssDownloader::saveEditedRule()