1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/treelist.cpp
3 // Purpose: Generic wxTreeListCtrl implementation.
4 // Author: Vadim Zeitlin
6 // RCS-ID: $Id: wxhead.cpp,v 1.11 2010-04-22 12:44:51 zeitlin Exp $
7 // Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
26 #if wxUSE_TREELISTCTRL
32 #include "wx/treelist.h"
34 #include "wx/dataview.h"
35 #include "wx/renderer.h"
36 #include "wx/scopedarray.h"
37 #include "wx/scopedptr.h"
39 // ----------------------------------------------------------------------------
41 // ----------------------------------------------------------------------------
43 const char wxTreeListCtrlNameStr
[] = "wxTreeListCtrl";
45 const wxTreeListItem
wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode
*>(-1));
46 const wxTreeListItem
wxTLI_LAST(reinterpret_cast<wxTreeListModelNode
*>(-2));
48 // ----------------------------------------------------------------------------
49 // wxTreeListModelNode: a node in the internal tree representation.
50 // ----------------------------------------------------------------------------
52 class wxTreeListModelNode
55 wxTreeListModelNode(wxTreeListModelNode
* parent
,
56 const wxString
& text
= wxString(),
57 int imageClosed
= wxWithImages::NO_IMAGE
,
58 int imageOpened
= wxWithImages::NO_IMAGE
,
59 wxClientData
* data
= NULL
)
66 m_imageClosed
= imageClosed
;
67 m_imageOpened
= imageOpened
;
69 m_checkedState
= wxCHK_UNCHECKED
;
73 m_columnsTexts
= NULL
;
76 // Destroying the node also (recursively) destroys its children.
77 ~wxTreeListModelNode()
79 for ( wxTreeListModelNode
* node
= m_child
; node
; )
81 wxTreeListModelNode
* child
= node
;
88 delete [] m_columnsTexts
;
92 // Public fields for the first column text and other simple attributes:
93 // there is no need to have accessors/mutators for those as there is no
94 // encapsulation anyhow, all of those are exposed in our public API.
100 wxCheckBoxState m_checkedState
;
103 // Accessors for the fields that are not directly exposed.
105 // Client data is owned by us so delete the old value when setting the new
107 wxClientData
* GetClientData() const { return m_data
; }
108 void SetClientData(wxClientData
* data
) { delete m_data
; m_data
= data
; }
110 // Setting or getting the non-first column text. Getting is simple but you
111 // need to call HasColumnsTexts() first as the column data is only
112 // allocated on demand. And when setting the text we require to be given
113 // the total number of columns as we allocate the entire array at once,
114 // this is more efficient than using dynamically-expandable wxVector that
115 // we know won't be needed as the number of columns is usually fixed. But
116 // if it does change, our OnInsertColumn() must be called.
118 // Notice the presence of -1 everywhere in these methods: this is because
119 // the text for the first column is always stored in m_text and so we don't
120 // store it in m_columnsTexts.
122 bool HasColumnsTexts() const { return m_columnsTexts
!= NULL
; }
123 const wxString
& GetColumnText(unsigned col
) const
125 return m_columnsTexts
[col
- 1];
128 void SetColumnText(const wxString
& text
, unsigned col
, unsigned numColumns
)
130 if ( !m_columnsTexts
)
131 m_columnsTexts
= new wxString
[numColumns
- 1];
133 m_columnsTexts
[col
- 1] = text
;
136 void OnInsertColumn(unsigned col
, unsigned numColumns
)
138 wxASSERT_MSG( col
, "Shouldn't be called for the first column" );
140 // Nothing to do if we don't have any text.
141 if ( !m_columnsTexts
)
144 wxScopedArray
<wxString
> oldTexts(m_columnsTexts
);
145 m_columnsTexts
= new wxString
[numColumns
- 1];
147 // In the loop below n is the index in the new column texts array and m
148 // is the index in the old one.
149 for ( unsigned n
= 1, m
= 1; n
< numColumns
- 1; n
++, m
++ )
153 // Leave the new array text initially empty and just adjust the
154 // index (to compensate for "m++" done by the loop anyhow).
157 else // Not the newly inserted column.
159 // Copy the old text value.
160 m_columnsTexts
[n
- 1] = oldTexts
[m
- 1];
165 void OnDeleteColumn(unsigned col
, unsigned numColumns
)
167 wxASSERT_MSG( col
, "Shouldn't be called for the first column" );
169 if ( !m_columnsTexts
)
172 wxScopedArray
<wxString
> oldTexts(m_columnsTexts
);
173 m_columnsTexts
= new wxString
[numColumns
- 2];
174 for ( unsigned n
= 1, m
= 1; n
< numColumns
- 1; n
++, m
++ )
180 else // Not the deleted column.
182 m_columnsTexts
[n
- 1] = oldTexts
[m
- 1];
187 void OnClearColumns()
189 if ( m_columnsTexts
)
191 delete [] m_columnsTexts
;
192 m_columnsTexts
= NULL
;
197 // Functions for modifying the tree.
199 // Insert the given item as the first child of this one. The parent pointer
200 // must have been already set correctly at creation and we take ownership
201 // of the pointer and will delete it later.
202 void InsertChild(wxTreeListModelNode
* child
)
204 wxASSERT( child
->m_parent
== this );
206 // Our previous first child becomes the next sibling of the new child.
207 child
->m_next
= m_child
;
211 // Insert the given item as our next sibling. As above, the item must have
212 // the correct parent pointer and we take ownership of it.
213 void InsertNext(wxTreeListModelNode
* next
)
215 wxASSERT( next
->m_parent
== m_parent
);
217 next
->m_next
= m_next
;
221 // Remove the first child of this item from the tree and delete it.
224 wxTreeListModelNode
* const oldChild
= m_child
;
225 m_child
= m_child
->m_next
;
229 // Remove the next sibling of this item from the tree and deletes it.
232 wxTreeListModelNode
* const oldNext
= m_next
;
233 m_next
= m_next
->m_next
;
238 // Functions for tree traversal. All of them can return NULL.
240 // Only returns NULL when called on the root item.
241 wxTreeListModelNode
* GetParent() const { return m_parent
; }
243 // Returns the first child of this item.
244 wxTreeListModelNode
* GetChild() const { return m_child
; }
246 // Returns the next sibling of this item.
247 wxTreeListModelNode
* GetNext() const { return m_next
; }
249 // Unlike the previous two functions, this one is not a simple accessor
250 // (hence it's not called "GetSomething") but computes the next node after
251 // this one in tree order.
252 wxTreeListModelNode
* NextInTree() const
260 // Recurse upwards until we find the next sibling.
261 for ( wxTreeListModelNode
* node
= m_parent
; node
; node
= node
->m_parent
)
272 // The (never changing after creation) parent of this node and the possibly
273 // NULL pointers to its first child and next sibling.
274 wxTreeListModelNode
* const m_parent
;
275 wxTreeListModelNode
* m_child
;
276 wxTreeListModelNode
* m_next
;
278 // Client data pointer owned by the control. May be NULL.
279 wxClientData
* m_data
;
281 // Array of column values for all the columns except the first one. May be
282 // NULL if no values had been set for them.
283 wxString
* m_columnsTexts
;
286 // ----------------------------------------------------------------------------
287 // wxTreeListModel: wxDataViewModel implementation used by wxTreeListCtrl.
288 // ----------------------------------------------------------------------------
290 class wxTreeListModel
: public wxDataViewModel
293 typedef wxTreeListModelNode Node
;
295 // Unlike a general wxDataViewModel, this model can only be used with a
296 // single control at once. The main reason for this is that we need to
297 // support different icons for opened and closed items and the item state
298 // is associated with the control, not the model, so our GetValue() is also
299 // bound to it (otherwise, what would it return for an item expanded in one
300 // associated control and collapsed in another one?).
301 wxTreeListModel(wxTreeListCtrl
* treelist
);
302 virtual ~wxTreeListModel();
305 // Helpers for converting between wxDataViewItem and wxTreeListItem. These
306 // methods simply cast the pointer to/from wxDataViewItem except for the
307 // root node that we handle specially unless explicitly disabled.
309 // The advantage of using them is that they're greppable and stand out
310 // better, hopefully making the code more clear.
311 Node
* FromNonRootDVI(wxDataViewItem dvi
) const
313 return static_cast<Node
*>(dvi
.GetID());
316 Node
* FromDVI(wxDataViewItem dvi
) const
321 return FromNonRootDVI(dvi
);
324 wxDataViewItem
ToNonRootDVI(Node
* node
) const
326 return wxDataViewItem(node
);
329 wxDataViewItem
ToDVI(Node
* node
) const
331 // Our root item must be represented as NULL at wxDVC level to map to
332 // its own invisible root.
333 if ( !node
->GetParent() )
334 return wxDataViewItem();
336 return ToNonRootDVI(node
);
340 // Methods called by wxTreeListCtrl.
341 void InsertColumn(unsigned col
);
342 void DeleteColumn(unsigned col
);
345 Node
* InsertItem(Node
* parent
,
347 const wxString
& text
,
351 void DeleteItem(Node
* item
);
352 void DeleteAllItems();
354 Node
* GetRootItem() const { return m_root
; }
356 const wxString
& GetItemText(Node
* item
, unsigned col
) const;
357 void SetItemText(Node
* item
, unsigned col
, const wxString
& text
);
358 void SetItemImage(Node
* item
, int closed
, int opened
);
359 wxClientData
* GetItemData(Node
* item
) const;
360 void SetItemData(Node
* item
, wxClientData
* data
);
362 void CheckItem(Node
* item
, wxCheckBoxState checkedState
);
363 void ToggleItem(wxDataViewItem item
);
366 // Implement the base class pure virtual methods.
367 virtual unsigned GetColumnCount() const;
368 virtual wxString
GetColumnType(unsigned col
) const;
369 virtual void GetValue(wxVariant
& variant
,
370 const wxDataViewItem
& item
,
372 virtual bool SetValue(const wxVariant
& variant
,
373 const wxDataViewItem
& item
,
375 virtual wxDataViewItem
GetParent(const wxDataViewItem
& item
) const;
376 virtual bool IsContainer(const wxDataViewItem
& item
) const;
377 virtual bool HasContainerColumns(const wxDataViewItem
& item
) const;
378 virtual unsigned GetChildren(const wxDataViewItem
& item
,
379 wxDataViewItemArray
& children
) const;
380 virtual bool IsListModel() const { return m_isFlat
; }
381 virtual int Compare(const wxDataViewItem
& item1
,
382 const wxDataViewItem
& item2
,
384 bool ascending
) const;
387 // The control we're associated with.
388 wxTreeListCtrl
* const m_treelist
;
390 // The unique invisible root element.
393 // Number of columns we maintain.
394 unsigned m_numColumns
;
396 // Set to false as soon as we have more than one level, i.e. as soon as any
397 // items with non-root item as parent are added (and currently never reset
402 // ============================================================================
403 // wxDataViewCheckIconText[Renderer]: special renderer for our first column.
404 // ============================================================================
406 // Currently this class is private but it could be extracted and made part of
407 // public API later as could be used directly with wxDataViewCtrl as well.
411 const char* CHECK_ICON_TEXT_TYPE
= "wxDataViewCheckIconText";
413 // The value used by wxDataViewCheckIconTextRenderer
414 class wxDataViewCheckIconText
: public wxDataViewIconText
417 wxDataViewCheckIconText(const wxString
& text
= wxString(),
418 const wxIcon
& icon
= wxNullIcon
,
419 wxCheckBoxState checkedState
= wxCHK_UNDETERMINED
)
420 : wxDataViewIconText(text
, icon
),
421 m_checkedState(checkedState
)
425 wxDataViewCheckIconText(const wxDataViewCheckIconText
& other
)
426 : wxDataViewIconText(other
),
427 m_checkedState(other
.m_checkedState
)
431 bool IsSameAs(const wxDataViewCheckIconText
& other
) const
433 return wxDataViewIconText::IsSameAs(other
) &&
434 m_checkedState
== other
.m_checkedState
;
437 // There is no encapsulation anyhow, so just expose this field directly.
438 wxCheckBoxState m_checkedState
;
442 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText
);
445 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText
, wxDataViewIconText
);
447 DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText
)
448 IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText
)
451 class wxDataViewCheckIconTextRenderer
: public wxDataViewCustomRenderer
454 wxDataViewCheckIconTextRenderer()
455 : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE
,
456 wxDATAVIEW_CELL_ACTIVATABLE
)
460 virtual bool SetValue(const wxVariant
& value
)
466 virtual bool GetValue(wxVariant
& WXUNUSED(value
)) const
471 wxSize
GetSize() const
473 wxSize size
= GetCheckSize();
474 size
.x
+= MARGIN_CHECK_ICON
;
476 if ( m_value
.GetIcon().IsOk() )
478 const wxSize sizeIcon
= m_value
.GetIcon().GetSize();
479 if ( sizeIcon
.y
> size
.y
)
482 size
.x
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
485 wxString text
= m_value
.GetText();
489 const wxSize sizeText
= GetTextExtent(text
);
490 if ( sizeText
.y
> size
.y
)
493 size
.x
+= sizeText
.x
;
498 virtual bool Render(wxRect cell
, wxDC
* dc
, int state
)
500 // Draw the checkbox first.
502 switch ( m_value
.m_checkedState
)
504 case wxCHK_UNCHECKED
:
508 renderFlags
|= wxCONTROL_CHECKED
;
511 case wxCHK_UNDETERMINED
:
512 renderFlags
|= wxCONTROL_UNDETERMINED
;
516 if ( state
& wxDATAVIEW_CELL_PRELIT
)
517 renderFlags
|= wxCONTROL_CURRENT
;
519 const wxSize sizeCheck
= GetCheckSize();
521 wxRect
rectCheck(cell
.GetPosition(), sizeCheck
);
522 rectCheck
= rectCheck
.CentreIn(cell
, wxVERTICAL
);
524 wxRendererNative::Get().DrawCheckBox
526 GetView(), *dc
, rectCheck
, renderFlags
529 // Then the icon, if any.
530 int xoffset
= sizeCheck
.x
+ MARGIN_CHECK_ICON
;
532 const wxIcon
& icon
= m_value
.GetIcon();
535 const wxSize sizeIcon
= icon
.GetSize();
536 wxRect
rectIcon(cell
.GetPosition(), sizeIcon
);
537 rectIcon
.x
+= xoffset
;
538 rectIcon
= rectIcon
.CentreIn(cell
, wxVERTICAL
);
540 dc
->DrawIcon(icon
, rectIcon
.GetPosition());
542 xoffset
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
546 RenderText(m_value
.GetText(), xoffset
, cell
, dc
, state
);
551 // Event handlers toggling the items checkbox if it was clicked.
552 virtual bool Activate(const wxRect
& WXUNUSED(cell
),
553 wxDataViewModel
* model
,
554 const wxDataViewItem
& item
,
555 unsigned int WXUNUSED(col
))
557 static_cast<wxTreeListModel
*>(model
)->ToggleItem(item
);
561 virtual bool LeftClick(const wxPoint
& pos
,
562 const wxRect
& WXUNUSED(cell
),
563 wxDataViewModel
* model
,
564 const wxDataViewItem
& item
,
565 unsigned int WXUNUSED(col
))
567 if ( !wxRect(GetCheckSize()).Contains(pos
) )
570 static_cast<wxTreeListModel
*>(model
)->ToggleItem(item
);
575 wxSize
GetCheckSize() const
577 return wxRendererNative::Get().GetCheckBoxSize(GetView());
581 // Just some arbitrary constants defining margins, in pixels.
584 MARGIN_CHECK_ICON
= 3,
588 wxDataViewCheckIconText m_value
;
591 } // anonymous namespace
593 // ============================================================================
594 // wxTreeListModel implementation
595 // ============================================================================
597 wxTreeListModel::wxTreeListModel(wxTreeListCtrl
* treelist
)
598 : m_treelist(treelist
),
599 m_root(new Node(NULL
))
605 wxTreeListModel::~wxTreeListModel()
610 void wxTreeListModel::InsertColumn(unsigned col
)
614 // There is no need to update anything when inserting the first column.
615 if ( m_numColumns
== 1 )
618 // Update all the items as they may have texts for the old columns.
619 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
621 node
->OnInsertColumn(col
, m_numColumns
);
625 void wxTreeListModel::DeleteColumn(unsigned col
)
627 wxCHECK_RET( col
< m_numColumns
, "Invalid column index" );
629 // Update all the items to remove the text for the non first columns.
632 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
634 node
->OnDeleteColumn(col
, m_numColumns
);
641 void wxTreeListModel::ClearColumns()
645 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
647 node
->OnClearColumns();
652 wxTreeListModel::InsertItem(Node
* parent
,
654 const wxString
& text
,
659 wxCHECK_MSG( parent
, NULL
,
660 "Must have a valid parent (maybe GetRootItem()?)" );
662 wxCHECK_MSG( previous
, NULL
,
663 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
665 if ( m_isFlat
&& parent
!= m_root
)
667 // Not flat any more, this is a second level child.
672 newItem(new Node(parent
, text
, imageClosed
, imageOpened
, data
));
674 // If we have no children at all, then inserting as last child is the same
675 // as inserting as the first one so check for it here too.
676 if ( previous
== wxTLI_FIRST
||
677 (previous
== wxTLI_LAST
&& !parent
->GetChild()) )
679 parent
->InsertChild(newItem
.get());
681 else // Not the first item, find the previous one.
683 if ( previous
== wxTLI_LAST
)
685 previous
= parent
->GetChild();
687 // Find the last child.
690 Node
* const next
= previous
->GetNext();
697 else // We already have the previous item.
699 // Just check it's under the correct parent.
700 wxCHECK_MSG( previous
->GetParent() == parent
, NULL
,
701 "Previous item is not under the right parent" );
704 previous
->InsertNext(newItem
.get());
707 ItemAdded(ToDVI(parent
), ToDVI(newItem
.get()));
709 // The item was successfully inserted in the tree and so will be deleted by
710 // it, we can detach it now.
711 return newItem
.release();
714 void wxTreeListModel::DeleteItem(Node
* item
)
716 wxCHECK_RET( item
, "Invalid item" );
718 wxCHECK_RET( item
!= m_root
, "Can't delete the root item" );
720 Node
* const parent
= item
->GetParent();
722 ItemDeleted(ToDVI(parent
), ToDVI(item
));
724 Node
* previous
= parent
->GetChild();
725 if ( previous
== item
)
727 parent
->DeleteChild();
729 else // Not the first child of its parent.
731 // Find the sibling just before it.
734 Node
* const next
= previous
->GetNext();
738 wxCHECK_RET( next
, "Item not a child of its parent?" );
743 previous
->DeleteNext();
747 void wxTreeListModel::DeleteAllItems()
749 while ( m_root
->GetChild() )
751 m_root
->DeleteChild();
757 const wxString
& wxTreeListModel::GetItemText(Node
* item
, unsigned col
) const
759 // Returning root item text here is bogus, it just happens to be an always
760 // empty string we can return reference to.
761 wxCHECK_MSG( item
, m_root
->m_text
, "Invalid item" );
763 return col
== 0 ? item
->m_text
: item
->GetColumnText(col
);
766 void wxTreeListModel::SetItemText(Node
* item
, unsigned col
, const wxString
& text
)
768 wxCHECK_RET( item
, "Invalid item" );
773 item
->SetColumnText(text
, col
, m_numColumns
);
775 ValueChanged(ToDVI(item
), col
);
778 void wxTreeListModel::SetItemImage(Node
* item
, int closed
, int opened
)
780 wxCHECK_RET( item
, "Invalid item" );
782 item
->m_imageClosed
= closed
;
783 item
->m_imageOpened
= opened
;
785 ValueChanged(ToDVI(item
), 0);
788 wxClientData
* wxTreeListModel::GetItemData(Node
* item
) const
790 wxCHECK_MSG( item
, NULL
, "Invalid item" );
792 return item
->GetClientData();
795 void wxTreeListModel::SetItemData(Node
* item
, wxClientData
* data
)
797 wxCHECK_RET( item
, "Invalid item" );
799 item
->SetClientData(data
);
802 void wxTreeListModel::CheckItem(Node
* item
, wxCheckBoxState checkedState
)
804 wxCHECK_RET( item
, "Invalid item" );
806 item
->m_checkedState
= checkedState
;
808 ItemChanged(ToDVI(item
));
811 void wxTreeListModel::ToggleItem(wxDataViewItem dvi
)
813 Node
* const item
= FromDVI(dvi
);
815 wxCHECK_RET( item
, "Invalid item" );
817 const wxCheckBoxState stateOld
= item
->m_checkedState
;
819 // If the 3rd state is user-settable then the cycle is
820 // unchecked->checked->undetermined.
824 item
->m_checkedState
= m_treelist
->HasFlag(wxTL_USER_3STATE
)
829 case wxCHK_UNDETERMINED
:
830 // Whether 3rd state is user-settable or not, the next state is
832 item
->m_checkedState
= wxCHK_UNCHECKED
;
835 case wxCHK_UNCHECKED
:
836 item
->m_checkedState
= wxCHK_CHECKED
;
840 ItemChanged(ToDVI(item
));
842 m_treelist
->OnItemToggled(item
, stateOld
);
845 unsigned wxTreeListModel::GetColumnCount() const
850 wxString
wxTreeListModel::GetColumnType(unsigned col
) const
854 return m_treelist
->HasFlag(wxTL_CHECKBOX
)
855 ? wxS("wxDataViewCheckIconText")
856 : wxS("wxDataViewIconText");
858 else // All the other columns contain just text.
860 return wxS("string");
865 wxTreeListModel::GetValue(wxVariant
& variant
,
866 const wxDataViewItem
& item
,
869 Node
* const node
= FromDVI(item
);
873 // Determine the correct image to use depending on the item state.
874 int image
= wxWithImages::NO_IMAGE
;
875 if ( m_treelist
->IsExpanded(node
) )
876 image
= node
->m_imageOpened
;
878 if ( image
== wxWithImages::NO_IMAGE
)
879 image
= node
->m_imageClosed
;
881 wxIcon icon
= m_treelist
->GetImage(image
);
883 if ( m_treelist
->HasFlag(wxTL_CHECKBOX
) )
884 variant
<< wxDataViewCheckIconText(node
->m_text
, icon
,
885 node
->m_checkedState
);
887 variant
<< wxDataViewIconText(node
->m_text
, icon
);
891 // Notice that we must still assign wxString to wxVariant to ensure
892 // that it at least has the correct type.
894 if ( node
->HasColumnsTexts() )
895 text
= node
->GetColumnText(col
);
902 wxTreeListModel::SetValue(const wxVariant
& WXUNUSED(variant
),
903 const wxDataViewItem
& WXUNUSED(item
),
904 unsigned WXUNUSED(col
))
906 // We are not editable currently.
910 wxDataViewItem
wxTreeListModel::GetParent(const wxDataViewItem
& item
) const
912 Node
* const node
= FromDVI(item
);
914 return ToDVI(node
->GetParent());
917 bool wxTreeListModel::IsContainer(const wxDataViewItem
& item
) const
919 // FIXME: In the generic (and native OS X) versions we implement this
920 // method normally, i.e. only items with children are containers.
921 // But for the native GTK version we must pretend that all items are
922 // containers because otherwise adding children to them later would
923 // fail because wxGTK code calls IsContainer() too early (when
924 // adding the item itself) and we can't know whether we're container
925 // or not by then. Luckily, always returning true doesn't have any
926 // serious drawbacks for us.
932 Node
* const node
= FromDVI(item
);
934 return node
->GetChild() != NULL
;
939 wxTreeListModel::HasContainerColumns(const wxDataViewItem
& WXUNUSED(item
)) const
945 wxTreeListModel::GetChildren(const wxDataViewItem
& item
,
946 wxDataViewItemArray
& children
) const
948 Node
* const node
= FromDVI(item
);
950 unsigned numChildren
= 0;
951 for ( Node
* child
= node
->GetChild(); child
; child
= child
->GetNext() )
953 children
.push_back(ToDVI(child
));
961 wxTreeListModel::Compare(const wxDataViewItem
& item1
,
962 const wxDataViewItem
& item2
,
964 bool ascending
) const
966 // Compare using default alphabetical order if no custom comparator.
967 wxTreeListItemComparator
* const comp
= m_treelist
->m_comparator
;
969 return wxDataViewModel::Compare(item1
, item2
, col
, ascending
);
971 // Forward comparison to the comparator:
972 int result
= comp
->Compare(m_treelist
, col
, FromDVI(item1
), FromDVI(item2
));
974 // And adjust by the sort order if necessary.
981 // ============================================================================
982 // wxTreeListCtrl implementation
983 // ============================================================================
985 BEGIN_EVENT_TABLE(wxTreeListCtrl
, wxWindow
)
986 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY
, wxTreeListCtrl::OnSelectionChanged
)
987 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY
, wxTreeListCtrl::OnItemExpanding
)
988 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY
, wxTreeListCtrl::OnItemExpanded
)
989 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY
, wxTreeListCtrl::OnItemActivated
)
990 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY
, wxTreeListCtrl::OnItemContextMenu
)
991 EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY
, wxTreeListCtrl::OnColumnSorted
)
993 EVT_SIZE(wxTreeListCtrl::OnSize
)
996 // ----------------------------------------------------------------------------
998 // ----------------------------------------------------------------------------
1000 void wxTreeListCtrl::Init()
1004 m_comparator
= NULL
;
1007 bool wxTreeListCtrl::Create(wxWindow
* parent
,
1012 const wxString
& name
)
1014 if ( style
& wxTL_USER_3STATE
)
1015 style
|= wxTL_3STATE
;
1017 if ( style
& wxTL_3STATE
)
1018 style
|= wxTL_CHECKBOX
;
1020 // Create the window itself and wxDataViewCtrl used by it.
1021 if ( !wxWindow::Create(parent
, id
,
1028 m_view
= new wxDataViewCtrl
;
1029 if ( !m_view
->Create(this, wxID_ANY
,
1030 wxPoint(0, 0), GetClientSize(),
1031 HasFlag(wxTL_MULTIPLE
) ? wxDV_MULTIPLE
1041 // Set up the model for wxDataViewCtrl.
1042 m_model
= new wxTreeListModel(this);
1043 m_view
->AssociateModel(m_model
);
1048 wxTreeListCtrl::~wxTreeListCtrl()
1054 wxWindowList
wxTreeListCtrl::GetCompositeWindowParts() const
1057 parts
.push_back(m_view
);
1061 // ----------------------------------------------------------------------------
1063 // ----------------------------------------------------------------------------
1066 wxTreeListCtrl::DoInsertColumn(const wxString
& title
,
1072 wxCHECK_MSG( m_view
, wxNOT_FOUND
, "Must Create() first" );
1074 const unsigned oldNumColumns
= m_view
->GetColumnCount();
1076 if ( pos
== wxNOT_FOUND
)
1077 pos
= oldNumColumns
;
1079 wxDataViewRenderer
* renderer
;
1082 // Inserting the first column which is special as it uses a different
1085 // Also, currently it can be done only once.
1086 wxCHECK_MSG( !oldNumColumns
, wxNOT_FOUND
,
1087 "Inserting column at position 0 currently not supported" );
1089 if ( HasFlag(wxTL_CHECKBOX
) )
1091 // Use our custom renderer to show the checkbox.
1092 renderer
= new wxDataViewCheckIconTextRenderer
;
1094 else // We still need a special renderer to show the icons.
1096 renderer
= new wxDataViewIconTextRenderer
;
1099 else // Not the first column.
1101 // All the other ones use a simple text renderer.
1102 renderer
= new wxDataViewTextRenderer
;
1106 column
= new wxDataViewColumn(title
, renderer
, pos
, width
, align
, flags
);
1108 m_model
->InsertColumn(pos
);
1110 m_view
->InsertColumn(pos
, column
);
1115 unsigned wxTreeListCtrl::GetColumnCount() const
1117 return m_view
? m_view
->GetColumnCount() : 0u;
1120 bool wxTreeListCtrl::DeleteColumn(unsigned col
)
1122 wxCHECK_MSG( col
< GetColumnCount(), false, "Invalid column index" );
1124 if ( !m_view
->DeleteColumn(m_view
->GetColumn(col
)) )
1127 m_model
->DeleteColumn(col
);
1132 void wxTreeListCtrl::ClearColumns()
1134 // Don't assert here, clearing columns of the control before it's created
1135 // can be considered valid (just useless).
1139 m_view
->ClearColumns();
1141 m_model
->ClearColumns();
1144 void wxTreeListCtrl::SetColumnWidth(unsigned col
, int width
)
1146 wxCHECK_RET( col
< GetColumnCount(), "Invalid column index" );
1148 wxDataViewColumn
* const column
= m_view
->GetColumn(col
);
1149 wxCHECK_RET( column
, "No such column?" );
1151 return column
->SetWidth(width
);
1154 int wxTreeListCtrl::GetColumnWidth(unsigned col
) const
1156 wxCHECK_MSG( col
< GetColumnCount(), -1, "Invalid column index" );
1158 wxDataViewColumn
* column
= m_view
->GetColumn(col
);
1159 wxCHECK_MSG( column
, -1, "No such column?" );
1161 return column
->GetWidth();
1164 int wxTreeListCtrl::WidthFor(const wxString
& text
) const
1166 return GetTextExtent(text
).x
;
1169 // ----------------------------------------------------------------------------
1171 // ----------------------------------------------------------------------------
1174 wxTreeListCtrl::DoInsertItem(wxTreeListItem parent
,
1175 wxTreeListItem previous
,
1176 const wxString
& text
,
1181 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1183 return wxTreeListItem(m_model
->InsertItem(parent
, previous
, text
,
1184 imageClosed
, imageOpened
, data
));
1187 void wxTreeListCtrl::DeleteItem(wxTreeListItem item
)
1189 wxCHECK_RET( m_model
, "Must create first" );
1191 m_model
->DeleteItem(item
);
1194 void wxTreeListCtrl::DeleteAllItems()
1197 m_model
->DeleteAllItems();
1200 // ----------------------------------------------------------------------------
1202 // ----------------------------------------------------------------------------
1204 // The simple accessors in this section are implemented directly using
1205 // wxTreeListModelNode methods, without passing by the model. This is just a
1206 // shortcut and avoids us the trouble of defining more trivial methods in
1209 wxTreeListItem
wxTreeListCtrl::GetRootItem() const
1211 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1213 return m_model
->GetRootItem();
1216 wxTreeListItem
wxTreeListCtrl::GetItemParent(wxTreeListItem item
) const
1218 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1220 return item
->GetParent();
1223 wxTreeListItem
wxTreeListCtrl::GetFirstChild(wxTreeListItem item
) const
1225 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1227 return item
->GetChild();
1231 wxTreeListCtrl::GetNextSibling(wxTreeListItem item
) const
1233 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1235 return item
->GetNext();
1238 wxTreeListItem
wxTreeListCtrl::GetNextItem(wxTreeListItem item
) const
1240 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1242 return item
->NextInTree();
1245 // ----------------------------------------------------------------------------
1247 // ----------------------------------------------------------------------------
1250 wxTreeListCtrl::GetItemText(wxTreeListItem item
, unsigned col
) const
1252 // We can't use wxCHECK_MSG() here because we don't have any empty string
1253 // reference to return so we use a static variable that exists just for the
1254 // purpose of this check -- and so we put it in its own scope so that it's
1255 // never even created during normal program execution.
1256 if ( !m_model
|| col
>= m_model
->GetColumnCount() )
1258 static wxString s_empty
;
1262 wxFAIL_MSG( "Must create first" );
1264 else if ( col
>= m_model
->GetColumnCount() )
1266 wxFAIL_MSG( "Invalid column index" );
1272 return m_model
->GetItemText(item
, col
);
1276 wxTreeListCtrl::SetItemText(wxTreeListItem item
,
1278 const wxString
& text
)
1280 wxCHECK_RET( m_model
, "Must create first" );
1281 wxCHECK_RET( col
< m_model
->GetColumnCount(), "Invalid column index" );
1283 m_model
->SetItemText(item
, col
, text
);
1286 void wxTreeListCtrl::SetItemImage(wxTreeListItem item
, int closed
, int opened
)
1288 wxCHECK_RET( m_model
, "Must create first" );
1290 if ( closed
!= NO_IMAGE
|| opened
!= NO_IMAGE
)
1292 wxImageList
* const imageList
= GetImageList();
1293 wxCHECK_RET( imageList
, "Can't set images without image list" );
1295 const int imageCount
= imageList
->GetImageCount();
1297 wxCHECK_RET( closed
< imageCount
, "Invalid image index" );
1298 wxCHECK_RET( opened
< imageCount
, "Invalid opened image index" );
1301 m_model
->SetItemImage(item
, closed
, opened
);
1304 wxClientData
* wxTreeListCtrl::GetItemData(wxTreeListItem item
) const
1306 wxCHECK_MSG( m_model
, NULL
, "Must create first" );
1308 return m_model
->GetItemData(item
);
1311 void wxTreeListCtrl::SetItemData(wxTreeListItem item
, wxClientData
* data
)
1313 wxCHECK_RET( m_model
, "Must create first" );
1315 m_model
->SetItemData(item
, data
);
1318 // ----------------------------------------------------------------------------
1319 // Expanding and collapsing
1320 // ----------------------------------------------------------------------------
1322 void wxTreeListCtrl::Expand(wxTreeListItem item
)
1324 wxCHECK_RET( m_view
, "Must create first" );
1326 m_view
->Expand(m_model
->ToDVI(item
));
1329 void wxTreeListCtrl::Collapse(wxTreeListItem item
)
1331 wxCHECK_RET( m_view
, "Must create first" );
1333 m_view
->Collapse(m_model
->ToDVI(item
));
1336 bool wxTreeListCtrl::IsExpanded(wxTreeListItem item
) const
1338 wxCHECK_MSG( m_view
, false, "Must create first" );
1340 return m_view
->IsExpanded(m_model
->ToDVI(item
));
1343 // ----------------------------------------------------------------------------
1345 // ----------------------------------------------------------------------------
1347 wxTreeListItem
wxTreeListCtrl::GetSelection() const
1349 wxCHECK_MSG( m_view
, wxTreeListItem(), "Must create first" );
1351 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE
), wxTreeListItem(),
1352 "Must use GetSelections() with multi-selection controls!" );
1354 const wxDataViewItem dvi
= m_view
->GetSelection();
1356 return m_model
->FromNonRootDVI(dvi
);
1359 unsigned wxTreeListCtrl::GetSelections(wxTreeListItems
& selections
) const
1361 wxCHECK_MSG( m_view
, 0, "Must create first" );
1363 wxDataViewItemArray selectionsDV
;
1364 const unsigned numSelected
= m_view
->GetSelections(selectionsDV
);
1365 selections
.resize(numSelected
);
1366 for ( unsigned n
= 0; n
< numSelected
; n
++ )
1367 selections
[n
] = m_model
->FromNonRootDVI(selectionsDV
[n
]);
1372 void wxTreeListCtrl::Select(wxTreeListItem item
)
1374 wxCHECK_RET( m_view
, "Must create first" );
1376 m_view
->Select(m_model
->ToNonRootDVI(item
));
1379 void wxTreeListCtrl::Unselect(wxTreeListItem item
)
1381 wxCHECK_RET( m_view
, "Must create first" );
1383 m_view
->Unselect(m_model
->ToNonRootDVI(item
));
1386 bool wxTreeListCtrl::IsSelected(wxTreeListItem item
) const
1388 wxCHECK_MSG( m_view
, false, "Must create first" );
1390 return m_view
->IsSelected(m_model
->ToNonRootDVI(item
));
1393 void wxTreeListCtrl::SelectAll()
1395 wxCHECK_RET( m_view
, "Must create first" );
1397 m_view
->SelectAll();
1400 void wxTreeListCtrl::UnselectAll()
1402 wxCHECK_RET( m_view
, "Must create first" );
1404 m_view
->UnselectAll();
1407 // ----------------------------------------------------------------------------
1408 // Checkbox handling
1409 // ----------------------------------------------------------------------------
1411 void wxTreeListCtrl::CheckItem(wxTreeListItem item
, wxCheckBoxState state
)
1413 wxCHECK_RET( m_model
, "Must create first" );
1415 m_model
->CheckItem(item
, state
);
1419 wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item
, wxCheckBoxState state
)
1421 wxCHECK_RET( m_model
, "Must create first" );
1423 m_model
->CheckItem(item
, state
);
1425 for ( wxTreeListItem child
= GetFirstChild(item
);
1427 child
= GetNextSibling(child
) )
1429 CheckItemRecursively(child
, state
);
1433 void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item
)
1435 wxCHECK_RET( item
.IsOk(), "Invalid item" );
1437 wxASSERT_MSG( HasFlag(wxTL_3STATE
), "Can only be used with wxTL_3STATE" );
1441 wxTreeListItem parent
= GetItemParent(item
);
1442 if ( parent
== GetRootItem() )
1444 // There is no checked state associated with the root item.
1448 // Set parent state to the state of this item if all the other children
1449 // have the same state too. Otherwise make it indeterminate.
1450 const wxCheckBoxState stateItem
= GetCheckedState(item
);
1451 CheckItem(parent
, AreAllChildrenInState(parent
, stateItem
)
1453 : wxCHK_UNDETERMINED
);
1455 // And do the same thing with the parent's parent too.
1460 wxCheckBoxState
wxTreeListCtrl::GetCheckedState(wxTreeListItem item
) const
1462 wxCHECK_MSG( item
.IsOk(), wxCHK_UNDETERMINED
, "Invalid item" );
1464 return item
->m_checkedState
;
1468 wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item
,
1469 wxCheckBoxState state
) const
1471 wxCHECK_MSG( item
.IsOk(), false, "Invalid item" );
1473 for ( wxTreeListItem child
= GetFirstChild(item
);
1475 child
= GetNextSibling(child
) )
1477 if ( GetCheckedState(child
) != state
)
1484 // ----------------------------------------------------------------------------
1486 // ----------------------------------------------------------------------------
1488 void wxTreeListCtrl::SetSortColumn(unsigned col
, bool ascendingOrder
)
1490 wxCHECK_RET( col
< m_view
->GetColumnCount(), "Invalid column index" );
1492 m_view
->GetColumn(col
)->SetSortOrder(ascendingOrder
);
1495 bool wxTreeListCtrl::GetSortColumn(unsigned* col
, bool* ascendingOrder
)
1497 const unsigned numColumns
= m_view
->GetColumnCount();
1498 for ( unsigned n
= 0; n
< numColumns
; n
++ )
1500 wxDataViewColumn
* const column
= m_view
->GetColumn(n
);
1501 if ( column
->IsSortKey() )
1506 if ( ascendingOrder
)
1507 *ascendingOrder
= column
->IsSortOrderAscending();
1516 void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator
* comparator
)
1518 m_comparator
= comparator
;
1521 // ----------------------------------------------------------------------------
1523 // ----------------------------------------------------------------------------
1525 void wxTreeListCtrl::SendItemEvent(wxEventType evt
, wxDataViewEvent
& eventDV
)
1527 wxTreeListEvent
eventTL(evt
, this, m_model
->FromDVI(eventDV
.GetItem()));
1529 if ( !ProcessWindowEvent(eventTL
) )
1535 if ( !eventTL
.IsAllowed() )
1541 void wxTreeListCtrl::SendColumnEvent(wxEventType evt
, wxDataViewEvent
& eventDV
)
1543 wxTreeListEvent
eventTL(evt
, this, wxTreeListItem());
1544 eventTL
.SetColumn(eventDV
.GetColumn());
1546 if ( !ProcessWindowEvent(eventTL
) )
1552 if ( !eventTL
.IsAllowed() )
1559 wxTreeListCtrl::OnItemToggled(wxTreeListItem item
, wxCheckBoxState stateOld
)
1561 wxTreeListEvent
event(wxEVT_COMMAND_TREELIST_ITEM_CHECKED
, this, item
);
1562 event
.SetOldCheckedState(stateOld
);
1564 ProcessWindowEvent(event
);
1567 void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent
& event
)
1569 SendItemEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED
, event
);
1572 void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent
& event
)
1574 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING
, event
);
1577 void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent
& event
)
1579 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED
, event
);
1582 void wxTreeListCtrl::OnItemActivated(wxDataViewEvent
& event
)
1584 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED
, event
);
1587 void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent
& event
)
1589 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU
, event
);
1592 void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent
& event
)
1594 SendColumnEvent(wxEVT_COMMAND_TREELIST_COLUMN_SORTED
, event
);
1597 // ----------------------------------------------------------------------------
1599 // ----------------------------------------------------------------------------
1601 void wxTreeListCtrl::OnSize(wxSizeEvent
& event
)
1607 // Resize the real control to cover our entire client area.
1608 const wxRect rect
= GetClientRect();
1609 m_view
->SetSize(rect
);
1611 // Resize the first column to take the remaining available space, if
1613 const unsigned numColumns
= GetColumnCount();
1617 // There is a bug in generic wxDataViewCtrl: if the column width sums
1618 // up to the total size, horizontal scrollbar (unnecessarily) appears,
1619 // so subtract 10 pixels to ensure this doesn't happen.
1620 int remainingWidth
= rect
.width
- 10;
1621 for ( unsigned n
= 1; n
< GetColumnCount(); n
++ )
1623 remainingWidth
-= GetColumnWidth(n
);
1624 if ( remainingWidth
< 0 )
1628 // We don't decrease the width of the first column, even if we had
1629 // increased it ourselves, because we want to avoid changing its size
1630 // if the user resized it. We might want to remember if this was the
1631 // case or if we only ever adjusted it automatically in the future.
1632 if ( remainingWidth
> GetColumnWidth(0) )
1633 SetColumnWidth(0, remainingWidth
);
1637 wxWindow
* wxTreeListCtrl::GetView() const
1639 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
1640 return m_view
->GetMainWindow();
1646 // ============================================================================
1647 // wxTreeListEvent implementation
1648 // ============================================================================
1650 wxIMPLEMENT_ABSTRACT_CLASS(wxTreeListEvent
, wxNotifyEvent
)
1652 #define wxDEFINE_TREELIST_EVENT(name) \
1653 wxDEFINE_EVENT(wxEVT_COMMAND_TREELIST_##name, wxTreeListEvent)
1655 wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED
);
1656 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING
);
1657 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED
);
1658 wxDEFINE_TREELIST_EVENT(ITEM_CHECKED
);
1659 wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED
);
1660 wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU
);
1661 wxDEFINE_TREELIST_EVENT(COLUMN_SORTED
);
1663 #undef wxDEFINE_TREELIST_EVENT
1665 #endif // wxUSE_TREELISTCTRL