From 2fa0f86df45b73bd26ec1736deb1b3b902959926 Mon Sep 17 00:00:00 2001 From: Nick Tiskov Date: Fri, 5 Jul 2013 21:24:20 +0400 Subject: [PATCH 1/2] Use number-aware sort in transfer list --- src/src.pro | 1 + src/transferlistsortmodel.h | 115 ++++++++++++++++++++++++++++++++++++ src/transferlistwidget.cpp | 2 +- src/transferlistwidget.h | 3 +- 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/transferlistsortmodel.h diff --git a/src/src.pro b/src/src.pro index ae889da72..d40590e68 100644 --- a/src/src.pro +++ b/src/src.pro @@ -125,6 +125,7 @@ nox { transferlistwidget.h \ transferlistdelegate.h \ transferlistfilterswidget.h \ + transferlistsortmodel.h \ torrentcontentmodel.h \ torrentcontentmodelitem.h \ torrentcontentmodelfolder.h \ diff --git a/src/transferlistsortmodel.h b/src/transferlistsortmodel.h new file mode 100644 index 000000000..6f7670552 --- /dev/null +++ b/src/transferlistsortmodel.h @@ -0,0 +1,115 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2013 Nick Tiskov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : daymansmail@gmail.com + */ + +#ifndef TRANSFERLISTSORTMODEL_H +#define TRANSFERLISTSORTMODEL_H + +#include +#include "torrentmodel.h" + +class TransferListSortModel : public QSortFilterProxyModel { + Q_OBJECT + +public: + TransferListSortModel(QObject *parent = 0) : QSortFilterProxyModel(parent) {} + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const { + if (sortColumn() == TorrentModelItem::TR_NAME) { + QVariant vL = sourceModel()->data(left); + QVariant vR = sourceModel()->data(right); + if (!(vL.isValid() && vR.isValid())) + return QSortFilterProxyModel::lessThan(left, right); + Q_ASSERT(vL.isValid()); + Q_ASSERT(vR.isValid()); + + QString nameLeft = vL.toString(); + QString nameRight = vR.toString(); + + do { + int posL = nameLeft.indexOf(QRegExp("[0-9]")); + int posR = nameRight.indexOf(QRegExp("[0-9]")); + if (posL == -1 || posR == -1) + break; // No data + else if (posL != posR) + break; // Digit positions mismatch + else if (nameLeft.left(posL) != nameRight.left(posR)) + break; // Strings' subsets before digit do not match + + + bool second_digit = false; + if (nameLeft.size() > posL + 1) + second_digit = nameLeft.at(posL + 1).isDigit(); + if (nameRight.size() > posR + 1) + second_digit = second_digit ? + second_digit : + nameRight.at(posR + 1).isDigit(); + + if (!second_digit) + break; // Single digit in both, normal sort could handle this + + QString temp; + while (posL < nameLeft.size()) { + if (nameLeft.at(posL).isDigit()) + temp += nameLeft.at(posL); + else + break; + posL++; + } + int numL = temp.toInt(); + temp.clear(); + + while (posR < nameRight.size()) { + if (nameRight.at(posR).isDigit()) + temp += nameRight.at(posR); + else + break; + posR++; + } + int numR = temp.toInt(); + + if (numL != numR) + return numL < numR; + + // Strings + digits do match and we haven't hit string end + // Do another round + nameLeft.remove(0, posL); + nameRight.remove(0, posR); + continue; + + } while (true); + + return QSortFilterProxyModel::lessThan(left, right); + } + return QSortFilterProxyModel::lessThan(left, right); + } +}; + +#endif // TRANSFERLISTSORTMODEL_H diff --git a/src/transferlistwidget.cpp b/src/transferlistwidget.cpp index a8326097a..c7205325a 100644 --- a/src/transferlistwidget.cpp +++ b/src/transferlistwidget.cpp @@ -91,7 +91,7 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *main_window, statusFilterModel->setFilterKeyColumn(TorrentModelItem::TR_STATUS); statusFilterModel->setFilterRole(Qt::DisplayRole); - nameFilterModel = new QSortFilterProxyModel(); + nameFilterModel = new TransferListSortModel(); nameFilterModel->setDynamicSortFilter(true); nameFilterModel->setSourceModel(statusFilterModel); nameFilterModel->setFilterKeyColumn(TorrentModelItem::TR_NAME); diff --git a/src/transferlistwidget.h b/src/transferlistwidget.h index 98fd23530..d8971c308 100644 --- a/src/transferlistwidget.h +++ b/src/transferlistwidget.h @@ -34,6 +34,7 @@ #include #include #include "qtorrenthandle.h" +#include "transferlistsortmodel.h" class QBtSession; class TransferListDelegate; @@ -110,7 +111,7 @@ signals: private: TransferListDelegate *listDelegate; TorrentModel *listModel; - QSortFilterProxyModel *nameFilterModel; + TransferListSortModel *nameFilterModel; QSortFilterProxyModel *statusFilterModel; QSortFilterProxyModel *labelFilterModel; QBtSession* BTSession; From 89e3500a8eef6bdd46cfb175ee7e613d01afc012 Mon Sep 17 00:00:00 2001 From: Nick Tiskov Date: Mon, 8 Jul 2013 18:31:20 +0400 Subject: [PATCH 2/2] Move number-aware comparison logic into misc class. --- src/misc.cpp | 63 +++++++++++++++++++++++++++++++++++++ src/misc.h | 4 +++ src/transferlistsortmodel.h | 59 +++------------------------------- 3 files changed, 71 insertions(+), 55 deletions(-) diff --git a/src/misc.cpp b/src/misc.cpp index e12a50421..6eab2efad 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -521,3 +521,66 @@ QString misc::toQString(time_t t) return QDateTime::fromTime_t(t).toString(Qt::DefaultLocaleLongDate); } #endif + +#ifndef DISABLE_GUI +bool misc::naturalSort(QString left, 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 + do { + int posL = left.indexOf(QRegExp("[0-9]")); + int posR = right.indexOf(QRegExp("[0-9]")); + if (posL == -1 || posR == -1) + break; // No data + else if (posL != posR) + break; // Digit positions mismatch + else if (left.left(posL) != right.left(posR)) + break; // Strings' subsets before digit do not match + + + bool second_digit = false; + if (left.size() > posL + 1) + second_digit = left.at(posL + 1).isDigit(); + if (right.size() > posR + 1) + second_digit = second_digit ? + second_digit : + right.at(posR + 1).isDigit(); + + if (!second_digit) + break; // Single digit in both, normal sort could handle this + + QString temp; + while (posL < left.size()) { + if (left.at(posL).isDigit()) + temp += left.at(posL); + else + break; + posL++; + } + int numL = temp.toInt(); + temp.clear(); + + while (posR < right.size()) { + if (right.at(posR).isDigit()) + temp += right.at(posR); + else + break; + posR++; + } + int numR = temp.toInt(); + + if (numL != numR) { + result = (numL < numR); + return true; + } + + // Strings + digits do match and we haven't hit string end + // Do another round + left.remove(0, posL); + right.remove(0, posR); + continue; + + } while (true); + + return false; +} +#endif diff --git a/src/misc.h b/src/misc.h index e5e490f51..6d9883574 100644 --- a/src/misc.h +++ b/src/misc.h @@ -119,6 +119,10 @@ public: #else static QString toQString(time_t t); #endif + +#ifndef DISABLE_GUI + static bool naturalSort(QString left, QString right, bool& result); +#endif }; // Trick to get a portable sleep() function diff --git a/src/transferlistsortmodel.h b/src/transferlistsortmodel.h index 6f7670552..9e9565529 100644 --- a/src/transferlistsortmodel.h +++ b/src/transferlistsortmodel.h @@ -33,6 +33,7 @@ #include #include "torrentmodel.h" +#include "misc.h" class TransferListSortModel : public QSortFilterProxyModel { Q_OBJECT @@ -50,61 +51,9 @@ protected: Q_ASSERT(vL.isValid()); Q_ASSERT(vR.isValid()); - QString nameLeft = vL.toString(); - QString nameRight = vR.toString(); - - do { - int posL = nameLeft.indexOf(QRegExp("[0-9]")); - int posR = nameRight.indexOf(QRegExp("[0-9]")); - if (posL == -1 || posR == -1) - break; // No data - else if (posL != posR) - break; // Digit positions mismatch - else if (nameLeft.left(posL) != nameRight.left(posR)) - break; // Strings' subsets before digit do not match - - - bool second_digit = false; - if (nameLeft.size() > posL + 1) - second_digit = nameLeft.at(posL + 1).isDigit(); - if (nameRight.size() > posR + 1) - second_digit = second_digit ? - second_digit : - nameRight.at(posR + 1).isDigit(); - - if (!second_digit) - break; // Single digit in both, normal sort could handle this - - QString temp; - while (posL < nameLeft.size()) { - if (nameLeft.at(posL).isDigit()) - temp += nameLeft.at(posL); - else - break; - posL++; - } - int numL = temp.toInt(); - temp.clear(); - - while (posR < nameRight.size()) { - if (nameRight.at(posR).isDigit()) - temp += nameRight.at(posR); - else - break; - posR++; - } - int numR = temp.toInt(); - - if (numL != numR) - return numL < numR; - - // Strings + digits do match and we haven't hit string end - // Do another round - nameLeft.remove(0, posL); - nameRight.remove(0, posR); - continue; - - } while (true); + bool res = false; + if (misc::naturalSort(vL.toString(), vR.toString(), res)) + return res; return QSortFilterProxyModel::lessThan(left, right); }