diff --git a/sources/TerminalStrip/UndoCommand/sortterminalstripcommand.cpp b/sources/TerminalStrip/UndoCommand/sortterminalstripcommand.cpp index 125e48507..7abf06699 100644 --- a/sources/TerminalStrip/UndoCommand/sortterminalstripcommand.cpp +++ b/sources/TerminalStrip/UndoCommand/sortterminalstripcommand.cpp @@ -19,6 +19,7 @@ #include "../terminalstrip.h" #include "../physicalterminal.h" #include "../realterminal.h" +#include "../../utils/qetutils.h" SortTerminalStripCommand::SortTerminalStripCommand(TerminalStrip *strip, QUndoCommand *parent) : QUndoCommand(parent), @@ -46,46 +47,16 @@ void SortTerminalStripCommand::redo() void SortTerminalStripCommand::sort() { - std::sort(m_new_order.begin(), m_new_order.end(), [](QSharedPointer arg1, QSharedPointer arg2) + auto label_of = [](const QSharedPointer &t) -> QString { - const QRegularExpression rx(QStringLiteral("^\\d+")); + return t->realTerminalCount() ? t->realTerminals().constLast()->label() + : QString(); + }; - QString str1; - QString str2; - int int1 =-1; - int int2 =-1; - - if (arg1->realTerminalCount()) - { - str1 = arg1->realTerminals().constLast()->label(); - - auto match = rx.match(str1); - if (match.hasMatch()) { - int1 = match.captured(0).toInt(); - } - } - - if (arg2->realTerminalCount()) - { - str2 = arg2->realTerminals().constLast()->label(); - - auto match = rx.match(str2); - if (match.hasMatch()) { - int2 = match.captured(0).toInt(); - } - } - - //Sort as numbers if both string - //start at least by a digit and - //the number of each string are different. - //Else sort as string - if (int1 >= 0 && - int2 >= 0 && - int1 != int2) { - return int1 &arg1, + const QSharedPointer &arg2) + { + return QETUtils::naturalLessThan(label_of(arg1), label_of(arg2)); }); } diff --git a/sources/TerminalStrip/ui/freeterminalmodel.cpp b/sources/TerminalStrip/ui/freeterminalmodel.cpp index 7bfe62674..02e5dfb2a 100644 --- a/sources/TerminalStrip/ui/freeterminalmodel.cpp +++ b/sources/TerminalStrip/ui/freeterminalmodel.cpp @@ -400,7 +400,7 @@ void FreeTerminalModel::fillTerminalVector() std::sort(free_terminal_vector.begin(), free_terminal_vector.end(), [](TerminalElement *a, TerminalElement *b) { - return QETUtils::sortBeginIntString(a->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString(), + return QETUtils::naturalLessThan(a->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString(), b->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString()); }); diff --git a/sources/TerminalStrip/ui/terminalstriptreedockwidget.cpp b/sources/TerminalStrip/ui/terminalstriptreedockwidget.cpp index a4c3576a8..2278d1102 100644 --- a/sources/TerminalStrip/ui/terminalstriptreedockwidget.cpp +++ b/sources/TerminalStrip/ui/terminalstriptreedockwidget.cpp @@ -27,6 +27,7 @@ #include "../../qetgraphicsitem/terminalelement.h" #include "../terminalstrip.h" #include "../../qetinformation.h" +#include "../../utils/qetutils.h" #include "freeterminalmodel.h" #include @@ -257,7 +258,7 @@ void TerminalStripTreeDockWidget::buildTree() auto ts_vector = m_project->terminalStrip(); std::sort(ts_vector.begin(), ts_vector.end(), [](TerminalStrip *a, TerminalStrip *b) { - return a->name() < b->name(); + return QETUtils::naturalLessThan(a->name(), b->name()); }); for (const auto &ts : std::as_const(ts_vector)) { @@ -355,9 +356,9 @@ void TerminalStripTreeDockWidget::addFreeTerminal() //Sort the terminal element by label std::sort(vector_.begin(), vector_.end(), [](TerminalElement *a, TerminalElement *b) { - return a->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString() - < - b->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString(); + return QETUtils::naturalLessThan( + a->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString(), + b->elementData().m_informations.value(QETInformation::ELMT_LABEL).toString()); }); auto free_terminal_item = ui->m_tree_view->topLevelItem(1); diff --git a/sources/utils/qetutils.cpp b/sources/utils/qetutils.cpp index da4f43126..e62723e1a 100644 --- a/sources/utils/qetutils.cpp +++ b/sources/utils/qetutils.cpp @@ -133,6 +133,77 @@ bool QETUtils::sortBeginIntString(const QString &str_a, const QString &str_b) } } +/** + * @brief QETUtils::naturalLessThan + * Natural order comparison, case insensitive : + * "2" < "10" and "XAUZ1:2" < "XAUZ1:10". + * Runs of digits are compared by value wherever they are in the string. + * Implemented by hand because QCollator numeric mode is not honored by + * every Qt backend (it is ignored without ICU). + * The result is a strict weak ordering, so it is safe to use with std::sort. + * @param str_a + * @param str_b + * @return true if str_a must be placed before str_b + */ +bool QETUtils::naturalLessThan(const QString &str_a, const QString &str_b) +{ + const int len_a = str_a.size(); + const int len_b = str_b.size(); + int i = 0; + int j = 0; + int zeros_diff = 0; //Tie breaker : "01" and "1" have the same value + + while (i < len_a && j < len_b) + { + const QChar ca = str_a.at(i); + const QChar cb = str_b.at(j); + + if (ca.isDigit() && cb.isDigit()) + { + int start_a = i; + int start_b = j; + while (i < len_a && str_a.at(i).isDigit()) { ++i; } + while (j < len_b && str_b.at(j).isDigit()) { ++j; } + + int sig_a = start_a; + int sig_b = start_b; + while (sig_a < i - 1 && str_a.at(sig_a) == QLatin1Char('0')) { ++sig_a; } + while (sig_b < j - 1 && str_b.at(sig_b) == QLatin1Char('0')) { ++sig_b; } + + const int digits_a = i - sig_a; + const int digits_b = j - sig_b; + if (digits_a != digits_b) { + return digits_a < digits_b; + } + + const int cmp = QStringView(str_a).mid(sig_a, digits_a) + .compare(QStringView(str_b).mid(sig_b, digits_b)); + if (cmp != 0) { + return cmp < 0; + } + + if (zeros_diff == 0) { + zeros_diff = (sig_a - start_a) - (sig_b - start_b); + } + } + else + { + const QChar la = ca.toCaseFolded(); + const QChar lb = cb.toCaseFolded(); + if (la != lb) { + return la < lb; + } + ++i; + ++j; + } + } + + if (i < len_a || j < len_b) { + return (len_a - i) < (len_b - j); //The shortest remaining part first + } + return zeros_diff > 0 ? false : zeros_diff < 0; //"1" before "01" +} + /** * @brief QETUtils::pixelSizedFont * Set the font size to pixelSize instead of pointSize (if needed). diff --git a/sources/utils/qetutils.h b/sources/utils/qetutils.h index 86fef778b..e5677dc73 100644 --- a/sources/utils/qetutils.h +++ b/sources/utils/qetutils.h @@ -59,6 +59,7 @@ namespace QETUtils }; bool sortBeginIntString(const QString &str_a, const QString &str_b); + bool naturalLessThan(const QString &str_a, const QString &str_b); template QVector> sharedVectorToWeak(const QVector> &vector)