]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treelist.cpp
missing commit
[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$
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 virtual int Compare(const wxDataViewItem& item1,
382 const wxDataViewItem& item2,
383 unsigned col,
384 bool ascending) const;
385
386 private:
387 // The control we're associated with.
388 wxTreeListCtrl* const m_treelist;
389
390 // The unique invisible root element.
391 Node* const m_root;
392
393 // Number of columns we maintain.
394 unsigned m_numColumns;
395
396 // Set to false as soon as we have more than one level, i.e. as soon as any
397 // items with non-root item as parent are added (and currently never reset
398 // after this).
399 bool m_isFlat;
400 };
401
402 // ============================================================================
403 // wxDataViewCheckIconText[Renderer]: special renderer for our first column.
404 // ============================================================================
405
406 // Currently this class is private but it could be extracted and made part of
407 // public API later as could be used directly with wxDataViewCtrl as well.
408 namespace
409 {
410
411 const char* CHECK_ICON_TEXT_TYPE = "wxDataViewCheckIconText";
412
413 // The value used by wxDataViewCheckIconTextRenderer
414 class wxDataViewCheckIconText : public wxDataViewIconText
415 {
416 public:
417 wxDataViewCheckIconText(const wxString& text = wxString(),
418 const wxIcon& icon = wxNullIcon,
419 wxCheckBoxState checkedState = wxCHK_UNDETERMINED)
420 : wxDataViewIconText(text, icon),
421 m_checkedState(checkedState)
422 {
423 }
424
425 wxDataViewCheckIconText(const wxDataViewCheckIconText& other)
426 : wxDataViewIconText(other),
427 m_checkedState(other.m_checkedState)
428 {
429 }
430
431 bool IsSameAs(const wxDataViewCheckIconText& other) const
432 {
433 return wxDataViewIconText::IsSameAs(other) &&
434 m_checkedState == other.m_checkedState;
435 }
436
437 // There is no encapsulation anyhow, so just expose this field directly.
438 wxCheckBoxState m_checkedState;
439
440
441 private:
442 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText);
443 };
444
445 wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
446
447 DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText)
448 IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText)
449
450
451 class wxDataViewCheckIconTextRenderer : public wxDataViewCustomRenderer
452 {
453 public:
454 wxDataViewCheckIconTextRenderer()
455 : wxDataViewCustomRenderer(CHECK_ICON_TEXT_TYPE,
456 wxDATAVIEW_CELL_ACTIVATABLE)
457 {
458 }
459
460 virtual bool SetValue(const wxVariant& value)
461 {
462 m_value << value;
463 return true;
464 }
465
466 virtual bool GetValue(wxVariant& WXUNUSED(value)) const
467 {
468 return false;
469 }
470
471 wxSize GetSize() const
472 {
473 wxSize size = GetCheckSize();
474 size.x += MARGIN_CHECK_ICON;
475
476 if ( m_value.GetIcon().IsOk() )
477 {
478 const wxSize sizeIcon = m_value.GetIcon().GetSize();
479 if ( sizeIcon.y > size.y )
480 size.y = sizeIcon.y;
481
482 size.x += sizeIcon.x + MARGIN_ICON_TEXT;
483 }
484
485 wxString text = m_value.GetText();
486 if ( text.empty() )
487 text = "Dummy";
488
489 const wxSize sizeText = GetTextExtent(text);
490 if ( sizeText.y > size.y )
491 size.y = sizeText.y;
492
493 size.x += sizeText.x;
494
495 return size;
496 }
497
498 virtual bool Render(wxRect cell, wxDC* dc, int state)
499 {
500 // Draw the checkbox first.
501 int renderFlags = 0;
502 switch ( m_value.m_checkedState )
503 {
504 case wxCHK_UNCHECKED:
505 break;
506
507 case wxCHK_CHECKED:
508 renderFlags |= wxCONTROL_CHECKED;
509 break;
510
511 case wxCHK_UNDETERMINED:
512 renderFlags |= wxCONTROL_UNDETERMINED;
513 break;
514 }
515
516 if ( state & wxDATAVIEW_CELL_PRELIT )
517 renderFlags |= wxCONTROL_CURRENT;
518
519 const wxSize sizeCheck = GetCheckSize();
520
521 wxRect rectCheck(cell.GetPosition(), sizeCheck);
522 rectCheck = rectCheck.CentreIn(cell, wxVERTICAL);
523
524 wxRendererNative::Get().DrawCheckBox
525 (
526 GetView(), *dc, rectCheck, renderFlags
527 );
528
529 // Then the icon, if any.
530 int xoffset = sizeCheck.x + MARGIN_CHECK_ICON;
531
532 const wxIcon& icon = m_value.GetIcon();
533 if ( icon.IsOk() )
534 {
535 const wxSize sizeIcon = icon.GetSize();
536 wxRect rectIcon(cell.GetPosition(), sizeIcon);
537 rectIcon.x += xoffset;
538 rectIcon = rectIcon.CentreIn(cell, wxVERTICAL);
539
540 dc->DrawIcon(icon, rectIcon.GetPosition());
541
542 xoffset += sizeIcon.x + MARGIN_ICON_TEXT;
543 }
544
545 // Finally the text.
546 RenderText(m_value.GetText(), xoffset, cell, dc, state);
547
548 return true;
549 }
550
551 // Event handlers toggling the items checkbox if it was clicked.
552 virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
553 wxDataViewModel *model,
554 const wxDataViewItem & item,
555 unsigned int WXUNUSED(col),
556 const wxMouseEvent *mouseEvent)
557 {
558 if ( mouseEvent )
559 {
560 if ( !wxRect(GetCheckSize()).Contains(mouseEvent->GetPosition()) )
561 return false;
562 }
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 // FIXME-VC6: This compiler refuses to compare "Node* previous" with
669 // wxTLI_XXX without some help.
670 const wxTreeListItem previousItem(previous);
671
672 // If we have no children at all, then inserting as last child is the same
673 // as inserting as the first one so check for it here too.
674 if ( previousItem == wxTLI_FIRST ||
675 (previousItem == wxTLI_LAST && !parent->GetChild()) )
676 {
677 parent->InsertChild(newItem.get());
678 }
679 else // Not the first item, find the previous one.
680 {
681 if ( previousItem == wxTLI_LAST )
682 {
683 previous = parent->GetChild();
684
685 // Find the last child.
686 for ( ;; )
687 {
688 Node* const next = previous->GetNext();
689 if ( !next )
690 break;
691
692 previous = next;
693 }
694 }
695 else // We already have the previous item.
696 {
697 // Just check it's under the correct parent.
698 wxCHECK_MSG( previous->GetParent() == parent, NULL,
699 "Previous item is not under the right parent" );
700 }
701
702 previous->InsertNext(newItem.get());
703 }
704
705 ItemAdded(ToDVI(parent), ToDVI(newItem.get()));
706
707 // The item was successfully inserted in the tree and so will be deleted by
708 // it, we can detach it now.
709 return newItem.release();
710 }
711
712 void wxTreeListModel::DeleteItem(Node* item)
713 {
714 wxCHECK_RET( item, "Invalid item" );
715
716 wxCHECK_RET( item != m_root, "Can't delete the root item" );
717
718 Node* const parent = item->GetParent();
719
720 ItemDeleted(ToDVI(parent), ToDVI(item));
721
722 Node* previous = parent->GetChild();
723 if ( previous == item )
724 {
725 parent->DeleteChild();
726 }
727 else // Not the first child of its parent.
728 {
729 // Find the sibling just before it.
730 for ( ;; )
731 {
732 Node* const next = previous->GetNext();
733 if ( next == item )
734 break;
735
736 wxCHECK_RET( next, "Item not a child of its parent?" );
737
738 previous = next;
739 }
740
741 previous->DeleteNext();
742 }
743 }
744
745 void wxTreeListModel::DeleteAllItems()
746 {
747 while ( m_root->GetChild() )
748 {
749 m_root->DeleteChild();
750 }
751
752 Cleared();
753 }
754
755 const wxString& wxTreeListModel::GetItemText(Node* item, unsigned col) const
756 {
757 // Returning root item text here is bogus, it just happens to be an always
758 // empty string we can return reference to.
759 wxCHECK_MSG( item, m_root->m_text, "Invalid item" );
760
761 // Notice that asking for the text of a column of an item that doesn't have
762 // any column texts is not an error so we simply return an empty string in
763 // this case.
764 return col == 0 ? item->m_text
765 : item->HasColumnsTexts() ? item->GetColumnText(col)
766 : m_root->m_text;
767 }
768
769 void wxTreeListModel::SetItemText(Node* item, unsigned col, const wxString& text)
770 {
771 wxCHECK_RET( item, "Invalid item" );
772
773 if ( col == 0 )
774 item->m_text = text;
775 else
776 item->SetColumnText(text, col, m_numColumns);
777
778 ValueChanged(ToDVI(item), col);
779 }
780
781 void wxTreeListModel::SetItemImage(Node* item, int closed, int opened)
782 {
783 wxCHECK_RET( item, "Invalid item" );
784
785 item->m_imageClosed = closed;
786 item->m_imageOpened = opened;
787
788 ValueChanged(ToDVI(item), 0);
789 }
790
791 wxClientData* wxTreeListModel::GetItemData(Node* item) const
792 {
793 wxCHECK_MSG( item, NULL, "Invalid item" );
794
795 return item->GetClientData();
796 }
797
798 void wxTreeListModel::SetItemData(Node* item, wxClientData* data)
799 {
800 wxCHECK_RET( item, "Invalid item" );
801
802 item->SetClientData(data);
803 }
804
805 void wxTreeListModel::CheckItem(Node* item, wxCheckBoxState checkedState)
806 {
807 wxCHECK_RET( item, "Invalid item" );
808
809 item->m_checkedState = checkedState;
810
811 ItemChanged(ToDVI(item));
812 }
813
814 void wxTreeListModel::ToggleItem(wxDataViewItem dvi)
815 {
816 Node* const item = FromDVI(dvi);
817
818 wxCHECK_RET( item, "Invalid item" );
819
820 const wxCheckBoxState stateOld = item->m_checkedState;
821
822 // If the 3rd state is user-settable then the cycle is
823 // unchecked->checked->undetermined.
824 switch ( stateOld )
825 {
826 case wxCHK_CHECKED:
827 item->m_checkedState = m_treelist->HasFlag(wxTL_USER_3STATE)
828 ? wxCHK_UNDETERMINED
829 : wxCHK_UNCHECKED;
830 break;
831
832 case wxCHK_UNDETERMINED:
833 // Whether 3rd state is user-settable or not, the next state is
834 // unchecked.
835 item->m_checkedState = wxCHK_UNCHECKED;
836 break;
837
838 case wxCHK_UNCHECKED:
839 item->m_checkedState = wxCHK_CHECKED;
840 break;
841 }
842
843 ItemChanged(ToDVI(item));
844
845 m_treelist->OnItemToggled(item, stateOld);
846 }
847
848 unsigned wxTreeListModel::GetColumnCount() const
849 {
850 return m_numColumns;
851 }
852
853 wxString wxTreeListModel::GetColumnType(unsigned col) const
854 {
855 if ( col == 0 )
856 {
857 return m_treelist->HasFlag(wxTL_CHECKBOX)
858 ? wxS("wxDataViewCheckIconText")
859 : wxS("wxDataViewIconText");
860 }
861 else // All the other columns contain just text.
862 {
863 return wxS("string");
864 }
865 }
866
867 void
868 wxTreeListModel::GetValue(wxVariant& variant,
869 const wxDataViewItem& item,
870 unsigned col) const
871 {
872 Node* const node = FromDVI(item);
873
874 if ( col == 0 )
875 {
876 // Determine the correct image to use depending on the item state.
877 int image = wxWithImages::NO_IMAGE;
878 if ( m_treelist->IsExpanded(node) )
879 image = node->m_imageOpened;
880
881 if ( image == wxWithImages::NO_IMAGE )
882 image = node->m_imageClosed;
883
884 wxIcon icon = m_treelist->GetImage(image);
885
886 if ( m_treelist->HasFlag(wxTL_CHECKBOX) )
887 variant << wxDataViewCheckIconText(node->m_text, icon,
888 node->m_checkedState);
889 else
890 variant << wxDataViewIconText(node->m_text, icon);
891 }
892 else
893 {
894 // Notice that we must still assign wxString to wxVariant to ensure
895 // that it at least has the correct type.
896 wxString text;
897 if ( node->HasColumnsTexts() )
898 text = node->GetColumnText(col);
899
900 variant = text;
901 }
902 }
903
904 bool
905 wxTreeListModel::SetValue(const wxVariant& WXUNUSED(variant),
906 const wxDataViewItem& WXUNUSED(item),
907 unsigned WXUNUSED(col))
908 {
909 // We are not editable currently.
910 return false;
911 }
912
913 wxDataViewItem wxTreeListModel::GetParent(const wxDataViewItem& item) const
914 {
915 Node* const node = FromDVI(item);
916
917 return ToDVI(node->GetParent());
918 }
919
920 bool wxTreeListModel::IsContainer(const wxDataViewItem& item) const
921 {
922 // FIXME: In the generic (and native OS X) versions we implement this
923 // method normally, i.e. only items with children are containers.
924 // But for the native GTK version we must pretend that all items are
925 // containers because otherwise adding children to them later would
926 // fail because wxGTK code calls IsContainer() too early (when
927 // adding the item itself) and we can't know whether we're container
928 // or not by then. Luckily, always returning true doesn't have any
929 // serious drawbacks for us.
930 #ifdef __WXGTK__
931 wxUnusedVar(item);
932
933 return true;
934 #else
935 Node* const node = FromDVI(item);
936
937 return node->GetChild() != NULL;
938 #endif
939 }
940
941 bool
942 wxTreeListModel::HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const
943 {
944 return true;
945 }
946
947 unsigned
948 wxTreeListModel::GetChildren(const wxDataViewItem& item,
949 wxDataViewItemArray& children) const
950 {
951 Node* const node = FromDVI(item);
952
953 unsigned numChildren = 0;
954 for ( Node* child = node->GetChild(); child; child = child->GetNext() )
955 {
956 children.push_back(ToDVI(child));
957 numChildren++;
958 }
959
960 return numChildren;
961 }
962
963 int
964 wxTreeListModel::Compare(const wxDataViewItem& item1,
965 const wxDataViewItem& item2,
966 unsigned col,
967 bool ascending) const
968 {
969 // Compare using default alphabetical order if no custom comparator.
970 wxTreeListItemComparator* const comp = m_treelist->m_comparator;
971 if ( !comp )
972 return wxDataViewModel::Compare(item1, item2, col, ascending);
973
974 // Forward comparison to the comparator:
975 int result = comp->Compare(m_treelist, col, FromDVI(item1), FromDVI(item2));
976
977 // And adjust by the sort order if necessary.
978 if ( !ascending )
979 result = -result;
980
981 return result;
982 }
983
984 // ============================================================================
985 // wxTreeListCtrl implementation
986 // ============================================================================
987
988 BEGIN_EVENT_TABLE(wxTreeListCtrl, wxWindow)
989 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY, wxTreeListCtrl::OnSelectionChanged)
990 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY, wxTreeListCtrl::OnItemExpanding)
991 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
992 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
993 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
994 EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY, wxTreeListCtrl::OnColumnSorted)
995
996 EVT_SIZE(wxTreeListCtrl::OnSize)
997 END_EVENT_TABLE()
998
999 // ----------------------------------------------------------------------------
1000 // Creation
1001 // ----------------------------------------------------------------------------
1002
1003 void wxTreeListCtrl::Init()
1004 {
1005 m_view = NULL;
1006 m_model = NULL;
1007 m_comparator = NULL;
1008 }
1009
1010 bool wxTreeListCtrl::Create(wxWindow* parent,
1011 wxWindowID id,
1012 const wxPoint& pos,
1013 const wxSize& size,
1014 long style,
1015 const wxString& name)
1016 {
1017 if ( style & wxTL_USER_3STATE )
1018 style |= wxTL_3STATE;
1019
1020 if ( style & wxTL_3STATE )
1021 style |= wxTL_CHECKBOX;
1022
1023 // Create the window itself and wxDataViewCtrl used by it.
1024 if ( !wxWindow::Create(parent, id,
1025 pos, size,
1026 style, name) )
1027 {
1028 return false;
1029 }
1030
1031 m_view = new wxDataViewCtrl;
1032 long styleDataView = HasFlag(wxTL_MULTIPLE) ? wxDV_MULTIPLE
1033 : wxDV_SINGLE;
1034 if ( HasFlag(wxTL_NO_HEADER) )
1035 styleDataView |= wxDV_NO_HEADER;
1036
1037 if ( !m_view->Create(this, wxID_ANY,
1038 wxPoint(0, 0), GetClientSize(),
1039 styleDataView) )
1040 {
1041 delete m_view;
1042 m_view = NULL;
1043
1044 return false;
1045 }
1046
1047
1048 // Set up the model for wxDataViewCtrl.
1049 m_model = new wxTreeListModel(this);
1050 m_view->AssociateModel(m_model);
1051
1052 return true;
1053 }
1054
1055 wxTreeListCtrl::~wxTreeListCtrl()
1056 {
1057 if ( m_model )
1058 m_model->DecRef();
1059 }
1060
1061 wxWindowList wxTreeListCtrl::GetCompositeWindowParts() const
1062 {
1063 wxWindowList parts;
1064 parts.push_back(m_view);
1065 return parts;
1066 }
1067
1068 // ----------------------------------------------------------------------------
1069 // Columns
1070 // ----------------------------------------------------------------------------
1071
1072 int
1073 wxTreeListCtrl::DoInsertColumn(const wxString& title,
1074 int pos,
1075 int width,
1076 wxAlignment align,
1077 int flags)
1078 {
1079 wxCHECK_MSG( m_view, wxNOT_FOUND, "Must Create() first" );
1080
1081 const unsigned oldNumColumns = m_view->GetColumnCount();
1082
1083 if ( pos == wxNOT_FOUND )
1084 pos = oldNumColumns;
1085
1086 wxDataViewRenderer* renderer;
1087 if ( pos == 0 )
1088 {
1089 // Inserting the first column which is special as it uses a different
1090 // renderer.
1091
1092 // Also, currently it can be done only once.
1093 wxCHECK_MSG( !oldNumColumns, wxNOT_FOUND,
1094 "Inserting column at position 0 currently not supported" );
1095
1096 if ( HasFlag(wxTL_CHECKBOX) )
1097 {
1098 // Use our custom renderer to show the checkbox.
1099 renderer = new wxDataViewCheckIconTextRenderer;
1100 }
1101 else // We still need a special renderer to show the icons.
1102 {
1103 renderer = new wxDataViewIconTextRenderer;
1104 }
1105 }
1106 else // Not the first column.
1107 {
1108 // All the other ones use a simple text renderer.
1109 renderer = new wxDataViewTextRenderer;
1110 }
1111
1112 wxDataViewColumn*
1113 column = new wxDataViewColumn(title, renderer, pos, width, align, flags);
1114
1115 m_model->InsertColumn(pos);
1116
1117 m_view->InsertColumn(pos, column);
1118
1119 return pos;
1120 }
1121
1122 unsigned wxTreeListCtrl::GetColumnCount() const
1123 {
1124 return m_view ? m_view->GetColumnCount() : 0u;
1125 }
1126
1127 bool wxTreeListCtrl::DeleteColumn(unsigned col)
1128 {
1129 wxCHECK_MSG( col < GetColumnCount(), false, "Invalid column index" );
1130
1131 if ( !m_view->DeleteColumn(m_view->GetColumn(col)) )
1132 return false;
1133
1134 m_model->DeleteColumn(col);
1135
1136 return true;
1137 }
1138
1139 void wxTreeListCtrl::ClearColumns()
1140 {
1141 // Don't assert here, clearing columns of the control before it's created
1142 // can be considered valid (just useless).
1143 if ( !m_model )
1144 return;
1145
1146 m_view->ClearColumns();
1147
1148 m_model->ClearColumns();
1149 }
1150
1151 void wxTreeListCtrl::SetColumnWidth(unsigned col, int width)
1152 {
1153 wxCHECK_RET( col < GetColumnCount(), "Invalid column index" );
1154
1155 wxDataViewColumn* const column = m_view->GetColumn(col);
1156 wxCHECK_RET( column, "No such column?" );
1157
1158 column->SetWidth(width);
1159 }
1160
1161 int wxTreeListCtrl::GetColumnWidth(unsigned col) const
1162 {
1163 wxCHECK_MSG( col < GetColumnCount(), -1, "Invalid column index" );
1164
1165 wxDataViewColumn* column = m_view->GetColumn(col);
1166 wxCHECK_MSG( column, -1, "No such column?" );
1167
1168 return column->GetWidth();
1169 }
1170
1171 int wxTreeListCtrl::WidthFor(const wxString& text) const
1172 {
1173 return GetTextExtent(text).x;
1174 }
1175
1176 // ----------------------------------------------------------------------------
1177 // Items
1178 // ----------------------------------------------------------------------------
1179
1180 wxTreeListItem
1181 wxTreeListCtrl::DoInsertItem(wxTreeListItem parent,
1182 wxTreeListItem previous,
1183 const wxString& text,
1184 int imageClosed,
1185 int imageOpened,
1186 wxClientData* data)
1187 {
1188 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1189
1190 return wxTreeListItem(m_model->InsertItem(parent, previous, text,
1191 imageClosed, imageOpened, data));
1192 }
1193
1194 void wxTreeListCtrl::DeleteItem(wxTreeListItem item)
1195 {
1196 wxCHECK_RET( m_model, "Must create first" );
1197
1198 m_model->DeleteItem(item);
1199 }
1200
1201 void wxTreeListCtrl::DeleteAllItems()
1202 {
1203 if ( m_model )
1204 m_model->DeleteAllItems();
1205 }
1206
1207 // ----------------------------------------------------------------------------
1208 // Tree navigation
1209 // ----------------------------------------------------------------------------
1210
1211 // The simple accessors in this section are implemented directly using
1212 // wxTreeListModelNode methods, without passing by the model. This is just a
1213 // shortcut and avoids us the trouble of defining more trivial methods in
1214 // wxTreeListModel.
1215
1216 wxTreeListItem wxTreeListCtrl::GetRootItem() const
1217 {
1218 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1219
1220 return m_model->GetRootItem();
1221 }
1222
1223 wxTreeListItem wxTreeListCtrl::GetItemParent(wxTreeListItem item) const
1224 {
1225 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1226
1227 return item->GetParent();
1228 }
1229
1230 wxTreeListItem wxTreeListCtrl::GetFirstChild(wxTreeListItem item) const
1231 {
1232 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1233
1234 return item->GetChild();
1235 }
1236
1237 wxTreeListItem
1238 wxTreeListCtrl::GetNextSibling(wxTreeListItem item) const
1239 {
1240 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1241
1242 return item->GetNext();
1243 }
1244
1245 wxTreeListItem wxTreeListCtrl::GetNextItem(wxTreeListItem item) const
1246 {
1247 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1248
1249 return item->NextInTree();
1250 }
1251
1252 // ----------------------------------------------------------------------------
1253 // Item attributes
1254 // ----------------------------------------------------------------------------
1255
1256 const wxString&
1257 wxTreeListCtrl::GetItemText(wxTreeListItem item, unsigned col) const
1258 {
1259 // We can't use wxCHECK_MSG() here because we don't have any empty string
1260 // reference to return so we use a static variable that exists just for the
1261 // purpose of this check -- and so we put it in its own scope so that it's
1262 // never even created during normal program execution.
1263 if ( !m_model || col >= m_model->GetColumnCount() )
1264 {
1265 static wxString s_empty;
1266
1267 if ( !m_model )
1268 {
1269 wxFAIL_MSG( "Must create first" );
1270 }
1271 else if ( col >= m_model->GetColumnCount() )
1272 {
1273 wxFAIL_MSG( "Invalid column index" );
1274 }
1275
1276 return s_empty;
1277 }
1278
1279 return m_model->GetItemText(item, col);
1280 }
1281
1282 void
1283 wxTreeListCtrl::SetItemText(wxTreeListItem item,
1284 unsigned col,
1285 const wxString& text)
1286 {
1287 wxCHECK_RET( m_model, "Must create first" );
1288 wxCHECK_RET( col < m_model->GetColumnCount(), "Invalid column index" );
1289
1290 m_model->SetItemText(item, col, text);
1291 }
1292
1293 void wxTreeListCtrl::SetItemImage(wxTreeListItem item, int closed, int opened)
1294 {
1295 wxCHECK_RET( m_model, "Must create first" );
1296
1297 if ( closed != NO_IMAGE || opened != NO_IMAGE )
1298 {
1299 wxImageList* const imageList = GetImageList();
1300 wxCHECK_RET( imageList, "Can't set images without image list" );
1301
1302 const int imageCount = imageList->GetImageCount();
1303
1304 wxCHECK_RET( closed < imageCount, "Invalid image index" );
1305 wxCHECK_RET( opened < imageCount, "Invalid opened image index" );
1306 }
1307
1308 m_model->SetItemImage(item, closed, opened);
1309 }
1310
1311 wxClientData* wxTreeListCtrl::GetItemData(wxTreeListItem item) const
1312 {
1313 wxCHECK_MSG( m_model, NULL, "Must create first" );
1314
1315 return m_model->GetItemData(item);
1316 }
1317
1318 void wxTreeListCtrl::SetItemData(wxTreeListItem item, wxClientData* data)
1319 {
1320 wxCHECK_RET( m_model, "Must create first" );
1321
1322 m_model->SetItemData(item, data);
1323 }
1324
1325 // ----------------------------------------------------------------------------
1326 // Expanding and collapsing
1327 // ----------------------------------------------------------------------------
1328
1329 void wxTreeListCtrl::Expand(wxTreeListItem item)
1330 {
1331 wxCHECK_RET( m_view, "Must create first" );
1332
1333 m_view->Expand(m_model->ToDVI(item));
1334 }
1335
1336 void wxTreeListCtrl::Collapse(wxTreeListItem item)
1337 {
1338 wxCHECK_RET( m_view, "Must create first" );
1339
1340 m_view->Collapse(m_model->ToDVI(item));
1341 }
1342
1343 bool wxTreeListCtrl::IsExpanded(wxTreeListItem item) const
1344 {
1345 wxCHECK_MSG( m_view, false, "Must create first" );
1346
1347 return m_view->IsExpanded(m_model->ToDVI(item));
1348 }
1349
1350 // ----------------------------------------------------------------------------
1351 // Selection
1352 // ----------------------------------------------------------------------------
1353
1354 wxTreeListItem wxTreeListCtrl::GetSelection() const
1355 {
1356 wxCHECK_MSG( m_view, wxTreeListItem(), "Must create first" );
1357
1358 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE), wxTreeListItem(),
1359 "Must use GetSelections() with multi-selection controls!" );
1360
1361 const wxDataViewItem dvi = m_view->GetSelection();
1362
1363 return m_model->FromNonRootDVI(dvi);
1364 }
1365
1366 unsigned wxTreeListCtrl::GetSelections(wxTreeListItems& selections) const
1367 {
1368 wxCHECK_MSG( m_view, 0, "Must create first" );
1369
1370 wxDataViewItemArray selectionsDV;
1371 const unsigned numSelected = m_view->GetSelections(selectionsDV);
1372 selections.resize(numSelected);
1373 for ( unsigned n = 0; n < numSelected; n++ )
1374 selections[n] = m_model->FromNonRootDVI(selectionsDV[n]);
1375
1376 return numSelected;
1377 }
1378
1379 void wxTreeListCtrl::Select(wxTreeListItem item)
1380 {
1381 wxCHECK_RET( m_view, "Must create first" );
1382
1383 m_view->Select(m_model->ToNonRootDVI(item));
1384 }
1385
1386 void wxTreeListCtrl::Unselect(wxTreeListItem item)
1387 {
1388 wxCHECK_RET( m_view, "Must create first" );
1389
1390 m_view->Unselect(m_model->ToNonRootDVI(item));
1391 }
1392
1393 bool wxTreeListCtrl::IsSelected(wxTreeListItem item) const
1394 {
1395 wxCHECK_MSG( m_view, false, "Must create first" );
1396
1397 return m_view->IsSelected(m_model->ToNonRootDVI(item));
1398 }
1399
1400 void wxTreeListCtrl::SelectAll()
1401 {
1402 wxCHECK_RET( m_view, "Must create first" );
1403
1404 m_view->SelectAll();
1405 }
1406
1407 void wxTreeListCtrl::UnselectAll()
1408 {
1409 wxCHECK_RET( m_view, "Must create first" );
1410
1411 m_view->UnselectAll();
1412 }
1413
1414 // ----------------------------------------------------------------------------
1415 // Checkbox handling
1416 // ----------------------------------------------------------------------------
1417
1418 void wxTreeListCtrl::CheckItem(wxTreeListItem item, wxCheckBoxState state)
1419 {
1420 wxCHECK_RET( m_model, "Must create first" );
1421
1422 m_model->CheckItem(item, state);
1423 }
1424
1425 void
1426 wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item, wxCheckBoxState state)
1427 {
1428 wxCHECK_RET( m_model, "Must create first" );
1429
1430 m_model->CheckItem(item, state);
1431
1432 for ( wxTreeListItem child = GetFirstChild(item);
1433 child.IsOk();
1434 child = GetNextSibling(child) )
1435 {
1436 CheckItemRecursively(child, state);
1437 }
1438 }
1439
1440 void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item)
1441 {
1442 wxCHECK_RET( item.IsOk(), "Invalid item" );
1443
1444 wxASSERT_MSG( HasFlag(wxTL_3STATE), "Can only be used with wxTL_3STATE" );
1445
1446 for ( ;; )
1447 {
1448 wxTreeListItem parent = GetItemParent(item);
1449 if ( parent == GetRootItem() )
1450 {
1451 // There is no checked state associated with the root item.
1452 return;
1453 }
1454
1455 // Set parent state to the state of this item if all the other children
1456 // have the same state too. Otherwise make it indeterminate.
1457 const wxCheckBoxState stateItem = GetCheckedState(item);
1458 CheckItem(parent, AreAllChildrenInState(parent, stateItem)
1459 ? stateItem
1460 : wxCHK_UNDETERMINED);
1461
1462 // And do the same thing with the parent's parent too.
1463 item = parent;
1464 }
1465 }
1466
1467 wxCheckBoxState wxTreeListCtrl::GetCheckedState(wxTreeListItem item) const
1468 {
1469 wxCHECK_MSG( item.IsOk(), wxCHK_UNDETERMINED, "Invalid item" );
1470
1471 return item->m_checkedState;
1472 }
1473
1474 bool
1475 wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item,
1476 wxCheckBoxState state) const
1477 {
1478 wxCHECK_MSG( item.IsOk(), false, "Invalid item" );
1479
1480 for ( wxTreeListItem child = GetFirstChild(item);
1481 child.IsOk();
1482 child = GetNextSibling(child) )
1483 {
1484 if ( GetCheckedState(child) != state )
1485 return false;
1486 }
1487
1488 return true;
1489 }
1490
1491 // ----------------------------------------------------------------------------
1492 // Sorting
1493 // ----------------------------------------------------------------------------
1494
1495 void wxTreeListCtrl::SetSortColumn(unsigned col, bool ascendingOrder)
1496 {
1497 wxCHECK_RET( col < m_view->GetColumnCount(), "Invalid column index" );
1498
1499 m_view->GetColumn(col)->SetSortOrder(ascendingOrder);
1500 }
1501
1502 bool wxTreeListCtrl::GetSortColumn(unsigned* col, bool* ascendingOrder)
1503 {
1504 const unsigned numColumns = m_view->GetColumnCount();
1505 for ( unsigned n = 0; n < numColumns; n++ )
1506 {
1507 wxDataViewColumn* const column = m_view->GetColumn(n);
1508 if ( column->IsSortKey() )
1509 {
1510 if ( col )
1511 *col = n;
1512
1513 if ( ascendingOrder )
1514 *ascendingOrder = column->IsSortOrderAscending();
1515
1516 return true;
1517 }
1518 }
1519
1520 return false;
1521 }
1522
1523 void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator* comparator)
1524 {
1525 m_comparator = comparator;
1526 }
1527
1528 // ----------------------------------------------------------------------------
1529 // Events
1530 // ----------------------------------------------------------------------------
1531
1532 void wxTreeListCtrl::SendItemEvent(wxEventType evt, wxDataViewEvent& eventDV)
1533 {
1534 wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
1535
1536 if ( !ProcessWindowEvent(eventTL) )
1537 {
1538 eventDV.Skip();
1539 return;
1540 }
1541
1542 if ( !eventTL.IsAllowed() )
1543 {
1544 eventDV.Veto();
1545 }
1546 }
1547
1548 void wxTreeListCtrl::SendColumnEvent(wxEventType evt, wxDataViewEvent& eventDV)
1549 {
1550 wxTreeListEvent eventTL(evt, this, wxTreeListItem());
1551 eventTL.SetColumn(eventDV.GetColumn());
1552
1553 if ( !ProcessWindowEvent(eventTL) )
1554 {
1555 eventDV.Skip();
1556 return;
1557 }
1558
1559 if ( !eventTL.IsAllowed() )
1560 {
1561 eventDV.Veto();
1562 }
1563 }
1564
1565 void
1566 wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
1567 {
1568 wxTreeListEvent event(wxEVT_TREELIST_ITEM_CHECKED, this, item);
1569 event.SetOldCheckedState(stateOld);
1570
1571 ProcessWindowEvent(event);
1572 }
1573
1574 void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
1575 {
1576 SendItemEvent(wxEVT_TREELIST_SELECTION_CHANGED, event);
1577 }
1578
1579 void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
1580 {
1581 SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDING, event);
1582 }
1583
1584 void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
1585 {
1586 SendItemEvent(wxEVT_TREELIST_ITEM_EXPANDED, event);
1587 }
1588
1589 void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
1590 {
1591 SendItemEvent(wxEVT_TREELIST_ITEM_ACTIVATED, event);
1592 }
1593
1594 void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
1595 {
1596 SendItemEvent(wxEVT_TREELIST_ITEM_CONTEXT_MENU, event);
1597 }
1598
1599 void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent& event)
1600 {
1601 SendColumnEvent(wxEVT_TREELIST_COLUMN_SORTED, event);
1602 }
1603
1604 // ----------------------------------------------------------------------------
1605 // Geometry
1606 // ----------------------------------------------------------------------------
1607
1608 void wxTreeListCtrl::OnSize(wxSizeEvent& event)
1609 {
1610 event.Skip();
1611
1612 if ( m_view )
1613 {
1614 // Resize the real control to cover our entire client area.
1615 const wxRect rect = GetClientRect();
1616 m_view->SetSize(rect);
1617
1618 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
1619 // The generic implementation doesn't refresh itself immediately which
1620 // is annoying during "live resizing", so do it forcefully here to
1621 // ensure that the items are re-laid out and the focus rectangle is
1622 // redrawn correctly (instead of leaving traces) while our size is
1623 // being changed.
1624 wxWindow* const view = GetView();
1625 view->Refresh();
1626 view->Update();
1627 #endif // wxHAS_GENERIC_DATAVIEWCTRL
1628
1629 // Resize the first column to take the remaining available space.
1630 const unsigned numColumns = GetColumnCount();
1631 if ( !numColumns )
1632 return;
1633
1634 // There is a bug in generic wxDataViewCtrl: if the column width sums
1635 // up to the total size, horizontal scrollbar (unnecessarily) appears,
1636 // so subtract a bit to ensure this doesn't happen.
1637 int remainingWidth = rect.width - 5;
1638 for ( unsigned n = 1; n < GetColumnCount(); n++ )
1639 {
1640 remainingWidth -= GetColumnWidth(n);
1641 if ( remainingWidth <= 0 )
1642 {
1643 // There is not enough space, as we're not going to give the
1644 // first column negative width anyhow, just don't do anything.
1645 return;
1646 }
1647 }
1648
1649 SetColumnWidth(0, remainingWidth);
1650 }
1651 }
1652
1653 wxWindow* wxTreeListCtrl::GetView() const
1654 {
1655 #ifdef wxHAS_GENERIC_DATAVIEWCTRL
1656 return m_view->GetMainWindow();
1657 #else
1658 return m_view;
1659 #endif
1660 }
1661
1662 // ============================================================================
1663 // wxTreeListEvent implementation
1664 // ============================================================================
1665
1666 wxIMPLEMENT_DYNAMIC_CLASS(wxTreeListEvent, wxNotifyEvent)
1667
1668 #define wxDEFINE_TREELIST_EVENT(name) \
1669 wxDEFINE_EVENT(wxEVT_TREELIST_##name, wxTreeListEvent)
1670
1671 wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED);
1672 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING);
1673 wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED);
1674 wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
1675 wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
1676 wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
1677 wxDEFINE_TREELIST_EVENT(COLUMN_SORTED);
1678
1679 #undef wxDEFINE_TREELIST_EVENT
1680
1681 #endif // wxUSE_TREELISTCTRL