Skip to content

Commit 794e4cd

Browse files
committed
more intuitive ordering of strings. Based on PR #45. Fixes #27
1 parent 3e40b0c commit 794e4cd

File tree

6 files changed

+390
-33
lines changed

6 files changed

+390
-33
lines changed

include/PlotJuggler/alphanum.hpp

+318
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
#ifndef ALPHANUM__HPP
2+
#define ALPHANUM__HPP
3+
4+
/*
5+
The Alphanum Algorithm is an improved sorting algorithm for strings
6+
containing numbers. Instead of sorting numbers in ASCII order like a
7+
standard sort, this algorithm sorts numbers in numeric order.
8+
9+
The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
10+
11+
This implementation is Copyright (c) 2008 Dirk Jagdmann <[email protected]>.
12+
It is a cleanroom implementation of the algorithm and not derived by
13+
other's works. In contrast to the versions written by Dave Koelle this
14+
source code is distributed with the libpng/zlib license.
15+
16+
This software is provided 'as-is', without any express or implied
17+
warranty. In no event will the authors be held liable for any damages
18+
arising from the use of this software.
19+
20+
Permission is granted to anyone to use this software for any purpose,
21+
including commercial applications, and to alter it and redistribute it
22+
freely, subject to the following restrictions:
23+
24+
1. The origin of this software must not be misrepresented; you
25+
must not claim that you wrote the original software. If you use
26+
this software in a product, an acknowledgment in the product
27+
documentation would be appreciated but is not required.
28+
29+
2. Altered source versions must be plainly marked as such, and
30+
must not be misrepresented as being the original software.
31+
32+
3. This notice may not be removed or altered from any source
33+
distribution. */
34+
35+
/* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */
36+
37+
#include <cassert>
38+
#include <functional>
39+
#include <string>
40+
#include <sstream>
41+
42+
#ifdef ALPHANUM_LOCALE
43+
#include <cctype>
44+
#endif
45+
46+
#ifdef DOJDEBUG
47+
#include <iostream>
48+
#include <typeinfo>
49+
#endif
50+
51+
// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal.
52+
53+
namespace doj
54+
{
55+
56+
// anonymous namespace for functions we use internally. But if you
57+
// are coding in C, you can use alphanum_impl() directly, since it
58+
// uses not C++ features.
59+
namespace {
60+
61+
// if you want to honour the locale settings for detecting digit
62+
// characters, you should define ALPHANUM_LOCALE
63+
#ifdef ALPHANUM_LOCALE
64+
/** wrapper function for ::isdigit() */
65+
bool alphanum_isdigit(int c)
66+
{
67+
return isdigit(c);
68+
}
69+
#else
70+
/** this function does not consider the current locale and only
71+
works with ASCII digits.
72+
@return true if c is a digit character
73+
*/
74+
bool alphanum_isdigit(const char c)
75+
{
76+
return c>='0' && c<='9';
77+
}
78+
#endif
79+
80+
/**
81+
compare l and r with strcmp() semantics, but using
82+
the "Alphanum Algorithm". This function is designed to read
83+
through the l and r strings only one time, for
84+
maximum performance. It does not allocate memory for
85+
substrings. It can either use the C-library functions isdigit()
86+
and atoi() to honour your locale settings, when recognizing
87+
digit characters when you "#define ALPHANUM_LOCALE=1" or use
88+
it's own digit character handling which only works with ASCII
89+
digit characters, but provides better performance.
90+
91+
@param l NULL-terminated C-style string
92+
@param r NULL-terminated C-style string
93+
@return negative if l<r, 0 if l equals r, positive if l>r
94+
*/
95+
int alphanum_impl(const char *l, const char *r)
96+
{
97+
enum mode_t { STRING, NUMBER } mode=STRING;
98+
99+
while(*l && *r)
100+
{
101+
if(mode == STRING)
102+
{
103+
char l_char, r_char;
104+
while((l_char=*l) && (r_char=*r))
105+
{
106+
// check if this are digit characters
107+
const bool l_digit=alphanum_isdigit(l_char), r_digit=alphanum_isdigit(r_char);
108+
// if both characters are digits, we continue in NUMBER mode
109+
if(l_digit && r_digit)
110+
{
111+
mode=NUMBER;
112+
break;
113+
}
114+
// if only the left character is a digit, we have a result
115+
if(l_digit) return -1;
116+
// if only the right character is a digit, we have a result
117+
if(r_digit) return +1;
118+
// compute the difference of both characters
119+
const int diff=l_char - r_char;
120+
// if they differ we have a result
121+
if(diff != 0) return diff;
122+
// otherwise process the next characters
123+
++l;
124+
++r;
125+
}
126+
}
127+
else // mode==NUMBER
128+
{
129+
#ifdef ALPHANUM_LOCALE
130+
// get the left number
131+
char *end;
132+
unsigned long l_int=strtoul(l, &end, 0);
133+
l=end;
134+
135+
// get the right number
136+
unsigned long r_int=strtoul(r, &end, 0);
137+
r=end;
138+
#else
139+
// get the left number
140+
unsigned long l_int=0;
141+
while(*l && alphanum_isdigit(*l))
142+
{
143+
// TODO: this can overflow
144+
l_int=l_int*10 + *l-'0';
145+
++l;
146+
}
147+
148+
// get the right number
149+
unsigned long r_int=0;
150+
while(*r && alphanum_isdigit(*r))
151+
{
152+
// TODO: this can overflow
153+
r_int=r_int*10 + *r-'0';
154+
++r;
155+
}
156+
#endif
157+
158+
// if the difference is not equal to zero, we have a comparison result
159+
const long diff=l_int-r_int;
160+
if(diff != 0)
161+
return diff;
162+
163+
// otherwise we process the next substring in STRING mode
164+
mode=STRING;
165+
}
166+
}
167+
168+
if(*r) return -1;
169+
if(*l) return +1;
170+
return 0;
171+
}
172+
173+
}
174+
175+
/**
176+
Compare left and right with the same semantics as strcmp(), but with the
177+
"Alphanum Algorithm" which produces more human-friendly
178+
results. The classes lT and rT must implement "std::ostream
179+
operator<< (std::ostream&, const Ty&)".
180+
181+
@return negative if left<right, 0 if left==right, positive if left>right.
182+
*/
183+
template <typename lT, typename rT>
184+
int alphanum_comp(const lT& left, const rT& right)
185+
{
186+
#ifdef DOJDEBUG
187+
std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl;
188+
#endif
189+
std::ostringstream l; l << left;
190+
std::ostringstream r; r << right;
191+
return alphanum_impl(l.str().c_str(), r.str().c_str());
192+
}
193+
194+
/**
195+
Compare l and r with the same semantics as strcmp(), but with
196+
the "Alphanum Algorithm" which produces more human-friendly
197+
results.
198+
199+
@return negative if l<r, 0 if l==r, positive if l>r.
200+
*/
201+
template <>
202+
int alphanum_comp<std::string>(const std::string& l, const std::string& r)
203+
{
204+
#ifdef DOJDEBUG
205+
std::clog << "alphanum_comp<std::string,std::string> " << l << "," << r << std::endl;
206+
#endif
207+
return alphanum_impl(l.c_str(), r.c_str());
208+
}
209+
210+
////////////////////////////////////////////////////////////////////////////
211+
212+
// now follow a lot of overloaded alphanum_comp() functions to get a
213+
// direct call to alphanum_impl() upon the various combinations of c
214+
// and c++ strings.
215+
216+
/**
217+
Compare l and r with the same semantics as strcmp(), but with
218+
the "Alphanum Algorithm" which produces more human-friendly
219+
results.
220+
221+
@return negative if l<r, 0 if l==r, positive if l>r.
222+
*/
223+
int alphanum_comp(char* l, char* r)
224+
{
225+
assert(l);
226+
assert(r);
227+
#ifdef DOJDEBUG
228+
std::clog << "alphanum_comp<char*,char*> " << l << "," << r << std::endl;
229+
#endif
230+
return alphanum_impl(l, r);
231+
}
232+
233+
int alphanum_comp(const char* l, const char* r)
234+
{
235+
assert(l);
236+
assert(r);
237+
#ifdef DOJDEBUG
238+
std::clog << "alphanum_comp<const char*,const char*> " << l << "," << r << std::endl;
239+
#endif
240+
return alphanum_impl(l, r);
241+
}
242+
243+
int alphanum_comp(char* l, const char* r)
244+
{
245+
assert(l);
246+
assert(r);
247+
#ifdef DOJDEBUG
248+
std::clog << "alphanum_comp<char*,const char*> " << l << "," << r << std::endl;
249+
#endif
250+
return alphanum_impl(l, r);
251+
}
252+
253+
int alphanum_comp(const char* l, char* r)
254+
{
255+
assert(l);
256+
assert(r);
257+
#ifdef DOJDEBUG
258+
std::clog << "alphanum_comp<const char*,char*> " << l << "," << r << std::endl;
259+
#endif
260+
return alphanum_impl(l, r);
261+
}
262+
263+
int alphanum_comp(const std::string& l, char* r)
264+
{
265+
assert(r);
266+
#ifdef DOJDEBUG
267+
std::clog << "alphanum_comp<std::string,char*> " << l << "," << r << std::endl;
268+
#endif
269+
return alphanum_impl(l.c_str(), r);
270+
}
271+
272+
int alphanum_comp(char* l, const std::string& r)
273+
{
274+
assert(l);
275+
#ifdef DOJDEBUG
276+
std::clog << "alphanum_comp<char*,std::string> " << l << "," << r << std::endl;
277+
#endif
278+
return alphanum_impl(l, r.c_str());
279+
}
280+
281+
int alphanum_comp(const std::string& l, const char* r)
282+
{
283+
assert(r);
284+
#ifdef DOJDEBUG
285+
std::clog << "alphanum_comp<std::string,const char*> " << l << "," << r << std::endl;
286+
#endif
287+
return alphanum_impl(l.c_str(), r);
288+
}
289+
290+
int alphanum_comp(const char* l, const std::string& r)
291+
{
292+
assert(l);
293+
#ifdef DOJDEBUG
294+
std::clog << "alphanum_comp<const char*,std::string> " << l << "," << r << std::endl;
295+
#endif
296+
return alphanum_impl(l, r.c_str());
297+
}
298+
299+
////////////////////////////////////////////////////////////////////////////
300+
301+
/**
302+
Functor class to compare two objects with the "Alphanum
303+
Algorithm". If the objects are no std::string, they must
304+
implement "std::ostream operator<< (std::ostream&, const Ty&)".
305+
*/
306+
template<class Ty>
307+
struct alphanum_less : public std::binary_function<Ty, Ty, bool>
308+
{
309+
bool operator()(const Ty& left, const Ty& right) const
310+
{
311+
return alphanum_comp(left, right) < 0;
312+
}
313+
};
314+
315+
}
316+
317+
318+
#endif

