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