]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treelist.cpp
Use wxTE_PROCESS_ENTER with wxDataViewCtrl text controls.
[wxWidgets.git] / src / generic / treelist.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/treelist.cpp
3 // Purpose: Generic wxTreeListCtrl implementation.
4 // Author: Vadim Zeitlin
5 // Created: 2011-08-19
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 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // Declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // Headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/dc.h"
28 #endif // WX_PRECOMP
29
30 #include "wx/treelist.h"
31
32 #include "wx/dataview.h"
33 #include "wx/renderer.h"
34 #include "wx/scopedarray.h"
35 #include "wx/scopedptr.h"
36
37 // ----------------------------------------------------------------------------
38 // Constants
39 // ----------------------------------------------------------------------------
40
41 const char wxTreeListCtrlNameStr[] = "wxTreeListCtrl";
42
43 const wxTreeListItem wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode*>(-1));
44 const wxTreeListItem wxTLI_LAST(reinterpret_cast<wxTreeListModelNode*>(-2));
45
46 // ----------------------------------------------------------------------------
47 // wxTreeListModelNode: a node in the internal tree representation.
48 // ----------------------------------------------------------------------------
49
50 class wxTreeListModelNode
51 {
52 public:
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)
58 : m_text(text),
59 m_parent(parent)
60 {
61 m_child =
62 m_next = NULL;
63
64 m_imageClosed = imageClosed;
65 m_imageOpened = imageOpened;
66
67 m_checkedState = wxCHK_UNCHECKED;
68
69 m_data = data;
70
71 m_columnsTexts = NULL;
72 }
73
74 // Destroying the node also (recursively) destroys its children.
75 ~wxTreeListModelNode()
76 {
77 for ( wxTreeListModelNode* node = m_child; node; )
78 {
79 wxTreeListModelNode* child = node;
80 node = node->m_next;
81 delete child;
82 }
83
84 delete m_data;
85
86 delete [] m_columnsTexts;
87 }
88
89
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.
93 wxString m_text;
94
95 int m_imageClosed,
96 m_imageOpened;
97
98 wxCheckBoxState m_checkedState;
99
100
101 // Accessors for the fields that are not directly exposed.
102
103 // Client data is owned by us so delete the old value when setting the new
104 // one.
105 wxClientData* GetClientData() const { return m_data; }
106 void SetClientData(wxClientData* data) { delete m_data; m_data = data; }
107
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.
115 //
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.
119
120 bool HasColumnsTexts() const { return m_columnsTexts != NULL; }
121 const wxString& GetColumnText(unsigned col) const
122 {
123 return m_columnsTexts[col - 1];
124 }
125
126 void SetColumnText(const wxString& text, unsigned col, unsigned numColumns)
127 {
128 if ( !m_columnsTexts )
129 m_columnsTexts = new wxString[numColumns - 1];
130
131 m_columnsTexts[col - 1] = text;
132 }
133
134 void OnInsertColumn(unsigned col, unsigned numColumns)
135 {
136 wxASSERT_MSG( col, "Shouldn't be called for the first column" );
137
138 // Nothing to do if we don't have any text.
139 if ( !m_columnsTexts )
140 return;
141
142 wxScopedArray<wxString> oldTexts(m_columnsTexts);
143 m_columnsTexts = new wxString[numColumns - 1];
144
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++ )
148 {
149 if ( n == col )
150 {
151 // Leave the new array text initially empty and just adjust the
152 // index (to compensate for "m++" done by the loop anyhow).
153 m--;
154 }
155 else // Not the newly inserted column.
156 {
157 // Copy the old text value.
158 m_columnsTexts[n - 1] = oldTexts[m - 1];
159 }
160 }
161 }
162
163
164 // Functions for modifying the tree.
165
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)
170 {
171 wxASSERT( child->m_parent == this );
172
173 // Our previous first child becomes the next sibling of the new child.
174 child->m_next = m_child;
175 m_child = child;
176 }
177
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)
181 {
182 wxASSERT( next->m_parent == m_parent );
183
184 next->m_next = m_next;
185 m_next = next;
186 }
187
188 // Remove the first child of this item from the tree and delete it.
189 void DeleteChild()
190 {
191 wxTreeListModelNode* const oldChild = m_child;
192 m_child = m_child->m_next;
193 delete oldChild;
194 }
195
196 // Remove the next sibling of this item from the tree and deletes it.
197 void DeleteNext()
198 {
199 wxTreeListModelNode* const oldNext = m_next;
200 m_next = m_next->m_next;
201 delete oldNext;
202 }
203
204
205 // Functions for tree traversal. All of them can return NULL.
206
207 // Only returns NULL when called on the root item.
208 wxTreeListModelNode* GetParent() const { return m_parent; }
209
210 // Returns the first child of this item.
211 wxTreeListModelNode* GetChild() const { return m_child; }
212
213 // Returns the next sibling of this item.
214 wxTreeListModelNode* GetNext() const { return m_next; }
215
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
220 {
221 if ( m_child )
222 return m_child;
223
224 if ( m_next )
225 return m_next;
226
227 // Recurse upwards until we find the next sibling.
228 for ( wxTreeListModelNode* node = m_parent; node; node = node->m_parent )
229 {
230 if ( node->m_next )
231 return node->m_next;
232 }
233
234 return NULL;
235 }
236
237
238 private:
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;
244
245 // Client data pointer owned by the control. May be NULL.
246 wxClientData* m_data;
247
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;
251 };
252
253 // ----------------------------------------------------------------------------
254 // wxTreeListModel: wxDataViewModel implementation used by wxTreeListCtrl.
255 // ----------------------------------------------------------------------------
256
257 class wxTreeListModel : public wxDataViewModel
258 {
259 public:
260 typedef wxTreeListModelNode Node;
261
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();
270
271
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.
275 //
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
279 {
280 return static_cast<Node*>(dvi.GetID());
281 }
282
283 Node* FromDVI(wxDataViewItem dvi) const
284 {
285 if ( !dvi.IsOk() )
286 return m_root;
287
288 return FromNonRootDVI(dvi);
289 }
290
291 wxDataViewItem ToNonRootDVI(Node* node) const
292 {
293 return wxDataViewItem(node);
294 }
295
296 wxDataViewItem ToDVI(Node* node) const
297 {
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();
302
303 return ToNonRootDVI(node);
304 }
305
306
307 // Methods called by wxTreeListCtrl.
308 void InsertColumn(unsigned col);
309
310 Node* InsertItem(Node* parent,
311 Node* previous,
312 const wxString& text,
313 int imageClosed,
314 int imageOpened,
315 wxClientData* data);
316 void DeleteItem(Node* item);
317 void DeleteAllItems();
318
319 Node* GetRootItem() const { return m_root; }
320
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);
326
327 void CheckItem(Node* item, wxCheckBoxState checkedState);
328 void ToggleItem(wxDataViewItem item);
329
330
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,
336 unsigned col) const;
337 virtual bool SetValue(const wxVariant& variant,
338 const wxDataViewItem& item,
339 unsigned col);
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
346 private:
347 // The control we're associated with.
348 wxTreeListCtrl* const m_treelist;
349
350 // The unique invisible root element.
351 Node* const m_root;
352
353 // Number of columns we maintain.
354 unsigned m_numColumns;
355 };
356
357 // ============================================================================
358 // wxDataViewCheckIconText[Renderer]: special renderer for our first column.
359 // ============================================================================
360
361 // Currently this class is private but it could be extracted and made part of
362 // public API later as could be used directly with wxDataViewCtrl as well.
363 namespace
364 {
365
366 const char* CHECK_ICON_TEXT_TYPE = "wxDataViewCheckIconText";
367
368 // The value used by wxDataViewCheckIconTextRenderer
369 class wxDataViewCheckIconText : public wxDataViewIconText
370 {
371 public:
372 wxDataViewCheckIconText(const wxString& text = wxString(),
373 const wxIcon& icon = wxNullIcon,
374 wxCheckBoxState checkedState = wxCHK_UNDETERMINED)
375 : wxDataViewIconText(text, icon),
376 m_checkedState(checkedState)
377 {
378 }
379
380 wxDataViewCheckIconText(const wxDataViewCheckIconText& other)
381 : wxDataViewIconText(other),
382 m_checkedState(other.m_checkedState)
383 {
384 }
385
386 bool IsSameAs(const wxDataViewCheckIconText& other) const
387 {
388 return wxDataViewIconText::IsSameAs(other) &&
389 m_checkedState == other.m_checkedState;
390 }
391
392 // There is no encapsulation anyhow, so just expose this field directly.
393 wxCheckBoxState m_checkedState;
394
395
396 private:
397 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText);
398 };
399
400 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
401
402 DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText)
403 IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText)
404
405
406 class wxDataViewCheckIconTextRenderer : public wxDataViewCustomRenderer
407 {
408 public:
409 wxDataViewCheckIconTextRenderer()
410 : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE,
411 wxDATAVIEW_CELL_ACTIVATABLE)
412 {
413 }
414
415 virtual bool SetValue(const wxVariant& value)
416 {
417 m_value << value;
418 return true;
419 }
420
421 virtual bool GetValue(wxVariant& WXUNUSED(value)) const
422 {
423 return false;
424 }
425
426 wxSize GetSize() const
427 {
428 wxSize size = GetCheckSize();
429 size.x += MARGIN_CHECK_ICON;
430
431 if ( m_value.GetIcon().IsOk() )
432 {
433 const wxSize sizeIcon = m_value.GetIcon().GetSize();
434 if ( sizeIcon.y > size.y )
435 size.y = sizeIcon.y;
436
437 size.x += sizeIcon.x + MARGIN_ICON_TEXT;
438 }
439
440 wxString text = m_value.GetText();
441 if ( text.empty() )
442 text = "Dummy";
443
444 const wxSize sizeText = GetTextExtent(text);
445 if ( sizeText.y > size.y )
446 size.y = sizeText.y;
447
448 size.x += sizeText.x;
449
450 return size;
451 }
452
453 virtual bool Render(wxRect cell, wxDC* dc, int state)
454 {
455 // Draw the checkbox first.
456 int renderFlags = 0;
457 switch ( m_value.m_checkedState )
458 {
459 case wxCHK_UNCHECKED:
460 break;
461
462 case wxCHK_CHECKED:
463 renderFlags |= wxCONTROL_CHECKED;
464 break;
465
466 case wxCHK_UNDETERMINED:
467 renderFlags |= wxCONTROL_UNDETERMINED;
468 break;
469 }
470
471 if ( state & wxDATAVIEW_CELL_PRELIT )
472 renderFlags |= wxCONTROL_CURRENT;
473
474 const wxSize sizeCheck = GetCheckSize();
475
476 wxRect rectCheck(cell.GetPosition(), sizeCheck);
477 rectCheck = rectCheck.CentreIn(cell, wxVERTICAL);
478
479 wxRendererNative::Get().DrawCheckBox
480 (
481 GetView(), *dc, rectCheck, renderFlags
482 );
483
484 // Then the icon, if any.
485 int xoffset = sizeCheck.x + MARGIN_CHECK_ICON;
486
487 const wxIcon& icon = m_value.GetIcon();
488 if ( icon.IsOk() )
489 {
490 const wxSize sizeIcon = icon.GetSize();
491 wxRect rectIcon(cell.GetPosition(), sizeIcon);
492 rectIcon.x += xoffset;
493 rectIcon = rectIcon.CentreIn(cell, wxVERTICAL);
494
495 dc->DrawIcon(icon, rectIcon.GetPosition());
496
497 xoffset += sizeIcon.x + MARGIN_ICON_TEXT;
498 }
499
500 // Finally the text.
501 RenderText(m_value.GetText(), xoffset, cell, dc, state);
502
503 return true;
504 }
505
506 // Event handlers toggling the items checkbox if it was clicked.
507 virtual bool Activate(const wxRect& WXUNUSED(cell),
508 wxDataViewModel* model,
509 const wxDataViewItem& item,
510 unsigned int WXUNUSED(col))
511 {
512 static_cast<wxTreeListModel*>(model)->ToggleItem(item);
513 return true;
514 }
515
516 virtual bool LeftClick(const wxPoint& pos,
517 const wxRect& WXUNUSED(cell),
518 wxDataViewModel* model,
519 const wxDataViewItem& item,
520 unsigned int WXUNUSED(col))
521 {
522 if ( !wxRect(GetCheckSize()).Contains(pos) )
523 return false;
524
525 static_cast<wxTreeListModel*>(model)->ToggleItem(item);
526 return true;
527 }
528
529 protected:
530 wxSize GetCheckSize() const
531 {
532 return wxRendererNative::Get().GetCheckBoxSize(GetView());
533 }
534
535 private:
536 // Just some arbitrary constants defining margins, in pixels.
537 enum
538 {
539 MARGIN_CHECK_ICON = 3,
540 MARGIN_ICON_TEXT = 4
541 };
542
543 wxDataViewCheckIconText m_value;
544 };
545
546 } // anonymous namespace
547
548 // ============================================================================
549 // wxTreeListModel implementation
550 // ============================================================================
551
552 wxTreeListModel::wxTreeListModel(wxTreeListCtrl* treelist)
553 : m_treelist(treelist),
554 m_root(new Node(NULL))
555 {
556 m_numColumns = 0;
557 }
558
559 wxTreeListModel::~wxTreeListModel()
560 {
561 delete m_root;
562 }
563
564 void wxTreeListModel::InsertColumn(unsigned col)
565 {
566 m_numColumns++;
567
568 // There is no need to update anything when inserting the first column.
569 if ( m_numColumns == 1 )
570 return;
571
572 // Update all the items as they may have texts for the old columns.
573 for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
574 {
575 node->OnInsertColumn(col, m_numColumns);
576 }
577 }
578
579 wxTreeListModelNode*
580 wxTreeListModel::InsertItem(Node* parent,
581 Node* previous,
582 const wxString& text,
583 int imageClosed,
584 int imageOpened,
585 wxClientData* data)
586 {
587 wxCHECK_MSG( parent, NULL,
588 "Must have a valid parent (maybe GetRootItem()?)" );
589
590 wxCHECK_MSG( previous, NULL,
591 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
592
593 wxScopedPtr<Node>
594 newItem(new Node(parent, text, imageClosed, imageOpened, data));
595
596 // If we have no children at all, then inserting as last child is the same
597 // as inserting as the first one so check for it here too.
598 if ( previous == wxTLI_FIRST ||
599 (previous == wxTLI_LAST && !parent->GetChild()) )
600 {
601 parent->InsertChild(newItem.get());
602 }
603 else // Not the first item, find the previous one.
604 {
605 if ( previous == wxTLI_LAST )
606 {
607 previous = parent->GetChild();
608
609 // Find the last child.
610 for ( ;; )
611 {
612 Node* const next = previous->GetNext();
613 if ( !next )
614 break;
615
616 previous = next;
617 }
618 }
619 else // We already have the previous item.
620 {
621 // Just check it's under the correct parent.
622 wxCHECK_MSG( previous->GetParent() == parent, NULL,
623 "Previous item is not under the right parent" );
624 }
625
626 previous->InsertNext(newItem.get());
627 }
628
629 ItemAdded(ToDVI(parent), ToDVI(newItem.get()));
630
631 // The item was successfully inserted in the tree and so will be deleted by
632 // it, we can detach it now.
633 return newItem.release();
634 }
635
636 void wxTreeListModel::DeleteItem(Node* item)
637 {
638 wxCHECK_RET( item, "Invalid item" );
639
640 wxCHECK_RET( item != m_root, "Can't delete the root item" );
641
642 Node* const parent = item->GetParent();
643
644 ItemDeleted(ToDVI(parent), ToDVI(item));
645
646 Node* previous = parent->GetChild();
647 if ( previous == item )
648 {
649 parent->DeleteChild();
650 }
651 else // Not the first child of its parent.
652 {
653 // Find the sibling just before it.
654 for ( ;; )
655 {
656 Node* const next = previous->GetNext();
657 if ( next == item )
658 break;
659
660 wxCHECK_RET( next, "Item not a child of its parent?" );
661
662 previous = next;
663 }
664
665 previous->DeleteNext();
666 }
667 }
668
669 void wxTreeListModel::DeleteAllItems()
670 {
671 while ( m_root->GetChild() )
672 {
673 m_root->DeleteChild();
674 }
675
676 Cleared();
677 }
678
679 const wxString& wxTreeListModel::GetItemText(Node* item, unsigned col) const
680 {
681 // Returning root item text here is bogus, it just happens to be an always
682 // empty string we can return reference to.
683 wxCHECK_MSG( item, m_root->m_text, "Invalid item" );
684
685 return col == 0 ? item->m_text : item->GetColumnText(col);
686 }
687
688 void wxTreeListModel::SetItemText(Node* item, unsigned col, const wxString& text)
689 {
690 wxCHECK_RET( item, "Invalid item" );
691
692 if ( col == 0 )
693 item->m_text = text;
694 else
695 item->SetColumnText(text, col, m_numColumns);
696
697 ValueChanged(ToDVI(item), col);
698 }
699
700 void wxTreeListModel::SetItemImage(Node* item, int closed, int opened)
701 {
702 wxCHECK_RET( item, "Invalid item" );
703
704 item->m_imageClosed = closed;
705 item->m_imageOpened = opened;
706
707 ValueChanged(ToDVI(item), 0);
708 }
709
710 wxClientData* wxTreeListModel::GetItemData(Node* item) const
711 {
712 wxCHECK_MSG( item, NULL, "Invalid item" );
713
714 return item->GetClientData();
715 }
716
717 void wxTreeListModel::SetItemData(Node* item, wxClientData* data)
718 {
719 wxCHECK_RET( item, "Invalid item" );
720
721 item->SetClientData(data);
722 }
723
724 void wxTreeListModel::CheckItem(Node* item, wxCheckBoxState checkedState)
725 {
726 wxCHECK_RET( item, "Invalid item" );
727
728 item->m_checkedState = checkedState;
729
730 ItemChanged(ToDVI(item));
731 }
732
733 void wxTreeListModel::ToggleItem(wxDataViewItem dvi)
734 {
735 Node* const item = FromDVI(dvi);
736
737 wxCHECK_RET( item, "Invalid item" );
738
739 const wxCheckBoxState stateOld = item->m_checkedState;
740
741 // If the 3rd state is user-settable then the cycle is
742 // unchecked->checked->undetermined.
743 switch ( stateOld )
744 {
745 case wxCHK_CHECKED:
746 item->m_checkedState = m_treelist->HasFlag(wxTL_USER_3STATE)
747 ? wxCHK_UNDETERMINED
748 : wxCHK_UNCHECKED;
749 break;
750
751 case wxCHK_UNDETERMINED:
752 // Whether 3rd state is user-settable or not, the next state is
753 // unchecked.
754 item->m_checkedState = wxCHK_UNCHECKED;
755 break;
756
757 case wxCHK_UNCHECKED:
758 item->m_checkedState = wxCHK_CHECKED;
759 break;
760 }
761
762 ItemChanged(ToDVI(item));
763
764 m_treelist->OnItemToggled(item, stateOld);
765 }
766
767 unsigned wxTreeListModel::GetColumnCount() const
768 {
769 return m_numColumns;
770 }
771
772 wxString wxTreeListModel::GetColumnType(unsigned col) const
773 {
774 if ( col == 0 )
775 {
776 return m_treelist->HasFlag(wxTL_CHECKBOX)
777 ? wxS("wxDataViewCheckIconText")
778 : wxS("wxDataViewIconText");
779 }
780 else // All the other columns contain just text.
781 {
782 return wxS("string");
783 }
784 }
785
786 void
787 wxTreeListModel::GetValue(wxVariant& variant,
788 const wxDataViewItem& item,
789 unsigned col) const
790 {
791 Node* const node = FromDVI(item);
792
793 if ( col == 0 )
794 {
795 // Determine the correct image to use depending on the item state.
796 int image = wxWithImages::NO_IMAGE;
797 if ( m_treelist->IsExpanded(node) )
798 image = node->m_imageOpened;
799
800 if ( image == wxWithImages::NO_IMAGE )
801 image = node->m_imageClosed;
802
803 wxIcon icon = m_treelist->GetImage(image);
804
805 if ( m_treelist->HasFlag(wxTL_CHECKBOX) )
806 variant << wxDataViewCheckIconText(node->m_text, icon,
807 node->m_checkedState);
808 else
809 variant << wxDataViewIconText(node->m_text, icon);
810 }
811 else
812 {
813 // Notice that we must still assign wxString to wxVariant to ensure
814 // that it at least has the correct type.
815 wxString text;
816 if ( node->HasColumnsTexts() )
817 text = node->GetColumnText(col);
818
819 variant = text;
820 }
821 }
822
823 bool
824 wxTreeListModel::SetValue(const wxVariant& WXUNUSED(variant),
825 const wxDataViewItem& WXUNUSED(item),
826 unsigned WXUNUSED(col))
827 {
828 // We are not editable currently.
829 return false;
830 }
831
832 wxDataViewItem wxTreeListModel::GetParent(const wxDataViewItem& item) const
833 {
834 Node* const node = FromDVI(item);
835
836 return ToDVI(node->GetParent());
837 }
838
839 bool wxTreeListModel::IsContainer(const wxDataViewItem& item) const
840 {
841 // FIXME: In the generic (and native OS X) versions we implement this
842 // method normally, i.e. only items with children are containers.
843 // But for the native GTK version we must pretend that all items are
844 // containers because otherwise adding children to them later would
845 // fail because wxGTK code calls IsContainer() too early (when
846 // adding the item itself) and we can't know whether we're container
847 // or not by then. Luckily, always returning true doesn't have any
848 // serious drawbacks for us.
849 #ifdef __WXGTK__
850 wxUnusedVar(item);
851
852 return true;
853 #else
854 Node* const node = FromDVI(item);
855
856 return node->GetChild() != NULL;
857 #endif
858 }
859
860 bool
861 wxTreeListModel::HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const
862 {
863 return true;
864 }
865
866 unsigned
867 wxTreeListModel::GetChildren(const wxDataViewItem& item,
868 wxDataViewItemArray& children) const
869 {
870 Node* const node = FromDVI(item);
871
872 unsigned numChildren = 0;
873 for ( Node* child = node->GetChild(); child; child = child->GetNext() )
874 {
875 children.push_back(ToDVI(child));
876 numChildren++;
877 }
878
879 return numChildren;
880 }
881
882 // ============================================================================
883 // wxTreeListCtrl implementation
884 // ============================================================================
885
886 BEGIN_EVENT_TABLE(wxTreeListCtrl, wxWindow)
887 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY, wxTreeListCtrl::OnSelectionChanged)
888 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY, wxTreeListCtrl::OnItemExpanding)
889 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
890 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
891 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
892
893 EVT_SIZE(wxTreeListCtrl::OnSize)
894 END_EVENT_TABLE()
895
896 // ----------------------------------------------------------------------------
897 // Creation
898 // ----------------------------------------------------------------------------
899
900 void wxTreeListCtrl::Init()
901 {
902 m_view = NULL;
903 m_model = NULL;
904 }
905
906 bool wxTreeListCtrl::Create(wxWindow* parent,
907 wxWindowID id,
908 const wxPoint& pos,
909 const wxSize& size,
910 long style,
911 const wxString& name)
912 {
913 if ( style & wxTL_USER_3STATE )
914 style |= wxTL_3STATE;
915
916 if ( style & wxTL_3STATE )
917 style |= wxTL_CHECKBOX;
918
919 // Create the window itself and wxDataViewCtrl used by it.
920 if ( !wxWindow::Create(parent, id,
921 pos, size,
922 style, name) )
923 {
924 return false;
925 }
926
927 m_view = new wxDataViewCtrl;
928 if ( !m_view->Create(this, wxID_ANY,
929 wxPoint(0, 0), GetClientSize(),
930 HasFlag(wxTL_MULTIPLE) ? wxDV_MULTIPLE
931 : wxDV_SINGLE) )
932 {
933 delete m_view;
934 m_view = NULL;
935
936 return false;
937 }
938
939
940 // Set up the model for wxDataViewCtrl.
941 m_model = new wxTreeListModel(this);
942 m_view->AssociateModel(m_model);
943
944 return true;
945 }
946
947 wxTreeListCtrl::~wxTreeListCtrl()
948 {
949 if ( m_model )
950 m_model->DecRef();
951 }
952
953 // ----------------------------------------------------------------------------
954 // Columns
955 // ----------------------------------------------------------------------------
956
957 int
958 wxTreeListCtrl::DoInsertColumn(const wxString& title,
959 int pos,
960 int width,
961 wxAlignment align,
962 int flags)
963 {
964 wxCHECK_MSG( m_view, wxNOT_FOUND, "Must Create() first" );
965
966 const unsigned oldNumColumns = m_view->GetColumnCount();
967
968 if ( pos == wxNOT_FOUND )
969 pos = oldNumColumns;
970
971 wxDataViewRenderer* renderer;
972 if ( pos == 0 )
973 {
974 // Inserting the first column which is special as it uses a different
975 // renderer.
976
977 // Also, currently it can be done only once.
978 wxCHECK_MSG( !oldNumColumns, wxNOT_FOUND,
979 "Inserting column at position 0 currently not supported" );
980
981 if ( HasFlag(wxTL_CHECKBOX) )
982 {
983 // Use our custom renderer to show the checkbox.
984 renderer = new wxDataViewCheckIconTextRenderer;
985 }
986 else // We still need a special renderer to show the icons.
987 {
988 renderer = new wxDataViewIconTextRenderer;
989 }
990 }
991 else // Not the first column.
992 {
993 // All the other ones use a simple text renderer.
994 renderer = new wxDataViewTextRenderer;
995 }
996
997 wxDataViewColumn*
998 column = new wxDataViewColumn(title, renderer, pos, width, align, flags);
999
1000 m_model->InsertColumn(pos);
1001
1002 m_view->InsertColumn(pos, column);
1003
1004 return pos;
1005 }
1006
1007 unsigned wxTreeListCtrl::GetColumnCount() const
1008 {
1009 return m_view ? m_view->GetColumnCount() : 0u;
1010 }
1011
1012 bool wxTreeListCtrl::DeleteColumn(unsigned col)
1013 {
1014 wxCHECK_MSG( col < GetColumnCount(), false, "Invalid column index" );
1015
1016 return m_view->DeleteColumn(m_view->GetColumn(col));
1017 }
1018
1019 void wxTreeListCtrl::ClearColumns()
1020 {
1021 if ( m_view )
1022 m_view->ClearColumns();
1023 }
1024
1025 void wxTreeListCtrl::SetColumnWidth(unsigned col, int width)
1026 {
1027 wxCHECK_RET( col < GetColumnCount(), "Invalid column index" );
1028
1029 wxDataViewColumn* const column = m_view->GetColumn(col);
1030 wxCHECK_RET( column, "No such column?" );
1031
1032 return column->SetWidth(width);
1033 }
1034
1035 int wxTreeListCtrl::GetColumnWidth(unsigned col) const
1036 {
1037 wxCHECK_MSG( col < GetColumnCount(), -1, "Invalid column index" );
1038
1039 wxDataViewColumn* column = m_view->GetColumn(col);
1040 wxCHECK_MSG( column, -1, "No such column?" );
1041
1042 return column->GetWidth();
1043 }
1044
1045 int wxTreeListCtrl::WidthFor(const wxString& text) const
1046 {
1047 return GetTextExtent(text).x;
1048 }
1049
1050 // ----------------------------------------------------------------------------
1051 // Items
1052 // ----------------------------------------------------------------------------
1053
1054 wxTreeListItem
1055 wxTreeListCtrl::DoInsertItem(wxTreeListItem parent,
1056 wxTreeListItem previous,
1057 const wxString& text,
1058 int imageClosed,
1059 int imageOpened,
1060 wxClientData* data)
1061 {
1062 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1063
1064 return wxTreeListItem(m_model->InsertItem(parent, previous, text,
1065 imageClosed, imageOpened, data));
1066 }
1067
1068 void wxTreeListCtrl::DeleteItem(wxTreeListItem item)
1069 {
1070 wxCHECK_RET( m_model, "Must create first" );
1071
1072 m_model->DeleteItem(item);
1073 }
1074
1075 void wxTreeListCtrl::DeleteAllItems()
1076 {
1077 if ( m_model )
1078 m_model->DeleteAllItems();
1079 }
1080
1081 // ----------------------------------------------------------------------------
1082 // Tree navigation
1083 // ----------------------------------------------------------------------------
1084
1085 // The simple accessors in this section are implemented directly using
1086 // wxTreeListModelNode methods, without passing by the model. This is just a
1087 // shortcut and avoids us the trouble of defining more trivial methods in
1088 // wxTreeListModel.
1089
1090 wxTreeListItem wxTreeListCtrl::GetRootItem() const
1091 {
1092 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1093
1094 return m_model->GetRootItem();
1095 }
1096
1097 wxTreeListItem wxTreeListCtrl::GetItemParent(wxTreeListItem item) const
1098 {
1099 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1100
1101 return item->GetParent();
1102 }
1103
1104 wxTreeListItem wxTreeListCtrl::GetFirstChild(wxTreeListItem item) const
1105 {
1106 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1107
1108 return item->GetChild();
1109 }
1110
1111 wxTreeListItem
1112 wxTreeListCtrl::GetNextSibling(wxTreeListItem item) const
1113 {
1114 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1115
1116 return item->GetNext();
1117 }
1118
1119 wxTreeListItem wxTreeListCtrl::GetNextItem(wxTreeListItem item) const
1120 {
1121 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1122
1123 return item->NextInTree();
1124 }
1125
1126 // ----------------------------------------------------------------------------
1127 // Item attributes
1128 // ----------------------------------------------------------------------------
1129
1130 const wxString&
1131 wxTreeListCtrl::GetItemText(wxTreeListItem item, unsigned col) const
1132 {
1133 // We can't use wxCHECK_MSG() here because we don't have any empty string
1134 // reference to return so we use a static variable that exists just for the
1135 // purpose of this check -- and so we put it in its own scope so that it's
1136 // never even created during normal program execution.
1137 if ( !m_model || col >= m_model->GetColumnCount() )
1138 {
1139 static wxString s_empty;
1140
1141 if ( !m_model )
1142 {
1143 wxFAIL_MSG( "Must create first" );
1144 }
1145 else if ( col >= m_model->GetColumnCount() )
1146 {
1147 wxFAIL_MSG( "Invalid column index" );
1148 }
1149
1150 return s_empty;
1151 }
1152
1153 return m_model->GetItemText(item, col);
1154 }
1155
1156 void
1157 wxTreeListCtrl::SetItemText(wxTreeListItem item,
1158 unsigned col,
1159 const wxString& text)
1160 {
1161 wxCHECK_RET( m_model, "Must create first" );
1162 wxCHECK_RET( col < m_model->GetColumnCount(), "Invalid column index" );
1163
1164 m_model->SetItemText(item, col, text);
1165 }
1166
1167 void wxTreeListCtrl::SetItemImage(wxTreeListItem item, int closed, int opened)
1168 {
1169 wxCHECK_RET( m_model, "Must create first" );
1170
1171 if ( closed != NO_IMAGE || opened != NO_IMAGE )
1172 {
1173 wxImageList* const imageList = GetImageList();
1174 wxCHECK_RET( imageList, "Can't set images without image list" );
1175
1176 const int imageCount = imageList->GetImageCount();
1177
1178 wxCHECK_RET( closed < imageCount, "Invalid image index" );
1179 wxCHECK_RET( opened < imageCount, "Invalid opened image index" );
1180 }
1181
1182 m_model->SetItemImage(item, closed, opened);
1183 }
1184
1185 wxClientData* wxTreeListCtrl::GetItemData(wxTreeListItem item) const
1186 {
1187 wxCHECK_MSG( m_model, NULL, "Must create first" );
1188
1189 return m_model->GetItemData(item);
1190 }
1191
1192 void wxTreeListCtrl::SetItemData(wxTreeListItem item, wxClientData* data)
1193 {
1194 wxCHECK_RET( m_model, "Must create first" );
1195
1196 m_model->SetItemData(item, data);
1197 }
1198
1199 // ----------------------------------------------------------------------------
1200 // Expanding and collapsing
1201 // ----------------------------------------------------------------------------
1202
1203 void wxTreeListCtrl::Expand(wxTreeListItem item)
1204 {
1205 wxCHECK_RET( m_view, "Must create first" );
1206
1207 m_view->Expand(m_model->ToDVI(item));
1208 }
1209
1210 void wxTreeListCtrl::Collapse(wxTreeListItem item)
1211 {
1212 wxCHECK_RET( m_view, "Must create first" );
1213
1214 m_view->Collapse(m_model->ToDVI(item));
1215 }
1216
1217 bool wxTreeListCtrl::IsExpanded(wxTreeListItem item) const
1218 {
1219 wxCHECK_MSG( m_view, false, "Must create first" );
1220
1221 return m_view->IsExpanded(m_model->ToDVI(item));
1222 }
1223
1224 // ----------------------------------------------------------------------------
1225 // Selection
1226 // ----------------------------------------------------------------------------
1227
1228 wxTreeListItem wxTreeListCtrl::GetSelection() const
1229 {
1230 wxCHECK_MSG( m_view, wxTreeListItem(), "Must create first" );
1231
1232 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE), wxTreeListItem(),
1233 "Must use GetSelections() with multi-selection controls!" );
1234
1235 const wxDataViewItem dvi = m_view->GetSelection();
1236
1237 return m_model->FromNonRootDVI(dvi);
1238 }
1239
1240 unsigned wxTreeListCtrl::GetSelections(wxTreeListItems& selections) const
1241 {
1242 wxCHECK_MSG( m_view, 0, "Must create first" );
1243
1244 wxDataViewItemArray selectionsDV;
1245 const unsigned numSelected = m_view->GetSelections(selectionsDV);
1246 selections.resize(numSelected);
1247 for ( unsigned n = 0; n < numSelected; n++ )
1248 selections[n] = m_model->FromNonRootDVI(selectionsDV[n]);
1249
1250 return numSelected;
1251 }
1252
1253 void wxTreeListCtrl::Select(wxTreeListItem item)
1254 {
1255 wxCHECK_RET( m_view, "Must create first" );
1256
1257 m_view->Select(m_model->ToNonRootDVI(item));
1258 }
1259
1260 void wxTreeListCtrl::Unselect(wxTreeListItem item)
1261 {
1262 wxCHECK_RET( m_view, "Must create first" );
1263
1264 m_view->Unselect(m_model->ToNonRootDVI(item));
1265 }
1266
1267 bool wxTreeListCtrl::IsSelected(wxTreeListItem item) const
1268 {
1269 wxCHECK_MSG( m_view, false, "Must create first" );
1270
1271 return m_view->IsSelected(m_model->ToNonRootDVI(item));
1272 }
1273
1274 void wxTreeListCtrl::SelectAll()
1275 {
1276 wxCHECK_RET( m_view, "Must create first" );
1277
1278 m_view->SelectAll();
1279 }
1280
1281 void wxTreeListCtrl::UnselectAll()
1282 {
1283 wxCHECK_RET( m_view, "Must create first" );
1284
1285 m_view->UnselectAll();
1286 }
1287
1288 // ----------------------------------------------------------------------------
1289 // Checkbox handling
1290 // ----------------------------------------------------------------------------
1291
1292 void wxTreeListCtrl::CheckItem(wxTreeListItem item, wxCheckBoxState state)
1293 {
1294 wxCHECK_RET( m_model, "Must create first" );
1295
1296 m_model->CheckItem(item, state);
1297 }
1298
1299 void
1300 wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item, wxCheckBoxState state)
1301 {
1302 wxCHECK_RET( m_model, "Must create first" );
1303
1304 m_model->CheckItem(item, state);
1305
1306 for ( wxTreeListItem child = GetFirstChild(item);
1307 child.IsOk();
1308 child = GetNextSibling(child) )
1309 {
1310 CheckItemRecursively(child, state);
1311 }
1312 }
1313
1314 void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item)
1315 {
1316 wxCHECK_RET( item.IsOk(), "Invalid item" );
1317
1318 wxASSERT_MSG( HasFlag(wxTL_3STATE), "Can only be used with wxTL_3STATE" );
1319
1320 for ( ;; )
1321 {
1322 wxTreeListItem parent = GetItemParent(item);
1323 if ( parent == GetRootItem() )
1324 {
1325 // There is no checked state associated with the root item.
1326 return;
1327 }
1328
1329 // Set parent state to the state of this item if all the other children
1330 // have the same state too. Otherwise make it indeterminate.
1331 const wxCheckBoxState stateItem = GetCheckedState(item);
1332 CheckItem(parent, AreAllChildrenInState(parent, stateItem)
1333 ? stateItem
1334 : wxCHK_UNDETERMINED);
1335
1336 // And do the same thing with the parent's parent too.
1337 item = parent;
1338 }
1339 }
1340
1341 wxCheckBoxState wxTreeListCtrl::GetCheckedState(wxTreeListItem item) const
1342 {
1343 wxCHECK_MSG( item.IsOk(), wxCHK_UNDETERMINED, "Invalid item" );
1344
1345 return item->m_checkedState;
1346 }
1347
1348 bool
1349 wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item,
1350 wxCheckBoxState state) const
1351 {
1352 wxCHECK_MSG( item.IsOk(), false, "Invalid item" );
1353
1354 for ( wxTreeListItem child = GetFirstChild(item);
1355 child.IsOk();
1356 child = GetNextSibling(child) )
1357 {
1358 if ( GetCheckedState(child) != state )
1359 return false;
1360 }
1361
1362 return true;
1363 }
1364
1365 // ----------------------------------------------------------------------------
1366 // Events
1367 // ----------------------------------------------------------------------------
1368
1369 void wxTreeListCtrl::SendEvent(wxEventType evt, wxDataViewEvent& eventDV)
1370 {
1371 wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
1372
1373 if ( !ProcessWindowEvent(eventTL) )
1374 {
1375 eventDV.Skip();
1376 return;
1377 }
1378
1379 if ( !eventTL.IsAllowed() )
1380 {
1381 eventDV.Veto();
1382 }
1383 }
1384
1385 void
1386 wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
1387 {
1388 wxTreeListEvent event(wxEVT_COMMAND_TREELIST_ITEM_CHECKED, this, item);
1389 event.SetOldCheckedState(stateOld);
1390
1391 ProcessWindowEvent(event);
1392 }
1393
1394 void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
1395 {
1396 SendEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
1397 }
1398
1399 void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
1400 {
1401 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
1402 }
1403
1404 void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
1405 {
1406 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
1407 }
1408
1409 void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
1410 {
1411 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
1412 }
1413
1414 void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
1415 {
1416 SendEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
1417 }
1418
1419 // ----------------------------------------------------------------------------
1420 // Geometry
1421 // ----------------------------------------------------------------------------
1422
1423 void wxTreeListCtrl::OnSize(wxSizeEvent& event)
1424 {
1425 event.Skip();
1426
1427 if ( m_view )
1428 {
1429 // Resize the real control to cover our entire client area.
1430 const wxRect rect = GetClientRect();
1431 m_view->SetSize(rect);
1432
1433 // Resize the first column to take the remaining available space, if
1434 // any.
1435 const unsigned numColumns = GetColumnCount();
1436 if ( !numColumns )
1437 return;
1438
1439 // There is a bug in generic wxDataViewCtrl: if the column width sums
1440 // up to the total size, horizontal scrollbar (unnecessarily) appears,
1441 // so subtract 10 pixels to ensure this doesn't happen.
1442 int remainingWidth = rect.width - 10;
1443 for ( unsigned n = 1; n < GetColumnCount(); n++ )
1444 {
1445 remainingWidth -= GetColumnWidth(n);
1446 if ( remainingWidth < 0 )
1447 break;
1448 }
1449
1450 // We don't decrease the width of the first column, even if we had
1451 // increased it ourselves, because we want to avoid changing its size
1452 // if the user resized it. We might want to remember if this was the
1453 // case or if we only ever adjusted it automatically in the future.
1454 if ( remainingWidth > GetColumnWidth(0) )
1455 SetColumnWidth(0, remainingWidth);
1456 }
1457 }
1458
1459 // ============================================================================
1460 // wxTreeListEvent implementation
1461 // ============================================================================
1462
1463 wxIMPLEMENT_ABSTRACT_CLASS(wxTreeListEvent, wxNotifyEvent)
1464
1465 #define wxDEFINE_TREELIST_EVENT(name) \
1466 wxDEFINE_EVENT(wxEVT_COMMAND_TREELIST_##name, wxTreeListEvent)
1467
1468 wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED);
1469 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING);
1470 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED);
1471 wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
1472 wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
1473 wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
1474
1475 #undef wxDEFINE_TREELIST_EVENT