FListView now has the ability to sort by columns

This commit is contained in:
Markus Gans 2018-09-28 06:45:02 +02:00
parent 64c525fc79
commit 851e3db49c
8 changed files with 359 additions and 2 deletions

View File

@ -1,3 +1,6 @@
2018-09-28 Markus Gans <guru.mail@muenster.de>
* FListView now has the ability to sort by columns
2018-09-27 Markus Gans <guru.mail@muenster.de>
* Move time event processing from FApplication to FObject

View File

@ -80,6 +80,18 @@ Listview::Listview (finalcut::FWidget* parent)
listView->setColumnAlignment (4, finalcut::fc::alignRight);
listView->setColumnAlignment (5, finalcut::fc::alignRight);
// Set the type of sorting
listView->setColumnSortType (1, finalcut::fc::by_name);
listView->setColumnSortType (2, finalcut::fc::by_name);
listView->setColumnSortType (3, finalcut::fc::by_number);
listView->setColumnSortType (4, finalcut::fc::by_number);
listView->setColumnSortType (5, finalcut::fc::by_number);
// Sort in ascending order by the 1st column
listView->setColumnSort (1, finalcut::fc::ascending);
// The sorting occurs later automatically at insert().
// Otherwise you could start the sorting directly with sort()
// Populate FListView with a list of items
populate (listView);

View File

@ -33,7 +33,7 @@ bool sortByName ( const FFileDialog::dir_entry& lhs
, const FFileDialog::dir_entry& rhs )
{
// lhs < rhs
return bool(strcasecmp(lhs.name, rhs.name) < 0);
return bool( strcasecmp(lhs.name, rhs.name) < 0 );
}
//----------------------------------------------------------------------

View File

