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