]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
Keyboard navigatino in tree ctrl
[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|wxHSCROLL|wxVSCROLL, 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 ( GetEventHandler()->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 GetEventHandler()->ProcessEvent( event );
797 }
798 }
799
800 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
801 {
802 wxGenericTreeItem *gitem = item.m_pItem;
803
804 int item_y = gitem->GetY();
805
806 int start_x = 0;
807 int start_y = 0;
808 ViewStart( &start_x, &start_y );
809 start_y *= 10;
810
811 if (item_y < start_y+3)
812 {
813 int x = 0;
814 int y = 0;
815 m_anchor->GetSize( x, y );
816 y += 2*m_lineHeight;
817 int x_pos = GetScrollPos( wxHORIZONTAL );
818 SetScrollbars( 10, 10, x/10, y/10, x_pos, item_y/10 );
819 return;
820 }
821
822 int w = 0;
823 int h = 0;
824 GetClientSize( &w, &h );
825
826 if (item_y > start_y+h-26)
827 {
828 int x = 0;
829 int y = 0;
830 m_anchor->GetSize( x, y );
831 y += 2*m_lineHeight;
832 int x_pos = GetScrollPos( wxHORIZONTAL );
833 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-h+30)/10 );
834 return;
835 }
836 }
837
838 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
839 {
840 wxFAIL_MSG("not implemented");
841 }
842
843 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
844 wxClassInfo* WXUNUSED(textCtrlClass) )
845 {
846 wxFAIL_MSG("not implemented");
847
848 return NULL;
849 }
850
851 wxTextCtrl *wxTreeCtrl::GetEditControl() const
852 {
853 wxFAIL_MSG("not implemented");
854
855 return NULL;
856 }
857
858 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
859 {
860 wxFAIL_MSG("not implemented");
861 }
862
863 void wxTreeCtrl::SortChildren( const wxTreeItemId& WXUNUSED(item),
864 wxTreeItemCmpFunc *WXUNUSED(cmpFunction))
865 {
866 wxFAIL_MSG("not implemented");
867 }
868
869 wxImageList *wxTreeCtrl::GetImageList() const
870 {
871 return m_imageListNormal;
872 }
873
874 wxImageList *wxTreeCtrl::GetStateImageList() const
875 {
876 return m_imageListState;
877 }
878
879 void wxTreeCtrl::SetImageList(wxImageList *imageList)
880 {
881 m_imageListNormal = imageList;
882 }
883
884 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
885 {
886 m_imageListState = imageList;
887 }
888
889 // -----------------------------------------------------------------------------
890 // helpers
891 // -----------------------------------------------------------------------------
892 void wxTreeCtrl::AdjustMyScrollbars()
893 {
894 if (m_anchor)
895 {
896 int x = 0;
897 int y = 0;
898 m_anchor->GetSize( x, y );
899 y += 2*m_lineHeight;
900 int x_pos = GetScrollPos( wxHORIZONTAL );
901 int y_pos = GetScrollPos( wxVERTICAL );
902 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
903 }
904 else
905 {
906 SetScrollbars( 0, 0, 0, 0 );
907 }
908 }
909
910 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
911 {
912 int horizX = level*m_indent;
913
914 item->SetX( horizX+33 );
915 item->SetY( y-m_lineHeight/3 );
916 item->SetHeight( m_lineHeight );
917
918 item->SetCross( horizX+15, y );
919
920 int oldY = y;
921
922 int exposed_x = dc.LogicalToDeviceX( 0 );
923 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
924
925 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
926 {
927 int startX = horizX;
928 int endX = horizX + 10;
929
930 if (!item->HasChildren()) endX += 20;
931
932 dc.DrawLine( startX, y, endX, y );
933
934 if (item->HasPlus())
935 {
936 dc.DrawLine( horizX+20, y, horizX+30, y );
937 dc.SetPen( *wxGREY_PEN );
938 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
939 dc.SetPen( *wxBLACK_PEN );
940 dc.DrawLine( horizX+13, y, horizX+18, y );
941
942 if (!item->IsExpanded())
943 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
944 }
945
946 if (item->HasHilight())
947 {
948 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
949
950 dc.SetBrush( *m_hilightBrush );
951
952 long text_w = 0;
953 long text_h = 0;
954 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
955
956 int image_h = 0;
957 int image_w = 0;
958 if (item->GetImage() != -1)
959 {
960 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
961 image_w += 4;
962 }
963
964 if (m_hasFocus)
965 dc.SetPen( *wxBLACK_PEN );
966 else
967 dc.SetPen( *wxTRANSPARENT_PEN );
968
969 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
970
971 if (item->GetImage() != -1)
972 {
973 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
974 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
975 dc.DestroyClippingRegion();
976 }
977 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
978
979 dc.SetPen( *wxBLACK_PEN );
980 dc.SetTextForeground( *wxBLACK );
981 dc.SetBrush( *wxWHITE_BRUSH );
982 }
983 else
984 {
985 dc.SetBrush( *wxWHITE_BRUSH );
986 dc.SetPen( *wxTRANSPARENT_PEN );
987
988 long text_w = 0;
989 long text_h = 0;
990 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
991
992 int image_h = 0;
993 int image_w = 0;
994 if (item->GetImage() != -1)
995 {
996 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
997 image_w += 4;
998 }
999
1000 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
1001
1002 if (item->GetImage() != -1)
1003 {
1004 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1005 m_imageListNormal->Draw( item->GetImage(), dc, item->GetX(), item->GetY()-1, wxIMAGELIST_DRAW_TRANSPARENT );
1006 dc.DestroyClippingRegion();
1007 }
1008
1009 dc.DrawText( item->GetText(), image_w+item->GetX(), item->GetY() );
1010 dc.SetPen( *wxBLACK_PEN );
1011 }
1012 }
1013
1014 if ( !item->IsExpanded() )
1015 return;
1016
1017 int semiOldY = y;
1018
1019 wxArrayTreeItems& children = item->GetChildren();
1020 size_t count = children.Count();
1021 for ( size_t n = 0; n < count; n++ )
1022 {
1023 y += m_lineHeight;
1024 semiOldY = y;
1025
1026 PaintLevel( children[n], dc, level+1, y );
1027 }
1028
1029 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1030 }
1031
1032 // -----------------------------------------------------------------------------
1033 // wxWindows callbacks
1034 // -----------------------------------------------------------------------------
1035
1036 void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1037 {
1038 if ( !m_anchor )
1039 return;
1040
1041 wxPaintDC dc(this);
1042 PrepareDC( dc );
1043
1044 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1045
1046 dc.SetPen( m_dottedPen );
1047 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1048
1049 int y = m_lineHeight / 2 + 2;
1050 PaintLevel( m_anchor, dc, 0, y );
1051 }
1052
1053 void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1054 {
1055 m_hasFocus = TRUE;
1056 if ( m_current )
1057 RefreshLine( m_current );
1058 }
1059
1060 void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1061 {
1062 m_hasFocus = FALSE;
1063 if ( m_current )
1064 RefreshLine( m_current );
1065 }
1066
1067 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1068 {
1069 if (m_current == 0)
1070 {
1071 event.Skip();
1072 return;
1073 }
1074
1075 switch (event.KeyCode())
1076 {
1077 case '+':
1078 case WXK_ADD:
1079 {
1080 if (HasChildren(m_current) && !IsExpanded(m_current))
1081 {
1082 Expand(m_current);
1083 }
1084 return;
1085 }
1086 case '-':
1087 case WXK_SUBTRACT:
1088 {
1089 if (IsExpanded(m_current))
1090 {
1091 Collapse(m_current);
1092 }
1093 return;
1094 }
1095 case ' ':
1096 case WXK_RETURN:
1097 {
1098 wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1099 event.m_item = m_current;
1100 event.m_code = 0;
1101 event.SetEventObject( this );
1102 GetEventHandler()->ProcessEvent( event );
1103 return;
1104 }
1105 case WXK_UP:
1106 {
1107 wxTreeItemId prev = GetPrevSibling( m_current );
1108 if (prev != 0)
1109 {
1110 SelectItem( prev );
1111 EnsureVisible( prev );
1112 }
1113 else
1114 {
1115 prev = GetParent( m_current );
1116 if (prev)
1117 {
1118 EnsureVisible( prev );
1119 SelectItem( prev );
1120 }
1121 }
1122 return;
1123 }
1124 case WXK_DOWN:
1125 {
1126 if (IsExpanded(m_current))
1127 {
1128 long cookie = 0;
1129 wxTreeItemId child = GetFirstChild( m_current, cookie );
1130 SelectItem( child );
1131 EnsureVisible( child );
1132 }
1133 else
1134 {
1135 wxTreeItemId next = GetNextSibling( m_current );
1136 if (next == 0)
1137 {
1138 wxTreeItemId current = m_current;
1139 while (current && !next)
1140 {
1141 current = GetParent( current );
1142 if (current) next = GetNextSibling( current );
1143 }
1144 }
1145 if (next != 0)
1146 {
1147 SelectItem( next );
1148 EnsureVisible( next );
1149 }
1150 }
1151 return;
1152 }
1153 }
1154 event.Skip();
1155 }
1156
1157 void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1158 {
1159 if ( !(event.LeftDown() || event.LeftDClick()) )
1160 return;
1161
1162 if ( !m_anchor )
1163 return;
1164
1165 wxClientDC dc(this);
1166 PrepareDC(dc);
1167 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1168 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1169
1170 bool onButton = FALSE;
1171 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1172 if ( item == NULL )
1173 return;
1174
1175 if (!IsSelected(item)) SelectItem(item);
1176
1177 if ( event.LeftDClick() )
1178 {
1179 wxTreeEvent event( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1180 event.m_item = item;
1181 event.m_code = 0;
1182 event.SetEventObject( this );
1183 GetEventHandler()->ProcessEvent( event );
1184 }
1185
1186 if ( onButton )
1187 {
1188 Toggle( item );
1189 }
1190 }
1191
1192 void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1193 {
1194 if (!m_dirty) return;
1195
1196 m_dirty = FALSE;
1197
1198 CalculatePositions();
1199
1200 AdjustMyScrollbars();
1201 }
1202
1203 // -----------------------------------------------------------------------------
1204 // -----------------------------------------------------------------------------
1205 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1206 wxDC &dc,
1207 int level,
1208 int &y )
1209 {
1210 int horizX = level*m_indent;
1211
1212 item->SetX( horizX+33 );
1213 item->SetY( y-m_lineHeight/3-2 );
1214 item->SetHeight( m_lineHeight );
1215
1216 if ( item->IsExpanded() )
1217 return;
1218
1219 wxArrayTreeItems& children = item->GetChildren();
1220 size_t count = children.Count();
1221 for ( size_t n = 0; n < count; n++ )
1222 {
1223 y += m_lineHeight;
1224 CalculateLevel( children[n], dc, level+1, y );
1225 }
1226 }
1227
1228 void wxTreeCtrl::CalculatePositions()
1229 {
1230 if ( !m_anchor )
1231 return;
1232
1233 wxClientDC dc(this);
1234 PrepareDC( dc );
1235
1236 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1237
1238 dc.SetPen( m_dottedPen );
1239 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1240
1241 int y = m_lineHeight / 2 + 2;
1242 CalculateLevel( m_anchor, dc, 0, y );
1243 }
1244
1245 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1246 {
1247 wxClientDC dc(this);
1248 PrepareDC(dc);
1249
1250 int cw = 0;
1251 int ch = 0;
1252 GetClientSize( &cw, &ch );
1253
1254 wxRect rect;
1255 rect.x = dc.LogicalToDeviceX( 0 );
1256 rect.width = cw;
1257 rect.y = dc.LogicalToDeviceY( item->GetY() );
1258 rect.height = ch;
1259
1260 Refresh( TRUE, &rect );
1261
1262 AdjustMyScrollbars();
1263 }
1264
1265 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1266 {
1267 wxClientDC dc(this);
1268 PrepareDC( dc );
1269
1270 wxRect rect;
1271 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1272 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1273 rect.width = 1000;
1274 rect.height = dc.GetCharHeight() + 6;
1275 Refresh( TRUE, &rect );
1276 }
1277