]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
1. GetNextChild() bug fixed in generic version
[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 inline ~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
692 delete item;
693
694 Refresh();
695 }
696
697 void wxTreeCtrl::DeleteAllItems()
698 {
699 if ( m_anchor )
700 {
701 delete m_anchor;
702 m_anchor = NULL;
703
704 Refresh();
705 }
706 }
707
708 void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
709 {
710 wxGenericTreeItem *item = itemId.m_pItem;
711
712 if ( item->IsExpanded() )
713 return;
714
715 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
716 event.m_item = item;
717 event.SetEventObject( this );
718 if ( ProcessEvent( event ) && event.m_code )
719 {
720 // cancelled by program
721 return;
722 }
723
724 item->Expand();
725
726 RefreshSubtree(item);
727
728 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
729 ProcessEvent( event );
730 }
731
732 void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
733 {
734 wxGenericTreeItem *item = itemId.m_pItem;
735
736 if ( !item->IsExpanded() )
737 return;
738
739 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
740 event.m_item = item;
741 event.SetEventObject( this );
742 if ( ProcessEvent( event ) && event.m_code )
743 {
744 // cancelled by program
745 return;
746 }
747
748 item->Collapse();
749
750 wxArrayTreeItems& children = item->GetChildren();
751 size_t count = children.Count();
752 for ( size_t n = 0; n < count; n++ )
753 {
754 Collapse(children[n]);
755 }
756
757 CalculatePositions();
758
759 RefreshSubtree(item);
760
761 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
762 ProcessEvent( event );
763 }
764
765 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
766 {
767 Collapse(item);
768 Delete(item);
769 }
770
771 void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
772 {
773 wxGenericTreeItem *item = itemId.m_pItem;
774
775 if ( item->IsExpanded() )
776 Collapse(itemId);
777 else
778 Expand(itemId);
779 }
780
781 void wxTreeCtrl::Unselect()
782 {
783 if ( m_current )
784 {
785 m_current->SetHilight( FALSE );
786 RefreshLine( m_current );
787 }
788 }
789
790 void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
791 {
792 wxGenericTreeItem *item = itemId.m_pItem;
793
794 if ( m_current != item )
795 {
796 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
797 event.m_item = item;
798 event.m_itemOld = m_current;
799 event.SetEventObject( this );
800 if ( ProcessEvent( event ) && event.WasVetoed() )
801 return;
802
803 if ( m_current )
804 {
805 m_current->SetHilight( FALSE );
806 RefreshLine( m_current );
807 }
808
809 m_current = item;
810 m_current->SetHilight( TRUE );
811 RefreshLine( m_current );
812
813 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
814 ProcessEvent( event );
815 }
816 }
817
818 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& WXUNUSED(item))
819 {
820 wxFAIL_MSG("not implemented");
821 }
822
823 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
824 {
825 wxFAIL_MSG("not implemented");
826 }
827
828 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
829 wxClassInfo* WXUNUSED(textCtrlClass) )
830 {
831 wxFAIL_MSG("not implemented");
832
833 return NULL;
834 }
835
836 wxTextCtrl *wxTreeCtrl::GetEditControl() const
837 {
838 wxFAIL_MSG("not implemented");
839
840 return NULL;
841 }
842
843 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
844 {
845 wxFAIL_MSG("not implemented");
846 }
847
848 void wxTreeCtrl::SortChildren( const wxTreeItemId& WXUNUSED(item),
849 wxTreeItemCmpFunc *WXUNUSED(cmpFunction))
850 {
851 wxFAIL_MSG("not implemented");
852 }
853
854 // -----------------------------------------------------------------------------
855 // images are not currently supported, but we still provide stubs for these
856 // functions
857 // -----------------------------------------------------------------------------
858 wxImageList *wxTreeCtrl::GetImageList() const
859 {
860 return m_imageListNormal;
861 }
862
863 wxImageList *wxTreeCtrl::GetStateImageList() const
864 {
865 return m_imageListState;
866 }
867
868 void wxTreeCtrl::SetImageList(wxImageList *imageList)
869 {
870 m_imageListNormal = imageList;
871 }
872
873 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
874 {
875 m_imageListState = imageList;
876 }
877
878 // -----------------------------------------------------------------------------
879 // helpers
880 // -----------------------------------------------------------------------------
881 void wxTreeCtrl::AdjustMyScrollbars()
882 {
883 if (m_anchor)
884 {
885 int x = 0;
886 int y = 0;
887 m_anchor->GetSize( x, y );
888 y += 2*m_lineHeight;
889 int x_pos = GetScrollPos( wxHORIZONTAL );
890 int y_pos = GetScrollPos( wxVERTICAL );
891 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
892 }
893 else
894 {
895 SetScrollbars( 0, 0, 0, 0 );
896 }
897 }
898
899 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
900 {
901 int horizX = level*m_indent;
902
903 item->SetX( horizX+33 );
904 item->SetY( y-m_lineHeight/3 );
905 item->SetHeight( m_lineHeight );
906
907 item->SetCross( horizX+15, y );
908
909 int oldY = y;
910
911 int exposed_x = dc.LogicalToDeviceX( 0 );
912 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
913
914 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
915 {
916 int startX = horizX;
917 int endX = horizX + 10;
918
919 if (!item->HasChildren()) endX += 20;
920
921 dc.DrawLine( startX, y, endX, y );
922
923 if (item->HasPlus())
924 {
925 dc.DrawLine( horizX+20, y, horizX+30, y );
926 dc.SetPen( *wxGREY_PEN );
927 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
928 dc.SetPen( *wxBLACK_PEN );
929 dc.DrawLine( horizX+13, y, horizX+18, y );
930
931 if (!item->IsExpanded())
932 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
933 }
934
935 if (item->HasHilight())
936 {
937 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
938 dc.SetBrush( *m_hilightBrush );
939
940 long text_w = 0;
941 long text_h = 0;
942 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
943
944 int image_h = 0;
945 int image_w = 0;
946 if (item->GetImage() != -1)
947 {
948 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
949 image_w += 4;
950 }
951
952 if (m_hasFocus)
953 dc.SetPen( *wxBLACK_PEN );
954 else
955 dc.SetPen( *wxTRANSPARENT_PEN );
956
957 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
958
959 if (item->GetImage() != -1)
960 {
961 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
962 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
963 dc.DestroyClippingRegion();
964 }
965 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
966
967 dc.SetPen( *wxBLACK_PEN );
968 dc.SetTextForeground( *wxBLACK );
969 dc.SetBrush( *wxWHITE_BRUSH );
970 }
971 else
972 {
973 dc.SetBrush( *wxWHITE_BRUSH );
974 dc.SetPen( *wxTRANSPARENT_PEN );
975
976 long text_w = 0;
977 long text_h = 0;
978 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
979
980 int image_h = 0;
981 int image_w = 0;
982 if (item->GetImage() != -1)
983 {
984 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
985 image_w += 4;
986 }
987
988 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
989
990 if (item->GetImage() != -1)
991 {
992 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
993 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
994 dc.DestroyClippingRegion();
995 }
996
997 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
998 dc.SetPen( *wxBLACK_PEN );
999 }
1000 }
1001
1002 if ( !item->IsExpanded() )
1003 return;
1004
1005 int semiOldY = y;
1006
1007 wxArrayTreeItems& children = item->GetChildren();
1008 size_t count = children.Count();
1009 for ( size_t n = 0; n < count; n++ )
1010 {
1011 y += m_lineHeight;
1012 semiOldY = y;
1013
1014 PaintLevel( children[n], dc, level+1, y );
1015 }
1016
1017 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1018 }
1019
1020 // -----------------------------------------------------------------------------
1021 // wxWindows callbacks
1022 // -----------------------------------------------------------------------------
1023
1024 void wxTreeCtrl::OnPaint( const wxPaintEvent &WXUNUSED(event) )
1025 {
1026 if ( !m_anchor )
1027 return;
1028
1029 wxPaintDC dc(this);
1030 PrepareDC( dc );
1031
1032 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1033
1034 dc.SetPen( m_dottedPen );
1035 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1036
1037 int y = m_lineHeight / 2 + 2;
1038 PaintLevel( m_anchor, dc, 0, y );
1039 }
1040
1041 void wxTreeCtrl::OnSetFocus( const wxFocusEvent &WXUNUSED(event) )
1042 {
1043 m_hasFocus = TRUE;
1044 if ( m_current )
1045 RefreshLine( m_current );
1046 }
1047
1048 void wxTreeCtrl::OnKillFocus( const wxFocusEvent &WXUNUSED(event) )
1049 {
1050 m_hasFocus = FALSE;
1051 if ( m_current )
1052 RefreshLine( m_current );
1053 }
1054
1055 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1056 {
1057 // TODO process '+', '-' (expand/collapse branch) and cursor keys
1058 event.Skip();
1059 }
1060
1061 void wxTreeCtrl::OnMouse( const wxMouseEvent &event )
1062 {
1063 if ( !(event.LeftDown() || event.LeftDClick()) )
1064 return;
1065
1066 if ( !m_anchor )
1067 return;
1068
1069 wxClientDC dc(this);
1070 PrepareDC(dc);
1071 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1072 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1073
1074 bool onButton = FALSE;
1075 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1076 if ( item == NULL )
1077 return;
1078
1079 SelectItem(item);
1080
1081 if ( event.LeftDClick() )
1082 {
1083 wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1084 event.m_item = item;
1085 event.m_code = 0;
1086 event.SetEventObject( this );
1087 ProcessEvent( event );
1088 }
1089
1090 if ( onButton )
1091 {
1092 Toggle( item );
1093 }
1094 }
1095
1096 // -----------------------------------------------------------------------------
1097 // -----------------------------------------------------------------------------
1098 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1099 wxDC &dc,
1100 int level,
1101 int &y )
1102 {
1103 int horizX = level*m_indent;
1104
1105 item->SetX( horizX+33 );
1106 item->SetY( y-m_lineHeight/3-2 );
1107 item->SetHeight( m_lineHeight );
1108
1109 if ( item->IsExpanded() )
1110 return;
1111
1112 wxArrayTreeItems& children = item->GetChildren();
1113 size_t count = children.Count();
1114 for ( size_t n = 0; n < count; n++ )
1115 {
1116 y += m_lineHeight;
1117 CalculateLevel( children[n], dc, level+1, y );
1118 }
1119 }
1120
1121 void wxTreeCtrl::CalculatePositions()
1122 {
1123 if ( !m_anchor )
1124 return;
1125
1126 wxClientDC dc(this);
1127 PrepareDC( dc );
1128
1129 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1130
1131 dc.SetPen( m_dottedPen );
1132 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1133
1134 int y = m_lineHeight / 2 + 2;
1135 CalculateLevel( m_anchor, dc, 0, y );
1136 }
1137
1138 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1139 {
1140 wxClientDC dc(this);
1141 PrepareDC(dc);
1142
1143 int cw = 0;
1144 int ch = 0;
1145 GetClientSize( &cw, &ch );
1146
1147 wxRect rect;
1148 rect.x = dc.LogicalToDeviceX( 0 );
1149 rect.width = cw;
1150 rect.y = dc.LogicalToDeviceY( item->GetY() );
1151 rect.height = ch;
1152
1153 Refresh( TRUE, &rect );
1154
1155 AdjustMyScrollbars();
1156 }
1157
1158 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1159 {
1160 wxClientDC dc(this);
1161 PrepareDC( dc );
1162
1163 wxRect rect;
1164 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1165 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1166 rect.width = 1000;
1167 rect.height = dc.GetCharHeight() + 6;
1168
1169 Refresh( TRUE, &rect );
1170 }
1171