Decimal number sorting

For help with anything that CEGUI doesn't offer straight out-of-the-box, e.g.:
- Implementation of new features, such as new Core classes, widgets, WindowRenderers, etc. ...
- Modification of any existing features for specific purposes
- Integration of CEGUI in new engines or frameworks and writing of new plugins (Renderer, Parser, ...) or modules

Moderators: CEGUI MVP, CEGUI Team

User avatar
IJs
Just popping in
Just popping in
Posts: 9
Joined: Sun Mar 13, 2005 13:42
Contact:

Decimal number sorting

Postby IJs » Wed Feb 22, 2006 11:58

Hey, I'm using CEGUI 0.4.0 for one of the main projects I work on.

However I stumbled upon a sorting issue within our application. Whenever I sort an entire column in a MultiColumnList, that is made out of ListboxItems that only contains numbers, it is sorted character-wise instead of number-wise.

I'll give you an example. Let's say we have a MultiColumnList that contains a list with servers with 3 columns: "Hostname", "IP" and "Ping". The "Ping" column would contain numbers only, unlike the other ones.

I'll use the following rows for a MultiColumnList.

Code: Select all

[Hostname] - [IP] - [Ping]
Server A - 10.0.0.1 - 4
Server B - 10.0.0.4 - 69
Server C - 10.0.0.9 - 40
Server D - 10.0.0.56 - 10


Now say we wanted to sort on pings. We would click the "Ping" header and in a perfectly coded application this would pop out (4 < 10 < 40 < 69):

Code: Select all

Server A - 10.0.0.1 - 4
Server D - 10.0.0.56 - 10
Server C - 10.0.0.9 - 40
Server B - 10.0.0.4 - 69


However, in my case this would be the result:

Code: Select all

Server D - 10.0.0.56 - 10
Server A - 10.0.0.1 - 4
Server C - 10.0.0.9 - 40
Server B - 10.0.0.4 - 69


You can see the numbers are not interpreted as numbers but as strings/text. I'm passing normal CEGUI::String's containing the numbers to a CEGUI::ListboxTextItem constructor. I have no funky modes set whatsoever cause I don't know any. This is probably where I went wrong.

Bottom line: how do i sort numbers number-wise (1, 2, 3, 4, 5, 25, 33, etc.) instead of string-wise (1, 2, 25, 3, 33, 4, 5, etc.)?

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Postby CrazyEddie » Thu Feb 23, 2006 20:26

You need to subclass ListboxItem or ListboxTextItem and override the virtual operator< and operator> members. This way you should be able to control how items are compared and, thus, how they are sorted.

Of course, depending how you do the implementation, you need to be careful that you are comparing like subclasses ;) (meaning make sure all items in one column are all of the same type).

HTH

CE.

User avatar
IJs
Just popping in
Just popping in
Posts: 9
Joined: Sun Mar 13, 2005 13:42
Contact:

Postby IJs » Fri Feb 24, 2006 22:05

I tried that with the following class structure:

1. CEGUI::ListboxItem
2. CEGUI::ListboxTextItem, derived from CEGUI::ListboxItem
3. MyListboxItem, derived from CEGUI::ListboxTextItem, with the following (overridden?) functions declared:

Code: Select all

   bool   operator <         ( const ListboxItem& rhs ) const;
   bool   operator >         ( const ListboxItem& rhs ) const;


And passing a few new MyListboxItems to the SetItem function:

Code: Select all

MyListboxItem* abc = new MyListboxItem ( ... ); // Strings containing the numbers 0 to 5
MyMCL->setItem ( abc, ..., ... );


Except this just doesn't call my own operators.

I found out that the operators that actually do the work are the < and > operators from MultiColumnList::ListRow.
The < operator contains this line on the end:

Code: Select all

return a.getText() < b.getText();

(a and b are ListboxItems)

So apparently it compares it by calling getText() on the two items instead of calling the operators of the two items. This renders the ListboxItem's own operators useless. Or am I wrong here.

Anyways, I thought it would work if I turned that into the following:

Code: Select all

return a < b;


But that didn't work either :(

Am I missing something?

User avatar
Dalfy
CEGUI Team (Retired)
Posts: 130
Joined: Tue Oct 11, 2005 16:13
Location: Paris, FRANCE
Contact:

Postby Dalfy » Sat Feb 25, 2006 11:40

You have to convert the strings into number. and then compare the number value.


someting like

Code: Select all

int v1 = atoi(a.getText().c_str());
int v2 = atoi(a.getText().c_str());

return v1 < v2;

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Postby CrazyEddie » Sat Feb 25, 2006 12:44

This is actually a bug; the operator for ListRow should be comparing the items and not the Strings held by the items.

CE.

User avatar
IJs
Just popping in
Just popping in
Posts: 9
Joined: Sun Mar 13, 2005 13:42
Contact:

Postby IJs » Sat Feb 25, 2006 15:34

Dalfy wrote:You have to convert the strings into number. and then compare the number value.


someting like

Code: Select all

int v1 = atoi(a.getText().c_str());
int v2 = atoi(a.getText().c_str());

return v1 < v2;


1. I can't override that ListRow function.
2. I can't just make a hack in the CEGUI source, cause that function needs to know whether it's actually a number or a string (no point in using atoi when it doesnt have any numbers). That would mean I'll have to add new functions like IsNumber() and SetNumber().

I constructed virtual > and < operators in the MyListboxItem class (see the code) that should be overriding the CEGUI::ListItem ones.. but they're not :(

Here's the code of one of my custom operators.

Class declarations (header file)

Code: Select all

   bool   operator <         ( const ListboxItem& rhs ) const;

Source definitions (source file)

Code: Select all

bool MyListboxItem::operator < ( const ListboxItem& rhs ) const
{
   bool bReturn = false;

   switch ( ItemType )
   {
      case ITEM_STRING:
         bReturn = d_itemText < rhs.getText();
         break;
      case ITEM_NUMBER:
         bReturn = atoi ( d_itemText.c_str () ) < atoi ( rhs.getText ().c_str () );
         break;
   }

   return bReturn;
}


And yeah, I figured it was a bug.

User avatar
IJs
Just popping in
Just popping in
Posts: 9
Joined: Sun Mar 13, 2005 13:42
Contact:

Postby IJs » Sat Feb 25, 2006 17:02

So I solved the problem.

It turned out, I had to change the (bugged) original code of MultiColumnList::ListRow::operator<(const ListRow& rhs) const:

Code: Select all

return a.getText() < b.getText();


Not to the following (which I had first):

Code: Select all

return a < b;


But to the following:

Code: Select all

return *a < *b;

(Idem dito for the > operator)

That was pretty dumb. I was comparing pointer values in the first CEGUI hack. That resulted in random (non-sorted) columns.

But just before I found out I already added a new class CEGUI::ListboxNumberItem in the CEGUI sources.
So you can either override the virtual functions by making your own class.. or incorporating my CEGUI files in your CEGUI source.

The files can be downloaded here for anyone that may need them:
http://modpro.be/CEGUIListboxNumberItem.cpp
http://modpro.be/CEGUIListboxNumberItem.h

EDIT:

While I'm at it, I also added two ListboxItem derived classes that can contain an Image instead of text only. Pretty basic. Here are the files:
http://modpro.be/CEGUIListboxImageItem.cpp
http://modpro.be/CEGUIListboxImageItem.h

Thanks for your help guys! Feel free to use any of my files if you want to.


Return to “Modifications / Integrations / Customisations”

Who is online

Users browsing this forum: No registered users and 12 guests