]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
aa767db434e289d07db9500cfb473aa32f6eaf42
[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 if ( y < m_y ) y = m_y;
235 int width = m_x + m_width;
236 if (width > x) x = width;
237
238 if (IsExpanded())
239 {
240 size_t count = m_children.Count();
241 for ( size_t n = 0; n < count; n++ )
242 {
243 m_children[n]->GetSize( x, y );
244 }
245 }
246 }
247
248 wxGenericTreeItem *wxGenericTreeItem::HitTest( const wxPoint& point,
249 bool &onButton )
250 {
251 if ((point.y > m_y) && (point.y < m_y + m_height))
252 {
253 // FIXME why +5?
254 if ((point.x > m_xCross-5) && (point.x < m_xCross+5) &&
255 (point.y > m_yCross-5) && (point.y < m_yCross+5) &&
256 (IsExpanded() || HasPlus()))
257 {
258 onButton = TRUE;
259 return this;
260 }
261
262 if ((point.x > m_x) && (point.x < m_x+m_width))
263 {
264 onButton = FALSE;
265 return this;
266 }
267 }
268 else
269 {
270 if (!m_isCollapsed)
271 {
272 size_t count = m_children.Count();
273 for ( size_t n = 0; n < count; n++ )
274 {
275 wxGenericTreeItem *res = m_children[n]->HitTest( point, onButton );
276 if ( res != NULL )
277 return res;
278 }
279 }
280 }
281
282 return NULL;
283 }
284
285 // -----------------------------------------------------------------------------
286 // wxTreeCtrl implementation
287 // -----------------------------------------------------------------------------
288
289 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxScrolledWindow)
290
291 BEGIN_EVENT_TABLE(wxTreeCtrl,wxScrolledWindow)
292 EVT_PAINT (wxTreeCtrl::OnPaint)
293 EVT_MOUSE_EVENTS (wxTreeCtrl::OnMouse)
294 EVT_CHAR (wxTreeCtrl::OnChar)
295 EVT_SET_FOCUS (wxTreeCtrl::OnSetFocus)
296 EVT_KILL_FOCUS (wxTreeCtrl::OnKillFocus)
297 EVT_IDLE (wxTreeCtrl::OnIdle)
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 m_dirty = FALSE;
309
310 m_xScroll = 0;
311 m_yScroll = 0;
312 m_lineHeight = 10;
313 m_indent = 15;
314
315 m_hilightBrush = new wxBrush
316 (
317 wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT),
318 wxSOLID
319 );
320
321 m_imageListNormal =
322 m_imageListState = (wxImageList *) NULL;
323 }
324
325 bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
326 const wxPoint& pos, const wxSize& size,
327 long style, const wxString& name )
328 {
329 Init();
330
331 wxScrolledWindow::Create( parent, id, pos, size, style, name );
332
333 SetBackgroundColour( *wxWHITE );
334 m_dottedPen = wxPen( *wxBLACK, 0, 0 );
335
336 return TRUE;
337 }
338
339 wxTreeCtrl::~wxTreeCtrl()
340 {
341 wxDELETE( m_hilightBrush );
342 wxDELETE( m_anchor );
343 }
344
345 // -----------------------------------------------------------------------------
346 // accessors
347 // -----------------------------------------------------------------------------
348
349 size_t wxTreeCtrl::GetCount() const
350 {
351 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
352 }
353
354 void wxTreeCtrl::SetIndent(unsigned int indent)
355 {
356 m_indent = indent;
357 Refresh();
358 }
359
360 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
361 {
362 wxCHECK_MSG( item.IsOk(), 0u, "invalid tree item" );
363
364 return item.m_pItem->GetChildrenCount(recursively);
365 }
366
367 // -----------------------------------------------------------------------------
368 // functions to work with tree items
369 // -----------------------------------------------------------------------------
370
371 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
372 {
373 wxCHECK_MSG( item.IsOk(), "", "invalid tree item" );
374
375 return item.m_pItem->GetText();
376 }
377
378 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
379 {
380 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
381
382 return item.m_pItem->GetImage();
383 }
384
385 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
386 {
387 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
388
389 return item.m_pItem->GetSelectedImage();
390 }
391
392 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
393 {
394 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
395
396 return item.m_pItem->GetData();
397 }
398
399 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
400 {
401 wxCHECK_RET( item.IsOk(), "invalid tree item" );
402
403 wxClientDC dc(this);
404 item.m_pItem->SetText(text, dc);
405 }
406
407 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
408 {
409 wxCHECK_RET( item.IsOk(), "invalid tree item" );
410
411 item.m_pItem->SetImage(image);
412 }
413
414 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
415 {
416 wxCHECK_RET( item.IsOk(), "invalid tree item" );
417
418 item.m_pItem->SetSelectedImage(image);
419 }
420
421 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
422 {
423 wxCHECK_RET( item.IsOk(), "invalid tree item" );
424
425 item.m_pItem->SetData(data);
426 }
427
428 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
429 {
430 wxCHECK_RET( item.IsOk(), "invalid tree item" );
431
432 item.m_pItem->SetHasPlus(has);
433 }
434
435 // -----------------------------------------------------------------------------
436 // item status inquiries
437 // -----------------------------------------------------------------------------
438
439 bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
440 {
441 wxFAIL_MSG("not implemented");
442
443 return TRUE;
444 }
445
446 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
447 {
448 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
449
450 return !item.m_pItem->GetChildren().IsEmpty();
451 }
452
453 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
454 {
455 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
456
457 return item.m_pItem->IsExpanded();
458 }
459
460 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
461 {
462 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
463
464 return item.m_pItem->HasHilight();
465 }
466
467 // -----------------------------------------------------------------------------
468 // navigation
469 // -----------------------------------------------------------------------------
470
471 wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
472 {
473 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
474
475 return item.m_pItem->GetParent();
476 }
477
478 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
479 {
480 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
481
482 cookie = 0;
483 return GetNextChild(item, cookie);
484 }
485
486 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
487 {
488 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
489
490 wxArrayTreeItems& children = item.m_pItem->GetChildren();
491 if ( (size_t)cookie < children.Count() )
492 {
493 return item.m_pItem->GetChildren().Item(cookie++);
494 }
495 else
496 {
497 // there are no more of them
498 return NULL;
499 }
500 }
501
502 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
503 {
504 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
505
506 wxGenericTreeItem *i = item.m_pItem;
507 wxGenericTreeItem *parent = i->GetParent();
508 if ( parent == NULL )
509 {
510 // root item doesn't have any siblings
511 return NULL;
512 }
513
514 wxArrayTreeItems& siblings = parent->GetChildren();
515 int index = siblings.Index(i);
516 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
517
518 size_t n = (size_t)(index + 1);
519 return n == siblings.Count() ? (wxGenericTreeItem*)NULL : siblings[n];
520 }
521
522 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
523 {
524 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
525
526 wxGenericTreeItem *i = item.m_pItem;
527 wxGenericTreeItem *parent = i->GetParent();
528 if ( parent == NULL )
529 {
530 // root item doesn't have any siblings
531 return NULL;
532 }
533
534 wxArrayTreeItems& siblings = parent->GetChildren();
535 int index = siblings.Index(i);
536 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
537
538 return index == 0 ? (wxGenericTreeItem*)NULL : siblings[(size_t)(index - 1)];
539 }
540
541 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
542 {
543 wxFAIL_MSG("not implemented");
544
545 return NULL;
546 }
547
548 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
549 {
550 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
551
552 wxFAIL_MSG("not implemented");
553
554 return NULL;
555 }
556
557 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
558 {
559 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
560
561 wxFAIL_MSG("not implemented");
562
563 return NULL;
564 }
565
566 // -----------------------------------------------------------------------------
567 // operations
568 // -----------------------------------------------------------------------------
569
570 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
571 size_t previous,
572 const wxString& text,
573 int image, int selImage,
574 wxTreeItemData *data)
575 {
576 wxGenericTreeItem *parent = parentId.m_pItem;
577 if ( !parent )
578 {
579 // should we give a warning here?
580 return AddRoot(text, image, selImage, data);
581 }
582
583 wxClientDC dc(this);
584 wxGenericTreeItem *item = new wxGenericTreeItem(parent,
585 text, dc,
586 image, selImage,
587 data);
588
589 if ( data != NULL )
590 {
591 data->m_pItem = item;
592 }
593
594 parent->Insert( item, previous );
595
596 m_dirty = TRUE;
597
598 return item;
599 }
600
601 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
602 int image, int selImage,
603 wxTreeItemData *data)
604 {
605 wxCHECK_MSG( !m_anchor, NULL, "tree can have only one root" );
606
607 wxClientDC dc(this);
608 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
609 image, selImage, data);
610 if ( data != NULL )
611 {
612 data->m_pItem = m_anchor;
613 }
614
615 AdjustMyScrollbars();
616 Refresh();
617
618 return m_anchor;
619 }
620
621 wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
622 const wxString& text,
623 int image, int selImage,
624 wxTreeItemData *data)
625 {
626 return DoInsertItem(parent, 0u, text, image, selImage, data);
627 }
628
629 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
630 const wxTreeItemId& idPrevious,
631 const wxString& text,
632 int image, int selImage,
633 wxTreeItemData *data)
634 {
635 wxGenericTreeItem *parent = parentId.m_pItem;
636 if ( !parent )
637 {
638 // should we give a warning here?
639 return AddRoot(text, image, selImage, data);
640 }
641
642 int index = parent->GetChildren().Index(idPrevious.m_pItem);
643 wxASSERT_MSG( index != NOT_FOUND,
644 "previous item in wxTreeCtrl::InsertItem() is not a sibling" );
645 return DoInsertItem(parentId, (size_t)index, text, image, selImage, data);
646 }
647
648 wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
649 const wxString& text,
650 int image, int selImage,
651 wxTreeItemData *data)
652 {
653 wxGenericTreeItem *parent = parentId.m_pItem;
654 if ( !parent )
655 {
656 // should we give a warning here?
657 return AddRoot(text, image, selImage, data);
658 }
659
660 return DoInsertItem(parent, parent->GetChildren().Count(), text,
661 image, selImage, data);
662 }
663
664 void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
665 {
666 wxGenericTreeItem *item = itemId.m_pItem;
667 wxGenericTreeItem *parent = item->GetParent();
668
669 if ( parent )
670 {
671 parent->GetChildren().Remove(item);
672 }
673
674 delete item;
675
676 Refresh();
677 }
678
679 void wxTreeCtrl::DeleteAllItems()
680 {
681 if ( m_anchor )
682 {
683 delete m_anchor;
684 m_anchor = NULL;
685
686 Refresh();
687 }
688 }
689
690 void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
691 {
692 wxGenericTreeItem *item = itemId.m_pItem;
693
694 if ( item->IsExpanded() )
695 return;
696
697 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
698 event.m_item = item;
699 event.SetEventObject( this );
700 if ( ProcessEvent( event ) && event.m_code )
701 {
702 // cancelled by program
703 return;
704 }
705
706 item->Expand();
707
708 RefreshSubtree(item);
709
710 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
711 ProcessEvent( event );
712 }
713
714 void wxTreeCtrl::Collapse(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_COLLAPSING, 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->Collapse();
731
732 wxArrayTreeItems& children = item->GetChildren();
733 size_t count = children.Count();
734 for ( size_t n = 0; n < count; n++ )
735 {
736 Collapse(children[n]);
737 }
738
739 CalculatePositions();
740
741 RefreshSubtree(item);
742
743 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
744 ProcessEvent( event );
745 }
746
747 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
748 {
749 Collapse(item);
750 Delete(item);
751 }
752
753 void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
754 {
755 wxGenericTreeItem *item = itemId.m_pItem;
756
757 if ( item->IsExpanded() )
758 Collapse(itemId);
759 else
760 Expand(itemId);
761 }
762
763 void wxTreeCtrl::Unselect()
764 {
765 if ( m_current )
766 {
767 m_current->SetHilight( FALSE );
768 RefreshLine( m_current );
769 }
770 }
771
772 void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
773 {
774 wxGenericTreeItem *item = itemId.m_pItem;
775
776 if ( m_current != item )
777 {
778 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
779 event.m_item = item;
780 event.m_itemOld = m_current;
781 event.SetEventObject( this );
782 if ( ProcessEvent( event ) && event.WasVetoed() )
783 return;
784
785 if ( m_current )
786 {
787 m_current->SetHilight( FALSE );
788 RefreshLine( m_current );
789 }
790
791 m_current = item;
792 m_current->SetHilight( TRUE );
793 RefreshLine( m_current );
794
795 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
796 ProcessEvent( event );
797 }
798 }
799
800 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& WXUNUSED(item))
801 {
802 wxFAIL_MSG("not implemented");
803 }
804
805 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
806 {
807 wxFAIL_MSG("not implemented");
808 }
809
810 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
811 wxClassInfo* WXUNUSED(textCtrlClass) )
812 {
813 wxFAIL_MSG("not implemented");
814
815 return NULL;
816 }
817
818 wxTextCtrl *wxTreeCtrl::GetEditControl() const
819 {
820 wxFAIL_MSG("not implemented");
821
822 return NULL;
823 }
824
825 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
826 {
827 wxFAIL_MSG("not implemented");
828 }
829
830 void wxTreeCtrl::SortChildren( const wxTreeItemId& WXUNUSED(item),
831 wxTreeItemCmpFunc *WXUNUSED(cmpFunction))
832 {
833 wxFAIL_MSG("not implemented");
834 }
835
836 wxImageList *wxTreeCtrl::GetImageList() const
837 {
838 return m_imageListNormal;
839 }
840
841 wxImageList *wxTreeCtrl::GetStateImageList() const
842 {
843 return m_imageListState;
844 }
845
846 void wxTreeCtrl::SetImageList(wxImageList *imageList)
847 {
848 m_imageListNormal = imageList;
849 }
850
851 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
852 {
853 m_imageListState = imageList;
854 }
855
856 // -----------------------------------------------------------------------------
857 // helpers
858 // -----------------------------------------------------------------------------
859 void wxTreeCtrl::AdjustMyScrollbars()
860 {
861 if (m_anchor)
862 {
863 int x = 0;
864 int y = 0;
865 m_anchor->GetSize( x, y );
866 y += 2*m_lineHeight;
867 int x_pos = GetScrollPos( wxHORIZONTAL );
868 int y_pos = GetScrollPos( wxVERTICAL );
869 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
870 }
871 else
872 {
873 SetScrollbars( 0, 0, 0, 0 );
874 }
875 }
876
877 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
878 {
879 int horizX = level*m_indent;
880
881 item->SetX( horizX+33 );
882 item->SetY( y-m_lineHeight/3 );
883 item->SetHeight( m_lineHeight );
884
885 item->SetCross( horizX+15, y );
886
887 int oldY = y;
888
889 int exposed_x = dc.LogicalToDeviceX( 0 );
890 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
891
892 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
893 {
894 int startX = horizX;
895 int endX = horizX + 10;
896
897 if (!item->HasChildren()) endX += 20;
898
899 dc.DrawLine( startX, y, endX, y );
900
901 if (item->HasPlus())
902 {
903 dc.DrawLine( horizX+20, y, horizX+30, y );
904 dc.SetPen( *wxGREY_PEN );
905 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
906 dc.SetPen( *wxBLACK_PEN );
907 dc.DrawLine( horizX+13, y, horizX+18, y );
908
909 if (!item->IsExpanded())
910 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
911 }
912
913 if (item->HasHilight())
914 {
915 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
916 dc.SetBrush( *m_hilightBrush );
917
918 long text_w = 0;
919 long text_h = 0;
920 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
921
922 int image_h = 0;
923 int image_w = 0;
924 if (item->GetImage() != -1)
925 {
926 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
927 image_w += 4;
928 }
929
930 if (m_hasFocus)
931 dc.SetPen( *wxBLACK_PEN );
932 else
933 dc.SetPen( *wxTRANSPARENT_PEN );
934
935 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
936
937 if (item->GetImage() != -1)
938 {
939 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
940 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
941 dc.DestroyClippingRegion();
942 }
943 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
944
945 dc.SetPen( *wxBLACK_PEN );
946 dc.SetTextForeground( *wxBLACK );
947 dc.SetBrush( *wxWHITE_BRUSH );
948 }
949 else
950 {
951 dc.SetBrush( *wxWHITE_BRUSH );
952 dc.SetPen( *wxTRANSPARENT_PEN );
953
954 long text_w = 0;
955 long text_h = 0;
956 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
957
958 int image_h = 0;
959 int image_w = 0;
960 if (item->GetImage() != -1)
961 {
962 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
963 image_w += 4;
964 }
965
966 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
967
968 if (item->GetImage() != -1)
969 {
970 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
971 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
972 dc.DestroyClippingRegion();
973 }
974
975 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
976 dc.SetPen( *wxBLACK_PEN );
977 }
978 }
979
980 if ( !item->IsExpanded() )
981 return;
982
983 int semiOldY = y;
984
985 wxArrayTreeItems& children = item->GetChildren();
986 size_t count = children.Count();
987 for ( size_t n = 0; n < count; n++ )
988 {
989 y += m_lineHeight;
990 semiOldY = y;
991
992 PaintLevel( children[n], dc, level+1, y );
993 }
994
995 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
996 }
997
998 // -----------------------------------------------------------------------------
999 // wxWindows callbacks
1000 // -----------------------------------------------------------------------------
1001
1002 void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1003 {
1004 if ( !m_anchor )
1005 return;
1006
1007 wxPaintDC dc(this);
1008 PrepareDC( dc );
1009
1010 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1011
1012 dc.SetPen( m_dottedPen );
1013 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1014
1015 int y = m_lineHeight / 2 + 2;
1016 PaintLevel( m_anchor, dc, 0, y );
1017 }
1018
1019 void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1020 {
1021 m_hasFocus = TRUE;
1022 if ( m_current )
1023 RefreshLine( m_current );
1024 }
1025
1026 void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1027 {
1028 m_hasFocus = FALSE;
1029 if ( m_current )
1030 RefreshLine( m_current );
1031 }
1032
1033 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1034 {
1035 // TODO process '+', '-' (expand/collapse branch) and cursor keys
1036 event.Skip();
1037 }
1038
1039 void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1040 {
1041 if ( !(event.LeftDown() || event.LeftDClick()) )
1042 return;
1043
1044 if ( !m_anchor )
1045 return;
1046
1047 wxClientDC dc(this);
1048 PrepareDC(dc);
1049 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1050 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1051
1052 bool onButton = FALSE;
1053 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1054 if ( item == NULL )
1055 return;
1056
1057 SelectItem(item);
1058
1059 if ( event.LeftDClick() )
1060 {
1061 wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1062 event.m_item = item;
1063 event.m_code = 0;
1064 event.SetEventObject( this );
1065 ProcessEvent( event );
1066 }
1067
1068 if ( onButton )
1069 {
1070 Toggle( item );
1071 }
1072 }
1073
1074 void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1075 {
1076 if (!m_dirty) return;
1077
1078 m_dirty = FALSE;
1079
1080 CalculatePositions();
1081
1082 AdjustMyScrollbars();
1083 }
1084
1085 // -----------------------------------------------------------------------------
1086 // -----------------------------------------------------------------------------
1087 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1088 wxDC &dc,
1089 int level,
1090 int &y )
1091 {
1092 int horizX = level*m_indent;
1093
1094 item->SetX( horizX+33 );
1095 item->SetY( y-m_lineHeight/3-2 );
1096 item->SetHeight( m_lineHeight );
1097
1098 if ( item->IsExpanded() )
1099 return;
1100
1101 wxArrayTreeItems& children = item->GetChildren();
1102 size_t count = children.Count();
1103 for ( size_t n = 0; n < count; n++ )
1104 {
1105 y += m_lineHeight;
1106 CalculateLevel( children[n], dc, level+1, y );
1107 }
1108 }
1109
1110 void wxTreeCtrl::CalculatePositions()
1111 {
1112 if ( !m_anchor )
1113 return;
1114
1115 wxClientDC dc(this);
1116 PrepareDC( dc );
1117
1118 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1119
1120 dc.SetPen( m_dottedPen );
1121 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1122
1123 int y = m_lineHeight / 2 + 2;
1124 CalculateLevel( m_anchor, dc, 0, y );
1125 }
1126
1127 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1128 {
1129 wxClientDC dc(this);
1130 PrepareDC(dc);
1131
1132 int cw = 0;
1133 int ch = 0;
1134 GetClientSize( &cw, &ch );
1135
1136 wxRect rect;
1137 rect.x = dc.LogicalToDeviceX( 0 );
1138 rect.width = cw;
1139 rect.y = dc.LogicalToDeviceY( item->GetY() );
1140 rect.height = ch;
1141
1142 Refresh( TRUE, &rect );
1143
1144 AdjustMyScrollbars();
1145 }
1146
1147 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1148 {
1149 wxClientDC dc(this);
1150 PrepareDC( dc );
1151
1152 wxRect rect;
1153 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1154 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1155 rect.width = 1000;
1156 rect.height = dc.GetCharHeight() + 6;
1157
1158 Refresh( TRUE, &rect );
1159 }
1160