extern WXDLLIMPEXP_DATA_CORE(const char) wxTreeListCtrlNameStr[];
+class wxTreeListCtrl;
class wxTreeListModel;
class wxTreeListModelNode;
extern WXDLLIMPEXP_DATA_ADV(const wxTreeListItem) wxTLI_FIRST;
extern WXDLLIMPEXP_DATA_ADV(const wxTreeListItem) wxTLI_LAST;
+// ----------------------------------------------------------------------------
+// wxTreeListItemComparator: defines order of wxTreeListCtrl items.
+// ----------------------------------------------------------------------------
+
+class wxTreeListItemComparator
+{
+public:
+ wxTreeListItemComparator() { }
+
+ // The comparison function should return negative, null or positive value
+ // depending on whether the first item is less than, equal to or greater
+ // than the second one. The items should be compared using their values for
+ // the given column.
+ virtual int
+ Compare(wxTreeListCtrl* treelist,
+ unsigned column,
+ wxTreeListItem first,
+ wxTreeListItem second) = 0;
+
+ // Although this class is not used polymorphically by wxWidgets itself,
+ // provide virtual dtor in case it's used like this in the user code.
+ virtual ~wxTreeListItemComparator() { }
+
+private:
+ wxDECLARE_NO_COPY_CLASS(wxTreeListItemComparator);
+};
+
// ----------------------------------------------------------------------------
// wxTreeListCtrl: a control combining wxTree- and wxListCtrl features.
// ----------------------------------------------------------------------------
+ // Sorting.
+ // --------
+
+ // Sort by the given column, either in ascending (default) or descending
+ // sort order.
+ //
+ // By default, simple alphabetical sorting is done by this column contents
+ // but SetItemComparator() may be called to perform comparison in some
+ // other way.
+ void SetSortColumn(unsigned col, bool ascendingOrder = true);
+
+ // If the control contents is sorted, return true and fill the output
+ // parameters with the column which is currently used for sorting and
+ // whether we sort using ascending or descending order. Otherwise, i.e. if
+ // the control contents is unsorted, simply return false.
+ bool GetSortColumn(unsigned* col, bool* ascendingOrder = NULL);
+
+ // Set the object to use for comparing the items. It will be called when
+ // the control is being sorted because the user clicked on a sortable
+ // column.
+ //
+ // The provided pointer is stored by the control so the object it points to
+ // must have a life-time equal or greater to that of the control itself. In
+ // addition, the pointer can be NULL to stop using custom comparator and
+ // revert to the default alphabetical comparison.
+ void SetItemComparator(wxTreeListItemComparator* comparator);
+
+
// View window functions.
// ----------------------
int imageOpened,
wxClientData* data);
- // Send wxTreeListEvent corresponding to the given wxDataViewEvent.
+ // Send wxTreeListEvent corresponding to the given wxDataViewEvent for an
+ // item (as opposed for column-oriented events).
//
// Also updates the original event "skipped" and "vetoed" flags.
- void SendEvent(wxEventType evt, wxDataViewEvent& event);
+ void SendItemEvent(wxEventType evt, wxDataViewEvent& event);
+
+ // Send wxTreeListEvent corresponding to the given column wxDataViewEvent.
+ void SendColumnEvent(wxEventType evt, wxDataViewEvent& event);
// Called by wxTreeListModel when an item is toggled by the user.
void OnItemExpanded(wxDataViewEvent& event);
void OnItemActivated(wxDataViewEvent& event);
void OnItemContextMenu(wxDataViewEvent& event);
+ void OnColumnSorted(wxDataViewEvent& event);
void OnSize(wxSizeEvent& event);
wxDECLARE_EVENT_TABLE();
wxDataViewCtrl* m_view;
wxTreeListModel* m_model;
+ wxTreeListItemComparator* m_comparator;
+
// It calls our inherited protected wxWithImages::GetImage() method.
friend class wxTreeListModel;
class wxTreeListEvent : public wxNotifyEvent
{
public:
- // The item affected by the event.
+ // The item affected by the event. Valid for all events except
+ // column-specific ones such as COLUMN_SORTED.
wxTreeListItem GetItem() const { return m_item; }
// The previous state of the item checkbox for ITEM_CHECKED events only.
wxCheckBoxState GetOldCheckedState() const { return m_oldCheckedState; }
+ // The index of the column affected by the event. Currently only used by
+ // COLUMN_SORTED event.
+ unsigned GetColumn() const { return m_column; }
virtual wxEvent* Clone() const { return new wxTreeListEvent(*this); }
m_item(item)
{
SetEventObject(treelist);
+
+ m_column = static_cast<unsigned>(-1);
+
+ m_oldCheckedState = wxCHK_UNDETERMINED;
}
// Set the checkbox state before this event for ITEM_CHECKED events.
m_oldCheckedState = state;
}
+ // Set the column affected by this event for COLUMN_SORTED events.
+ void SetColumn(unsigned column)
+ {
+ m_column = column;
+ }
+
const wxTreeListItem m_item;
wxCheckBoxState m_oldCheckedState;
+ unsigned m_column;
+
friend class wxTreeListCtrl;
wxDECLARE_ABSTRACT_CLASS(wxTreeListEvent);
#define EVT_TREELIST_ITEM_CONTEXT_MENU(id, fn) \
wxEVT_TREELIST_GENERIC(ITEM_CONTEXT_MENU, id, fn)
+wxDECLARE_TREELIST_EVENT(COLUMN_SORTED);
+#define EVT_TREELIST_COLUMN_SORTED(id, fn) \
+ wxEVT_TREELIST_GENERIC(COLUMN_SORTED, id, fn)
+
#undef wxDECLARE_TREELIST_EVENT
#endif // wxUSE_TREELISTCTRL
bool IsOk() const;
};
+/**
+ Class defining sort order for the items in wxTreeListCtrl.
+
+ @see wxTreeListCtrl
+
+ @library{wxadv}
+ @category{ctrl}
+
+ @since 2.9.3
+ */
+class wxTreeListItemComparator
+{
+public:
+ /**
+ Default constructor.
+
+ Notice that this class is not copyable, comparators are not passed by
+ value.
+ */
+ wxTreeListItemComparator();
+
+ /**
+ Pure virtual function which must be overridden to define sort order.
+
+ The comparison function should return negative, null or positive value
+ depending on whether the first item is less than, equal to or greater
+ than the second one. The items should be compared using their values
+ for the given column.
+
+ @param treelist
+ The control whose contents is being sorted.
+ @param column
+ The column of this control used for sorting.
+ @param first
+ First item to compare.
+ @param second
+ Second item to compare.
+ @return
+ A negative value if the first item is less than (i.e. should appear
+ above) the second one, zero if the two items are equal or a
+ positive value if the first item is greater than (i.e. should
+ appear below) the second one.
+ */
+ virtual int
+ Compare(wxTreeListCtrl* treelist,
+ unsigned column,
+ wxTreeListItem first,
+ wxTreeListItem second) = 0;
+
+ /**
+ Trivial but virtual destructor.
+
+ Although this class is not used polymorphically by wxWidgets itself,
+ provide virtual dtor in case it's used like this in the user code.
+ */
+ virtual ~wxTreeListItemComparator();
+};
+
/**
Container of multiple items.
*/
the other columns.
+ Unlike wxTreeCtrl or wxListCtrl this control can sort its items on its own.
+ To allow user to sort the control contents by clicking on some column you
+ should use wxCOL_SORTABLE flag when adding that column to the control. When
+ a column with this flag is clicked, the control resorts itself using the
+ values in this column. By default the sort is done using alphabetical order
+ comparison of the items text, which is not always correct (e.g. this
+ doesn't work for the numeric columns). To change this you may use
+ SetItemComparator() method to provide a custom comparator, i.e. simply an
+ object that implements comparison between the two items. The treelist
+ sample shows an example of doing this. And if you need to sort the control
+ programmatically, you can call SetSortColumn() method.
+
+
Here are the styles supported by this control. Notice that using
wxTL_USER_3STATE implies wxTL_3STATE and wxTL_3STATE in turn implies
wxTL_CHECKBOX.
@event{EVT_TREELIST_ITEM_CONTEXT_MENU(id, func)}
Process @c wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU event indicating
that the popup menu for the given item should be displayed.
+ @event{EVT_TREELIST_COLUMN_SORTED(id, func)}
+ Process @c wxEVT_COMMAND_TREELIST_COLUMN_SORTED event indicating that
+ the control contents has just been resorted using the specified column.
+ The event doesn't carry the sort direction, use GetSortColumn() method
+ if you need to know it.
@endEventTable
@library{wxadv}
@param align
Alignment of both the column header and its items.
@param flags
- Column flags, currently can only include wxCOL_RESIZABLE to allow
- the user to resize the column.
+ Column flags, currently can include wxCOL_RESIZABLE to allow the
+ user to resize the column and wxCOL_SORTABLE to allow the user to
+ resort the control contents by clicking on this column.
@return
Index of the new column or -1 on failure.
*/
//@}
+ /**
+ Sorting.
+
+ If some control columns were added with wxCOL_SORTABLE flag, clicking
+ on them will automatically resort the control using the custom
+ comparator set by SetItemComparator() or by doing alphabetical
+ comparison by default.
+
+ In any case, i.e. even if the user can't sort the control by clicking
+ on its header, you may call SetSortColumn() to sort it programmatically
+ and call GetSortColumn() to determine whether it's sorted now and, if
+ so, by which column and in which order.
+ */
+ //@{
+
+ /**
+ Set the column to use for sorting and the order in which to sort.
+
+ Calling this method resorts the control contents using the values of
+ the items in the specified column. Sorting uses custom comparator set
+ with SetItemComparator() or alphabetical comparison of items texts if
+ none was specified.
+
+ Notice that currently there is no way to reset sort order.
+
+ @param col
+ A valid column index.
+ @param ascendingOrder
+ Indicates whether the items should be sorted in ascending (A to Z)
+ or descending (Z to A) order.
+ */
+ void SetSortColumn(unsigned col, bool ascendingOrder = true);
+
+ /**
+ Return the column currently used for sorting, if any.
+
+ If the control is currently unsorted, the function simply returns
+ @false and doesn't modify any of its output parameters.
+
+ @param col
+ Receives the index of the column used for sorting if non-@NULL.
+ @param ascendingOrder
+ Receives @true or @false depending on whether the items are sorted
+ in ascending or descending order.
+ @return
+ @true if the control is sorted or @false if it isn't sorted at all.
+ */
+ bool GetSortColumn(unsigned* col, bool* ascendingOrder = NULL);
+
+ /**
+ Set the object to use for comparing the items.
+
+ This object will be used when the control is being sorted because the
+ user clicked on a sortable column or SetSortColumn() was called.
+
+ The provided pointer is stored by the control so the object it points
+ to must have a life-time equal or greater to that of the control
+ itself. In addition, the pointer can be @NULL to stop using custom
+ comparator and revert to the default alphabetical comparison.
+ */
+ void SetItemComparator(wxTreeListItemComparator* comparator);
+
+ //@}
+
+
/**
View window.
wxTreeListCtrl::GetCheckedState().
*/
wxCheckBoxState GetOldCheckedState() const;
+
+ /**
+ Return the column affected by the event.
+
+ This is currently only used with @c wxEVT_COMMAND_TREELIST_COLUMN_SORTED
+ event.
+ */
+ unsigned GetColumn() const;
};
#endif
// ----------------------------------------------------------------------------
-// Constants for menu items
+// Constants
// ----------------------------------------------------------------------------
+// Menu items.
enum
{
Id_MultiSelect = 100,
Id_Select_HTMLDocs
};
+// Tree list columns.
+enum
+{
+ Col_Component,
+ Col_Files,
+ Col_Size
+};
+
+// ----------------------------------------------------------------------------
+// Custom comparator for tree list items comparison
+// ----------------------------------------------------------------------------
+
+// This is a toy class as in a real program you would have the original numeric
+// data somewhere and wouldn't need to parse it back from strings presumably.
+// Nevertheless it shows how to implement a custom comparator which is needed
+// if you want to sort by a column with non-textual contents.
+class MyComparator : public wxTreeListItemComparator
+{
+public:
+ virtual int
+ Compare(wxTreeListCtrl* treelist,
+ unsigned column,
+ wxTreeListItem item1,
+ wxTreeListItem item2)
+ {
+ wxString text1 = treelist->GetItemText(item1, column),
+ text2 = treelist->GetItemText(item2, column);
+
+ switch ( column )
+ {
+ case Col_Component:
+ // Simple alphabetical comparison is fine for those.
+ return text1.CmpNoCase(text2);
+
+ case Col_Files:
+ // Compare strings as numbers.
+ return GetNumFilesFromText(text1) - GetNumFilesFromText(text2);
+
+ case Col_Size:
+ // Compare strings as numbers but also take care of "KiB" and
+ // "MiB" suffixes.
+ return GetSizeFromText(text1) - GetSizeFromText(text2);
+ }
+
+ wxFAIL_MSG( "Sorting on unknown column?" );
+
+ return 0;
+ }
+
+private:
+ // Return the number of files handling special value "many". Notice that
+ // the returned value is signed to allow using it in subtraction above.
+ int GetNumFilesFromText(const wxString& text) const
+ {
+ unsigned long n;
+ if ( !text.ToULong(&n) )
+ {
+ if ( text == "many" )
+ n = 9999;
+ else
+ n = 0;
+ }
+
+ return n;
+ }
+
+ // Return the size in KiB from a string with either KiB or MiB suffix.
+ int GetSizeFromText(const wxString& text) const
+ {
+ wxString size;
+ unsigned factor = 1;
+ if ( text.EndsWith(" MiB", &size) )
+ factor = 1024;
+ else if ( !text.EndsWith(" KiB", &size) )
+ return 0;
+
+ unsigned long n = 0;
+ size.ToULong(&n);
+
+ return n*factor;
+ }
+};
+
// ----------------------------------------------------------------------------
// Application class
// ----------------------------------------------------------------------------
wxTreeListCtrl* m_treelist;
+ MyComparator m_comparator;
+
wxTreeListItem m_itemHTMLDocs;
wxLog* m_oldLogTarget;
style);
tree->SetImageList(m_imageList);
- enum
- {
- Col_Component,
- Col_Files,
- Col_Size
- };
-
- tree->AppendColumn("Component");
+ tree->AppendColumn("Component",
+ wxCOL_WIDTH_AUTOSIZE,
+ wxALIGN_LEFT,
+ wxCOL_RESIZABLE | wxCOL_SORTABLE);
tree->AppendColumn("# Files",
tree->WidthFor("1,000,000"),
- wxALIGN_RIGHT);
+ wxALIGN_RIGHT,
+ wxCOL_RESIZABLE | wxCOL_SORTABLE);
tree->AppendColumn("Size",
tree->WidthFor("1,000,000 KiB"),
- wxALIGN_RIGHT);
+ wxALIGN_RIGHT,
+ wxCOL_RESIZABLE | wxCOL_SORTABLE);
// Define a shortcut to save on typing here.
#define ADD_ITEM(item, parent, files, size) \
// Remember this one for subsequent tests.
m_itemHTMLDocs = HTML;
+ // Set a custom comparator to compare strings containing numbers correctly.
+ tree->SetItemComparator(&m_comparator);
+
return tree;
}
virtual unsigned GetChildren(const wxDataViewItem& item,
wxDataViewItemArray& children) const;
virtual bool IsListModel() const { return m_isFlat; }
+ virtual int Compare(const wxDataViewItem& item1,
+ const wxDataViewItem& item2,
+ unsigned col,
+ bool ascending) const;
private:
// The control we're associated with.
return numChildren;
}
+int
+wxTreeListModel::Compare(const wxDataViewItem& item1,
+ const wxDataViewItem& item2,
+ unsigned col,
+ bool ascending) const
+{
+ // Compare using default alphabetical order if no custom comparator.
+ wxTreeListItemComparator* const comp = m_treelist->m_comparator;
+ if ( !comp )
+ return wxDataViewModel::Compare(item1, item2, col, ascending);
+
+ // Forward comparison to the comparator:
+ int result = comp->Compare(m_treelist, col, FromDVI(item1), FromDVI(item2));
+
+ // And adjust by the sort order if necessary.
+ if ( !ascending )
+ result = -result;
+
+ return result;
+}
+
// ============================================================================
// wxTreeListCtrl implementation
// ============================================================================
EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
+ EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY, wxTreeListCtrl::OnColumnSorted)
EVT_SIZE(wxTreeListCtrl::OnSize)
END_EVENT_TABLE()
{
m_view = NULL;
m_model = NULL;
+ m_comparator = NULL;
}
bool wxTreeListCtrl::Create(wxWindow* parent,
return true;
}
+// ----------------------------------------------------------------------------
+// Sorting
+// ----------------------------------------------------------------------------
+
+void wxTreeListCtrl::SetSortColumn(unsigned col, bool ascendingOrder)
+{
+ wxCHECK_RET( col < m_view->GetColumnCount(), "Invalid column index" );
+
+ m_view->GetColumn(col)->SetSortOrder(ascendingOrder);
+}
+
+bool wxTreeListCtrl::GetSortColumn(unsigned* col, bool* ascendingOrder)
+{
+ const unsigned numColumns = m_view->GetColumnCount();
+ for ( unsigned n = 0; n < numColumns; n++ )
+ {
+ wxDataViewColumn* const column = m_view->GetColumn(n);
+ if ( column->IsSortKey() )
+ {
+ if ( col )
+ *col = n;
+
+ if ( ascendingOrder )
+ *ascendingOrder = column->IsSortOrderAscending();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator* comparator)
+{
+ m_comparator = comparator;
+}
+
// ----------------------------------------------------------------------------
// Events
// ----------------------------------------------------------------------------
-void wxTreeListCtrl::SendEvent(wxEventType evt, wxDataViewEvent& eventDV)
+void wxTreeListCtrl::SendItemEvent(wxEventType evt, wxDataViewEvent& eventDV)
{
wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
}
}
+void wxTreeListCtrl::SendColumnEvent(wxEventType evt, wxDataViewEvent& eventDV)
+{
+ wxTreeListEvent eventTL(evt, this, wxTreeListItem());
+ eventTL.SetColumn(eventDV.GetColumn());
+
+ if ( !ProcessWindowEvent(eventTL) )
+ {
+ eventDV.Skip();
+ return;
+ }
+
+ if ( !eventTL.IsAllowed() )
+ {
+ eventDV.Veto();
+ }
+}
+
void
wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
{
void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
}
void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
}
void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
}
void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
}
void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
{
- SendEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
+ SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
+}
+
+void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent& event)
+{
+ SendColumnEvent(wxEVT_COMMAND_TREELIST_COLUMN_SORTED, event);
}
// ----------------------------------------------------------------------------
wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
+wxDEFINE_TREELIST_EVENT(COLUMN_SORTED);
#undef wxDEFINE_TREELIST_EVENT