@ -34,6 +34,118 @@ namespace finalcut
// Static class attribute
FObject::FObjectIterator FListView::null_iter;
// Function prototypes
long firstNumberFromString (const FString&);
bool sortAscendingByName (const FObject*, const FObject*);
bool sortDescendingByName (const FObject*, const FObject*);
bool sortAscendingByNumber (const FObject*, const FObject*);
bool sortDescendingByNumber (const FObject*, const FObject*);
// non-member functions
//----------------------------------------------------------------------
long firstNumberFromString (const FString& str)
{
const FString::iterator last = str.end();
FString::iterator iter = str.begin();
FString::iterator first_pos;
FString::iterator last_pos;
long number;
while ( iter != last )
{
if ( wchar_t(*iter) >= L'0' && wchar_t(*iter) <= L'9' )
break;
++iter;
}
first_pos = iter;
if ( first_pos == last )
return 0;
while ( iter != last )
{
if ( wchar_t(*iter) < L'0' || wchar_t(*iter) > L'9' )
break;
++iter;
}
last_pos = iter;
if ( last_pos == last )
return 0;
uInt pos = uInt(std::distance(str.begin(), first_pos)) + 1;
uInt length = uInt(std::distance(first_pos, last_pos));
const FString num_str = str.mid(pos, length);
try
{
number = num_str.toLong();
}
catch (const std::exception&)
{
return 0;
}
return number;
}
//----------------------------------------------------------------------
bool sortAscendingByName (const FObject* lhs, const FObject* rhs)
{
const FListViewItem* l_item = static_cast<const FListViewItem*>(lhs);
const FListViewItem* r_item = static_cast<const FListViewItem*>(rhs);
const int column = l_item->getSortColumn();
const FString l_string = l_item->getText(column);
const FString r_string = r_item->getText(column);
// lhs < rhs
return bool( strcasecmp(l_string.c_str(), r_string.c_str()) < 0 );
}
//----------------------------------------------------------------------
bool sortDescendingByName (const FObject* lhs, const FObject* rhs)
{
const FListViewItem* l_item = static_cast<const FListViewItem*>(lhs);
const FListViewItem* r_item = static_cast<const FListViewItem*>(rhs);
const int column = l_item->getSortColumn();
const FString l_string = l_item->getText(column);
const FString r_string = r_item->getText(column);
// lhs > rhs
return bool( strcasecmp(l_string.c_str(), r_string.c_str()) > 0 );
}
//----------------------------------------------------------------------
bool sortAscendingByNumber (const FObject* lhs, const FObject* rhs)
{
const FListViewItem* l_item = static_cast<const FListViewItem*>(lhs);
const FListViewItem* r_item = static_cast<const FListViewItem*>(rhs);
const int column = l_item->getSortColumn();
const long l_number = firstNumberFromString(l_item->getText(column));
const long r_number = firstNumberFromString(r_item->getText(column));
// lhs < rhs
return bool( l_number < r_number );
}
//----------------------------------------------------------------------
bool sortDescendingByNumber (const FObject* lhs, const FObject* rhs)
{
const FListViewItem* l_item = static_cast<const FListViewItem*>(lhs);
const FListViewItem* r_item = static_cast<const FListViewItem*>(rhs);
const int column = l_item->getSortColumn();
const long l_number = firstNumberFromString(l_item->getText(column));
const long r_number = firstNumberFromString(r_item->getText(column));
// lhs > rhs
return bool( l_number > r_number );
}
//----------------------------------------------------------------------
// class FListViewItem
//----------------------------------------------------------------------
@ -44,6 +156,7 @@ FListViewItem::FListViewItem (const FListViewItem& item)
: FObject(item.getParent())
, column_list(item.column_list)
, data_pointer(item.data_pointer)
, root()
, visible_lines(1)
, expandable(false)
, is_expand(false)
@ -68,6 +181,7 @@ FListViewItem::FListViewItem (FObjectIterator parent_iter)
: FObject((*parent_iter)->getParent())
, column_list()
, data_pointer(0)
, root()
, visible_lines(1)
, expandable(false)
, is_expand(false)
@ -82,6 +196,7 @@ FListViewItem::FListViewItem ( const FStringList& cols
: FObject(0)
, column_list(cols)
, data_pointer(data)
, root()
, visible_lines(1)
, expandable(false)
, is_expand(false)
@ -99,6 +214,16 @@ FListViewItem::~FListViewItem() // destructor
// public methods of FListViewItem
//----------------------------------------------------------------------
int FListViewItem::getSortColumn() const
{
if ( ! *root )
return -1;
FListView* root_obj = static_cast<FListView*>(*root);
return root_obj->getSortColumn();
}
//----------------------------------------------------------------------
FString FListViewItem::getText (int column) const
{
@ -208,11 +333,40 @@ void FListViewItem::collapse()
}
// private methods of FListView
//----------------------------------------------------------------------
template<typename Compare>
void FListViewItem::sort (Compare cmp)
{
if ( ! expandable )
return;
// Sort the top level
FObject::FObjectList& children_list = getChildren();
if ( ! children_list.empty() )
children_list.sort(cmp);
// Sort the sublevels
FListViewIterator iter = children_list.begin();
while ( iter != children_list.end() )
{
if ( *iter )
{
FListViewItem* item = static_cast<FListViewItem*>(*iter);
item->sort(cmp);
}
++iter;
}
}
//----------------------------------------------------------------------
FObject::FObjectIterator FListViewItem::appendItem (FListViewItem* child)
{
expandable = true;
resetVisibleLineCounter();
child->root = root;
addChild (child);
// Return iterator to child/last element
return --FObject::end();
@ -451,6 +605,11 @@ FListView::FListView (FWidget* parent)
, xoffset(0)
, nf_offset(0)
, max_line_width(1)
, sort_column(-1)
, sort_type()
, sort_order(fc::unsorted)
, user_defined_ascending(0)
, user_defined_descending(0)
{
init();
}
@ -504,6 +663,24 @@ FString FListView::getColumnText (int column) const
return header[uInt(column)].name;
}
//----------------------------------------------------------------------
fc::sorting_type FListView::getColumnSortType (int column) const
{
fc::sorting_type type;
std::size_t size = uInt(column);
try
{
type = sort_type.at(size);
}
catch (const std::out_of_range&)
{
type = fc::unknown;
}
return type;
}
//----------------------------------------------------------------------
void FListView::setGeometry (int x, int y, int w, int h, bool adjust)
{
@ -556,6 +733,34 @@ void FListView::setColumnText (int column, const FString& label)
header[uInt(column)].name = label;
}
//----------------------------------------------------------------------
void FListView::setColumnSortType (int column, fc::sorting_type type)
{
// Sets the sort type by which the list is to be sorted
if ( column < 1 || header.empty() || column > int(header.size()) )
return;
std::size_t size = uInt(column + 1);
if ( sort_type.empty() || sort_type.size() < size )
sort_type.resize(size);
sort_type[uInt(column)] = type;
}
//----------------------------------------------------------------------
void FListView::setColumnSort (int column, fc::sorting_order order)
{
// Sets the column to sort by + the sorting order
if ( column < 1 || header.empty() || column > int(header.size()) )
column = -1;
sort_column = column;
sort_order = order;
}
//----------------------------------------------------------------------
int FListView::addColumn (const FString& label, int width)
{
@ -649,6 +854,9 @@ FObject::FObjectIterator FListView::insert ( FListViewItem* item
first_visible_line = itemlist.begin();
}
// Sort list by a column (only if activated)
sort();
int element_count = int(getCount());
recalculateVerticalBar (element_count);
return item_iter;
@ -700,6 +908,55 @@ FObject::FObjectIterator FListView::insert ( const std::vector<long>& cols
return item_iter;
}
//----------------------------------------------------------------------
void FListView::sort()
{
// Sorts the list view according to the specified setting
if ( sort_column < 1 && sort_column > int(header.size()) )
return;
switch ( getColumnSortType(sort_column) )
{
case fc::unknown:
case fc::by_name:
if ( sort_order == fc::ascending )
{
sort (sortAscendingByName);
}
else if ( sort_order == fc::descending )
{
sort (sortDescendingByName);
}
break;
case fc::by_number:
if ( sort_order == fc::ascending )
{
sort (sortAscendingByNumber);
}
else if ( sort_order == fc::descending )
{
sort (sortDescendingByNumber);
}
break;
case fc::user_defined:
if ( sort_order == fc::ascending && user_defined_ascending )
{
sort (user_defined_ascending);
}
else if ( sort_order == fc::descending && user_defined_descending )
{
sort (user_defined_descending);
}
break;
}
current_iter = itemlist.begin();
first_visible_line = itemlist.begin();
}
//----------------------------------------------------------------------
void FListView::onKeyPress (FKeyEvent* ev)
{
@ -1186,6 +1443,28 @@ void FListView::init()
setRightPadding(1 + nf_offset);
}
//----------------------------------------------------------------------
template<typename Compare>
void FListView::sort (Compare cmp)
{
// Sort the top level
itemlist.sort(cmp);
// Sort the sublevels
FListViewIterator iter = itemlist.begin();
while ( iter != itemlist.end() )
{
if ( *iter )
{
FListViewItem* item = static_cast<FListViewItem*>(*iter);
item->sort(cmp);
}
++iter;
}
}
//----------------------------------------------------------------------
uInt FListView::getAlignOffset ( fc::text_alignment align
, uInt txt_length
@ -1748,6 +2027,7 @@ void FListView::stopDragScroll()
//----------------------------------------------------------------------
FObject::FObjectIterator FListView::appendItem (FListViewItem* item)
{
item->root = root;
addChild (item);
itemlist.push_back (item);
return --itemlist.end();

View File

@ -1039,6 +1039,21 @@ enum sides
left = 3
};
enum sorting_type
{
by_name,
by_number,
user_defined,
unknown
};
enum sorting_order
{
ascending,
descending,
unsorted
};
enum brackets_type
{
NoBrackets = 0,

View File

@ -92,6 +92,7 @@ class FListViewItem : public FObject
// Accessors
const char* getClassName() const;
uInt getColumnCount() const;
int getSortColumn() const;
FString getText (int) const;
FWidget::data_ptr getData() const;
uInt getDepth() const;
@ -114,6 +115,8 @@ class FListViewItem : public FObject
bool isExpandable() const;
// Methods
template<typename Compare>
void sort (Compare);
FObjectIterator appendItem (FListViewItem*);
void replaceControlCodes();
int getVisibleLines();
@ -122,6 +125,7 @@ class FListViewItem : public FObject
// Data Members
FStringList column_list;
FWidget::data_ptr data_pointer;
FObjectIterator root;
int visible_lines;
bool expandable;
bool is_expand;
@ -259,12 +263,23 @@ class FListView : public FWidget
uInt getCount();
fc::text_alignment getColumnAlignment (int) const;
FString getColumnText (int) const;
fc::sorting_type getColumnSortType (int) const;
fc::sorting_order getSortOrder() const;
int getSortColumn() const;
FListViewItem* getCurrentItem();
// Mutators
virtual void setGeometry (int, int, int, int, bool = true);
void setColumnAlignment (int, fc::text_alignment);
void setColumnText (int, const FString&);
void setColumnSortType (int,fc::sorting_type \
= fc::by_name);
void setColumnSort (int, fc::sorting_order \
= fc::ascending);
template<typename Compare>
void setUserAscendingCompare (Compare);
template<typename Compare>
void setUserDescendingCompare (Compare);
bool setTreeView (bool);
bool setTreeView();
bool unsetTreeView();
@ -289,6 +304,7 @@ class FListView : public FWidget
, FObjectIterator );
FObjectIterator beginOfList();
FObjectIterator endOfList();
virtual void sort();
// Event handlers
virtual void onKeyPress (FKeyEvent*);
@ -313,6 +329,7 @@ class FListView : public FWidget
// Typedef
struct Header; // forward declaration
typedef std::vector<Header> headerItems;
typedef std::vector<fc::sorting_type> sortTypes;
// Constants
static const int USE_MAX_SIZE = -1;
@ -325,6 +342,8 @@ class FListView : public FWidget
// Methods
void init();
template<typename Compare>
void sort (Compare);
uInt getAlignOffset (fc::text_alignment, uInt, uInt);
virtual void draw();
void drawColumnLabels();
@ -389,6 +408,11 @@ class FListView : public FWidget
int xoffset;
int nf_offset;
int max_line_width;
int sort_column;
sortTypes sort_type;
fc::sorting_order sort_order;
bool (*user_defined_ascending) (const FObject*, const FObject*);
bool (*user_defined_descending) (const FObject*, const FObject*);
// Friend class
friend class FListViewItem;
@ -425,10 +449,28 @@ struct FListView::Header
inline const char* FListView::getClassName() const
{ return "FListView"; }
//----------------------------------------------------------------------
inline fc::sorting_order FListView::getSortOrder() const
{ return sort_order; }
//----------------------------------------------------------------------
inline int FListView::getSortColumn() const
{ return sort_column; }
//----------------------------------------------------------------------
inline FListViewItem* FListView::getCurrentItem()
{ return static_cast<FListViewItem*>(*current_iter); }
//----------------------------------------------------------------------
template<typename Compare>
inline void FListView::setUserAscendingCompare (Compare cmp)
{ user_defined_ascending = cmp; }
//----------------------------------------------------------------------
template<typename Compare>
inline void FListView::setUserDescendingCompare (Compare cmp)
{ user_defined_descending = cmp; }
//----------------------------------------------------------------------
inline bool FListView::setTreeView (bool on)
{ return tree_view = ( on ) ? true : false; }

View File

@ -74,6 +74,7 @@ class FObject
virtual const char* getClassName() const;
FObject* getParent() const;
FObject* getChild (int) const;
FObjectList& getChildren();
const FObjectList& getChildren() const;
int numOfChildren() const;
FObjectIterator begin();
@ -156,6 +157,10 @@ inline const char* FObject::getClassName() const
inline FObject* FObject::getParent() const
{ return parent_obj; }
//----------------------------------------------------------------------
inline FObject::FObjectList& FObject::getChildren()
{ return children_list; }
//----------------------------------------------------------------------
inline const FObject::FObjectList& FObject::getChildren() const
{ return children_list; }

View File

@ -137,7 +137,7 @@ void FObjectTest::noArgumentTest()
CPPUNIT_ASSERT ( o1.getChild(0) == 0 );
CPPUNIT_ASSERT ( o1.getChild(1) == 0 );
CPPUNIT_ASSERT ( o1.numOfChildren() == 0 );
const finalcut::FObject::FObjectList& children_list = o1.getChildren();
finalcut::FObject::FObjectList& children_list = o1.getChildren();
CPPUNIT_ASSERT ( children_list.begin() == o1.begin() );
CPPUNIT_ASSERT ( children_list.begin() == o1.end() );
CPPUNIT_ASSERT ( children_list.end() == o1.begin() );