]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
wxTreeCtrl::Delete() bug corrected, sample expanded to test it
[wxWidgets.git] / src / generic / treectrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: treectrl.cpp
3 // Purpose: generic tree control implementation
4 // Author: Robert Roebling
5 // Created: 01/02/97
6 // Modified: 22/10/98 - almost total rewrite, simpler interface (VZ)
7 // Id: $Id$
8 // Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // =============================================================================
13 // declarations
14 // =============================================================================
15
16 // -----------------------------------------------------------------------------
17 // headers
18 // -----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "treectrl.h"
22 #endif
23
24 #include "wx/treectrl.h"
25 #include "wx/settings.h"
26 #include "wx/log.h"
27 #include "wx/intl.h"
28 #include "wx/dynarray.h"
29 #include "wx/dcclient.h"
30 #include "wx/imaglist.h"
31
32 // -----------------------------------------------------------------------------
33 // array types
34 // -----------------------------------------------------------------------------
35
36 WX_DEFINE_ARRAY(wxGenericTreeItem *, wxArrayTreeItems);
37
38 // -----------------------------------------------------------------------------
39 // private classes
40 // -----------------------------------------------------------------------------
41
42 // a tree item
43 class WXDLLEXPORT wxGenericTreeItem
44 {
45 public:
46 // ctors & dtor
47 wxGenericTreeItem() { m_data = NULL; }
48 wxGenericTreeItem( wxGenericTreeItem *parent,
49 const wxString& text,
50 wxDC& dc,
51 int image, int selImage,
52 wxTreeItemData *data );
53
54 ~wxGenericTreeItem();
55
56 // trivial accessors
57 wxArrayTreeItems& GetChildren() { return m_children; }
58
59 const wxString& GetText() const { return m_text; }
60 int GetImage() const { return m_image; }
61 int GetSelectedImage() const { return m_selImage; }
62 wxTreeItemData *GetData() const { return m_data; }
63
64 void SetText( const wxString &text, wxDC& dc );
65 void SetImage(int image) { m_image = image; }
66 void SetSelectedImage(int image) { m_selImage = image; }
67 void SetData(wxTreeItemData *data) { m_data = data; }
68
69 void SetHasPlus(bool has = TRUE) { m_hasPlus = has; }
70
71 int GetX() const { return m_x; }
72 int GetY() const { return m_y; }
73
74 void SetHeight(int h) { m_height = h; }
75
76 void SetX(int x) { m_x = x; }
77 void SetY(int y) { m_y = y; }
78
79 wxGenericTreeItem *GetParent() const { return m_parent; }
80
81 // operations
82 void Reset();
83
84 // get count of all children (and grand children if 'recursively')
85 size_t GetChildrenCount(bool recursively = TRUE) const;
86
87 void Insert(wxGenericTreeItem *child, size_t index)
88 { m_children.Insert(child, index); }
89
90 void SetCross( int x, int y );
91 void GetSize( int &x, int &y );
92
93 // return the item at given position (or NULL if no item), onButton is TRUE
94 // if the point belongs to the item's button, otherwise it lies on the
95 // button's label
96 wxGenericTreeItem *HitTest( const wxPoint& point, bool &onButton );
97
98 void Expand() { m_isCollapsed = FALSE; }
99 void Collapse() { m_isCollapsed = TRUE; }
100
101 void SetHilight( bool set = TRUE ) { m_hasHilight = set; }
102
103 // status inquiries
104 bool HasChildren() const { return !m_children.IsEmpty(); }
105 bool HasHilight() const { return m_hasHilight; }
106 bool IsExpanded() const { return !m_isCollapsed; }
107 bool HasPlus() const { return m_hasPlus || HasChildren(); }
108
109 private:
110 wxString m_text;
111
112 int m_image,
113 m_selImage;
114
115 wxTreeItemData *m_data;
116
117 // @@ probably should use bitfields to save size
118 bool m_isCollapsed,
119 m_hasHilight, // same as focused
120 m_hasPlus; // used for item which doesn't have
121 // children but still has a [+] button
122
123 int m_x, m_y;
124 long m_height, m_width;
125 int m_xCross, m_yCross;
126 int m_level;
127 wxArrayTreeItems m_children;
128 wxGenericTreeItem *m_parent;
129 };
130
131 // =============================================================================
132 // implementation
133 // =============================================================================
134
135 // -----------------------------------------------------------------------------
136 // wxTreeEvent
137 // -----------------------------------------------------------------------------
138
139 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxCommandEvent)
140
141 wxTreeEvent::wxTreeEvent( wxEventType commandType, int id )
142 : wxCommandEvent( commandType, id )
143 {
144 m_code = 0;
145 m_itemOld = (wxGenericTreeItem *)NULL;
146 }
147
148 // -----------------------------------------------------------------------------
149 // wxGenericTreeItem
150 // -----------------------------------------------------------------------------
151
152 wxGenericTreeItem::wxGenericTreeItem(wxGenericTreeItem *parent,
153 const wxString& text,
154 wxDC& dc,
155 int image, int selImage,
156 wxTreeItemData *data)
157 : m_text(text)
158 {
159 m_image = image;
160 m_selImage = selImage;
161 m_data = data;
162 m_x = m_y = 0;
163 m_xCross = m_yCross = 0;
164
165 m_level = 0;
166
167 m_isCollapsed = TRUE;
168 m_hasHilight = FALSE;
169 m_hasPlus = FALSE;
170
171 m_parent = parent;
172
173 dc.GetTextExtent( m_text, &m_width, &m_height );
174 }
175
176 wxGenericTreeItem::~wxGenericTreeItem()
177 {
178 delete m_data;
179
180 size_t count = m_children.Count();
181 for ( size_t n = 0; n < count; n++ )
182 delete m_children[n];
183 }
184
185 void wxGenericTreeItem::SetText( const wxString &text, wxDC& dc )
186 {
187 m_text = text;
188
189 dc.GetTextExtent( m_text, &m_width, &m_height );
190 }
191
192 void wxGenericTreeItem::Reset()
193 {
194 m_text.Empty();
195 m_image =
196 m_selImage = -1;
197 m_data = NULL;
198 m_x = m_y =
199 m_height = m_width = 0;
200 m_xCross =
201 m_yCross = 0;
202
203 m_level = 0;
204
205 m_children.Empty();
206 m_isCollapsed = TRUE;
207
208 m_parent = (wxGenericTreeItem *)NULL;
209 }
210
211 size_t wxGenericTreeItem::GetChildrenCount(bool recursively) const
212 {
213 size_t count = m_children.Count();
214 if ( !recursively )
215 return count;
216
217 size_t total = count;
218 for ( size_t n = 0; n < count; n++ )
219 {
220 total += m_children[n]->GetChildrenCount();
221 }
222
223 return total;
224 }
225
226 void wxGenericTreeItem::SetCross( int x, int y )
227 {
228 m_xCross = x;
229 m_yCross = y;
230 }
231
232 void wxGenericTreeItem::GetSize( int &x, int &y )
233 {
234 // FIXME what does this all mean??
235 if ( y < m_y ) y = m_y;
236 int width = m_x + m_width;
237 if (width > x) x = width;
238
239 if (IsExpanded())
240 {
241 size_t count = m_children.Count();
242 for ( size_t n = 0; n < count; n++ )
243 {
244 m_children[n]->GetSize( x, y );
245 }
246 }
247 }
248
249 wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point,
250 bool &onButton )
251 {
252 if ((point.y > m_y) && (point.y < m_y + m_height))
253 {
254 // FIXME why +5?
255 if ((point.x > m_xCross-5) && (point.x < m_xCross+5) &&
256 (point.y > m_yCross-5) && (point.y < m_yCross+5) &&
257 (IsExpanded() || HasPlus()))
258 {
259 onButton = TRUE;
260 return this;
261 }
262
263 if ((point.x > m_x) && (point.x < m_x+m_width))
264 {
265 onButton = FALSE;
266 return this;
267 }
268 }
269 else
270 {
271 if (!m_isCollapsed)
272 {
273 size_t count = m_children.Count();
274 for ( size_t n = 0; n < count; n++ )
275 {
276 wxGenericTreeItem *res = m_children[n]->HitTest( point, onButton );
277 if ( res != NULL )
278 return res;
279 }
280 }
281 }
282
283 return NULL;
284 }
285
286 // -----------------------------------------------------------------------------
287 // wxTreeCtrl implementation
288 // -----------------------------------------------------------------------------
289
290 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxScrolledWindow)
291
292 BEGIN_EVENT_TABLE(wxTreeCtrl,wxScrolledWindow)
293 EVT_PAINT (wxTreeCtrl::OnPaint)
294 EVT_MOUSE_EVENTS (wxTreeCtrl::OnMouse)
295 EVT_CHAR (wxTreeCtrl::OnChar)
296 EVT_SET_FOCUS (wxTreeCtrl::OnSetFocus)
297 EVT_KILL_FOCUS (wxTreeCtrl::OnKillFocus)
298 END_EVENT_TABLE()
299
300 // -----------------------------------------------------------------------------
301 // construction/destruction
302 // -----------------------------------------------------------------------------
303 void wxTreeCtrl::Init()
304 {
305 m_current =
306 m_anchor = (wxGenericTreeItem *) NULL;
307 m_hasFocus = FALSE;
308
309 m_xScroll = 0;
310 m_yScroll = 0;
311 m_lineHeight = 10;
312 m_indent = 15;
313
314 m_hilightBrush = new wxBrush
315 (
316 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT),
317 wxSOLID
318 );
319
320 m_imageListNormal =
321 m_imageListState = (wxImageList *) NULL;
322 }
323
324 bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
325 const wxPoint& pos, const wxSize& size,
326 long style, const wxString& name )
327 {
328 Init();
329
330 wxScrolledWindow::Create( parent, id, pos, size, style, name );
331
332 SetBackgroundColour( *wxWHITE );
333 m_dottedPen = wxPen( *wxBLACK, 0, 0 );
334
335 return TRUE;
336 }
337
338 wxTreeCtrl::~wxTreeCtrl()
339 {
340 wxDELETE( m_hilightBrush );
341 wxDELETE( m_anchor );
342 }
343
344 // -----------------------------------------------------------------------------
345 // accessors
346 // -----------------------------------------------------------------------------
347
348 size_t wxTreeCtrl::GetCount() const
349 {
350 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
351 }
352
353 void wxTreeCtrl::SetIndent(unsigned int indent)
354 {
355 m_indent = indent;
356 Refresh();
357 }
358
359 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
360 {
361 wxCHECK_MSG( item.IsOk(), 0u, "invalid tree item" );
362
363 return item.m_pItem->GetChildrenCount(recursively);
364 }
365
366 // -----------------------------------------------------------------------------
367 // functions to work with tree items
368 // -----------------------------------------------------------------------------
369
370 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
371 {
372 wxCHECK_MSG( item.IsOk(), "", "invalid tree item" );
373
374 return item.m_pItem->GetText();
375 }
376
377 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
378 {
379 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
380
381 return item.m_pItem->GetImage();
382 }
383
384 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
385 {
386 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
387
388 return item.m_pItem->GetSelectedImage();
389 }
390
391 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
392 {
393 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
394
395 return item.m_pItem->GetData();
396 }
397
398 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
399 {
400 wxCHECK_RET( item.IsOk(), "invalid tree item" );
401
402 wxClientDC dc(this);
403 item.m_pItem->SetText(text, dc);
404 }
405
406 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
407 {
408 wxCHECK_RET( item.IsOk(), "invalid tree item" );
409
410 item.m_pItem->SetImage(image);
411 }
412
413 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
414 {
415 wxCHECK_RET( item.IsOk(), "invalid tree item" );
416
417 item.m_pItem->SetSelectedImage(image);
418 }
419
420 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
421 {
422 wxCHECK_RET( item.IsOk(), "invalid tree item" );
423
424 item.m_pItem->SetData(data);
425 }
426
427 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
428 {
429 wxCHECK_RET( item.IsOk(), "invalid tree item" );
430
431 item.m_pItem->SetHasPlus(has);
432 }
433
434 // -----------------------------------------------------------------------------
435 // item status inquiries
436 // -----------------------------------------------------------------------------
437
438 bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
439 {
440 wxFAIL_MSG("not implemented");
441
442 return TRUE;
443 }
444
445 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
446 {
447 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
448
449 return !item.m_pItem->GetChildren().IsEmpty();
450 }
451
452 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
453 {
454 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
455
456 return item.m_pItem->IsExpanded();
457 }
458
459 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
460 {
461 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
462
463 return item.m_pItem->HasHilight();
464 }
465
466 // -----------------------------------------------------------------------------
467 // navigation
468 // -----------------------------------------------------------------------------
469
470 wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
471 {
472 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
473
474 return item.m_pItem->GetParent();
475 }
476
477 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
478 {
479 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
480
481 cookie = 0;
482 return GetNextChild(item, cookie);
483 }
484
485 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
486 {
487 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
488
489 wxArrayTreeItems& children = item.m_pItem->GetChildren();
490 if ( (size_t)cookie < children.Count() )
491 {
492 return item.m_pItem->GetChildren().Item(cookie++);
493 }
494 else
495 {
496 // there are no more of them
497 return NULL;
498 }
499 }
500
501 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
502 {
503 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
504
505 wxGenericTreeItem *i = item.m_pItem;
506 wxGenericTreeItem *parent = i->GetParent();
507 if ( parent == NULL )
508 {
509 // root item doesn't have any siblings
510 return NULL;
511 }
512
513 wxArrayTreeItems& siblings = parent->GetChildren();
514 int index = siblings.Index(i);
515 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
516
517 size_t n = (size_t)(index + 1);
518 return n == siblings.Count() ? (wxGenericTreeItem*)NULL : siblings[n];
519 }
520
521 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
522 {
523 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
524
525 wxGenericTreeItem *i = item.m_pItem;
526 wxGenericTreeItem *parent = i->GetParent();
527 if ( parent == NULL )
528 {
529 // root item doesn't have any siblings
530 return NULL;
531 }
532
533 wxArrayTreeItems& siblings = parent->GetChildren();
534 int index = siblings.Index(i);
535 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
536
537 return index == 0 ? (wxGenericTreeItem*)NULL : siblings[(size_t)(index - 1)];
538 }
539
540 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
541 {
542 wxFAIL_MSG("not implemented");
543
544 return NULL;
545 }
546
547 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
548 {
549 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
550
551 wxFAIL_MSG("not implemented");
552
553 return NULL;
554 }
555
556 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
557 {
558 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
559
560 wxFAIL_MSG("not implemented");
561
562 return NULL;
563 }
564
565 // -----------------------------------------------------------------------------
566 // operations
567 // -----------------------------------------------------------------------------
568
569 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
570 size_t previous,
571 const wxString& text,
572 int image, int selImage,
573 wxTreeItemData *data)
574 {
575 wxGenericTreeItem *parent = parentId.m_pItem;
576 if ( !parent )
577 {
578 // should we give a warning here?
579 return AddRoot(text, image, selImage, data);
580 }
581
582 wxClientDC dc(this);
583 wxGenericTreeItem *item = new wxGenericTreeItem(parent,
584 text, dc,
585 image, selImage,
586 data);
587
588 if ( data != NULL )
589 {
590 data->m_pItem = item;
591 }
592
593 parent->Insert( item, previous );
594
595 CalculatePositions();
596
597 int cw, ch;
598 GetClientSize( &cw, &ch );
599
600 PrepareDC( dc );
601
602 wxRectangle rect;
603 rect.x = dc.LogicalToDeviceX( 0 );
604 rect.y = 0;
605 rect.width = 10000; // @@@ not very elegant...
606 rect.height = ch;
607
608 if ( previous != 0 )
609 {
610 rect.y = dc.LogicalToDeviceY( parent->GetChildren().Item(previous)->GetY() );
611 }
612 else // it's the 1st child
613 {
614 rect.y = dc.LogicalToDeviceY( parent->GetY() );
615 }
616
617 AdjustMyScrollbars();
618
619 if ( rect.height > 0 )
620 Refresh( FALSE, &rect );
621
622 return item;
623 }
624
625 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
626 int image, int selImage,
627 wxTreeItemData *data)
628 {
629 wxCHECK_MSG( !m_anchor, NULL, "tree can have only one root" );
630
631 wxClientDC dc(this);
632 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
633 image, selImage, data);
634 if ( data != NULL )
635 {
636 data->m_pItem = m_anchor;
637 }
638
639 AdjustMyScrollbars();
640 Refresh();
641
642 return m_anchor;
643 }
644
645 wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
646 const wxString& text,
647 int image, int selImage,
648 wxTreeItemData *data)
649 {
650 return DoInsertItem(parent, 0u, text, image, selImage, data);
651 }
652
653 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
654 const wxTreeItemId& idPrevious,
655 const wxString& text,
656 int image, int selImage,
657 wxTreeItemData *data)
658 {
659 wxGenericTreeItem *parent = parentId.m_pItem;
660 if ( !parent )
661 {
662 // should we give a warning here?
663 return AddRoot(text, image, selImage, data);
664 }
665
666 int index = parent->GetChildren().Index(idPrevious.m_pItem);
667 wxASSERT_MSG( index != NOT_FOUND,
668 "previous item in wxTreeCtrl::InsertItem() is not a sibling" );
669 return DoInsertItem(parentId, (size_t)index, text, image, selImage, data);
670 }
671
672 wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
673 const wxString& text,
674 int image, int selImage,
675 wxTreeItemData *data)
676 {
677 wxGenericTreeItem *parent = parentId.m_pItem;
678 if ( !parent )
679 {
680 // should we give a warning here?
681 return AddRoot(text, image, selImage, data);
682 }
683
684 return DoInsertItem(parent, parent->GetChildren().Count(), text,
685 image, selImage, data);
686 }
687
688 void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
689 {
690 wxGenericTreeItem *item = itemId.m_pItem;
691 wxGenericTreeItem *parent = item->GetParent();
692
693 if ( parent )
694 {
695 parent->GetChildren().Remove(item);
696 }
697
698 delete item;
699
700 Refresh();
701 }
702
703 void wxTreeCtrl::DeleteAllItems()
704 {
705 if ( m_anchor )
706 {
707 delete m_anchor;
708 m_anchor = NULL;
709
710 Refresh();
711 }
712 }
713
714 void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
715 {
716 wxGenericTreeItem *item = itemId.m_pItem;
717
718 if ( item->IsExpanded() )
719 return;
720
721 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
722 event.m_item = item;
723 event.SetEventObject( this );
724 if ( ProcessEvent( event ) && event.m_code )
725 {
726 // cancelled by program
727 return;
728 }
729
730 item->Expand();
731
732 RefreshSubtree(item);
733
734 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
735 ProcessEvent( event );
736 }
737
738 void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
739 {
740 wxGenericTreeItem *item = itemId.m_pItem;
741
742 if ( !item->IsExpanded() )
743 return;
744
745 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
746 event.m_item = item;
747 event.SetEventObject( this );
748 if ( ProcessEvent( event ) && event.m_code )
749 {
750 // cancelled by program
751 return;
752 }
753
754 item->Collapse();
755
756 wxArrayTreeItems& children = item->GetChildren();
757 size_t count = children.Count();
758 for ( size_t n = 0; n < count; n++ )
759 {
760 Collapse(children[n]);
761 }
762
763 CalculatePositions();
764
765 RefreshSubtree(item);
766
767 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
768 ProcessEvent( event );
769 }
770
771 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
772 {
773 Collapse(item);
774 Delete(item);
775 }
776
777 void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
778 {
779 wxGenericTreeItem *item = itemId.m_pItem;
780
781 if ( item->IsExpanded() )
782 Collapse(itemId);
783 else
784 Expand(itemId);
785 }
786
787 void wxTreeCtrl::Unselect()
788 {
789 if ( m_current )
790 {
791 m_current->SetHilight( FALSE );
792 RefreshLine( m_current );
793 }
794 }
795
796 void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
797 {
798 wxGenericTreeItem *item = itemId.m_pItem;
799
800 if ( m_current != item )
801 {
802 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
803 event.m_item = item;
804 event.m_itemOld = m_current;
805 event.SetEventObject( this );
806 if ( ProcessEvent( event ) && event.WasVetoed() )
807 return;
808
809 if ( m_current )
810 {
811 m_current->SetHilight( FALSE );
812 RefreshLine( m_current );
813 }
814
815 m_current = item;
816 m_current->SetHilight( TRUE );
817 RefreshLine( m_current );
818
819 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
820 ProcessEvent( event );
821 }
822 }
823
824 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& WXUNUSED(item))
825 {
826 wxFAIL_MSG("not implemented");
827 }
828
829 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
830 {
831 wxFAIL_MSG("not implemented");
832 }
833
834 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
835 wxClassInfo* WXUNUSED(textCtrlClass) )
836 {
837 wxFAIL_MSG("not implemented");
838
839 return NULL;
840 }
841
842 wxTextCtrl *wxTreeCtrl::GetEditControl() const
843 {
844 wxFAIL_MSG("not implemented");
845
846 return NULL;
847 }
848
849 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
850 {
851 wxFAIL_MSG("not implemented");
852 }
853
854 void wxTreeCtrl::SortChildren( const wxTreeItemId& WXUNUSED(item),
855 wxTreeItemCmpFunc *WXUNUSED(cmpFunction))
856 {
857 wxFAIL_MSG("not implemented");
858 }
859
860 // -----------------------------------------------------------------------------
861 // images are not currently supported, but we still provide stubs for these
862 // functions
863 // -----------------------------------------------------------------------------
864 wxImageList *wxTreeCtrl::GetImageList() const
865 {
866 return m_imageListNormal;
867 }
868
869 wxImageList *wxTreeCtrl::GetStateImageList() const
870 {
871 return m_imageListState;
872 }
873
874 void wxTreeCtrl::SetImageList(wxImageList *imageList)
875 {
876 m_imageListNormal = imageList;
877 }
878
879 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
880 {
881 m_imageListState = imageList;
882 }
883
884 // -----------------------------------------------------------------------------
885 // helpers
886 // -----------------------------------------------------------------------------
887 void wxTreeCtrl::AdjustMyScrollbars()
888 {
889 if (m_anchor)
890 {
891 int x = 0;
892 int y = 0;
893 m_anchor->GetSize( x, y );
894 y += 2*m_lineHeight;
895 int x_pos = GetScrollPos( wxHORIZONTAL );
896 int y_pos = GetScrollPos( wxVERTICAL );
897 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
898 }
899 else
900 {
901 SetScrollbars( 0, 0, 0, 0 );
902 }
903 }
904
905 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
906 {
907 int horizX = level*m_indent;
908
909 item->SetX( horizX+33 );
910 item->SetY( y-m_lineHeight/3 );
911 item->SetHeight( m_lineHeight );
912
913 item->SetCross( horizX+15, y );
914
915 int oldY = y;
916
917 int exposed_x = dc.LogicalToDeviceX( 0 );
918 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
919
920 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
921 {
922 int startX = horizX;
923 int endX = horizX + 10;
924
925 if (!item->HasChildren()) endX += 20;
926
927 dc.DrawLine( startX, y, endX, y );
928
929 if (item->HasPlus())
930 {
931 dc.DrawLine( horizX+20, y, horizX+30, y );
932 dc.SetPen( *wxGREY_PEN );
933 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
934 dc.SetPen( *wxBLACK_PEN );
935 dc.DrawLine( horizX+13, y, horizX+18, y );
936
937 if (!item->IsExpanded())
938 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
939 }
940
941 if (item->HasHilight())
942 {
943 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
944 dc.SetBrush( *m_hilightBrush );
945
946 long text_w = 0;
947 long text_h = 0;
948 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
949
950 int image_h = 0;
951 int image_w = 0;
952 if (item->GetImage() != -1)
953 {
954 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
955 image_w += 4;
956 }
957
958 if (m_hasFocus)
959 dc.SetPen( *wxBLACK_PEN );
960 else
961 dc.SetPen( *wxTRANSPARENT_PEN );
962
963 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
964
965 if (item->GetImage() != -1)
966 {
967 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
968 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
969 dc.DestroyClippingRegion();
970 }
971 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
972
973 dc.SetPen( *wxBLACK_PEN );
974 dc.SetTextForeground( *wxBLACK );
975 dc.SetBrush( *wxWHITE_BRUSH );
976 }
977 else
978 {
979 dc.SetBrush( *wxWHITE_BRUSH );
980 dc.SetPen( *wxTRANSPARENT_PEN );
981
982 long text_w = 0;
983 long text_h = 0;
984 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
985
986 int image_h = 0;
987 int image_w = 0;
988 if (item->GetImage() != -1)
989 {
990 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
991 image_w += 4;
992 }
993
994 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
995
996 if (item->GetImage() != -1)
997 {
998 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
999 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
1000 dc.DestroyClippingRegion();
1001 }
1002
1003 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
1004 dc.SetPen( *wxBLACK_PEN );
1005 }
1006 }
1007
1008 if ( !item->IsExpanded() )
1009 return;
1010
1011 int semiOldY = y;
1012
1013 wxArrayTreeItems& children = item->GetChildren();
1014 size_t count = children.Count();
1015 for ( size_t n = 0; n < count; n++ )
1016 {
1017 y += m_lineHeight;
1018 semiOldY = y;
1019
1020 PaintLevel( children[n], dc, level+1, y );
1021 }
1022
1023 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1024 }
1025
1026 // -----------------------------------------------------------------------------
1027 // wxWindows callbacks
1028 // -----------------------------------------------------------------------------
1029
1030 void wxTreeCtrl::OnPaint( const wxPaintEvent &WXUNUSED(event) )
1031 {
1032 if ( !m_anchor )
1033 return;
1034
1035 wxPaintDC dc(this);
1036 PrepareDC( dc );
1037
1038 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1039
1040 dc.SetPen( m_dottedPen );
1041 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1042
1043 int y = m_lineHeight / 2 + 2;
1044 PaintLevel( m_anchor, dc, 0, y );
1045 }
1046
1047 void wxTreeCtrl::OnSetFocus( const wxFocusEvent &WXUNUSED(event) )
1048 {
1049 m_hasFocus = TRUE;
1050 if ( m_current )
1051 RefreshLine( m_current );
1052 }
1053
1054 void wxTreeCtrl::OnKillFocus( const wxFocusEvent &WXUNUSED(event) )
1055 {
1056 m_hasFocus = FALSE;
1057 if ( m_current )
1058 RefreshLine( m_current );
1059 }
1060
1061 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1062 {
1063 // TODO process '+', '-' (expand/collapse branch) and cursor keys
1064 event.Skip();
1065 }
1066
1067 void wxTreeCtrl::OnMouse( const wxMouseEvent &event )
1068 {
1069 if ( !(event.LeftDown() || event.LeftDClick()) )
1070 return;
1071
1072 if ( !m_anchor )
1073 return;
1074
1075 wxClientDC dc(this);
1076 PrepareDC(dc);
1077 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1078 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1079
1080 bool onButton = FALSE;
1081 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1082 if ( item == NULL )
1083 return;
1084
1085 SelectItem(item);
1086
1087 if ( event.LeftDClick() )
1088 {
1089 wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1090 event.m_item = item;
1091 event.m_code = 0;
1092 event.SetEventObject( this );
1093 ProcessEvent( event );
1094 }
1095
1096 if ( onButton )
1097 {
1098 Toggle( item );
1099 }
1100 }
1101
1102 // -----------------------------------------------------------------------------
1103 // -----------------------------------------------------------------------------
1104 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1105 wxDC &dc,
1106 int level,
1107 int &y )
1108 {
1109 int horizX = level*m_indent;
1110
1111 item->SetX( horizX+33 );
1112 item->SetY( y-m_lineHeight/3-2 );
1113 item->SetHeight( m_lineHeight );
1114
1115 if ( item->IsExpanded() )
1116 return;
1117
1118 wxArrayTreeItems& children = item->GetChildren();
1119 size_t count = children.Count();
1120 for ( size_t n = 0; n < count; n++ )
1121 {
1122 y += m_lineHeight;
1123 CalculateLevel( children[n], dc, level+1, y );
1124 }
1125 }
1126
1127 void wxTreeCtrl::CalculatePositions()
1128 {
1129 if ( !m_anchor )
1130 return;
1131
1132 wxClientDC dc(this);
1133 PrepareDC( dc );
1134
1135 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1136
1137 dc.SetPen( m_dottedPen );
1138 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1139
1140 int y = m_lineHeight / 2 + 2;
1141 CalculateLevel( m_anchor, dc, 0, y );
1142 }
1143
1144 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1145 {
1146 wxClientDC dc(this);
1147 PrepareDC(dc);
1148
1149 int cw = 0;
1150 int ch = 0;
1151 GetClientSize( &cw, &ch );
1152
1153 wxRect rect;
1154 rect.x = dc.LogicalToDeviceX( 0 );
1155 rect.width = cw;
1156 rect.y = dc.LogicalToDeviceY( item->GetY() );
1157 rect.height = ch;
1158
1159 Refresh( TRUE, &rect );
1160
1161 AdjustMyScrollbars();
1162 }
1163
1164 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1165 {
1166 wxClientDC dc(this);
1167 PrepareDC( dc );
1168
1169 wxRect rect;
1170 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1171 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1172 rect.width = 1000;
1173 rect.height = dc.GetCharHeight() + 6;
1174
1175 Refresh( TRUE, &rect );
1176 }
1177