1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/treelist.cpp
3 // Purpose: Generic wxTreeListCtrl implementation.
4 // Author: Vadim Zeitlin
6 // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // for compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
25 #if wxUSE_TREELISTCTRL
31 #include "wx/treelist.h"
33 #include "wx/dataview.h"
34 #include "wx/renderer.h"
35 #include "wx/scopedarray.h"
36 #include "wx/scopedptr.h"
38 // ----------------------------------------------------------------------------
40 // ----------------------------------------------------------------------------
42 const char wxTreeListCtrlNameStr
[] = "wxTreeListCtrl";
44 const wxTreeListItem
wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode
*>(-1));
45 const wxTreeListItem
wxTLI_LAST(reinterpret_cast<wxTreeListModelNode
*>(-2));
47 // ----------------------------------------------------------------------------
48 // wxTreeListModelNode: a node in the internal tree representation.
49 // ----------------------------------------------------------------------------
51 class wxTreeListModelNode
54 wxTreeListModelNode(wxTreeListModelNode
* parent
,
55 const wxString
& text
= wxString(),
56 int imageClosed
= wxWithImages::NO_IMAGE
,
57 int imageOpened
= wxWithImages::NO_IMAGE
,
58 wxClientData
* data
= NULL
)
65 m_imageClosed
= imageClosed
;
66 m_imageOpened
= imageOpened
;
68 m_checkedState
= wxCHK_UNCHECKED
;
72 m_columnsTexts
= NULL
;
75 // Destroying the node also (recursively) destroys its children.
76 ~wxTreeListModelNode()
78 for ( wxTreeListModelNode
* node
= m_child
; node
; )
80 wxTreeListModelNode
* child
= node
;
87 delete [] m_columnsTexts
;
91 // Public fields for the first column text and other simple attributes:
92 // there is no need to have accessors/mutators for those as there is no
93 // encapsulation anyhow, all of those are exposed in our public API.
99 wxCheckBoxState m_checkedState
;
102 // Accessors for the fields that are not directly exposed.
104 // Client data is owned by us so delete the old value when setting the new
106 wxClientData
* GetClientData() const { return m_data
; }
107 void SetClientData(wxClientData
* data
) { delete m_data
; m_data
= data
; }
109 // Setting or getting the non-first column text. Getting is simple but you
110 // need to call HasColumnsTexts() first as the column data is only
111 // allocated on demand. And when setting the text we require to be given
112 // the total number of columns as we allocate the entire array at once,
113 // this is more efficient than using dynamically-expandable wxVector that
114 // we know won't be needed as the number of columns is usually fixed. But
115 // if it does change, our OnInsertColumn() must be called.
117 // Notice the presence of -1 everywhere in these methods: this is because
118 // the text for the first column is always stored in m_text and so we don't
119 // store it in m_columnsTexts.
121 bool HasColumnsTexts() const { return m_columnsTexts
!= NULL
; }
122 const wxString
& GetColumnText(unsigned col
) const
124 return m_columnsTexts
[col
- 1];
127 void SetColumnText(const wxString
& text
, unsigned col
, unsigned numColumns
)
129 if ( !m_columnsTexts
)
130 m_columnsTexts
= new wxString
[numColumns
- 1];
132 m_columnsTexts
[col
- 1] = text
;
135 void OnInsertColumn(unsigned col
, unsigned numColumns
)
137 wxASSERT_MSG( col
, "Shouldn't be called for the first column" );
139 // Nothing to do if we don't have any text.
140 if ( !m_columnsTexts
)
143 wxScopedArray
<wxString
> oldTexts(m_columnsTexts
);
144 m_columnsTexts
= new wxString
[numColumns
- 1];
146 // In the loop below n is the index in the new column texts array and m
147 // is the index in the old one.
148 for ( unsigned n
= 1, m
= 1; n
< numColumns
- 1; n
++, m
++ )
152 // Leave the new array text initially empty and just adjust the
153 // index (to compensate for "m++" done by the loop anyhow).
156 else // Not the newly inserted column.
158 // Copy the old text value.
159 m_columnsTexts
[n
- 1] = oldTexts
[m
- 1];
164 void OnDeleteColumn(unsigned col
, unsigned numColumns
)
166 wxASSERT_MSG( col
, "Shouldn't be called for the first column" );
168 if ( !m_columnsTexts
)
171 wxScopedArray
<wxString
> oldTexts(m_columnsTexts
);
172 m_columnsTexts
= new wxString
[numColumns
- 2];
173 for ( unsigned n
= 1, m
= 1; n
< numColumns
- 1; n
++, m
++ )
179 else // Not the deleted column.
181 m_columnsTexts
[n
- 1] = oldTexts
[m
- 1];
186 void OnClearColumns()
188 if ( m_columnsTexts
)
190 delete [] m_columnsTexts
;
191 m_columnsTexts
= NULL
;
196 // Functions for modifying the tree.
198 // Insert the given item as the first child of this one. The parent pointer
199 // must have been already set correctly at creation and we take ownership
200 // of the pointer and will delete it later.
201 void InsertChild(wxTreeListModelNode
* child
)
203 wxASSERT( child
->m_parent
== this );
205 // Our previous first child becomes the next sibling of the new child.
206 child
->m_next
= m_child
;
210 // Insert the given item as our next sibling. As above, the item must have
211 // the correct parent pointer and we take ownership of it.
212 void InsertNext(wxTreeListModelNode
* next
)
214 wxASSERT( next
->m_parent
== m_parent
);
216 next
->m_next
= m_next
;
220 // Remove the first child of this item from the tree and delete it.
223 wxTreeListModelNode
* const oldChild
= m_child
;
224 m_child
= m_child
->m_next
;
228 // Remove the next sibling of this item from the tree and deletes it.
231 wxTreeListModelNode
* const oldNext
= m_next
;
232 m_next
= m_next
->m_next
;
237 // Functions for tree traversal. All of them can return NULL.
239 // Only returns NULL when called on the root item.
240 wxTreeListModelNode
* GetParent() const { return m_parent
; }
242 // Returns the first child of this item.
243 wxTreeListModelNode
* GetChild() const { return m_child
; }
245 // Returns the next sibling of this item.
246 wxTreeListModelNode
* GetNext() const { return m_next
; }
248 // Unlike the previous two functions, this one is not a simple accessor
249 // (hence it's not called "GetSomething") but computes the next node after
250 // this one in tree order.
251 wxTreeListModelNode
* NextInTree() const
259 // Recurse upwards until we find the next sibling.
260 for ( wxTreeListModelNode
* node
= m_parent
; node
; node
= node
->m_parent
)
271 // The (never changing after creation) parent of this node and the possibly
272 // NULL pointers to its first child and next sibling.
273 wxTreeListModelNode
* const m_parent
;
274 wxTreeListModelNode
* m_child
;
275 wxTreeListModelNode
* m_next
;
277 // Client data pointer owned by the control. May be NULL.
278 wxClientData
* m_data
;
280 // Array of column values for all the columns except the first one. May be
281 // NULL if no values had been set for them.
282 wxString
* m_columnsTexts
;
285 // ----------------------------------------------------------------------------
286 // wxTreeListModel: wxDataViewModel implementation used by wxTreeListCtrl.
287 // ----------------------------------------------------------------------------
289 class wxTreeListModel
: public wxDataViewModel
292 typedef wxTreeListModelNode Node
;
294 // Unlike a general wxDataViewModel, this model can only be used with a
295 // single control at once. The main reason for this is that we need to
296 // support different icons for opened and closed items and the item state
297 // is associated with the control, not the model, so our GetValue() is also
298 // bound to it (otherwise, what would it return for an item expanded in one
299 // associated control and collapsed in another one?).
300 wxTreeListModel(wxTreeListCtrl
* treelist
);
301 virtual ~wxTreeListModel();
304 // Helpers for converting between wxDataViewItem and wxTreeListItem. These
305 // methods simply cast the pointer to/from wxDataViewItem except for the
306 // root node that we handle specially unless explicitly disabled.
308 // The advantage of using them is that they're greppable and stand out
309 // better, hopefully making the code more clear.
310 Node
* FromNonRootDVI(wxDataViewItem dvi
) const
312 return static_cast<Node
*>(dvi
.GetID());
315 Node
* FromDVI(wxDataViewItem dvi
) const
320 return FromNonRootDVI(dvi
);
323 wxDataViewItem
ToNonRootDVI(Node
* node
) const
325 return wxDataViewItem(node
);
328 wxDataViewItem
ToDVI(Node
* node
) const
330 // Our root item must be represented as NULL at wxDVC level to map to
331 // its own invisible root.
332 if ( !node
->GetParent() )
333 return wxDataViewItem();
335 return ToNonRootDVI(node
);
339 // Methods called by wxTreeListCtrl.
340 void InsertColumn(unsigned col
);
341 void DeleteColumn(unsigned col
);
344 Node
* InsertItem(Node
* parent
,
346 const wxString
& text
,
350 void DeleteItem(Node
* item
);
351 void DeleteAllItems();
353 Node
* GetRootItem() const { return m_root
; }
355 const wxString
& GetItemText(Node
* item
, unsigned col
) const;
356 void SetItemText(Node
* item
, unsigned col
, const wxString
& text
);
357 void SetItemImage(Node
* item
, int closed
, int opened
);
358 wxClientData
* GetItemData(Node
* item
) const;
359 void SetItemData(Node
* item
, wxClientData
* data
);
361 void CheckItem(Node
* item
, wxCheckBoxState checkedState
);
362 void ToggleItem(wxDataViewItem item
);
365 // Implement the base class pure virtual methods.
366 virtual unsigned GetColumnCount() const;
367 virtual wxString
GetColumnType(unsigned col
) const;
368 virtual void GetValue(wxVariant
& variant
,
369 const wxDataViewItem
& item
,
371 virtual bool SetValue(const wxVariant
& variant
,
372 const wxDataViewItem
& item
,
374 virtual wxDataViewItem
GetParent(const wxDataViewItem
& item
) const;
375 virtual bool IsContainer(const wxDataViewItem
& item
) const;
376 virtual bool HasContainerColumns(const wxDataViewItem
& item
) const;
377 virtual unsigned GetChildren(const wxDataViewItem
& item
,
378 wxDataViewItemArray
& children
) const;
379 virtual bool IsListModel() const { return m_isFlat
; }
380 virtual int Compare(const wxDataViewItem
& item1
,
381 const wxDataViewItem
& item2
,
383 bool ascending
) const;
386 // The control we're associated with.
387 wxTreeListCtrl
* const m_treelist
;
389 // The unique invisible root element.
392 // Number of columns we maintain.
393 unsigned m_numColumns
;
395 // Set to false as soon as we have more than one level, i.e. as soon as any
396 // items with non-root item as parent are added (and currently never reset
401 // ============================================================================
402 // wxDataViewCheckIconText[Renderer]: special renderer for our first column.
403 // ============================================================================
405 // Currently this class is private but it could be extracted and made part of
406 // public API later as could be used directly with wxDataViewCtrl as well.
410 const char* CHECK_ICON_TEXT_TYPE
= "wxDataViewCheckIconText";
412 // The value used by wxDataViewCheckIconTextRenderer
413 class wxDataViewCheckIconText
: public wxDataViewIconText
416 wxDataViewCheckIconText(const wxString
& text
= wxString(),
417 const wxIcon
& icon
= wxNullIcon
,
418 wxCheckBoxState checkedState
= wxCHK_UNDETERMINED
)
419 : wxDataViewIconText(text
, icon
),
420 m_checkedState(checkedState
)
424 wxDataViewCheckIconText(const wxDataViewCheckIconText
& other
)
425 : wxDataViewIconText(other
),
426 m_checkedState(other
.m_checkedState
)
430 bool IsSameAs(const wxDataViewCheckIconText
& other
) const
432 return wxDataViewIconText::IsSameAs(other
) &&
433 m_checkedState
== other
.m_checkedState
;
436 // There is no encapsulation anyhow, so just expose this field directly.
437 wxCheckBoxState m_checkedState
;
441 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText
);
444 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText
, wxDataViewIconText
);
446 DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText
)
447 IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText
)
450 class wxDataViewCheckIconTextRenderer
: public wxDataViewCustomRenderer
453 wxDataViewCheckIconTextRenderer()
454 : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE
,
455 wxDATAVIEW_CELL_ACTIVATABLE
)
459 virtual bool SetValue(const wxVariant
& value
)
465 virtual bool GetValue(wxVariant
& WXUNUSED(value
)) const
470 wxSize
GetSize() const
472 wxSize size
= GetCheckSize();
473 size
.x
+= MARGIN_CHECK_ICON
;
475 if ( m_value
.GetIcon().IsOk() )
477 const wxSize sizeIcon
= m_value
.GetIcon().GetSize();
478 if ( sizeIcon
.y
> size
.y
)
481 size
.x
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
484 wxString text
= m_value
.GetText();
488 const wxSize sizeText
= GetTextExtent(text
);
489 if ( sizeText
.y
> size
.y
)
492 size
.x
+= sizeText
.x
;
497 virtual bool Render(wxRect cell
, wxDC
* dc
, int state
)
499 // Draw the checkbox first.
501 switch ( m_value
.m_checkedState
)
503 case wxCHK_UNCHECKED
:
507 renderFlags
|= wxCONTROL_CHECKED
;
510 case wxCHK_UNDETERMINED
:
511 renderFlags
|= wxCONTROL_UNDETERMINED
;
515 if ( state
& wxDATAVIEW_CELL_PRELIT
)
516 renderFlags
|= wxCONTROL_CURRENT
;
518 const wxSize sizeCheck
= GetCheckSize();
520 wxRect
rectCheck(cell
.GetPosition(), sizeCheck
);
521 rectCheck
= rectCheck
.CentreIn(cell
, wxVERTICAL
);
523 wxRendererNative::Get().DrawCheckBox
525 GetView(), *dc
, rectCheck
, renderFlags
528 // Then the icon, if any.
529 int xoffset
= sizeCheck
.x
+ MARGIN_CHECK_ICON
;
531 const wxIcon
& icon
= m_value
.GetIcon();
534 const wxSize sizeIcon
= icon
.GetSize();
535 wxRect
rectIcon(cell
.GetPosition(), sizeIcon
);
536 rectIcon
.x
+= xoffset
;
537 rectIcon
= rectIcon
.CentreIn(cell
, wxVERTICAL
);
539 dc
->DrawIcon(icon
, rectIcon
.GetPosition());
541 xoffset
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
545 RenderText(m_value
.GetText(), xoffset
, cell
, dc
, state
);
550 // Event handlers toggling the items checkbox if it was clicked.
551 virtual bool ActivateCell(const wxRect
& WXUNUSED(cell
),
552 wxDataViewModel
*model
,
553 const wxDataViewItem
& item
,
554 unsigned int WXUNUSED(col
),
555 const wxMouseEvent
*mouseEvent
)
559 if ( !wxRect(GetCheckSize()).Contains(mouseEvent
->GetPosition()) )
563 static_cast<wxTreeListModel
*>(model
)->ToggleItem(item
);
568 wxSize
GetCheckSize() const
570 return wxRendererNative::Get().GetCheckBoxSize(GetView());
574 // Just some arbitrary constants defining margins, in pixels.
577 MARGIN_CHECK_ICON
= 3,
581 wxDataViewCheckIconText m_value
;
584 } // anonymous namespace
586 // ============================================================================
587 // wxTreeListModel implementation
588 // ============================================================================
590 wxTreeListModel::wxTreeListModel(wxTreeListCtrl
* treelist
)
591 : m_treelist(treelist
),
592 m_root(new Node(NULL
))
598 wxTreeListModel::~wxTreeListModel()
603 void wxTreeListModel::InsertColumn(unsigned col
)
607 // There is no need to update anything when inserting the first column.
608 if ( m_numColumns
== 1 )
611 // Update all the items as they may have texts for the old columns.
612 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
614 node
->OnInsertColumn(col
, m_numColumns
);
618 void wxTreeListModel::DeleteColumn(unsigned col
)
620 wxCHECK_RET( col
< m_numColumns
, "Invalid column index" );
622 // Update all the items to remove the text for the non first columns.
625 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
627 node
->OnDeleteColumn(col
, m_numColumns
);
634 void wxTreeListModel::ClearColumns()
638 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
640 node
->OnClearColumns();
645 wxTreeListModel::InsertItem(Node
* parent
,
647 const wxString
& text
,
652 wxCHECK_MSG( parent
, NULL
,
653 "Must have a valid parent (maybe GetRootItem()?)" );
655 wxCHECK_MSG( previous
, NULL
,
656 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
658 if ( m_isFlat
&& parent
!= m_root
)
660 // Not flat any more, this is a second level child.
665 newItem(new Node(parent
, text
, imageClosed
, imageOpened
, data
));
667 // FIXME-VC6: This compiler refuses to compare "Node* previous" with
668 // wxTLI_XXX without some help.
669 const wxTreeListItem
previousItem(previous
);
671 // If we have no children at all, then inserting as last child is the same
672 // as inserting as the first one so check for it here too.
673 if ( previousItem
== wxTLI_FIRST
||
674 (previousItem
== wxTLI_LAST
&& !parent
->GetChild()) )
676 parent
->InsertChild(newItem
.get());
678 else // Not the first item, find the previous one.
680 if ( previousItem
== wxTLI_LAST
)
682 previous
= parent
->GetChild();
684 // Find the last child.
687 Node
* const next
= previous
->GetNext();
694 else // We already have the previous item.
696 // Just check it's under the correct parent.
697 wxCHECK_MSG( previous
->GetParent() == parent
, NULL
,
698 "Previous item is not under the right parent" );
701 previous
->InsertNext(newItem
.get());
704 ItemAdded(ToDVI(parent
), ToDVI(newItem
.get()));
706 // The item was successfully inserted in the tree and so will be deleted by
707 // it, we can detach it now.
708 return newItem
.release();
711 void wxTreeListModel::DeleteItem(Node
* item
)
713 wxCHECK_RET( item
, "Invalid item" );
715 wxCHECK_RET( item
!= m_root
, "Can't delete the root item" );
717 Node
* const parent
= item
->GetParent();
719 ItemDeleted(ToDVI(parent
), ToDVI(item
));
721 Node
* previous
= parent
->GetChild();
722 if ( previous
== item
)
724 parent
->DeleteChild();
726 else // Not the first child of its parent.
728 // Find the sibling just before it.
731 Node
* const next
= previous
->GetNext();
735 wxCHECK_RET( next
, "Item not a child of its parent?" );
740 previous
->DeleteNext();
744 void wxTreeListModel::DeleteAllItems()
746 while ( m_root
->GetChild() )
748 m_root
->DeleteChild();
754 const wxString
& wxTreeListModel::GetItemText(Node
* item
, unsigned col
) const
756 // Returning root item text here is bogus, it just happens to be an always
757 // empty string we can return reference to.
758 wxCHECK_MSG( item
, m_root
->m_text
, "Invalid item" );
760 // Notice that asking for the text of a column of an item that doesn't have
761 // any column texts is not an error so we simply return an empty string in
763 return col
== 0 ? item
->m_text
764 : item
->HasColumnsTexts() ? item
->GetColumnText(col
)
768 void wxTreeListModel::SetItemText(Node
* item
, unsigned col
, const wxString
& text
)
770 wxCHECK_RET( item
, "Invalid item" );
775 item
->SetColumnText(text
, col
, m_numColumns
);
777 ValueChanged(ToDVI(item
), col
);
780 void wxTreeListModel::SetItemImage(Node
* item
, int closed
, int opened
)
782 wxCHECK_RET( item
, "Invalid item" );
784 item
->m_imageClosed
= closed
;
785 item
->m_imageOpened
= opened
;
787 ValueChanged(ToDVI(item
), 0);
790 wxClientData
* wxTreeListModel::GetItemData(Node
* item
) const
792 wxCHECK_MSG( item
, NULL
, "Invalid item" );
794 return item
->GetClientData();
797 void wxTreeListModel::SetItemData(Node
* item
, wxClientData
* data
)
799 wxCHECK_RET( item
, "Invalid item" );
801 item
->SetClientData(data
);
804 void wxTreeListModel::CheckItem(Node
* item
, wxCheckBoxState checkedState
)
806 wxCHECK_RET( item
, "Invalid item" );
808 item
->m_checkedState
= checkedState
;
810 ItemChanged(ToDVI(item
));
813 void wxTreeListModel::ToggleItem(wxDataViewItem dvi
)
815 Node
* const item
= FromDVI(dvi
);
817 wxCHECK_RET( item
, "Invalid item" );
819 const wxCheckBoxState stateOld
= item
->m_checkedState
;
821 // If the 3rd state is user-settable then the cycle is
822 // unchecked->checked->undetermined.
826 item
->m_checkedState
= m_treelist
->HasFlag(wxTL_USER_3STATE
)
831 case wxCHK_UNDETERMINED
:
832 // Whether 3rd state is user-settable or not, the next state is
834 item
->m_checkedState
= wxCHK_UNCHECKED
;
837 case wxCHK_UNCHECKED
:
838 item
->m_checkedState
= wxCHK_CHECKED
;
842 ItemChanged(ToDVI(item
));
844 m_treelist
->OnItemToggled(item
, stateOld
);
847 unsigned wxTreeListModel::GetColumnCount() const
852 wxString
wxTreeListModel::GetColumnType(unsigned col
) const
856 return m_treelist
->HasFlag(wxTL_CHECKBOX
)
857 ? wxS("wxDataViewCheckIconText")
858 : wxS("wxDataViewIconText");
860 else // All the other columns contain just text.
862 return wxS("string");
867 wxTreeListModel::GetValue(wxVariant
& variant
,
868 const wxDataViewItem
& item
,
871 Node
* const node
= FromDVI(item
);
875 // Determine the correct image to use depending on the item state.
876 int image
= wxWithImages::NO_IMAGE
;
877 if ( m_treelist
->IsExpanded(node
) )
878 image
= node
->m_imageOpened
;
880 if ( image
== wxWithImages::NO_IMAGE
)
881 image
= node
->m_imageClosed
;
883 wxIcon icon
= m_treelist
->GetImage(image
);
885 if ( m_treelist
->HasFlag(wxTL_CHECKBOX
) )
886 variant
<< wxDataViewCheckIconText(node
->m_text
, icon
,
887 node
->m_checkedState
);
889 variant
<< wxDataViewIconText(node
->m_text
, icon
);
893 // Notice that we must still assign wxString to wxVariant to ensure
894 // that it at least has the correct type.
896 if ( node
->HasColumnsTexts() )
897 text
= node
->GetColumnText(col
);
904 wxTreeListModel::SetValue(const wxVariant
& WXUNUSED(variant
),
905 const wxDataViewItem
& WXUNUSED(item
),
906 unsigned WXUNUSED(col
))
908 // We are not editable currently.
912 wxDataViewItem
wxTreeListModel::GetParent(const wxDataViewItem
& item
) const
914 Node
* const node
= FromDVI(item
);
916 return ToDVI(node
->GetParent());
919 bool wxTreeListModel::IsContainer(const wxDataViewItem
& item
) const
921 // FIXME: In the generic (and native OS X) versions we implement this
922 // method normally, i.e. only items with children are containers.
923 // But for the native GTK version we must pretend that all items are
924 // containers because otherwise adding children to them later would
925 // fail because wxGTK code calls IsContainer() too early (when
926 // adding the item itself) and we can't know whether we're container
927 // or not by then. Luckily, always returning true doesn't have any
928 // serious drawbacks for us.
934 Node
* const node
= FromDVI(item
);
936 return node
->GetChild() != NULL
;
941 wxTreeListModel::HasContainerColumns(const wxDataViewItem
& WXUNUSED(item
)) const
947 wxTreeListModel::GetChildren(const wxDataViewItem
& item
,
948 wxDataViewItemArray
& children
) const
950 Node
* const node
= FromDVI(item
);
952 unsigned numChildren
= 0;
953 for ( Node
* child
= node
->GetChild(); child
; child
= child
->GetNext() )
955 children
.push_back(ToDVI(child
));
963 wxTreeListModel::Compare(const wxDataViewItem
& item1
,
964 const wxDataViewItem
& item2
,
966 bool ascending
) const
968 // Compare using default alphabetical order if no custom comparator.
969 wxTreeListItemComparator
* const comp
= m_treelist
->m_comparator
;
971 return wxDataViewModel::Compare(item1
, item2
, col
, ascending
);
973 // Forward comparison to the comparator:
974 int result
= comp
->Compare(m_treelist
, col
, FromDVI(item1
), FromDVI(item2
));
976 // And adjust by the sort order if necessary.
983 // ============================================================================
984 // wxTreeListCtrl implementation
985 // ============================================================================
987 BEGIN_EVENT_TABLE(wxTreeListCtrl
, wxWindow
)
988 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY
, wxTreeListCtrl::OnSelectionChanged
)
989 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY
, wxTreeListCtrl::OnItemExpanding
)
990 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY
, wxTreeListCtrl::OnItemExpanded
)
991 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY
, wxTreeListCtrl::OnItemActivated
)
992 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY
, wxTreeListCtrl::OnItemContextMenu
)
993 EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY
, wxTreeListCtrl::OnColumnSorted
)
995 EVT_SIZE(wxTreeListCtrl::OnSize
)
998 // ----------------------------------------------------------------------------
1000 // ----------------------------------------------------------------------------
1002 void wxTreeListCtrl::Init()
1006 m_comparator
= NULL
;
1009 bool wxTreeListCtrl::Create(wxWindow
* parent
,
1014 const wxString
& name
)
1016 if ( style
& wxTL_USER_3STATE
)
1017 style
|= wxTL_3STATE
;
1019 if ( style
& wxTL_3STATE
)
1020 style
|= wxTL_CHECKBOX
;
1022 // Create the window itself and wxDataViewCtrl used by it.
1023 if ( !wxWindow::Create(parent
, id
,
1030 m_view
= new wxDataViewCtrl
;
1031 long styleDataView
= HasFlag(wxTL_MULTIPLE
) ? wxDV_MULTIPLE
1033 if ( HasFlag(wxTL_NO_HEADER
) )
1034 styleDataView
|= wxDV_NO_HEADER
;
1036 if ( !m_view
->Create(this, wxID_ANY
,
1037 wxPoint(0, 0), GetClientSize(),
1047 // Set up the model for wxDataViewCtrl.
1048 m_model
= new wxTreeListModel(this);
1049 m_view
->AssociateModel(m_model
);
1054 wxTreeListCtrl::~wxTreeListCtrl()
1060 wxWindowList
wxTreeListCtrl::GetCompositeWindowParts() const
1063 parts
.push_back(m_view
);
1067 // ----------------------------------------------------------------------------
1069 // ----------------------------------------------------------------------------
1072 wxTreeListCtrl::DoInsertColumn(const wxString
& title
,
1078 wxCHECK_MSG( m_view
, wxNOT_FOUND
, "Must Create() first" );
1080 const unsigned oldNumColumns
= m_view
->GetColumnCount();
1082 if ( pos
== wxNOT_FOUND
)
1083 pos
= oldNumColumns
;
1085 wxDataViewRenderer
* renderer
;
1088 // Inserting the first column which is special as it uses a different
1091 // Also, currently it can be done only once.
1092 wxCHECK_MSG( !oldNumColumns
, wxNOT_FOUND
,
1093 "Inserting column at position 0 currently not supported" );
1095 if ( HasFlag(wxTL_CHECKBOX
) )
1097 // Use our custom renderer to show the checkbox.
1098 renderer
= new wxDataViewCheckIconTextRenderer
;
1100 else // We still need a special renderer to show the icons.
1102 renderer
= new wxDataViewIconTextRenderer
;
1105 else // Not the first column.
1107 // All the other ones use a simple text renderer.
1108 renderer
= new wxDataViewTextRenderer
;
1112 column
= new wxDataViewColumn(title
, renderer
, pos
, width
, align
, flags
);
1114 m_model
->InsertColumn(pos
);
1116 m_view
->InsertColumn(pos
, column
);
1121 unsigned wxTreeListCtrl::GetColumnCount() const
1123 return m_view
? m_view
->GetColumnCount() : 0u;
1126 bool wxTreeListCtrl::DeleteColumn(unsigned col
)
1128 wxCHECK_MSG( col
< GetColumnCount(), false, "Invalid column index" );
1130 if ( !m_view
->DeleteColumn(m_view
->GetColumn(col
)) )
1133 m_model
->DeleteColumn(col
);
1138 void wxTreeListCtrl::ClearColumns()
1140 // Don't assert here, clearing columns of the control before it's created
1141 // can be considered valid (just useless).
1145 m_view
->ClearColumns();
1147 m_model
->ClearColumns();
1150 void wxTreeListCtrl::SetColumnWidth(unsigned col
, int width
)
1152 wxCHECK_RET( col
< GetColumnCount(), "Invalid column index" );
1154 wxDataViewColumn
* const column
= m_view
->GetColumn(col
);
1155 wxCHECK_RET( column
, "No such column?" );
1157 column
->SetWidth(width
);
1160 int wxTreeListCtrl::GetColumnWidth(unsigned col
) const
1162 wxCHECK_MSG( col
< GetColumnCount(), -1, "Invalid column index" );
1164 wxDataViewColumn
* column
= m_view
->GetColumn(col
);
1165 wxCHECK_MSG( column
, -1, "No such column?" );
1167 return column
->GetWidth();
1170 int wxTreeListCtrl::WidthFor(const wxString
& text
) const
1172 return GetTextExtent(text
).x
;
1175 // ----------------------------------------------------------------------------
1177 // ----------------------------------------------------------------------------
1180 wxTreeListCtrl::DoInsertItem(wxTreeListItem parent
,
1181 wxTreeListItem previous
,
1182 const wxString
& text
,
1187 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1189 return wxTreeListItem(m_model
->InsertItem(parent
, previous
, text
,
1190 imageClosed
, imageOpened
, data
));
1193 void wxTreeListCtrl::DeleteItem(wxTreeListItem item
)
1195 wxCHECK_RET( m_model
, "Must create first" );
1197 m_model
->DeleteItem(item
);
1200 void wxTreeListCtrl::DeleteAllItems()
1203 m_model
->DeleteAllItems();
1206 // ----------------------------------------------------------------------------
1208 // ----------------------------------------------------------------------------
1210 // The simple accessors in this section are implemented directly using
1211 // wxTreeListModelNode methods, without passing by the model. This is just a
1212 // shortcut and avoids us the trouble of defining more trivial methods in
1215 wxTreeListItem
wxTreeListCtrl::GetRootItem() const
1217 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1219 return m_model
->GetRootItem();
1222 wxTreeListItem
wxTreeListCtrl::GetItemParent(wxTreeListItem item
) const
1224 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1226 return item
->GetParent();
1229 wxTreeListItem
wxTreeListCtrl::GetFirstChild(wxTreeListItem item
) const
1231 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1233 return item
->GetChild();
1237 wxTreeListCtrl::GetNextSibling(wxTreeListItem item
) const
1239 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1241 return item
->GetNext();
1244 wxTreeListItem
wxTreeListCtrl::GetNextItem(wxTreeListItem item
) const
1246 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1248 return item
->NextInTree();
1251 // ----------------------------------------------------------------------------
1253 // ----------------------------------------------------------------------------
1256 wxTreeListCtrl::GetItemText(wxTreeListItem item
, unsigned col
) const
1258 // We can't use wxCHECK_MSG() here because we don't have any empty string
1259 // reference to return so we use a static variable that exists just for the
1260 // purpose of this check -- and so we put it in its own scope so that it's
1261 // never even created during normal program execution.
1262 if ( !m_model
|| col
>= m_model
->GetColumnCount() )
1264 static wxString s_empty
;
1268 wxFAIL_MSG( "Must create first" );
1270 else if ( col
>= m_model
->GetColumnCount() )
1272 wxFAIL_MSG( "Invalid column index" );
1278 return m_model
->GetItemText(item
, col
);
1282 wxTreeListCtrl::SetItemText(wxTreeListItem item
,
1284 const wxString
& text
)
1286 wxCHECK_RET( m_model
, "Must create first" );
1287 wxCHECK_RET( col
< m_model
->GetColumnCount(), "Invalid column index" );
1289 m_model
->SetItemText(item
, col
, text
);
1292 void wxTreeListCtrl::SetItemImage(wxTreeListItem item
, int closed
, int opened
)
1294 wxCHECK_RET( m_model
, "Must create first" );
1296 if ( closed
!= NO_IMAGE
|| opened
!= NO_IMAGE
)
1298 wxImageList
* const imageList
= GetImageList();
1299 wxCHECK_RET( imageList
, "Can't set images without image list" );
1301 const int imageCount
= imageList
->GetImageCount();
1303 wxCHECK_RET( closed
< imageCount
, "Invalid image index" );
1304 wxCHECK_RET( opened
< imageCount
, "Invalid opened image index" );
1307 m_model
->SetItemImage(item
, closed
, opened
);
1310 wxClientData
* wxTreeListCtrl::GetItemData(wxTreeListItem item
) const
1312 wxCHECK_MSG( m_model
, NULL
, "Must create first" );
1314 return m_model
->GetItemData(item
);
1317 void wxTreeListCtrl::SetItemData(wxTreeListItem item
, wxClientData
* data
)
1319 wxCHECK_RET( m_model
, "Must create first" );
1321 m_model
->SetItemData(item
, data
);
1324 // ----------------------------------------------------------------------------
1325 // Expanding and collapsing
1326 // ----------------------------------------------------------------------------
1328 void wxTreeListCtrl::Expand(wxTreeListItem item
)
1330 wxCHECK_RET( m_view
, "Must create first" );
1332 m_view
->Expand(m_model
->ToDVI(item
));
1335 void wxTreeListCtrl::Collapse(wxTreeListItem item
)
1337 wxCHECK_RET( m_view
, "Must create first" );
1339 m_view
->Collapse(m_model
->ToDVI(item
));
1342 bool wxTreeListCtrl::IsExpanded(wxTreeListItem item
) const
1344 wxCHECK_MSG( m_view
, false, "Must create first" );
1346 return m_view
->IsExpanded(m_model
->ToDVI(item
));
1349 // ----------------------------------------------------------------------------
1351 // ----------------------------------------------------------------------------
1353 wxTreeListItem
wxTreeListCtrl::GetSelection() const
1355 wxCHECK_MSG( m_view
, wxTreeListItem(), "Must create first" );
1357 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE
), wxTreeListItem(),
1358 "Must use GetSelections() with multi-selection controls!" );
1360 const wxDataViewItem dvi
= m_view
->GetSelection();
1362 return m_model
->FromNonRootDVI(dvi
);
1365 unsigned wxTreeListCtrl::GetSelections(wxTreeListItems
& selections
) const
1367 wxCHECK_MSG( m_view
, 0, "Must create first" );
1369 wxDataViewItemArray selectionsDV
;
1370 const unsigned numSelected
= m_view
->GetSelections(selectionsDV
);
1371 selections
.resize(numSelected
);
1372 for ( unsigned n
= 0; n
< numSelected
; n
++ )
1373 selections
[n
] = m_model
->FromNonRootDVI(selectionsDV
[n
]);
1378 void wxTreeListCtrl::Select(wxTreeListItem item
)
1380 wxCHECK_RET( m_view
, "Must create first" );
1382 m_view
->Select(m_model
->ToNonRootDVI(item
));
1385 void wxTreeListCtrl::Unselect(wxTreeListItem item
)
1387 wxCHECK_RET( m_view
, "Must create first" );
1389 m_view
->Unselect(m_model
->ToNonRootDVI(item
));
1392 bool wxTreeListCtrl::IsSelected(wxTreeListItem item
) const
1394 wxCHECK_MSG( m_view
, false, "Must create first" );
1396 return m_view
->IsSelected(m_model
->ToNonRootDVI(item
));
1399 void wxTreeListCtrl::SelectAll()
1401 wxCHECK_RET( m_view
, "Must create first" );
1403 m_view
->SelectAll();
1406 void wxTreeListCtrl::UnselectAll()
1408 wxCHECK_RET( m_view
, "Must create first" );
1410 m_view
->UnselectAll();
1413 // ----------------------------------------------------------------------------
1414 // Checkbox handling
1415 // ----------------------------------------------------------------------------
1417 void wxTreeListCtrl::CheckItem(wxTreeListItem item
, wxCheckBoxState state
)
1419 wxCHECK_RET( m_model
, "Must create first" );
1421 m_model
->CheckItem(item
, state
);
1425 wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item
, wxCheckBoxState state
)
1427 wxCHECK_RET( m_model
, "Must create first" );
1429 m_model
->CheckItem(item
, state
);
1431 for ( wxTreeListItem child
= GetFirstChild(item
);
1433 child
= GetNextSibling(child
) )
1435 CheckItemRecursively(child
, state
);
1439 void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item
)
1441 wxCHECK_RET( item
.IsOk(), "Invalid item" );
1443 wxASSERT_MSG( HasFlag(wxTL_3STATE
), "Can only be used with wxTL_3STATE" );
1447 wxTreeListItem parent
= GetItemParent(item
);
1448 if ( parent
== GetRootItem() )
1450 // There is no checked state associated with the root item.
1454 // Set parent state to the state of this item if all the other children
1455 // have the same state too. Otherwise make it indeterminate.
1456 const wxCheckBoxState stateItem
= GetCheckedState(item
);
1457 CheckItem(parent
, AreAllChildrenInState(parent
, stateItem
)
1459 : wxCHK_UNDETERMINED
);
1461 // And do the same thing with the parent's parent too.
1466 wxCheckBoxState
wxTreeListCtrl::GetCheckedState(wxTreeListItem item
) const
1468 wxCHECK_MSG( item
.IsOk(), wxCHK_UNDETERMINED
, "Invalid item" );
1470 return item
->m_checkedState
;
1474 wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item
,
1475 wxCheckBoxState state
) const
1477 wxCHECK_MSG( item
.IsOk(), false, "Invalid item" );
1479 for ( wxTreeListItem child
= GetFirstChild(item
);
1481 child
= GetNextSibling(child
) )
1483 if ( GetCheckedState(child
) != state
)
1490 // ----------------------------------------------------------------------------
1492 // ----------------------------------------------------------------------------
1494 void wxTreeListCtrl::SetSortColumn(unsigned col
, bool ascendingOrder
)
1496 wxCHECK_RET( col
< m_view
->GetColumnCount(), "Invalid column index" );
1498 m_view
->GetColumn(col
)->SetSortOrder(ascendingOrder
);
1501 bool wxTreeListCtrl::GetSortColumn(unsigned* col
, bool* ascendingOrder
)
1503 const unsigned numColumns
= m_view
->GetColumnCount();
1504 for ( unsigned n
= 0; n
< numColumns
; n
++ )
1506 wxDataViewColumn
* const column
= m_view
->GetColumn(n
);
1507 if ( column
->IsSortKey() )
1512 if ( ascendingOrder
)
1513 *ascendingOrder
= column
->IsSortOrderAscending();
1522 void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator
* comparator
)
1524 m_comparator
= comparator
;
1527 // ----------------------------------------------------------------------------
1529 // ----------------------------------------------------------------------------
1531 void wxTreeListCtrl::SendItemEvent(wxEventType evt
, wxDataViewEvent
& eventDV
)
1533 wxTreeListEvent
eventTL(evt
, this, m_model
->FromDVI(eventDV
.GetItem()));
1535 if ( !ProcessWindowEvent(eventTL
) )
1541 if ( !eventTL
.IsAllowed() )
1547 void wxTreeListCtrl::SendColumnEvent(wxEventType evt
, wxDataViewEvent
& eventDV
)
1549 wxTreeListEvent
eventTL(evt
, this, wxTreeListItem());
1550 eventTL
.SetColumn(eventDV
.GetColumn());
1552 if ( !ProcessWindowEvent(eventTL
) )
1558 if ( !eventTL
.IsAllowed() )
1565 wxTreeListCtrl::OnItemToggled(wxTreeListItem item
, wxCheckBoxState stateOld
)
1567 wxTreeListEvent
event(wxEVT_TREELIST_ITEM_CHECKED
, this, item
);
1568 event
.SetOldCheckedState(stateOld
);
1570 ProcessWindowEvent(event
);
1573 void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent
& event
)
1575 SendItemEvent(wxEVT_TREELIST_SELECTION_CHANGED
, event
);
1578 void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent
& event
)
1580 SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDING
, event
);
1583 void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent
& event
)
1585 SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDED
, event
);
1588 void wxTreeListCtrl::OnItemActivated(wxDataViewEvent
& event
)
1590 SendItemEvent(wxEVT_TREELIST_ITEM_ACTIVATED
, event
);
1593 void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent
& event
)
1595 SendItemEvent(wxEVT_TREELIST_ITEM_CONTEXT_MENU
, event
);
1598 void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent
& event
)
1600 SendColumnEvent(wxEVT_TREELIST_COLUMN_SORTED
, event
);
1603 // ----------------------------------------------------------------------------
1605 // ----------------------------------------------------------------------------
1607 void wxTreeListCtrl::OnSize(wxSizeEvent
& event
)
1613 // Resize the real control to cover our entire client area.
1614 const wxRect rect
= GetClientRect();
1615 m_view
->SetSize(rect
);
1617 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
1618 // The generic implementation doesn't refresh itself immediately which
1619 // is annoying during "live resizing", so do it forcefully here to
1620 // ensure that the items are re-laid out and the focus rectangle is
1621 // redrawn correctly (instead of leaving traces) while our size is
1623 wxWindow
* const view
= GetView();
1626 #endif // wxHAS_GENERIC_DATAVIEWCTRL
1628 // Resize the first column to take the remaining available space.
1629 const unsigned numColumns
= GetColumnCount();
1633 // There is a bug in generic wxDataViewCtrl: if the column width sums
1634 // up to the total size, horizontal scrollbar (unnecessarily) appears,
1635 // so subtract a bit to ensure this doesn't happen.
1636 int remainingWidth
= rect
.width
- 5;
1637 for ( unsigned n
= 1; n
< GetColumnCount(); n
++ )
1639 remainingWidth
-= GetColumnWidth(n
);
1640 if ( remainingWidth
<= 0 )
1642 // There is not enough space, as we're not going to give the
1643 // first column negative width anyhow, just don't do anything.
1648 SetColumnWidth(0, remainingWidth
);
1652 wxWindow
* wxTreeListCtrl::GetView() const
1654 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
1655 return m_view
->GetMainWindow();
1661 // ============================================================================
1662 // wxTreeListEvent implementation
1663 // ============================================================================
1665 wxIMPLEMENT_DYNAMIC_CLASS(wxTreeListEvent
, wxNotifyEvent
)
1667 #define wxDEFINE_TREELIST_EVENT(name) \
1668 wxDEFINE_EVENT(wxEVT_TREELIST_##name, wxTreeListEvent)
1670 wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED
);
1671 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING
);
1672 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED
);
1673 wxDEFINE_TREELIST_EVENT(ITEM_CHECKED
);
1674 wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED
);
1675 wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU
);
1676 wxDEFINE_TREELIST_EVENT(COLUMN_SORTED
);
1678 #undef wxDEFINE_TREELIST_EVENT
1680 #endif // wxUSE_TREELISTCTRL