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