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"
30 #include "wx/treelist.h"
32 #include "wx/dataview.h"
33 #include "wx/renderer.h"
34 #include "wx/scopedarray.h"
35 #include "wx/scopedptr.h"
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 const char wxTreeListCtrlNameStr
[] = "wxTreeListCtrl";
43 const wxTreeListItem
wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode
*>(-1));
44 const wxTreeListItem
wxTLI_LAST(reinterpret_cast<wxTreeListModelNode
*>(-2));
46 // ----------------------------------------------------------------------------
47 // wxTreeListModelNode: a node in the internal tree representation.
48 // ----------------------------------------------------------------------------
50 class wxTreeListModelNode
53 wxTreeListModelNode(wxTreeListModelNode
* parent
,
54 const wxString
& text
= wxString(),
55 int imageClosed
= wxWithImages::NO_IMAGE
,
56 int imageOpened
= wxWithImages::NO_IMAGE
,
57 wxClientData
* data
= NULL
)
64 m_imageClosed
= imageClosed
;
65 m_imageOpened
= imageOpened
;
67 m_checkedState
= wxCHK_UNCHECKED
;
71 m_columnsTexts
= NULL
;
74 // Destroying the node also (recursively) destroys its children.
75 ~wxTreeListModelNode()
77 for ( wxTreeListModelNode
* node
= m_child
; node
; )
79 wxTreeListModelNode
* child
= node
;
86 delete [] m_columnsTexts
;
90 // Public fields for the first column text and other simple attributes:
91 // there is no need to have accessors/mutators for those as there is no
92 // encapsulation anyhow, all of those are exposed in our public API.
98 wxCheckBoxState m_checkedState
;
101 // Accessors for the fields that are not directly exposed.
103 // Client data is owned by us so delete the old value when setting the new
105 wxClientData
* GetClientData() const { return m_data
; }
106 void SetClientData(wxClientData
* data
) { delete m_data
; m_data
= data
; }
108 // Setting or getting the non-first column text. Getting is simple but you
109 // need to call HasColumnsTexts() first as the column data is only
110 // allocated on demand. And when setting the text we require to be given
111 // the total number of columns as we allocate the entire array at once,
112 // this is more efficient than using dynamically-expandable wxVector that
113 // we know won't be needed as the number of columns is usually fixed. But
114 // if it does change, our OnInsertColumn() must be called.
116 // Notice the presence of -1 everywhere in these methods: this is because
117 // the text for the first column is always stored in m_text and so we don't
118 // store it in m_columnsTexts.
120 bool HasColumnsTexts() const { return m_columnsTexts
!= NULL
; }
121 const wxString
& GetColumnText(unsigned col
) const
123 return m_columnsTexts
[col
- 1];
126 void SetColumnText(const wxString
& text
, unsigned col
, unsigned numColumns
)
128 if ( !m_columnsTexts
)
129 m_columnsTexts
= new wxString
[numColumns
- 1];
131 m_columnsTexts
[col
- 1] = text
;
134 void OnInsertColumn(unsigned col
, unsigned numColumns
)
136 wxASSERT_MSG( col
, "Shouldn't be called for the first column" );
138 // Nothing to do if we don't have any text.
139 if ( !m_columnsTexts
)
142 wxScopedArray
<wxString
> oldTexts(m_columnsTexts
);
143 m_columnsTexts
= new wxString
[numColumns
- 1];
145 // In the loop below n is the index in the new column texts array and m
146 // is the index in the old one.
147 for ( unsigned n
= 1, m
= 1; n
< numColumns
- 1; n
++, m
++ )
151 // Leave the new array text initially empty and just adjust the
152 // index (to compensate for "m++" done by the loop anyhow).
155 else // Not the newly inserted column.
157 // Copy the old text value.
158 m_columnsTexts
[n
- 1] = oldTexts
[m
- 1];
164 // Functions for modifying the tree.
166 // Insert the given item as the first child of this one. The parent pointer
167 // must have been already set correctly at creation and we take ownership
168 // of the pointer and will delete it later.
169 void InsertChild(wxTreeListModelNode
* child
)
171 wxASSERT( child
->m_parent
== this );
173 // Our previous first child becomes the next sibling of the new child.
174 child
->m_next
= m_child
;
178 // Insert the given item as our next sibling. As above, the item must have
179 // the correct parent pointer and we take ownership of it.
180 void InsertNext(wxTreeListModelNode
* next
)
182 wxASSERT( next
->m_parent
== m_parent
);
184 next
->m_next
= m_next
;
188 // Remove the first child of this item from the tree and delete it.
191 wxTreeListModelNode
* const oldChild
= m_child
;
192 m_child
= m_child
->m_next
;
196 // Remove the next sibling of this item from the tree and deletes it.
199 wxTreeListModelNode
* const oldNext
= m_next
;
200 m_next
= m_next
->m_next
;
205 // Functions for tree traversal. All of them can return NULL.
207 // Only returns NULL when called on the root item.
208 wxTreeListModelNode
* GetParent() const { return m_parent
; }
210 // Returns the first child of this item.
211 wxTreeListModelNode
* GetChild() const { return m_child
; }
213 // Returns the next sibling of this item.
214 wxTreeListModelNode
* GetNext() const { return m_next
; }
216 // Unlike the previous two functions, this one is not a simple accessor
217 // (hence it's not called "GetSomething") but computes the next node after
218 // this one in tree order.
219 wxTreeListModelNode
* NextInTree() const
227 // Recurse upwards until we find the next sibling.
228 for ( wxTreeListModelNode
* node
= m_parent
; node
; node
= node
->m_parent
)
239 // The (never changing after creation) parent of this node and the possibly
240 // NULL pointers to its first child and next sibling.
241 wxTreeListModelNode
* const m_parent
;
242 wxTreeListModelNode
* m_child
;
243 wxTreeListModelNode
* m_next
;
245 // Client data pointer owned by the control. May be NULL.
246 wxClientData
* m_data
;
248 // Array of column values for all the columns except the first one. May be
249 // NULL if no values had been set for them.
250 wxString
* m_columnsTexts
;
253 // ----------------------------------------------------------------------------
254 // wxTreeListModel: wxDataViewModel implementation used by wxTreeListCtrl.
255 // ----------------------------------------------------------------------------
257 class wxTreeListModel
: public wxDataViewModel
260 typedef wxTreeListModelNode Node
;
262 // Unlike a general wxDataViewModel, this model can only be used with a
263 // single control at once. The main reason for this is that we need to
264 // support different icons for opened and closed items and the item state
265 // is associated with the control, not the model, so our GetValue() is also
266 // bound to it (otherwise, what would it return for an item expanded in one
267 // associated control and collapsed in another one?).
268 wxTreeListModel(wxTreeListCtrl
* treelist
);
269 virtual ~wxTreeListModel();
272 // Helpers for converting between wxDataViewItem and wxTreeListItem. These
273 // methods simply cast the pointer to/from wxDataViewItem except for the
274 // root node that we handle specially unless explicitly disabled.
276 // The advantage of using them is that they're greppable and stand out
277 // better, hopefully making the code more clear.
278 Node
* FromNonRootDVI(wxDataViewItem dvi
) const
280 return static_cast<Node
*>(dvi
.GetID());
283 Node
* FromDVI(wxDataViewItem dvi
) const
288 return FromNonRootDVI(dvi
);
291 wxDataViewItem
ToNonRootDVI(Node
* node
) const
293 return wxDataViewItem(node
);
296 wxDataViewItem
ToDVI(Node
* node
) const
298 // Our root item must be represented as NULL at wxDVC level to map to
299 // its own invisible root.
300 if ( !node
->GetParent() )
301 return wxDataViewItem();
303 return ToNonRootDVI(node
);
307 // Methods called by wxTreeListCtrl.
308 void InsertColumn(unsigned col
);
310 Node
* InsertItem(Node
* parent
,
312 const wxString
& text
,
316 void DeleteItem(Node
* item
);
317 void DeleteAllItems();
319 Node
* GetRootItem() const { return m_root
; }
321 const wxString
& GetItemText(Node
* item
, unsigned col
) const;
322 void SetItemText(Node
* item
, unsigned col
, const wxString
& text
);
323 void SetItemImage(Node
* item
, int closed
, int opened
);
324 wxClientData
* GetItemData(Node
* item
) const;
325 void SetItemData(Node
* item
, wxClientData
* data
);
327 void CheckItem(Node
* item
, wxCheckBoxState checkedState
);
328 void ToggleItem(wxDataViewItem item
);
331 // Implement the base class pure virtual methods.
332 virtual unsigned GetColumnCount() const;
333 virtual wxString
GetColumnType(unsigned col
) const;
334 virtual void GetValue(wxVariant
& variant
,
335 const wxDataViewItem
& item
,
337 virtual bool SetValue(const wxVariant
& variant
,
338 const wxDataViewItem
& item
,
340 virtual wxDataViewItem
GetParent(const wxDataViewItem
& item
) const;
341 virtual bool IsContainer(const wxDataViewItem
& item
) const;
342 virtual bool HasContainerColumns(const wxDataViewItem
& item
) const;
343 virtual unsigned GetChildren(const wxDataViewItem
& item
,
344 wxDataViewItemArray
& children
) const;
345 virtual bool IsListModel() const { return m_isFlat
; }
348 // The control we're associated with.
349 wxTreeListCtrl
* const m_treelist
;
351 // The unique invisible root element.
354 // Number of columns we maintain.
355 unsigned m_numColumns
;
357 // Set to false as soon as we have more than one level, i.e. as soon as any
358 // items with non-root item as parent are added (and currently never reset
363 // ============================================================================
364 // wxDataViewCheckIconText[Renderer]: special renderer for our first column.
365 // ============================================================================
367 // Currently this class is private but it could be extracted and made part of
368 // public API later as could be used directly with wxDataViewCtrl as well.
372 const char* CHECK_ICON_TEXT_TYPE
= "wxDataViewCheckIconText";
374 // The value used by wxDataViewCheckIconTextRenderer
375 class wxDataViewCheckIconText
: public wxDataViewIconText
378 wxDataViewCheckIconText(const wxString
& text
= wxString(),
379 const wxIcon
& icon
= wxNullIcon
,
380 wxCheckBoxState checkedState
= wxCHK_UNDETERMINED
)
381 : wxDataViewIconText(text
, icon
),
382 m_checkedState(checkedState
)
386 wxDataViewCheckIconText(const wxDataViewCheckIconText
& other
)
387 : wxDataViewIconText(other
),
388 m_checkedState(other
.m_checkedState
)
392 bool IsSameAs(const wxDataViewCheckIconText
& other
) const
394 return wxDataViewIconText::IsSameAs(other
) &&
395 m_checkedState
== other
.m_checkedState
;
398 // There is no encapsulation anyhow, so just expose this field directly.
399 wxCheckBoxState m_checkedState
;
403 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText
);
406 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText
, wxDataViewIconText
);
408 DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText
)
409 IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText
)
412 class wxDataViewCheckIconTextRenderer
: public wxDataViewCustomRenderer
415 wxDataViewCheckIconTextRenderer()
416 : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE
,
417 wxDATAVIEW_CELL_ACTIVATABLE
)
421 virtual bool SetValue(const wxVariant
& value
)
427 virtual bool GetValue(wxVariant
& WXUNUSED(value
)) const
432 wxSize
GetSize() const
434 wxSize size
= GetCheckSize();
435 size
.x
+= MARGIN_CHECK_ICON
;
437 if ( m_value
.GetIcon().IsOk() )
439 const wxSize sizeIcon
= m_value
.GetIcon().GetSize();
440 if ( sizeIcon
.y
> size
.y
)
443 size
.x
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
446 wxString text
= m_value
.GetText();
450 const wxSize sizeText
= GetTextExtent(text
);
451 if ( sizeText
.y
> size
.y
)
454 size
.x
+= sizeText
.x
;
459 virtual bool Render(wxRect cell
, wxDC
* dc
, int state
)
461 // Draw the checkbox first.
463 switch ( m_value
.m_checkedState
)
465 case wxCHK_UNCHECKED
:
469 renderFlags
|= wxCONTROL_CHECKED
;
472 case wxCHK_UNDETERMINED
:
473 renderFlags
|= wxCONTROL_UNDETERMINED
;
477 if ( state
& wxDATAVIEW_CELL_PRELIT
)
478 renderFlags
|= wxCONTROL_CURRENT
;
480 const wxSize sizeCheck
= GetCheckSize();
482 wxRect
rectCheck(cell
.GetPosition(), sizeCheck
);
483 rectCheck
= rectCheck
.CentreIn(cell
, wxVERTICAL
);
485 wxRendererNative::Get().DrawCheckBox
487 GetView(), *dc
, rectCheck
, renderFlags
490 // Then the icon, if any.
491 int xoffset
= sizeCheck
.x
+ MARGIN_CHECK_ICON
;
493 const wxIcon
& icon
= m_value
.GetIcon();
496 const wxSize sizeIcon
= icon
.GetSize();
497 wxRect
rectIcon(cell
.GetPosition(), sizeIcon
);
498 rectIcon
.x
+= xoffset
;
499 rectIcon
= rectIcon
.CentreIn(cell
, wxVERTICAL
);
501 dc
->DrawIcon(icon
, rectIcon
.GetPosition());
503 xoffset
+= sizeIcon
.x
+ MARGIN_ICON_TEXT
;
507 RenderText(m_value
.GetText(), xoffset
, cell
, dc
, state
);
512 // Event handlers toggling the items checkbox if it was clicked.
513 virtual bool Activate(const wxRect
& WXUNUSED(cell
),
514 wxDataViewModel
* model
,
515 const wxDataViewItem
& item
,
516 unsigned int WXUNUSED(col
))
518 static_cast<wxTreeListModel
*>(model
)->ToggleItem(item
);
522 virtual bool LeftClick(const wxPoint
& pos
,
523 const wxRect
& WXUNUSED(cell
),
524 wxDataViewModel
* model
,
525 const wxDataViewItem
& item
,
526 unsigned int WXUNUSED(col
))
528 if ( !wxRect(GetCheckSize()).Contains(pos
) )
531 static_cast<wxTreeListModel
*>(model
)->ToggleItem(item
);
536 wxSize
GetCheckSize() const
538 return wxRendererNative::Get().GetCheckBoxSize(GetView());
542 // Just some arbitrary constants defining margins, in pixels.
545 MARGIN_CHECK_ICON
= 3,
549 wxDataViewCheckIconText m_value
;
552 } // anonymous namespace
554 // ============================================================================
555 // wxTreeListModel implementation
556 // ============================================================================
558 wxTreeListModel::wxTreeListModel(wxTreeListCtrl
* treelist
)
559 : m_treelist(treelist
),
560 m_root(new Node(NULL
))
566 wxTreeListModel::~wxTreeListModel()
571 void wxTreeListModel::InsertColumn(unsigned col
)
575 // There is no need to update anything when inserting the first column.
576 if ( m_numColumns
== 1 )
579 // Update all the items as they may have texts for the old columns.
580 for ( Node
* node
= m_root
->GetChild(); node
; node
= node
->NextInTree() )
582 node
->OnInsertColumn(col
, m_numColumns
);
587 wxTreeListModel::InsertItem(Node
* parent
,
589 const wxString
& text
,
594 wxCHECK_MSG( parent
, NULL
,
595 "Must have a valid parent (maybe GetRootItem()?)" );
597 wxCHECK_MSG( previous
, NULL
,
598 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
600 if ( m_isFlat
&& parent
!= m_root
)
602 // Not flat any more, this is a second level child.
607 newItem(new Node(parent
, text
, imageClosed
, imageOpened
, data
));
609 // If we have no children at all, then inserting as last child is the same
610 // as inserting as the first one so check for it here too.
611 if ( previous
== wxTLI_FIRST
||
612 (previous
== wxTLI_LAST
&& !parent
->GetChild()) )
614 parent
->InsertChild(newItem
.get());
616 else // Not the first item, find the previous one.
618 if ( previous
== wxTLI_LAST
)
620 previous
= parent
->GetChild();
622 // Find the last child.
625 Node
* const next
= previous
->GetNext();
632 else // We already have the previous item.
634 // Just check it's under the correct parent.
635 wxCHECK_MSG( previous
->GetParent() == parent
, NULL
,
636 "Previous item is not under the right parent" );
639 previous
->InsertNext(newItem
.get());
642 ItemAdded(ToDVI(parent
), ToDVI(newItem
.get()));
644 // The item was successfully inserted in the tree and so will be deleted by
645 // it, we can detach it now.
646 return newItem
.release();
649 void wxTreeListModel::DeleteItem(Node
* item
)
651 wxCHECK_RET( item
, "Invalid item" );
653 wxCHECK_RET( item
!= m_root
, "Can't delete the root item" );
655 Node
* const parent
= item
->GetParent();
657 ItemDeleted(ToDVI(parent
), ToDVI(item
));
659 Node
* previous
= parent
->GetChild();
660 if ( previous
== item
)
662 parent
->DeleteChild();
664 else // Not the first child of its parent.
666 // Find the sibling just before it.
669 Node
* const next
= previous
->GetNext();
673 wxCHECK_RET( next
, "Item not a child of its parent?" );
678 previous
->DeleteNext();
682 void wxTreeListModel::DeleteAllItems()
684 while ( m_root
->GetChild() )
686 m_root
->DeleteChild();
692 const wxString
& wxTreeListModel::GetItemText(Node
* item
, unsigned col
) const
694 // Returning root item text here is bogus, it just happens to be an always
695 // empty string we can return reference to.
696 wxCHECK_MSG( item
, m_root
->m_text
, "Invalid item" );
698 return col
== 0 ? item
->m_text
: item
->GetColumnText(col
);
701 void wxTreeListModel::SetItemText(Node
* item
, unsigned col
, const wxString
& text
)
703 wxCHECK_RET( item
, "Invalid item" );
708 item
->SetColumnText(text
, col
, m_numColumns
);
710 ValueChanged(ToDVI(item
), col
);
713 void wxTreeListModel::SetItemImage(Node
* item
, int closed
, int opened
)
715 wxCHECK_RET( item
, "Invalid item" );
717 item
->m_imageClosed
= closed
;
718 item
->m_imageOpened
= opened
;
720 ValueChanged(ToDVI(item
), 0);
723 wxClientData
* wxTreeListModel::GetItemData(Node
* item
) const
725 wxCHECK_MSG( item
, NULL
, "Invalid item" );
727 return item
->GetClientData();
730 void wxTreeListModel::SetItemData(Node
* item
, wxClientData
* data
)
732 wxCHECK_RET( item
, "Invalid item" );
734 item
->SetClientData(data
);
737 void wxTreeListModel::CheckItem(Node
* item
, wxCheckBoxState checkedState
)
739 wxCHECK_RET( item
, "Invalid item" );
741 item
->m_checkedState
= checkedState
;
743 ItemChanged(ToDVI(item
));
746 void wxTreeListModel::ToggleItem(wxDataViewItem dvi
)
748 Node
* const item
= FromDVI(dvi
);
750 wxCHECK_RET( item
, "Invalid item" );
752 const wxCheckBoxState stateOld
= item
->m_checkedState
;
754 // If the 3rd state is user-settable then the cycle is
755 // unchecked->checked->undetermined.
759 item
->m_checkedState
= m_treelist
->HasFlag(wxTL_USER_3STATE
)
764 case wxCHK_UNDETERMINED
:
765 // Whether 3rd state is user-settable or not, the next state is
767 item
->m_checkedState
= wxCHK_UNCHECKED
;
770 case wxCHK_UNCHECKED
:
771 item
->m_checkedState
= wxCHK_CHECKED
;
775 ItemChanged(ToDVI(item
));
777 m_treelist
->OnItemToggled(item
, stateOld
);
780 unsigned wxTreeListModel::GetColumnCount() const
785 wxString
wxTreeListModel::GetColumnType(unsigned col
) const
789 return m_treelist
->HasFlag(wxTL_CHECKBOX
)
790 ? wxS("wxDataViewCheckIconText")
791 : wxS("wxDataViewIconText");
793 else // All the other columns contain just text.
795 return wxS("string");
800 wxTreeListModel::GetValue(wxVariant
& variant
,
801 const wxDataViewItem
& item
,
804 Node
* const node
= FromDVI(item
);
808 // Determine the correct image to use depending on the item state.
809 int image
= wxWithImages::NO_IMAGE
;
810 if ( m_treelist
->IsExpanded(node
) )
811 image
= node
->m_imageOpened
;
813 if ( image
== wxWithImages::NO_IMAGE
)
814 image
= node
->m_imageClosed
;
816 wxIcon icon
= m_treelist
->GetImage(image
);
818 if ( m_treelist
->HasFlag(wxTL_CHECKBOX
) )
819 variant
<< wxDataViewCheckIconText(node
->m_text
, icon
,
820 node
->m_checkedState
);
822 variant
<< wxDataViewIconText(node
->m_text
, icon
);
826 // Notice that we must still assign wxString to wxVariant to ensure
827 // that it at least has the correct type.
829 if ( node
->HasColumnsTexts() )
830 text
= node
->GetColumnText(col
);
837 wxTreeListModel::SetValue(const wxVariant
& WXUNUSED(variant
),
838 const wxDataViewItem
& WXUNUSED(item
),
839 unsigned WXUNUSED(col
))
841 // We are not editable currently.
845 wxDataViewItem
wxTreeListModel::GetParent(const wxDataViewItem
& item
) const
847 Node
* const node
= FromDVI(item
);
849 return ToDVI(node
->GetParent());
852 bool wxTreeListModel::IsContainer(const wxDataViewItem
& item
) const
854 // FIXME: In the generic (and native OS X) versions we implement this
855 // method normally, i.e. only items with children are containers.
856 // But for the native GTK version we must pretend that all items are
857 // containers because otherwise adding children to them later would
858 // fail because wxGTK code calls IsContainer() too early (when
859 // adding the item itself) and we can't know whether we're container
860 // or not by then. Luckily, always returning true doesn't have any
861 // serious drawbacks for us.
867 Node
* const node
= FromDVI(item
);
869 return node
->GetChild() != NULL
;
874 wxTreeListModel::HasContainerColumns(const wxDataViewItem
& WXUNUSED(item
)) const
880 wxTreeListModel::GetChildren(const wxDataViewItem
& item
,
881 wxDataViewItemArray
& children
) const
883 Node
* const node
= FromDVI(item
);
885 unsigned numChildren
= 0;
886 for ( Node
* child
= node
->GetChild(); child
; child
= child
->GetNext() )
888 children
.push_back(ToDVI(child
));
895 // ============================================================================
896 // wxTreeListCtrl implementation
897 // ============================================================================
899 BEGIN_EVENT_TABLE(wxTreeListCtrl
, wxWindow
)
900 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY
, wxTreeListCtrl::OnSelectionChanged
)
901 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY
, wxTreeListCtrl::OnItemExpanding
)
902 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY
, wxTreeListCtrl::OnItemExpanded
)
903 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY
, wxTreeListCtrl::OnItemActivated
)
904 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY
, wxTreeListCtrl::OnItemContextMenu
)
906 EVT_SIZE(wxTreeListCtrl::OnSize
)
909 // ----------------------------------------------------------------------------
911 // ----------------------------------------------------------------------------
913 void wxTreeListCtrl::Init()
919 bool wxTreeListCtrl::Create(wxWindow
* parent
,
924 const wxString
& name
)
926 if ( style
& wxTL_USER_3STATE
)
927 style
|= wxTL_3STATE
;
929 if ( style
& wxTL_3STATE
)
930 style
|= wxTL_CHECKBOX
;
932 // Create the window itself and wxDataViewCtrl used by it.
933 if ( !wxWindow::Create(parent
, id
,
940 m_view
= new wxDataViewCtrl
;
941 if ( !m_view
->Create(this, wxID_ANY
,
942 wxPoint(0, 0), GetClientSize(),
943 HasFlag(wxTL_MULTIPLE
) ? wxDV_MULTIPLE
953 // Set up the model for wxDataViewCtrl.
954 m_model
= new wxTreeListModel(this);
955 m_view
->AssociateModel(m_model
);
960 wxTreeListCtrl::~wxTreeListCtrl()
966 // ----------------------------------------------------------------------------
968 // ----------------------------------------------------------------------------
971 wxTreeListCtrl::DoInsertColumn(const wxString
& title
,
977 wxCHECK_MSG( m_view
, wxNOT_FOUND
, "Must Create() first" );
979 const unsigned oldNumColumns
= m_view
->GetColumnCount();
981 if ( pos
== wxNOT_FOUND
)
984 wxDataViewRenderer
* renderer
;
987 // Inserting the first column which is special as it uses a different
990 // Also, currently it can be done only once.
991 wxCHECK_MSG( !oldNumColumns
, wxNOT_FOUND
,
992 "Inserting column at position 0 currently not supported" );
994 if ( HasFlag(wxTL_CHECKBOX
) )
996 // Use our custom renderer to show the checkbox.
997 renderer
= new wxDataViewCheckIconTextRenderer
;
999 else // We still need a special renderer to show the icons.
1001 renderer
= new wxDataViewIconTextRenderer
;
1004 else // Not the first column.
1006 // All the other ones use a simple text renderer.
1007 renderer
= new wxDataViewTextRenderer
;
1011 column
= new wxDataViewColumn(title
, renderer
, pos
, width
, align
, flags
);
1013 m_model
->InsertColumn(pos
);
1015 m_view
->InsertColumn(pos
, column
);
1020 unsigned wxTreeListCtrl::GetColumnCount() const
1022 return m_view
? m_view
->GetColumnCount() : 0u;
1025 bool wxTreeListCtrl::DeleteColumn(unsigned col
)
1027 wxCHECK_MSG( col
< GetColumnCount(), false, "Invalid column index" );
1029 return m_view
->DeleteColumn(m_view
->GetColumn(col
));
1032 void wxTreeListCtrl::ClearColumns()
1035 m_view
->ClearColumns();
1038 void wxTreeListCtrl::SetColumnWidth(unsigned col
, int width
)
1040 wxCHECK_RET( col
< GetColumnCount(), "Invalid column index" );
1042 wxDataViewColumn
* const column
= m_view
->GetColumn(col
);
1043 wxCHECK_RET( column
, "No such column?" );
1045 return column
->SetWidth(width
);
1048 int wxTreeListCtrl::GetColumnWidth(unsigned col
) const
1050 wxCHECK_MSG( col
< GetColumnCount(), -1, "Invalid column index" );
1052 wxDataViewColumn
* column
= m_view
->GetColumn(col
);
1053 wxCHECK_MSG( column
, -1, "No such column?" );
1055 return column
->GetWidth();
1058 int wxTreeListCtrl::WidthFor(const wxString
& text
) const
1060 return GetTextExtent(text
).x
;
1063 // ----------------------------------------------------------------------------
1065 // ----------------------------------------------------------------------------
1068 wxTreeListCtrl::DoInsertItem(wxTreeListItem parent
,
1069 wxTreeListItem previous
,
1070 const wxString
& text
,
1075 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1077 return wxTreeListItem(m_model
->InsertItem(parent
, previous
, text
,
1078 imageClosed
, imageOpened
, data
));
1081 void wxTreeListCtrl::DeleteItem(wxTreeListItem item
)
1083 wxCHECK_RET( m_model
, "Must create first" );
1085 m_model
->DeleteItem(item
);
1088 void wxTreeListCtrl::DeleteAllItems()
1091 m_model
->DeleteAllItems();
1094 // ----------------------------------------------------------------------------
1096 // ----------------------------------------------------------------------------
1098 // The simple accessors in this section are implemented directly using
1099 // wxTreeListModelNode methods, without passing by the model. This is just a
1100 // shortcut and avoids us the trouble of defining more trivial methods in
1103 wxTreeListItem
wxTreeListCtrl::GetRootItem() const
1105 wxCHECK_MSG( m_model
, wxTreeListItem(), "Must create first" );
1107 return m_model
->GetRootItem();
1110 wxTreeListItem
wxTreeListCtrl::GetItemParent(wxTreeListItem item
) const
1112 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1114 return item
->GetParent();
1117 wxTreeListItem
wxTreeListCtrl::GetFirstChild(wxTreeListItem item
) const
1119 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1121 return item
->GetChild();
1125 wxTreeListCtrl::GetNextSibling(wxTreeListItem item
) const
1127 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1129 return item
->GetNext();
1132 wxTreeListItem
wxTreeListCtrl::GetNextItem(wxTreeListItem item
) const
1134 wxCHECK_MSG( item
.IsOk(), wxTreeListItem(), "Invalid item" );
1136 return item
->NextInTree();
1139 // ----------------------------------------------------------------------------
1141 // ----------------------------------------------------------------------------
1144 wxTreeListCtrl::GetItemText(wxTreeListItem item
, unsigned col
) const
1146 // We can't use wxCHECK_MSG() here because we don't have any empty string
1147 // reference to return so we use a static variable that exists just for the
1148 // purpose of this check -- and so we put it in its own scope so that it's
1149 // never even created during normal program execution.
1150 if ( !m_model
|| col
>= m_model
->GetColumnCount() )
1152 static wxString s_empty
;
1156 wxFAIL_MSG( "Must create first" );
1158 else if ( col
>= m_model
->GetColumnCount() )
1160 wxFAIL_MSG( "Invalid column index" );
1166 return m_model
->GetItemText(item
, col
);
1170 wxTreeListCtrl::SetItemText(wxTreeListItem item
,
1172 const wxString
& text
)
1174 wxCHECK_RET( m_model
, "Must create first" );
1175 wxCHECK_RET( col
< m_model
->GetColumnCount(), "Invalid column index" );
1177 m_model
->SetItemText(item
, col
, text
);
1180 void wxTreeListCtrl::SetItemImage(wxTreeListItem item
, int closed
, int opened
)
1182 wxCHECK_RET( m_model
, "Must create first" );
1184 if ( closed
!= NO_IMAGE
|| opened
!= NO_IMAGE
)
1186 wxImageList
* const imageList
= GetImageList();
1187 wxCHECK_RET( imageList
, "Can't set images without image list" );
1189 const int imageCount
= imageList
->GetImageCount();
1191 wxCHECK_RET( closed
< imageCount
, "Invalid image index" );
1192 wxCHECK_RET( opened
< imageCount
, "Invalid opened image index" );
1195 m_model
->SetItemImage(item
, closed
, opened
);
1198 wxClientData
* wxTreeListCtrl::GetItemData(wxTreeListItem item
) const
1200 wxCHECK_MSG( m_model
, NULL
, "Must create first" );
1202 return m_model
->GetItemData(item
);
1205 void wxTreeListCtrl::SetItemData(wxTreeListItem item
, wxClientData
* data
)
1207 wxCHECK_RET( m_model
, "Must create first" );
1209 m_model
->SetItemData(item
, data
);
1212 // ----------------------------------------------------------------------------
1213 // Expanding and collapsing
1214 // ----------------------------------------------------------------------------
1216 void wxTreeListCtrl::Expand(wxTreeListItem item
)
1218 wxCHECK_RET( m_view
, "Must create first" );
1220 m_view
->Expand(m_model
->ToDVI(item
));
1223 void wxTreeListCtrl::Collapse(wxTreeListItem item
)
1225 wxCHECK_RET( m_view
, "Must create first" );
1227 m_view
->Collapse(m_model
->ToDVI(item
));
1230 bool wxTreeListCtrl::IsExpanded(wxTreeListItem item
) const
1232 wxCHECK_MSG( m_view
, false, "Must create first" );
1234 return m_view
->IsExpanded(m_model
->ToDVI(item
));
1237 // ----------------------------------------------------------------------------
1239 // ----------------------------------------------------------------------------
1241 wxTreeListItem
wxTreeListCtrl::GetSelection() const
1243 wxCHECK_MSG( m_view
, wxTreeListItem(), "Must create first" );
1245 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE
), wxTreeListItem(),
1246 "Must use GetSelections() with multi-selection controls!" );
1248 const wxDataViewItem dvi
= m_view
->GetSelection();
1250 return m_model
->FromNonRootDVI(dvi
);
1253 unsigned wxTreeListCtrl::GetSelections(wxTreeListItems
& selections
) const
1255 wxCHECK_MSG( m_view
, 0, "Must create first" );
1257 wxDataViewItemArray selectionsDV
;
1258 const unsigned numSelected
= m_view
->GetSelections(selectionsDV
);
1259 selections
.resize(numSelected
);
1260 for ( unsigned n
= 0; n
< numSelected
; n
++ )
1261 selections
[n
] = m_model
->FromNonRootDVI(selectionsDV
[n
]);
1266 void wxTreeListCtrl::Select(wxTreeListItem item
)
1268 wxCHECK_RET( m_view
, "Must create first" );
1270 m_view
->Select(m_model
->ToNonRootDVI(item
));
1273 void wxTreeListCtrl::Unselect(wxTreeListItem item
)
1275 wxCHECK_RET( m_view
, "Must create first" );
1277 m_view
->Unselect(m_model
->ToNonRootDVI(item
));
1280 bool wxTreeListCtrl::IsSelected(wxTreeListItem item
) const
1282 wxCHECK_MSG( m_view
, false, "Must create first" );
1284 return m_view
->IsSelected(m_model
->ToNonRootDVI(item
));
1287 void wxTreeListCtrl::SelectAll()
1289 wxCHECK_RET( m_view
, "Must create first" );
1291 m_view
->SelectAll();
1294 void wxTreeListCtrl::UnselectAll()
1296 wxCHECK_RET( m_view
, "Must create first" );
1298 m_view
->UnselectAll();
1301 // ----------------------------------------------------------------------------
1302 // Checkbox handling
1303 // ----------------------------------------------------------------------------
1305 void wxTreeListCtrl::CheckItem(wxTreeListItem item
, wxCheckBoxState state
)
1307 wxCHECK_RET( m_model
, "Must create first" );
1309 m_model
->CheckItem(item
, state
);
1313 wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item
, wxCheckBoxState state
)
1315 wxCHECK_RET( m_model
, "Must create first" );
1317 m_model
->CheckItem(item
, state
);
1319 for ( wxTreeListItem child
= GetFirstChild(item
);
1321 child
= GetNextSibling(child
) )
1323 CheckItemRecursively(child
, state
);
1327 void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item
)
1329 wxCHECK_RET( item
.IsOk(), "Invalid item" );
1331 wxASSERT_MSG( HasFlag(wxTL_3STATE
), "Can only be used with wxTL_3STATE" );
1335 wxTreeListItem parent
= GetItemParent(item
);
1336 if ( parent
== GetRootItem() )
1338 // There is no checked state associated with the root item.
1342 // Set parent state to the state of this item if all the other children
1343 // have the same state too. Otherwise make it indeterminate.
1344 const wxCheckBoxState stateItem
= GetCheckedState(item
);
1345 CheckItem(parent
, AreAllChildrenInState(parent
, stateItem
)
1347 : wxCHK_UNDETERMINED
);
1349 // And do the same thing with the parent's parent too.
1354 wxCheckBoxState
wxTreeListCtrl::GetCheckedState(wxTreeListItem item
) const
1356 wxCHECK_MSG( item
.IsOk(), wxCHK_UNDETERMINED
, "Invalid item" );
1358 return item
->m_checkedState
;
1362 wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item
,
1363 wxCheckBoxState state
) const
1365 wxCHECK_MSG( item
.IsOk(), false, "Invalid item" );
1367 for ( wxTreeListItem child
= GetFirstChild(item
);
1369 child
= GetNextSibling(child
) )
1371 if ( GetCheckedState(child
) != state
)
1378 // ----------------------------------------------------------------------------
1380 // ----------------------------------------------------------------------------
1382 void wxTreeListCtrl::SendEvent(wxEventType evt
, wxDataViewEvent
& eventDV
)
1384 wxTreeListEvent
eventTL(evt
, this, m_model
->FromDVI(eventDV
.GetItem()));
1386 if ( !ProcessWindowEvent(eventTL
) )
1392 if ( !eventTL
.IsAllowed() )
1399 wxTreeListCtrl::OnItemToggled(wxTreeListItem item
, wxCheckBoxState stateOld
)
1401 wxTreeListEvent
event(wxEVT_COMMAND_TREELIST_ITEM_CHECKED
, this, item
);
1402 event
.SetOldCheckedState(stateOld
);
1404 ProcessWindowEvent(event
);
1407 void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent
& event
)
1409 SendEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED
, event
);
1412 void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent
& event
)
1414 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING
, event
);
1417 void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent
& event
)
1419 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED
, event
);
1422 void wxTreeListCtrl::OnItemActivated(wxDataViewEvent
& event
)
1424 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED
, event
);
1427 void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent
& event
)
1429 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU
, event
);
1432 // ----------------------------------------------------------------------------
1434 // ----------------------------------------------------------------------------
1436 void wxTreeListCtrl::OnSize(wxSizeEvent
& event
)
1442 // Resize the real control to cover our entire client area.
1443 const wxRect rect
= GetClientRect();
1444 m_view
->SetSize(rect
);
1446 // Resize the first column to take the remaining available space, if
1448 const unsigned numColumns
= GetColumnCount();
1452 // There is a bug in generic wxDataViewCtrl: if the column width sums
1453 // up to the total size, horizontal scrollbar (unnecessarily) appears,
1454 // so subtract 10 pixels to ensure this doesn't happen.
1455 int remainingWidth
= rect
.width
- 10;
1456 for ( unsigned n
= 1; n
< GetColumnCount(); n
++ )
1458 remainingWidth
-= GetColumnWidth(n
);
1459 if ( remainingWidth
< 0 )
1463 // We don't decrease the width of the first column, even if we had
1464 // increased it ourselves, because we want to avoid changing its size
1465 // if the user resized it. We might want to remember if this was the
1466 // case or if we only ever adjusted it automatically in the future.
1467 if ( remainingWidth
> GetColumnWidth(0) )
1468 SetColumnWidth(0, remainingWidth
);
1472 // ============================================================================
1473 // wxTreeListEvent implementation
1474 // ============================================================================
1476 wxIMPLEMENT_ABSTRACT_CLASS(wxTreeListEvent
, wxNotifyEvent
)
1478 #define wxDEFINE_TREELIST_EVENT(name) \
1479 wxDEFINE_EVENT(wxEVT_COMMAND_TREELIST_##name, wxTreeListEvent)
1481 wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED
);
1482 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING
);
1483 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED
);
1484 wxDEFINE_TREELIST_EVENT(ITEM_CHECKED
);
1485 wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED
);
1486 wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU
);
1488 #undef wxDEFINE_TREELIST_EVENT