]> git.saurik.com Git - wxWidgets.git/blame - src/generic/treelist.cpp
Use wxDatePickerCtrl in wxDataViewDateRenderer.
[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
6// RCS-ID: $Id: wxhead.cpp,v 1.11 2010-04-22 12:44:51 zeitlin Exp $
7// Copyright: (c) 2011 Vadim Zeitlin <vadim@wxwidgets.org>
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// Declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// Headers
17// ----------------------------------------------------------------------------
18
19// for compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
f8ec7b81
VZ
26#if wxUSE_TREELISTCTRL
27
524cb040 28#ifndef WX_PRECOMP
00fa267c 29 #include "wx/dc.h"
524cb040
VZ
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
43const char wxTreeListCtrlNameStr[] = "wxTreeListCtrl";
44
45const wxTreeListItem wxTLI_FIRST(reinterpret_cast<wxTreeListModelNode*>(-1));
46const wxTreeListItem wxTLI_LAST(reinterpret_cast<wxTreeListModelNode*>(-2));
47
48// ----------------------------------------------------------------------------
49// wxTreeListModelNode: a node in the internal tree representation.
50// ----------------------------------------------------------------------------
51
52class wxTreeListModelNode
53{
54public:
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
2bad2b6d
VZ
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
524cb040
VZ
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
271private:
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
290class wxTreeListModel : public wxDataViewModel
291{
292public:
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);
2bad2b6d
VZ
342 void DeleteColumn(unsigned col);
343 void ClearColumns();
524cb040
VZ
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;
26693191 380 virtual bool IsListModel() const { return m_isFlat; }
da2e758f
VZ
381 virtual int Compare(const wxDataViewItem& item1,
382 const wxDataViewItem& item2,
383 unsigned col,
384 bool ascending) const;
524cb040
VZ
385
386private:
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;
26693191
VZ
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;
524cb040
VZ
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.
408namespace
409{
410
411const char* CHECK_ICON_TEXT_TYPE = "wxDataViewCheckIconText";
412
413// The value used by wxDataViewCheckIconTextRenderer
414class wxDataViewCheckIconText : public wxDataViewIconText
415{
416public:
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
441private:
442 wxDECLARE_DYNAMIC_CLASS(wxDataViewCheckIconText);
443};
444
445wxIMPLEMENT_DYNAMIC_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
446
447DECLARE_VARIANT_OBJECT(wxDataViewCheckIconText)
448IMPLEMENT_VARIANT_OBJECT(wxDataViewCheckIconText)
449
450
451class wxDataViewCheckIconTextRenderer : public wxDataViewCustomRenderer
452{
453public:
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 Activate(const wxRect& WXUNUSED(cell),
553 wxDataViewModel* model,
554 const wxDataViewItem& item,
555 unsigned int WXUNUSED(col))
556 {
557 static_cast<wxTreeListModel*>(model)->ToggleItem(item);
558 return true;
559 }
560
561 virtual bool LeftClick(const wxPoint& pos,
562 const wxRect& WXUNUSED(cell),
563 wxDataViewModel* model,
564 const wxDataViewItem& item,
565 unsigned int WXUNUSED(col))
566 {
567 if ( !wxRect(GetCheckSize()).Contains(pos) )
568 return false;
569
570 static_cast<wxTreeListModel*>(model)->ToggleItem(item);
571 return true;
572 }
573
574protected:
575 wxSize GetCheckSize() const
576 {
577 return wxRendererNative::Get().GetCheckBoxSize(GetView());
578 }
579
580private:
581 // Just some arbitrary constants defining margins, in pixels.
582 enum
583 {
584 MARGIN_CHECK_ICON = 3,
585 MARGIN_ICON_TEXT = 4
586 };
587
588 wxDataViewCheckIconText m_value;
589};
590
591} // anonymous namespace
592
593// ============================================================================
594// wxTreeListModel implementation
595// ============================================================================
596
597wxTreeListModel::wxTreeListModel(wxTreeListCtrl* treelist)
598 : m_treelist(treelist),
599 m_root(new Node(NULL))
600{
601 m_numColumns = 0;
26693191 602 m_isFlat = true;
524cb040
VZ
603}
604
605wxTreeListModel::~wxTreeListModel()
606{
607 delete m_root;
608}
609
610void wxTreeListModel::InsertColumn(unsigned col)
611{
612 m_numColumns++;
613
614 // There is no need to update anything when inserting the first column.
615 if ( m_numColumns == 1 )
616 return;
617
618 // Update all the items as they may have texts for the old columns.
619 for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
620 {
621 node->OnInsertColumn(col, m_numColumns);
622 }
623}
624
2bad2b6d
VZ
625void wxTreeListModel::DeleteColumn(unsigned col)
626{
627 wxCHECK_RET( col < m_numColumns, "Invalid column index" );
628
629 // Update all the items to remove the text for the non first columns.
630 if ( col > 0 )
631 {
632 for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
633 {
634 node->OnDeleteColumn(col, m_numColumns);
635 }
636 }
637
638 m_numColumns--;
639}
640
641void wxTreeListModel::ClearColumns()
642{
643 m_numColumns = 0;
644
645 for ( Node* node = m_root->GetChild(); node; node = node->NextInTree() )
646 {
647 node->OnClearColumns();
648 }
649}
650
524cb040
VZ
651wxTreeListModelNode*
652wxTreeListModel::InsertItem(Node* parent,
653 Node* previous,
654 const wxString& text,
655 int imageClosed,
656 int imageOpened,
657 wxClientData* data)
658{
659 wxCHECK_MSG( parent, NULL,
660 "Must have a valid parent (maybe GetRootItem()?)" );
661
662 wxCHECK_MSG( previous, NULL,
663 "Must have a valid previous item (maybe wxTLI_FIRST/LAST?)" );
664
26693191
VZ
665 if ( m_isFlat && parent != m_root )
666 {
667 // Not flat any more, this is a second level child.
668 m_isFlat = false;
669 }
670
524cb040
VZ
671 wxScopedPtr<Node>
672 newItem(new Node(parent, text, imageClosed, imageOpened, data));
673
276f3938
VZ
674 // FIXME-VC6: This compiler refuses to compare "Node* previous" with
675 // wxTLI_XXX without some help.
676 const wxTreeListItem previousItem(previous);
677
524cb040
VZ
678 // If we have no children at all, then inserting as last child is the same
679 // as inserting as the first one so check for it here too.
276f3938
VZ
680 if ( previousItem == wxTLI_FIRST ||
681 (previousItem == wxTLI_LAST && !parent->GetChild()) )
524cb040
VZ
682 {
683 parent->InsertChild(newItem.get());
684 }
685 else // Not the first item, find the previous one.
686 {
276f3938 687 if ( previousItem == wxTLI_LAST )
524cb040
VZ
688 {
689 previous = parent->GetChild();
690
691 // Find the last child.
692 for ( ;; )
693 {
694 Node* const next = previous->GetNext();
695 if ( !next )
696 break;
697
698 previous = next;
699 }
700 }
701 else // We already have the previous item.
702 {
703 // Just check it's under the correct parent.
704 wxCHECK_MSG( previous->GetParent() == parent, NULL,
705 "Previous item is not under the right parent" );
706 }
707
708 previous->InsertNext(newItem.get());
709 }
710
711 ItemAdded(ToDVI(parent), ToDVI(newItem.get()));
712
713 // The item was successfully inserted in the tree and so will be deleted by
714 // it, we can detach it now.
715 return newItem.release();
716}
717
718void wxTreeListModel::DeleteItem(Node* item)
719{
720 wxCHECK_RET( item, "Invalid item" );
721
722 wxCHECK_RET( item != m_root, "Can't delete the root item" );
723
724 Node* const parent = item->GetParent();
725
726 ItemDeleted(ToDVI(parent), ToDVI(item));
727
728 Node* previous = parent->GetChild();
729 if ( previous == item )
730 {
731 parent->DeleteChild();
732 }
733 else // Not the first child of its parent.
734 {
735 // Find the sibling just before it.
736 for ( ;; )
737 {
738 Node* const next = previous->GetNext();
739 if ( next == item )
740 break;
741
742 wxCHECK_RET( next, "Item not a child of its parent?" );
743
744 previous = next;
745 }
746
747 previous->DeleteNext();
748 }
749}
750
751void wxTreeListModel::DeleteAllItems()
752{
753 while ( m_root->GetChild() )
754 {
755 m_root->DeleteChild();
756 }
757
758 Cleared();
759}
760
761const wxString& wxTreeListModel::GetItemText(Node* item, unsigned col) const
762{
763 // Returning root item text here is bogus, it just happens to be an always
764 // empty string we can return reference to.
765 wxCHECK_MSG( item, m_root->m_text, "Invalid item" );
766
767 return col == 0 ? item->m_text : item->GetColumnText(col);
768}
769
770void wxTreeListModel::SetItemText(Node* item, unsigned col, const wxString& text)
771{
772 wxCHECK_RET( item, "Invalid item" );
773
774 if ( col == 0 )
775 item->m_text = text;
776 else
777 item->SetColumnText(text, col, m_numColumns);
778
779 ValueChanged(ToDVI(item), col);
780}
781
782void wxTreeListModel::SetItemImage(Node* item, int closed, int opened)
783{
784 wxCHECK_RET( item, "Invalid item" );
785
786 item->m_imageClosed = closed;
787 item->m_imageOpened = opened;
788
789 ValueChanged(ToDVI(item), 0);
790}
791
792wxClientData* wxTreeListModel::GetItemData(Node* item) const
793{
794 wxCHECK_MSG( item, NULL, "Invalid item" );
795
796 return item->GetClientData();
797}
798
799void wxTreeListModel::SetItemData(Node* item, wxClientData* data)
800{
801 wxCHECK_RET( item, "Invalid item" );
802
803 item->SetClientData(data);
804}
805
806void wxTreeListModel::CheckItem(Node* item, wxCheckBoxState checkedState)
807{
808 wxCHECK_RET( item, "Invalid item" );
809
810 item->m_checkedState = checkedState;
811
812 ItemChanged(ToDVI(item));
813}
814
815void wxTreeListModel::ToggleItem(wxDataViewItem dvi)
816{
817 Node* const item = FromDVI(dvi);
818
819 wxCHECK_RET( item, "Invalid item" );
820
821 const wxCheckBoxState stateOld = item->m_checkedState;
822
823 // If the 3rd state is user-settable then the cycle is
824 // unchecked->checked->undetermined.
825 switch ( stateOld )
826 {
827 case wxCHK_CHECKED:
828 item->m_checkedState = m_treelist->HasFlag(wxTL_USER_3STATE)
829 ? wxCHK_UNDETERMINED
830 : wxCHK_UNCHECKED;
831 break;
832
833 case wxCHK_UNDETERMINED:
834 // Whether 3rd state is user-settable or not, the next state is
835 // unchecked.
836 item->m_checkedState = wxCHK_UNCHECKED;
837 break;
838
839 case wxCHK_UNCHECKED:
840 item->m_checkedState = wxCHK_CHECKED;
841 break;
842 }
843
844 ItemChanged(ToDVI(item));
845
846 m_treelist->OnItemToggled(item, stateOld);
847}
848
849unsigned wxTreeListModel::GetColumnCount() const
850{
851 return m_numColumns;
852}
853
854wxString wxTreeListModel::GetColumnType(unsigned col) const
855{
856 if ( col == 0 )
857 {
858 return m_treelist->HasFlag(wxTL_CHECKBOX)
859 ? wxS("wxDataViewCheckIconText")
860 : wxS("wxDataViewIconText");
861 }
862 else // All the other columns contain just text.
863 {
864 return wxS("string");
865 }
866}
867
868void
869wxTreeListModel::GetValue(wxVariant& variant,
870 const wxDataViewItem& item,
871 unsigned col) const
872{
873 Node* const node = FromDVI(item);
874
875 if ( col == 0 )
876 {
877 // Determine the correct image to use depending on the item state.
878 int image = wxWithImages::NO_IMAGE;
879 if ( m_treelist->IsExpanded(node) )
880 image = node->m_imageOpened;
881
882 if ( image == wxWithImages::NO_IMAGE )
883 image = node->m_imageClosed;
884
885 wxIcon icon = m_treelist->GetImage(image);
886
887 if ( m_treelist->HasFlag(wxTL_CHECKBOX) )
888 variant << wxDataViewCheckIconText(node->m_text, icon,
889 node->m_checkedState);
890 else
891 variant << wxDataViewIconText(node->m_text, icon);
892 }
893 else
894 {
895 // Notice that we must still assign wxString to wxVariant to ensure
896 // that it at least has the correct type.
897 wxString text;
898 if ( node->HasColumnsTexts() )
899 text = node->GetColumnText(col);
900
901 variant = text;
902 }
903}
904
905bool
906wxTreeListModel::SetValue(const wxVariant& WXUNUSED(variant),
907 const wxDataViewItem& WXUNUSED(item),
908 unsigned WXUNUSED(col))
909{
910 // We are not editable currently.
911 return false;
912}
913
914wxDataViewItem wxTreeListModel::GetParent(const wxDataViewItem& item) const
915{
916 Node* const node = FromDVI(item);
917
918 return ToDVI(node->GetParent());
919}
920
921bool wxTreeListModel::IsContainer(const wxDataViewItem& item) const
922{
923 // FIXME: In the generic (and native OS X) versions we implement this
924 // method normally, i.e. only items with children are containers.
925 // But for the native GTK version we must pretend that all items are
926 // containers because otherwise adding children to them later would
927 // fail because wxGTK code calls IsContainer() too early (when
928 // adding the item itself) and we can't know whether we're container
929 // or not by then. Luckily, always returning true doesn't have any
930 // serious drawbacks for us.
931#ifdef __WXGTK__
932 wxUnusedVar(item);
933
934 return true;
935#else
936 Node* const node = FromDVI(item);
937
938 return node->GetChild() != NULL;
939#endif
940}
941
942bool
943wxTreeListModel::HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const
944{
945 return true;
946}
947
948unsigned
949wxTreeListModel::GetChildren(const wxDataViewItem& item,
950 wxDataViewItemArray& children) const
951{
952 Node* const node = FromDVI(item);
953
954 unsigned numChildren = 0;
955 for ( Node* child = node->GetChild(); child; child = child->GetNext() )
956 {
957 children.push_back(ToDVI(child));
958 numChildren++;
959 }
960
961 return numChildren;
962}
963
da2e758f
VZ
964int
965wxTreeListModel::Compare(const wxDataViewItem& item1,
966 const wxDataViewItem& item2,
967 unsigned col,
968 bool ascending) const
969{
970 // Compare using default alphabetical order if no custom comparator.
971 wxTreeListItemComparator* const comp = m_treelist->m_comparator;
972 if ( !comp )
973 return wxDataViewModel::Compare(item1, item2, col, ascending);
974
975 // Forward comparison to the comparator:
976 int result = comp->Compare(m_treelist, col, FromDVI(item1), FromDVI(item2));
977
978 // And adjust by the sort order if necessary.
979 if ( !ascending )
980 result = -result;
981
982 return result;
983}
984
524cb040
VZ
985// ============================================================================
986// wxTreeListCtrl implementation
987// ============================================================================
988
989BEGIN_EVENT_TABLE(wxTreeListCtrl, wxWindow)
990 EVT_DATAVIEW_SELECTION_CHANGED(wxID_ANY, wxTreeListCtrl::OnSelectionChanged)
991 EVT_DATAVIEW_ITEM_EXPANDING(wxID_ANY, wxTreeListCtrl::OnItemExpanding)
992 EVT_DATAVIEW_ITEM_EXPANDED(wxID_ANY, wxTreeListCtrl::OnItemExpanded)
993 EVT_DATAVIEW_ITEM_ACTIVATED(wxID_ANY, wxTreeListCtrl::OnItemActivated)
994 EVT_DATAVIEW_ITEM_CONTEXT_MENU(wxID_ANY, wxTreeListCtrl::OnItemContextMenu)
da2e758f 995 EVT_DATAVIEW_COLUMN_SORTED(wxID_ANY, wxTreeListCtrl::OnColumnSorted)
524cb040
VZ
996
997 EVT_SIZE(wxTreeListCtrl::OnSize)
998END_EVENT_TABLE()
999
1000// ----------------------------------------------------------------------------
1001// Creation
1002// ----------------------------------------------------------------------------
1003
1004void wxTreeListCtrl::Init()
1005{
1006 m_view = NULL;
1007 m_model = NULL;
da2e758f 1008 m_comparator = NULL;
524cb040
VZ
1009}
1010
1011bool wxTreeListCtrl::Create(wxWindow* parent,
1012 wxWindowID id,
1013 const wxPoint& pos,
1014 const wxSize& size,
1015 long style,
1016 const wxString& name)
1017{
1018 if ( style & wxTL_USER_3STATE )
1019 style |= wxTL_3STATE;
1020
1021 if ( style & wxTL_3STATE )
1022 style |= wxTL_CHECKBOX;
1023
1024 // Create the window itself and wxDataViewCtrl used by it.
1025 if ( !wxWindow::Create(parent, id,
1026 pos, size,
1027 style, name) )
1028 {
1029 return false;
1030 }
1031
1032 m_view = new wxDataViewCtrl;
1033 if ( !m_view->Create(this, wxID_ANY,
1034 wxPoint(0, 0), GetClientSize(),
1035 HasFlag(wxTL_MULTIPLE) ? wxDV_MULTIPLE
1036 : wxDV_SINGLE) )
1037 {
1038 delete m_view;
1039 m_view = NULL;
1040
1041 return false;
1042 }
1043
1044
1045 // Set up the model for wxDataViewCtrl.
1046 m_model = new wxTreeListModel(this);
1047 m_view->AssociateModel(m_model);
1048
1049 return true;
1050}
1051
1052wxTreeListCtrl::~wxTreeListCtrl()
1053{
1054 if ( m_model )
1055 m_model->DecRef();
1056}
1057
1a661779
VZ
1058wxWindowList wxTreeListCtrl::GetCompositeWindowParts() const
1059{
1060 wxWindowList parts;
1061 parts.push_back(m_view);
1062 return parts;
1063}
1064
524cb040
VZ
1065// ----------------------------------------------------------------------------
1066// Columns
1067// ----------------------------------------------------------------------------
1068
1069int
1070wxTreeListCtrl::DoInsertColumn(const wxString& title,
1071 int pos,
1072 int width,
1073 wxAlignment align,
1074 int flags)
1075{
1076 wxCHECK_MSG( m_view, wxNOT_FOUND, "Must Create() first" );
1077
1078 const unsigned oldNumColumns = m_view->GetColumnCount();
1079
1080 if ( pos == wxNOT_FOUND )
1081 pos = oldNumColumns;
1082
1083 wxDataViewRenderer* renderer;
1084 if ( pos == 0 )
1085 {
1086 // Inserting the first column which is special as it uses a different
1087 // renderer.
1088
1089 // Also, currently it can be done only once.
1090 wxCHECK_MSG( !oldNumColumns, wxNOT_FOUND,
1091 "Inserting column at position 0 currently not supported" );
1092
1093 if ( HasFlag(wxTL_CHECKBOX) )
1094 {
1095 // Use our custom renderer to show the checkbox.
1096 renderer = new wxDataViewCheckIconTextRenderer;
1097 }
1098 else // We still need a special renderer to show the icons.
1099 {
1100 renderer = new wxDataViewIconTextRenderer;
1101 }
1102 }
1103 else // Not the first column.
1104 {
1105 // All the other ones use a simple text renderer.
1106 renderer = new wxDataViewTextRenderer;
1107 }
1108
1109 wxDataViewColumn*
1110 column = new wxDataViewColumn(title, renderer, pos, width, align, flags);
1111
1112 m_model->InsertColumn(pos);
1113
1114 m_view->InsertColumn(pos, column);
1115
1116 return pos;
1117}
1118
1119unsigned wxTreeListCtrl::GetColumnCount() const
1120{
1121 return m_view ? m_view->GetColumnCount() : 0u;
1122}
1123
1124bool wxTreeListCtrl::DeleteColumn(unsigned col)
1125{
1126 wxCHECK_MSG( col < GetColumnCount(), false, "Invalid column index" );
1127
2bad2b6d
VZ
1128 if ( !m_view->DeleteColumn(m_view->GetColumn(col)) )
1129 return false;
1130
1131 m_model->DeleteColumn(col);
1132
1133 return true;
524cb040
VZ
1134}
1135
1136void wxTreeListCtrl::ClearColumns()
1137{
2bad2b6d
VZ
1138 // Don't assert here, clearing columns of the control before it's created
1139 // can be considered valid (just useless).
1140 if ( !m_model )
1141 return;
1142
1143 m_view->ClearColumns();
1144
1145 m_model->ClearColumns();
524cb040
VZ
1146}
1147
1148void wxTreeListCtrl::SetColumnWidth(unsigned col, int width)
1149{
1150 wxCHECK_RET( col < GetColumnCount(), "Invalid column index" );
1151
1152 wxDataViewColumn* const column = m_view->GetColumn(col);
1153 wxCHECK_RET( column, "No such column?" );
1154
276f3938 1155 column->SetWidth(width);
524cb040
VZ
1156}
1157
1158int wxTreeListCtrl::GetColumnWidth(unsigned col) const
1159{
1160 wxCHECK_MSG( col < GetColumnCount(), -1, "Invalid column index" );
1161
1162 wxDataViewColumn* column = m_view->GetColumn(col);
1163 wxCHECK_MSG( column, -1, "No such column?" );
1164
1165 return column->GetWidth();
1166}
1167
1168int wxTreeListCtrl::WidthFor(const wxString& text) const
1169{
1170 return GetTextExtent(text).x;
1171}
1172
1173// ----------------------------------------------------------------------------
1174// Items
1175// ----------------------------------------------------------------------------
1176
1177wxTreeListItem
1178wxTreeListCtrl::DoInsertItem(wxTreeListItem parent,
1179 wxTreeListItem previous,
1180 const wxString& text,
1181 int imageClosed,
1182 int imageOpened,
1183 wxClientData* data)
1184{
1185 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1186
1187 return wxTreeListItem(m_model->InsertItem(parent, previous, text,
1188 imageClosed, imageOpened, data));
1189}
1190
1191void wxTreeListCtrl::DeleteItem(wxTreeListItem item)
1192{
1193 wxCHECK_RET( m_model, "Must create first" );
1194
1195 m_model->DeleteItem(item);
1196}
1197
1198void wxTreeListCtrl::DeleteAllItems()
1199{
1200 if ( m_model )
1201 m_model->DeleteAllItems();
1202}
1203
1204// ----------------------------------------------------------------------------
1205// Tree navigation
1206// ----------------------------------------------------------------------------
1207
1208// The simple accessors in this section are implemented directly using
1209// wxTreeListModelNode methods, without passing by the model. This is just a
1210// shortcut and avoids us the trouble of defining more trivial methods in
1211// wxTreeListModel.
1212
1213wxTreeListItem wxTreeListCtrl::GetRootItem() const
1214{
1215 wxCHECK_MSG( m_model, wxTreeListItem(), "Must create first" );
1216
1217 return m_model->GetRootItem();
1218}
1219
1220wxTreeListItem wxTreeListCtrl::GetItemParent(wxTreeListItem item) const
1221{
1222 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1223
1224 return item->GetParent();
1225}
1226
1227wxTreeListItem wxTreeListCtrl::GetFirstChild(wxTreeListItem item) const
1228{
1229 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1230
1231 return item->GetChild();
1232}
1233
1234wxTreeListItem
1235wxTreeListCtrl::GetNextSibling(wxTreeListItem item) const
1236{
1237 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1238
1239 return item->GetNext();
1240}
1241
1242wxTreeListItem wxTreeListCtrl::GetNextItem(wxTreeListItem item) const
1243{
1244 wxCHECK_MSG( item.IsOk(), wxTreeListItem(), "Invalid item" );
1245
1246 return item->NextInTree();
1247}
1248
1249// ----------------------------------------------------------------------------
1250// Item attributes
1251// ----------------------------------------------------------------------------
1252
1253const wxString&
1254wxTreeListCtrl::GetItemText(wxTreeListItem item, unsigned col) const
1255{
1256 // We can't use wxCHECK_MSG() here because we don't have any empty string
1257 // reference to return so we use a static variable that exists just for the
1258 // purpose of this check -- and so we put it in its own scope so that it's
1259 // never even created during normal program execution.
1260 if ( !m_model || col >= m_model->GetColumnCount() )
1261 {
1262 static wxString s_empty;
1263
1264 if ( !m_model )
1265 {
1266 wxFAIL_MSG( "Must create first" );
1267 }
1268 else if ( col >= m_model->GetColumnCount() )
1269 {
1270 wxFAIL_MSG( "Invalid column index" );
1271 }
1272
1273 return s_empty;
1274 }
1275
1276 return m_model->GetItemText(item, col);
1277}
1278
1279void
1280wxTreeListCtrl::SetItemText(wxTreeListItem item,
1281 unsigned col,
1282 const wxString& text)
1283{
1284 wxCHECK_RET( m_model, "Must create first" );
1285 wxCHECK_RET( col < m_model->GetColumnCount(), "Invalid column index" );
1286
1287 m_model->SetItemText(item, col, text);
1288}
1289
1290void wxTreeListCtrl::SetItemImage(wxTreeListItem item, int closed, int opened)
1291{
1292 wxCHECK_RET( m_model, "Must create first" );
1293
1294 if ( closed != NO_IMAGE || opened != NO_IMAGE )
1295 {
1296 wxImageList* const imageList = GetImageList();
1297 wxCHECK_RET( imageList, "Can't set images without image list" );
1298
1299 const int imageCount = imageList->GetImageCount();
1300
1301 wxCHECK_RET( closed < imageCount, "Invalid image index" );
1302 wxCHECK_RET( opened < imageCount, "Invalid opened image index" );
1303 }
1304
1305 m_model->SetItemImage(item, closed, opened);
1306}
1307
1308wxClientData* wxTreeListCtrl::GetItemData(wxTreeListItem item) const
1309{
1310 wxCHECK_MSG( m_model, NULL, "Must create first" );
1311
1312 return m_model->GetItemData(item);
1313}
1314
1315void wxTreeListCtrl::SetItemData(wxTreeListItem item, wxClientData* data)
1316{
1317 wxCHECK_RET( m_model, "Must create first" );
1318
1319 m_model->SetItemData(item, data);
1320}
1321
1322// ----------------------------------------------------------------------------
1323// Expanding and collapsing
1324// ----------------------------------------------------------------------------
1325
1326void wxTreeListCtrl::Expand(wxTreeListItem item)
1327{
1328 wxCHECK_RET( m_view, "Must create first" );
1329
1330 m_view->Expand(m_model->ToDVI(item));
1331}
1332
1333void wxTreeListCtrl::Collapse(wxTreeListItem item)
1334{
1335 wxCHECK_RET( m_view, "Must create first" );
1336
1337 m_view->Collapse(m_model->ToDVI(item));
1338}
1339
1340bool wxTreeListCtrl::IsExpanded(wxTreeListItem item) const
1341{
1342 wxCHECK_MSG( m_view, false, "Must create first" );
1343
1344 return m_view->IsExpanded(m_model->ToDVI(item));
1345}
1346
1347// ----------------------------------------------------------------------------
1348// Selection
1349// ----------------------------------------------------------------------------
1350
1351wxTreeListItem wxTreeListCtrl::GetSelection() const
1352{
1353 wxCHECK_MSG( m_view, wxTreeListItem(), "Must create first" );
1354
1355 wxCHECK_MSG( !HasFlag(wxTL_MULTIPLE), wxTreeListItem(),
1356 "Must use GetSelections() with multi-selection controls!" );
1357
1358 const wxDataViewItem dvi = m_view->GetSelection();
1359
1360 return m_model->FromNonRootDVI(dvi);
1361}
1362
1363unsigned wxTreeListCtrl::GetSelections(wxTreeListItems& selections) const
1364{
1365 wxCHECK_MSG( m_view, 0, "Must create first" );
1366
1367 wxDataViewItemArray selectionsDV;
1368 const unsigned numSelected = m_view->GetSelections(selectionsDV);
1369 selections.resize(numSelected);
1370 for ( unsigned n = 0; n < numSelected; n++ )
1371 selections[n] = m_model->FromNonRootDVI(selectionsDV[n]);
1372
1373 return numSelected;
1374}
1375
1376void wxTreeListCtrl::Select(wxTreeListItem item)
1377{
1378 wxCHECK_RET( m_view, "Must create first" );
1379
1380 m_view->Select(m_model->ToNonRootDVI(item));
1381}
1382
1383void wxTreeListCtrl::Unselect(wxTreeListItem item)
1384{
1385 wxCHECK_RET( m_view, "Must create first" );
1386
1387 m_view->Unselect(m_model->ToNonRootDVI(item));
1388}
1389
1390bool wxTreeListCtrl::IsSelected(wxTreeListItem item) const
1391{
1392 wxCHECK_MSG( m_view, false, "Must create first" );
1393
1394 return m_view->IsSelected(m_model->ToNonRootDVI(item));
1395}
1396
1397void wxTreeListCtrl::SelectAll()
1398{
1399 wxCHECK_RET( m_view, "Must create first" );
1400
1401 m_view->SelectAll();
1402}
1403
1404void wxTreeListCtrl::UnselectAll()
1405{
1406 wxCHECK_RET( m_view, "Must create first" );
1407
1408 m_view->UnselectAll();
1409}
1410
1411// ----------------------------------------------------------------------------
1412// Checkbox handling
1413// ----------------------------------------------------------------------------
1414
1415void wxTreeListCtrl::CheckItem(wxTreeListItem item, wxCheckBoxState state)
1416{
1417 wxCHECK_RET( m_model, "Must create first" );
1418
1419 m_model->CheckItem(item, state);
1420}
1421
1422void
1423wxTreeListCtrl::CheckItemRecursively(wxTreeListItem item, wxCheckBoxState state)
1424{
1425 wxCHECK_RET( m_model, "Must create first" );
1426
1427 m_model->CheckItem(item, state);
1428
1429 for ( wxTreeListItem child = GetFirstChild(item);
1430 child.IsOk();
1431 child = GetNextSibling(child) )
1432 {
1433 CheckItemRecursively(child, state);
1434 }
1435}
1436
1437void wxTreeListCtrl::UpdateItemParentStateRecursively(wxTreeListItem item)
1438{
1439 wxCHECK_RET( item.IsOk(), "Invalid item" );
1440
1441 wxASSERT_MSG( HasFlag(wxTL_3STATE), "Can only be used with wxTL_3STATE" );
1442
1443 for ( ;; )
1444 {
1445 wxTreeListItem parent = GetItemParent(item);
1446 if ( parent == GetRootItem() )
1447 {
1448 // There is no checked state associated with the root item.
1449 return;
1450 }
1451
1452 // Set parent state to the state of this item if all the other children
1453 // have the same state too. Otherwise make it indeterminate.
1454 const wxCheckBoxState stateItem = GetCheckedState(item);
1455 CheckItem(parent, AreAllChildrenInState(parent, stateItem)
1456 ? stateItem
1457 : wxCHK_UNDETERMINED);
1458
1459 // And do the same thing with the parent's parent too.
1460 item = parent;
1461 }
1462}
1463
1464wxCheckBoxState wxTreeListCtrl::GetCheckedState(wxTreeListItem item) const
1465{
1466 wxCHECK_MSG( item.IsOk(), wxCHK_UNDETERMINED, "Invalid item" );
1467
1468 return item->m_checkedState;
1469}
1470
1471bool
1472wxTreeListCtrl::AreAllChildrenInState(wxTreeListItem item,
1473 wxCheckBoxState state) const
1474{
1475 wxCHECK_MSG( item.IsOk(), false, "Invalid item" );
1476
1477 for ( wxTreeListItem child = GetFirstChild(item);
1478 child.IsOk();
1479 child = GetNextSibling(child) )
1480 {
1481 if ( GetCheckedState(child) != state )
1482 return false;
1483 }
1484
1485 return true;
1486}
1487
da2e758f
VZ
1488// ----------------------------------------------------------------------------
1489// Sorting
1490// ----------------------------------------------------------------------------
1491
1492void wxTreeListCtrl::SetSortColumn(unsigned col, bool ascendingOrder)
1493{
1494 wxCHECK_RET( col < m_view->GetColumnCount(), "Invalid column index" );
1495
1496 m_view->GetColumn(col)->SetSortOrder(ascendingOrder);
1497}
1498
1499bool wxTreeListCtrl::GetSortColumn(unsigned* col, bool* ascendingOrder)
1500{
1501 const unsigned numColumns = m_view->GetColumnCount();
1502 for ( unsigned n = 0; n < numColumns; n++ )
1503 {
1504 wxDataViewColumn* const column = m_view->GetColumn(n);
1505 if ( column->IsSortKey() )
1506 {
1507 if ( col )
1508 *col = n;
1509
1510 if ( ascendingOrder )
1511 *ascendingOrder = column->IsSortOrderAscending();
1512
1513 return true;
1514 }
1515 }
1516
1517 return false;
1518}
1519
1520void wxTreeListCtrl::SetItemComparator(wxTreeListItemComparator* comparator)
1521{
1522 m_comparator = comparator;
1523}
1524
524cb040
VZ
1525// ----------------------------------------------------------------------------
1526// Events
1527// ----------------------------------------------------------------------------
1528
da2e758f 1529void wxTreeListCtrl::SendItemEvent(wxEventType evt, wxDataViewEvent& eventDV)
524cb040
VZ
1530{
1531 wxTreeListEvent eventTL(evt, this, m_model->FromDVI(eventDV.GetItem()));
1532
1533 if ( !ProcessWindowEvent(eventTL) )
1534 {
1535 eventDV.Skip();
1536 return;
1537 }
1538
1539 if ( !eventTL.IsAllowed() )
1540 {
1541 eventDV.Veto();
1542 }
1543}
1544
da2e758f
VZ
1545void wxTreeListCtrl::SendColumnEvent(wxEventType evt, wxDataViewEvent& eventDV)
1546{
1547 wxTreeListEvent eventTL(evt, this, wxTreeListItem());
1548 eventTL.SetColumn(eventDV.GetColumn());
1549
1550 if ( !ProcessWindowEvent(eventTL) )
1551 {
1552 eventDV.Skip();
1553 return;
1554 }
1555
1556 if ( !eventTL.IsAllowed() )
1557 {
1558 eventDV.Veto();
1559 }
1560}
1561
524cb040
VZ
1562void
1563wxTreeListCtrl::OnItemToggled(wxTreeListItem item, wxCheckBoxState stateOld)
1564{
1565 wxTreeListEvent event(wxEVT_COMMAND_TREELIST_ITEM_CHECKED, this, item);
1566 event.SetOldCheckedState(stateOld);
1567
1568 ProcessWindowEvent(event);
1569}
1570
1571void wxTreeListCtrl::OnSelectionChanged(wxDataViewEvent& event)
1572{
da2e758f 1573 SendItemEvent(wxEVT_COMMAND_TREELIST_SELECTION_CHANGED, event);
524cb040
VZ
1574}
1575
1576void wxTreeListCtrl::OnItemExpanding(wxDataViewEvent& event)
1577{
da2e758f 1578 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDING, event);
524cb040
VZ
1579}
1580
1581void wxTreeListCtrl::OnItemExpanded(wxDataViewEvent& event)
1582{
da2e758f 1583 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_EXPANDED, event);
524cb040
VZ
1584}
1585
1586void wxTreeListCtrl::OnItemActivated(wxDataViewEvent& event)
1587{
da2e758f 1588 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_ACTIVATED, event);
524cb040
VZ
1589}
1590
1591void wxTreeListCtrl::OnItemContextMenu(wxDataViewEvent& event)
1592{
da2e758f
VZ
1593 SendItemEvent(wxEVT_COMMAND_TREELIST_ITEM_CONTEXT_MENU, event);
1594}
1595
1596void wxTreeListCtrl::OnColumnSorted(wxDataViewEvent& event)
1597{
1598 SendColumnEvent(wxEVT_COMMAND_TREELIST_COLUMN_SORTED, event);
524cb040
VZ
1599}
1600
1601// ----------------------------------------------------------------------------
1602// Geometry
1603// ----------------------------------------------------------------------------
1604
1605void wxTreeListCtrl::OnSize(wxSizeEvent& event)
1606{
1607 event.Skip();
1608
1609 if ( m_view )
1610 {
1611 // Resize the real control to cover our entire client area.
1612 const wxRect rect = GetClientRect();
1613 m_view->SetSize(rect);
1614
e427c045
VZ
1615#ifdef wxHAS_GENERIC_DATAVIEWCTRL
1616 // The generic implementation doesn't refresh itself immediately which
1617 // is annoying during "live resizing", so do it forcefully here to
1618 // ensure that the items are re-laid out and the focus rectangle is
1619 // redrawn correctly (instead of leaving traces) while our size is
1620 // being changed.
1621 wxWindow* const view = GetView();
1622 view->Refresh();
1623 view->Update();
1624#endif // wxHAS_GENERIC_DATAVIEWCTRL
1625
6b719a1c 1626 // Resize the first column to take the remaining available space.
524cb040
VZ
1627 const unsigned numColumns = GetColumnCount();
1628 if ( !numColumns )
1629 return;
1630
1631 // There is a bug in generic wxDataViewCtrl: if the column width sums
1632 // up to the total size, horizontal scrollbar (unnecessarily) appears,
6b719a1c
VZ
1633 // so subtract a bit to ensure this doesn't happen.
1634 int remainingWidth = rect.width - 5;
524cb040
VZ
1635 for ( unsigned n = 1; n < GetColumnCount(); n++ )
1636 {
1637 remainingWidth -= GetColumnWidth(n);
6b719a1c
VZ
1638 if ( remainingWidth <= 0 )
1639 {
1640 // There is not enough space, as we're not going to give the
1641 // first column negative width anyhow, just don't do anything.
1642 return;
1643 }
524cb040
VZ
1644 }
1645
6b719a1c 1646 SetColumnWidth(0, remainingWidth);
524cb040
VZ
1647 }
1648}
1649
8148ae02
VZ
1650wxWindow* wxTreeListCtrl::GetView() const
1651{
1652#ifdef wxHAS_GENERIC_DATAVIEWCTRL
1653 return m_view->GetMainWindow();
1654#else
1655 return m_view;
1656#endif
1657}
1658
524cb040
VZ
1659// ============================================================================
1660// wxTreeListEvent implementation
1661// ============================================================================
1662
1663wxIMPLEMENT_ABSTRACT_CLASS(wxTreeListEvent, wxNotifyEvent)
1664
1665#define wxDEFINE_TREELIST_EVENT(name) \
1666 wxDEFINE_EVENT(wxEVT_COMMAND_TREELIST_##name, wxTreeListEvent)
1667
1668wxDEFINE_TREELIST_EVENT(SELECTION_CHANGED);
1669wxDEFINE_TREELIST_EVENT(ITEM_EXPANDING);
1670wxDEFINE_TREELIST_EVENT(ITEM_EXPANDED);
1671wxDEFINE_TREELIST_EVENT(ITEM_CHECKED);
1672wxDEFINE_TREELIST_EVENT(ITEM_ACTIVATED);
1673wxDEFINE_TREELIST_EVENT(ITEM_CONTEXT_MENU);
da2e758f 1674wxDEFINE_TREELIST_EVENT(COLUMN_SORTED);
524cb040
VZ
1675
1676#undef wxDEFINE_TREELIST_EVENT
f8ec7b81
VZ
1677
1678#endif // wxUSE_TREELISTCTRL