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