File indexing completed on 2023-09-24 04:04:55
0001 /* This file is part of the KDE libraries 0002 Copyright (C) 1999 Ian Zepp (icszepp@islc.net) 0003 Copyright (C) 2006 by Dominic Battre <dominic@battre.de> 0004 Copyright (C) 2006 by Martin Pool <mbp@canonical.com> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Library General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Library General Public License for more details. 0015 0016 You should have received a copy of the GNU Library General Public License 0017 along with this library; see the file COPYING.LIB. If not, write to 0018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 Boston, MA 02110-1301, USA. 0020 */ 0021 0022 #include "kstringhandler_deprecated.h" 0023 0024 int KStringHandler::naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity) 0025 { 0026 // This method chops the input a and b into pieces of 0027 // digits and non-digits (a1.05 becomes a | 1 | . | 05) 0028 // and compares these pieces of a and b to each other 0029 // (first with first, second with second, ...). 0030 // 0031 // This is based on the natural sort order code code by Martin Pool 0032 // http://sourcefrog.net/projects/natsort/ 0033 // Martin Pool agreed to license this under LGPL or GPL. 0034 0035 // FIXME: Using toLower() to implement case insensitive comparison is 0036 // sub-optimal, but is needed because we compare strings with 0037 // localeAwareCompare(), which does not know about case sensitivity. 0038 // A task has been filled for this in Qt Task Tracker with ID 205990. 0039 // http://trolltech.com/developer/task-tracker/index_html?method=entry&id=205990 0040 QString a; 0041 QString b; 0042 if (caseSensitivity == Qt::CaseSensitive) { 0043 a = _a; 0044 b = _b; 0045 } else { 0046 a = _a.toLower(); 0047 b = _b.toLower(); 0048 } 0049 0050 const QChar *currA = a.unicode(); // iterator over a 0051 const QChar *currB = b.unicode(); // iterator over b 0052 0053 if (currA == currB) { 0054 return 0; 0055 } 0056 0057 while (!currA->isNull() && !currB->isNull()) { 0058 const QChar *begSeqA = currA; // beginning of a new character sequence of a 0059 const QChar *begSeqB = currB; 0060 if (currA->unicode() == QChar::ObjectReplacementCharacter) { 0061 return 1; 0062 } 0063 0064 if (currB->unicode() == QChar::ObjectReplacementCharacter) { 0065 return -1; 0066 } 0067 0068 if (currA->unicode() == QChar::ReplacementCharacter) { 0069 return 1; 0070 } 0071 0072 if (currB->unicode() == QChar::ReplacementCharacter) { 0073 return -1; 0074 } 0075 0076 // find sequence of characters ending at the first non-character 0077 while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() && !currA->isSpace()) { 0078 ++currA; 0079 } 0080 0081 while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() && !currB->isSpace()) { 0082 ++currB; 0083 } 0084 0085 // compare these sequences 0086 const QStringRef &subA(a.midRef(begSeqA - a.unicode(), currA - begSeqA)); 0087 const QStringRef &subB(b.midRef(begSeqB - b.unicode(), currB - begSeqB)); 0088 const int cmp = QStringRef::localeAwareCompare(subA, subB); 0089 if (cmp != 0) { 0090 return cmp < 0 ? -1 : +1; 0091 } 0092 0093 if (currA->isNull() || currB->isNull()) { 0094 break; 0095 } 0096 0097 // find sequence of characters ending at the first non-character 0098 while ((currA->isPunct() || currA->isSpace()) && (currB->isPunct() || currB->isSpace())) { 0099 if (*currA != *currB) { 0100 return (*currA < *currB) ? -1 : +1; 0101 } 0102 ++currA; 0103 ++currB; 0104 if (currA->isNull() || currB->isNull()) { 0105 break; 0106 } 0107 } 0108 0109 // now some digits follow... 0110 if ((*currA == QLatin1Char('0')) || (*currB == QLatin1Char('0'))) { 0111 // one digit-sequence starts with 0 -> assume we are in a fraction part 0112 // do left aligned comparison (numbers are considered left aligned) 0113 while (1) { 0114 if (!currA->isDigit() && !currB->isDigit()) { 0115 break; 0116 } else if (!currA->isDigit()) { 0117 return +1; 0118 } else if (!currB->isDigit()) { 0119 return -1; 0120 } else if (*currA < *currB) { 0121 return -1; 0122 } else if (*currA > *currB) { 0123 return + 1; 0124 } 0125 ++currA; 0126 ++currB; 0127 } 0128 } else { 0129 // No digit-sequence starts with 0 -> assume we are looking at some integer 0130 // do right aligned comparison. 0131 // 0132 // The longest run of digits wins. That aside, the greatest 0133 // value wins, but we can't know that it will until we've scanned 0134 // both numbers to know that they have the same magnitude. 0135 0136 bool isFirstRun = true; 0137 int weight = 0; 0138 while (1) { 0139 if (!currA->isDigit() && !currB->isDigit()) { 0140 if (weight != 0) { 0141 return weight; 0142 } 0143 break; 0144 } else if (!currA->isDigit()) { 0145 if (isFirstRun) { 0146 return *currA < *currB ? -1 : +1; 0147 } else { 0148 return -1; 0149 } 0150 } else if (!currB->isDigit()) { 0151 if (isFirstRun) { 0152 return *currA < *currB ? -1 : +1; 0153 } else { 0154 return +1; 0155 } 0156 } else if ((*currA < *currB) && (weight == 0)) { 0157 weight = -1; 0158 } else if ((*currA > *currB) && (weight == 0)) { 0159 weight = + 1; 0160 } 0161 ++currA; 0162 ++currB; 0163 isFirstRun = false; 0164 } 0165 } 0166 } 0167 0168 if (currA->isNull() && currB->isNull()) { 0169 return 0; 0170 } 0171 0172 return currA->isNull() ? -1 : + 1; 0173 } 0174