FListView now has the ability to sort by columns
This commit is contained in:
parent
64c525fc79
commit
851e3db49c
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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() );
|
||||
|
|
Loading…
Reference in New Issue