plotter_gui/filterablelistwidget.cpp

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "filterablelistwidget.h"
22
#include "ui_filterablelistwidget.h"
3+
#include <PlotJuggler/alphanum.hpp>
34
#include <QDebug>
45
#include <QLayoutItem>
56
#include <QMenu>
@@ -93,8 +94,22 @@ void FilterableListWidget::clear()
9394
ui->labelNumberDisplayed->setText( "0 of 0");
9495
}
9596

96-
void FilterableListWidget::addItem(QTableWidgetItem *item)
97+
class CustomSortedTableItem: public QTableWidgetItem
9798
{
99+
100+
public:
101+
CustomSortedTableItem(const QString& name): QTableWidgetItem(name) {}
102+
103+
bool operator< (const QTableWidgetItem &other) const
104+
{
105+
return doj::alphanum_impl(this->text().toLocal8Bit().constData(),
106+
other.text().toLocal8Bit().constData()) < 0;
107+
}
108+
};
109+
110+
void FilterableListWidget::addItem(const QString &item_name)
111+
{
112+
auto item = new CustomSortedTableItem(item_name);
98113
const int row = rowCount();
99114
ui->tableWidget->setRowCount(row+1);
100115
ui->tableWidget->setItem(row, 0, item);
@@ -105,7 +120,9 @@ void FilterableListWidget::addItem(QTableWidgetItem *item)
105120
val_cell->setFont( QFontDatabase::systemFont(QFontDatabase::FixedFont) );
106121

107122
ui->tableWidget->setItem(row, 1, val_cell );
108-
addToCompletionTree(item);
123+
ui->tableWidget->sortByColumn(0,Qt::AscendingOrder);
124+
125+
addToCompletionTree(item);
109126
}
110127

111128

plotter_gui/filterablelistwidget.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ class FilterableListWidget : public QWidget
2727

2828
void clear();
2929

30-
void addItem(QTableWidgetItem *item);
31-
32-
void addItems(const QStringList& index_list);
30+
void addItem(const QString& item_name);
3331

3432
QList<int> findRowsByName(const QString& text) const;
3533

0 commit comments

Comments
 (0)