Patch by plasm with some modifications by myself: Implement natural sorting

in Tracker. Strings are compared in chunks where the type of each chunk can be
numer or string. Thanks a lot!


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29845 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2009-04-01 09:28:32 +00:00
parent 39f2d9e66b
commit 7ce973c676
3 changed files with 140 additions and 4 deletions
+1 -1
View File
@@ -321,7 +321,7 @@ Model::CompareFolderNamesFirst(const Model *compareModel) const
} else if (resolvedCompareModel->IsDirectory())
return 1;
return strcasecmp(Name(), compareModel->Name());
return NaturalCompare(Name(), compareModel->Name());
}
+137
View File
@@ -136,6 +136,7 @@ class PoseInfo {
BPoint fLocation;
};
// extends PoseInfo adding workspace support; used for desktop
// poses only
class ExtendedPoseInfo {
@@ -176,8 +177,144 @@ class ExtendedPoseInfo {
void DisallowMetaKeys(BTextView *);
void DisallowFilenameKeys(BTextView *);
inline bool
IsDigit(const char c)
{
if ((c >= 48 && c <= 57) || c == 32)
return true;
else
return false;
}
//! Compares two strings naturally, as opposed to lexicographically
inline int
NaturalCompare(const char *s1, const char *s2)
{
struct Chunk {
int32 type;
char* ascii;
// Type = 0
int32 num;
// Type = 1
};
Chunk a;
Chunk b;
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
char bufferA[len1 + 1];
char bufferB[len2 + 1];
uint32 i = 0;
uint32 j = 0;
while (true) {
// determine type of next chunks in each string based on first char
if (i == len1)
a.type = -1;
else if (IsDigit(s1[i]))
a.type = 1;
else
a.type = 0;
if (j == len2)
b.type = -1;
else if (IsDigit(s2[j]))
b.type = 1;
else
b.type = 0;
// check if we reached the end of either string
if (a.type == b.type && a.type == -1)
return 0;
if (a.type == -1)
return -1;
if (b.type == -1)
return 1;
if (a.type != b.type) {
// different chunk types, just compare the remaining strings
return strcasecmp(&s1[i], &s2[j]);
}
// fetch the next chunk for a
if (a.type == 0) {
// string chunk
int32 k = i;
while (!IsDigit(s1[k]) && s1[k] != 0) {
bufferA[k - i] = s1[k];
k++;
}
bufferA[k - i] = 0;
a.ascii = bufferA;
i += k - i;
} else {
// number chunk
int32 k = i;
while (IsDigit(s1[k]) && s1[k] != 0) {
bufferA[k - i] = s1[k];
k++;
}
bufferA[k - i] = 0;
a.ascii = bufferA;
a.num = atoi(bufferA);
i += k - i;
}
// fetch the next chunk for b
if (b.type == 0) {
// string chunk
int32 k = j;
while (!IsDigit(s2[k]) && s2[k] != 0) {
bufferB[k - j] = s2[k];
k++;
}
bufferB[k - j] = 0;
b.ascii = bufferB;
j += k - j;
} else {
// number chunk
int32 k = j;
while (IsDigit(s2[k]) && s2[k] != 0) {
bufferB[k - j] = s2[k];
k++;
}
bufferB[k - j] = 0;
b.ascii = bufferB;
b.num = atoi(bufferB);
j += k - j;
}
// compare the two chunks based on their type
if (a.type == 0) {
// string chunks
int stringCompareResult = strcasecmp(a.ascii, b.ascii);
// if the chunk strings are the same, keep using natural
// sorting for the next chunks
if (stringCompareResult != 0)
return stringCompareResult;
} else {
// number chunks
if (a.num != b.num) {
if (a.num < b.num)
return -1;
if (a.num > b.num)
return 1;
}
}
}
return 0;
}
bool ValidateStream(BMallocIO *, uint32, int32 version);
uint32 HashString(const char *string, uint32 seed);
uint32 AttrHashString(const char *string, uint32 type);
+2 -3
View File
@@ -63,7 +63,6 @@ All rights reserved.
const int32 kGenericReadBufferSize = 1024;
template <class View>
float
TruncStringBase(BString *result, const char *str, int32 length,
@@ -595,7 +594,7 @@ StringAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view)
if (fValueDirty)
ReadValue(&fFullValueText);
return strcasecmp(fFullValueText.String(), compareTo->ValueAsText(view));
return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view));
}
@@ -782,7 +781,7 @@ NameAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view)
if (NameAttributeText::sSortFolderNamesFirst)
return fModel->CompareFolderNamesFirst(attr.TargetModel());
return strcasecmp(fFullValueText.String(), compareTo->ValueAsText(view));
return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view));
}