]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
Removed minor differences between wxMSW and wxGTK
[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, wxNotifyEvent)
144
145 wxTreeEvent::wxTreeEvent( wxEventType commandType, int id )
146 : wxNotifyEvent( 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,
333 const wxValidator &validator,
334 const wxString& name )
335 {
336 Init();
337
338 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
339
340 SetValidator( validator );
341
342 SetBackgroundColour( *wxWHITE );
343 m_dottedPen = wxPen( *wxBLACK, 0, 0 );
344
345 return TRUE;
346 }
347
348 wxTreeCtrl::~wxTreeCtrl()
349 {
350 wxDELETE( m_hilightBrush );
351 wxDELETE( m_anchor );
352 }
353
354 // -----------------------------------------------------------------------------
355 // accessors
356 // -----------------------------------------------------------------------------
357
358 size_t wxTreeCtrl::GetCount() const
359 {
360 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
361 }
362
363 void wxTreeCtrl::SetIndent(unsigned int indent)
364 {
365 m_indent = indent;
366 Refresh();
367 }
368
369 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
370 {
371 wxCHECK_MSG( item.IsOk(), 0u, "invalid tree item" );
372
373 return item.m_pItem->GetChildrenCount(recursively);
374 }
375
376 // -----------------------------------------------------------------------------
377 // functions to work with tree items
378 // -----------------------------------------------------------------------------
379
380 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
381 {
382 wxCHECK_MSG( item.IsOk(), "", "invalid tree item" );
383
384 return item.m_pItem->GetText();
385 }
386
387 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
388 {
389 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
390
391 return item.m_pItem->GetImage();
392 }
393
394 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
395 {
396 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
397
398 return item.m_pItem->GetSelectedImage();
399 }
400
401 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
402 {
403 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
404
405 return item.m_pItem->GetData();
406 }
407
408 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
409 {
410 wxCHECK_RET( item.IsOk(), "invalid tree item" );
411
412 wxClientDC dc(this);
413 item.m_pItem->SetText(text, dc);
414 }
415
416 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
417 {
418 wxCHECK_RET( item.IsOk(), "invalid tree item" );
419
420 item.m_pItem->SetImage(image);
421 }
422
423 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
424 {
425 wxCHECK_RET( item.IsOk(), "invalid tree item" );
426
427 item.m_pItem->SetSelectedImage(image);
428 }
429
430 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
431 {
432 wxCHECK_RET( item.IsOk(), "invalid tree item" );
433
434 item.m_pItem->SetData(data);
435 }
436
437 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
438 {
439 wxCHECK_RET( item.IsOk(), "invalid tree item" );
440
441 item.m_pItem->SetHasPlus(has);
442 }
443
444 void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
445 {
446 wxCHECK_RET( item.IsOk(), "invalid tree item" );
447
448 // avoid redrawing the tree if no real change
449 wxGenericTreeItem *pItem = item.m_pItem;
450 if ( pItem->IsBold() != bold )
451 {
452 pItem->SetBold(bold);
453 RefreshLine(pItem);
454 }
455 }
456
457 // -----------------------------------------------------------------------------
458 // item status inquiries
459 // -----------------------------------------------------------------------------
460
461 bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
462 {
463 wxFAIL_MSG("not implemented");
464
465 return TRUE;
466 }
467
468 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
469 {
470 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
471
472 return !item.m_pItem->GetChildren().IsEmpty();
473 }
474
475 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
476 {
477 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
478
479 return item.m_pItem->IsExpanded();
480 }
481
482 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
483 {
484 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
485
486 return item.m_pItem->HasHilight();
487 }
488
489 bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
490 {
491 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
492
493 return item.m_pItem->IsBold();
494 }
495
496 // -----------------------------------------------------------------------------
497 // navigation
498 // -----------------------------------------------------------------------------
499
500 wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
501 {
502 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
503
504 return item.m_pItem->GetParent();
505 }
506
507 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
508 {
509 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
510
511 cookie = 0;
512 return GetNextChild(item, cookie);
513 }
514
515 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
516 {
517 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
518
519 wxArrayTreeItems& children = item.m_pItem->GetChildren();
520 if ( (size_t)cookie < children.Count() )
521 {
522 return item.m_pItem->GetChildren().Item(cookie++);
523 }
524 else
525 {
526 // there are no more of them
527 return NULL;
528 }
529 }
530
531 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
532 {
533 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
534
535 wxGenericTreeItem *i = item.m_pItem;
536 wxGenericTreeItem *parent = i->GetParent();
537 if ( parent == NULL )
538 {
539 // root item doesn't have any siblings
540 return NULL;
541 }
542
543 wxArrayTreeItems& siblings = parent->GetChildren();
544 int index = siblings.Index(i);
545 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
546
547 size_t n = (size_t)(index + 1);
548 return n == siblings.Count() ? (wxGenericTreeItem*)NULL : siblings[n];
549 }
550
551 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
552 {
553 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
554
555 wxGenericTreeItem *i = item.m_pItem;
556 wxGenericTreeItem *parent = i->GetParent();
557 if ( parent == NULL )
558 {
559 // root item doesn't have any siblings
560 return NULL;
561 }
562
563 wxArrayTreeItems& siblings = parent->GetChildren();
564 int index = siblings.Index(i);
565 wxASSERT( index != NOT_FOUND ); // I'm not a child of my parent?
566
567 return index == 0 ? (wxGenericTreeItem*)NULL : siblings[(size_t)(index - 1)];
568 }
569
570 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
571 {
572 wxFAIL_MSG("not implemented");
573
574 return NULL;
575 }
576
577 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
578 {
579 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
580
581 wxFAIL_MSG("not implemented");
582
583 return NULL;
584 }
585
586 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
587 {
588 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
589
590 wxFAIL_MSG("not implemented");
591
592 return NULL;
593 }
594
595 // -----------------------------------------------------------------------------
596 // operations
597 // -----------------------------------------------------------------------------
598
599 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
600 size_t previous,
601 const wxString& text,
602 int image, int selImage,
603 wxTreeItemData *data)
604 {
605 wxGenericTreeItem *parent = parentId.m_pItem;
606 if ( !parent )
607 {
608 // should we give a warning here?
609 return AddRoot(text, image, selImage, data);
610 }
611
612 wxClientDC dc(this);
613 wxGenericTreeItem *item = new wxGenericTreeItem(parent,
614 text, dc,
615 image, selImage,
616 data);
617
618 if ( data != NULL )
619 {
620 data->m_pItem = item;
621 }
622
623 parent->Insert( item, previous );
624
625 m_dirty = TRUE;
626
627 return item;
628 }
629
630 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
631 int image, int selImage,
632 wxTreeItemData *data)
633 {
634 wxCHECK_MSG( !m_anchor, NULL, "tree can have only one root" );
635
636 wxClientDC dc(this);
637 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
638 image, selImage, data);
639 if ( data != NULL )
640 {
641 data->m_pItem = m_anchor;
642 }
643
644 AdjustMyScrollbars();
645 Refresh();
646
647 return m_anchor;
648 }
649
650 wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
651 const wxString& text,
652 int image, int selImage,
653 wxTreeItemData *data)
654 {
655 return DoInsertItem(parent, 0u, text, image, selImage, data);
656 }
657
658 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
659 const wxTreeItemId& idPrevious,
660 const wxString& text,
661 int image, int selImage,
662 wxTreeItemData *data)
663 {
664 wxGenericTreeItem *parent = parentId.m_pItem;
665 if ( !parent )
666 {
667 // should we give a warning here?
668 return AddRoot(text, image, selImage, data);
669 }
670
671 int index = parent->GetChildren().Index(idPrevious.m_pItem);
672 wxASSERT_MSG( index != NOT_FOUND,
673 "previous item in wxTreeCtrl::InsertItem() is not a sibling" );
674 return DoInsertItem(parentId, (size_t)index, text, image, selImage, data);
675 }
676
677 wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
678 const wxString& text,
679 int image, int selImage,
680 wxTreeItemData *data)
681 {
682 wxGenericTreeItem *parent = parentId.m_pItem;
683 if ( !parent )
684 {
685 // should we give a warning here?
686 return AddRoot(text, image, selImage, data);
687 }
688
689 return DoInsertItem(parent, parent->GetChildren().Count(), text,
690 image, selImage, data);
691 }
692
693 void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
694 {
695 wxGenericTreeItem *item = itemId.m_pItem;
696 wxGenericTreeItem *parent = item->GetParent();
697
698 if ( parent )
699 {
700 parent->GetChildren().Remove(item);
701 }
702
703 delete item;
704
705 m_dirty = TRUE;
706 }
707
708 void wxTreeCtrl::DeleteAllItems()
709 {
710 if ( m_anchor )
711 {
712 delete m_anchor;
713 m_anchor = NULL;
714
715 m_dirty = TRUE;
716 }
717 }
718
719 void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
720 {
721 wxGenericTreeItem *item = itemId.m_pItem;
722
723 if ( !item->HasPlus() )
724 return;
725
726 if ( item->IsExpanded() )
727 return;
728
729 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
730 event.m_item = item;
731 event.SetEventObject( this );
732 if ( ProcessEvent( event ) && event.m_code )
733 {
734 // cancelled by program
735 return;
736 }
737
738 item->Expand();
739
740 RefreshSubtree(item);
741
742 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
743 ProcessEvent( event );
744 }
745
746 void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
747 {
748 wxGenericTreeItem *item = itemId.m_pItem;
749
750 if ( !item->IsExpanded() )
751 return;
752
753 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
754 event.m_item = item;
755 event.SetEventObject( this );
756 if ( ProcessEvent( event ) && event.m_code )
757 {
758 // cancelled by program
759 return;
760 }
761
762 item->Collapse();
763
764 wxArrayTreeItems& children = item->GetChildren();
765 size_t count = children.Count();
766 for ( size_t n = 0; n < count; n++ )
767 {
768 Collapse(children[n]);
769 }
770
771 CalculatePositions();
772
773 RefreshSubtree(item);
774
775 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
776 ProcessEvent( event );
777 }
778
779 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
780 {
781 Collapse(item);
782 Delete(item);
783 }
784
785 void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
786 {
787 wxGenericTreeItem *item = itemId.m_pItem;
788
789 if ( item->IsExpanded() )
790 Collapse(itemId);
791 else
792 Expand(itemId);
793 }
794
795 void wxTreeCtrl::Unselect()
796 {
797 if ( m_current )
798 {
799 m_current->SetHilight( FALSE );
800 RefreshLine( m_current );
801 }
802 }
803
804 void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
805 {
806 wxGenericTreeItem *item = itemId.m_pItem;
807
808 if ( m_current != item )
809 {
810 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
811 event.m_item = item;
812 event.m_itemOld = m_current;
813 event.SetEventObject( this );
814 if ( GetEventHandler()->ProcessEvent( event ) && event.WasVetoed() )
815 return;
816
817 if ( m_current )
818 {
819 m_current->SetHilight( FALSE );
820 RefreshLine( m_current );
821 }
822
823 m_current = item;
824 m_current->SetHilight( TRUE );
825 RefreshLine( m_current );
826
827 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
828 GetEventHandler()->ProcessEvent( event );
829 }
830 }
831
832 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
833 {
834 wxGenericTreeItem *gitem = item.m_pItem;
835
836 int item_y = gitem->GetY();
837
838 int start_x = 0;
839 int start_y = 0;
840 ViewStart( &start_x, &start_y );
841 start_y *= 10;
842
843 if (item_y < start_y+3)
844 {
845 int x = 0;
846 int y = 0;
847 m_anchor->GetSize( x, y );
848 y += 2*m_lineHeight;
849 int x_pos = GetScrollPos( wxHORIZONTAL );
850 SetScrollbars( 10, 10, x/10, y/10, x_pos, item_y/10 );
851 return;
852 }
853
854 int w = 0;
855 int h = 0;
856 GetClientSize( &w, &h );
857
858 if (item_y > start_y+h-26)
859 {
860 int x = 0;
861 int y = 0;
862 m_anchor->GetSize( x, y );
863 y += 2*m_lineHeight;
864 int x_pos = GetScrollPos( wxHORIZONTAL );
865 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-h+30)/10 );
866 return;
867 }
868 }
869
870 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
871 {
872 wxFAIL_MSG("not implemented");
873 }
874
875 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
876 wxClassInfo* WXUNUSED(textCtrlClass) )
877 {
878 wxFAIL_MSG("not implemented");
879
880 return NULL;
881 }
882
883 wxTextCtrl *wxTreeCtrl::GetEditControl() const
884 {
885 wxFAIL_MSG("not implemented");
886
887 return NULL;
888 }
889
890 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
891 {
892 wxFAIL_MSG("not implemented");
893 }
894
895 void wxTreeCtrl::SortChildren( const wxTreeItemId& WXUNUSED(item),
896 wxTreeItemCmpFunc *WXUNUSED(cmpFunction))
897 {
898 wxFAIL_MSG("not implemented");
899 }
900
901 wxImageList *wxTreeCtrl::GetImageList() const
902 {
903 return m_imageListNormal;
904 }
905
906 wxImageList *wxTreeCtrl::GetStateImageList() const
907 {
908 return m_imageListState;
909 }
910
911 void wxTreeCtrl::SetImageList(wxImageList *imageList)
912 {
913 m_imageListNormal = imageList;
914 }
915
916 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
917 {
918 m_imageListState = imageList;
919 }
920
921 // -----------------------------------------------------------------------------
922 // helpers
923 // -----------------------------------------------------------------------------
924 void wxTreeCtrl::AdjustMyScrollbars()
925 {
926 if (m_anchor)
927 {
928 int x = 0;
929 int y = 0;
930 m_anchor->GetSize( x, y );
931 y += 2*m_lineHeight;
932 int x_pos = GetScrollPos( wxHORIZONTAL );
933 int y_pos = GetScrollPos( wxVERTICAL );
934 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
935 }
936 else
937 {
938 SetScrollbars( 0, 0, 0, 0 );
939 }
940 }
941
942 void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
943 {
944 // render bold items in bold
945 wxFont fontOld;
946 wxFont fontNew;
947
948 if ( item->IsBold() )
949 {
950 fontOld = dc.GetFont();
951 if (fontOld.Ok())
952 {
953 // @@ is there any better way to make a bold variant of old font?
954 fontNew = wxFont( fontOld.GetPointSize(),
955 fontOld.GetFamily(),
956 fontOld.GetStyle(),
957 wxBOLD,
958 fontOld.GetUnderlined());
959 dc.SetFont(fontNew);
960 }
961 else
962 {
963 wxFAIL_MSG("wxDC::GetFont() failed!");
964 }
965 }
966
967 long text_w = 0;
968 long text_h = 0;
969 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
970
971 int image_h = 0;
972 int image_w = 0;
973 if (item->GetImage() != -1)
974 {
975 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
976 image_w += 4;
977 }
978
979 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
980
981 if (item->GetImage() != -1)
982 {
983 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
984 m_imageListNormal->Draw( item->GetImage(), dc,
985 item->GetX(), item->GetY()-1,
986 wxIMAGELIST_DRAW_TRANSPARENT );
987 dc.DestroyClippingRegion();
988 }
989
990 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY() );
991
992 // restore normal font for bold items
993 if (fontOld.Ok())
994 {
995 dc.SetFont( fontOld);
996 }
997 }
998
999 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1000 {
1001 int horizX = level*m_indent;
1002
1003 item->SetX( horizX+33 );
1004 item->SetY( y-m_lineHeight/3 );
1005 item->SetHeight( m_lineHeight );
1006
1007 item->SetCross( horizX+15, y );
1008
1009 int oldY = y;
1010
1011 int exposed_x = dc.LogicalToDeviceX( 0 );
1012 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
1013
1014 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
1015 {
1016 int startX = horizX;
1017 int endX = horizX + 10;
1018
1019 if (!item->HasChildren()) endX += 20;
1020
1021 dc.DrawLine( startX, y, endX, y );
1022
1023 if (item->HasPlus())
1024 {
1025 dc.DrawLine( horizX+20, y, horizX+30, y );
1026 dc.SetPen( *wxGREY_PEN );
1027 dc.SetBrush( *wxWHITE_BRUSH );
1028 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
1029 dc.SetPen( *wxBLACK_PEN );
1030 dc.DrawLine( horizX+13, y, horizX+18, y );
1031
1032 if (!item->IsExpanded())
1033 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
1034 }
1035
1036 if (item->HasHilight())
1037 {
1038 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
1039
1040 dc.SetBrush( *m_hilightBrush );
1041
1042 if (m_hasFocus)
1043 dc.SetPen( *wxBLACK_PEN );
1044 else
1045 dc.SetPen( *wxTRANSPARENT_PEN );
1046
1047 PaintItem(item, dc);
1048
1049 dc.SetPen( *wxBLACK_PEN );
1050 dc.SetTextForeground( *wxBLACK );
1051 dc.SetBrush( *wxWHITE_BRUSH );
1052 }
1053 else
1054 {
1055 dc.SetBrush( *wxWHITE_BRUSH );
1056 dc.SetPen( *wxTRANSPARENT_PEN );
1057
1058 PaintItem(item, dc);
1059
1060 dc.SetPen( *wxBLACK_PEN );
1061 }
1062 }
1063
1064 if ( !item->IsExpanded() )
1065 return;
1066
1067 int semiOldY = y;
1068
1069 wxArrayTreeItems& children = item->GetChildren();
1070 size_t count = children.Count();
1071 for ( size_t n = 0; n < count; n++ )
1072 {
1073 y += m_lineHeight;
1074 semiOldY = y;
1075
1076 PaintLevel( children[n], dc, level+1, y );
1077 }
1078
1079 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1080 }
1081
1082 // -----------------------------------------------------------------------------
1083 // wxWindows callbacks
1084 // -----------------------------------------------------------------------------
1085
1086 void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1087 {
1088 if ( !m_anchor )
1089 return;
1090
1091 wxPaintDC dc(this);
1092 PrepareDC( dc );
1093
1094 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1095
1096 dc.SetPen( m_dottedPen );
1097 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1098
1099 int y = m_lineHeight / 2 + 2;
1100 PaintLevel( m_anchor, dc, 0, y );
1101 }
1102
1103 void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1104 {
1105 m_hasFocus = TRUE;
1106 if ( m_current )
1107 RefreshLine( m_current );
1108 }
1109
1110 void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1111 {
1112 m_hasFocus = FALSE;
1113 if ( m_current )
1114 RefreshLine( m_current );
1115 }
1116
1117 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1118 {
1119 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1120 te.m_code = event.KeyCode();
1121 te.SetEventObject( this );
1122 GetEventHandler()->ProcessEvent( te );
1123
1124 if (m_current == 0)
1125 {
1126 event.Skip();
1127 return;
1128 }
1129
1130 switch (event.KeyCode())
1131 {
1132 case '+':
1133 case WXK_ADD:
1134 if (m_current->HasPlus() && !IsExpanded(m_current))
1135 {
1136 Expand(m_current);
1137 }
1138 break;
1139
1140 case '-':
1141 case WXK_SUBTRACT:
1142 if (IsExpanded(m_current))
1143 {
1144 Collapse(m_current);
1145 }
1146 break;
1147
1148 case '*':
1149 case WXK_MULTIPLY:
1150 Toggle(m_current);
1151 break;
1152
1153 case ' ':
1154 case WXK_RETURN:
1155 {
1156 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1157 event.m_item = m_current;
1158 event.m_code = 0;
1159 event.SetEventObject( this );
1160 GetEventHandler()->ProcessEvent( event );
1161 }
1162 break;
1163
1164 case WXK_LEFT:
1165 case WXK_UP:
1166 {
1167 wxTreeItemId prev = GetPrevSibling( m_current );
1168 if (prev != 0)
1169 {
1170 SelectItem( prev );
1171 EnsureVisible( prev );
1172 }
1173 else
1174 {
1175 prev = GetParent( m_current );
1176 if (prev)
1177 {
1178 EnsureVisible( prev );
1179 SelectItem( prev );
1180 }
1181 }
1182 }
1183 break;
1184
1185 case WXK_RIGHT:
1186 // this works the same as the down arrow except that we also expand the
1187 // item if it wasn't expanded yet
1188 Expand(m_current);
1189 // fall through
1190
1191 case WXK_DOWN:
1192 {
1193 if (IsExpanded(m_current))
1194 {
1195 long cookie = 0;
1196 wxTreeItemId child = GetFirstChild( m_current, cookie );
1197 SelectItem( child );
1198 EnsureVisible( child );
1199 }
1200 else
1201 {
1202 wxTreeItemId next = GetNextSibling( m_current );
1203 if (next == 0)
1204 {
1205 wxTreeItemId current = m_current;
1206 while (current && !next)
1207 {
1208 current = GetParent( current );
1209 if (current) next = GetNextSibling( current );
1210 }
1211 }
1212 if (next != 0)
1213 {
1214 SelectItem( next );
1215 EnsureVisible( next );
1216 }
1217 }
1218 }
1219 break;
1220
1221 default:
1222 event.Skip();
1223 }
1224 }
1225
1226 wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& WXUNUSED(flags))
1227 {
1228 bool onButton = FALSE;
1229 return m_anchor->HitTest( point, onButton );
1230 }
1231
1232 void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1233 {
1234 if ( !(event.LeftDown() || event.LeftDClick()) )
1235 return;
1236
1237 if ( !m_anchor )
1238 return;
1239
1240 wxClientDC dc(this);
1241 PrepareDC(dc);
1242 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1243 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1244
1245 bool onButton = FALSE;
1246 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1247 if ( item == NULL )
1248 return;
1249
1250 if (!IsSelected(item)) SelectItem(item);
1251
1252 if ( event.LeftDClick() )
1253 {
1254 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1255 event.m_item = item;
1256 event.m_code = 0;
1257 event.SetEventObject( this );
1258 GetEventHandler()->ProcessEvent( event );
1259 }
1260
1261 if ( onButton )
1262 {
1263 Toggle( item );
1264 }
1265 }
1266
1267 void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1268 {
1269 if (!m_dirty) return;
1270
1271 m_dirty = FALSE;
1272
1273 CalculatePositions();
1274
1275 AdjustMyScrollbars();
1276 }
1277
1278 // -----------------------------------------------------------------------------
1279 // -----------------------------------------------------------------------------
1280 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1281 wxDC &dc,
1282 int level,
1283 int &y )
1284 {
1285 int horizX = level*m_indent;
1286
1287 item->SetX( horizX+33 );
1288 item->SetY( y-m_lineHeight/3-2 );
1289 item->SetHeight( m_lineHeight );
1290
1291 if ( item->IsExpanded() )
1292 return;
1293
1294 wxArrayTreeItems& children = item->GetChildren();
1295 size_t count = children.Count();
1296 for ( size_t n = 0; n < count; n++ )
1297 {
1298 y += m_lineHeight;
1299 CalculateLevel( children[n], dc, level+1, y );
1300 }
1301 }
1302
1303 void wxTreeCtrl::CalculatePositions()
1304 {
1305 if ( !m_anchor )
1306 return;
1307
1308 wxClientDC dc(this);
1309 PrepareDC( dc );
1310
1311 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1312
1313 dc.SetPen( m_dottedPen );
1314 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1315
1316 int y = m_lineHeight / 2 + 2;
1317 CalculateLevel( m_anchor, dc, 0, y );
1318 }
1319
1320 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1321 {
1322 wxClientDC dc(this);
1323 PrepareDC(dc);
1324
1325 int cw = 0;
1326 int ch = 0;
1327 GetClientSize( &cw, &ch );
1328
1329 wxRect rect;
1330 rect.x = dc.LogicalToDeviceX( 0 );
1331 rect.width = cw;
1332 rect.y = dc.LogicalToDeviceY( item->GetY() );
1333 rect.height = ch;
1334
1335 Refresh( TRUE, &rect );
1336
1337 AdjustMyScrollbars();
1338 }
1339
1340 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1341 {
1342 wxClientDC dc(this);
1343 PrepareDC( dc );
1344
1345 wxRect rect;
1346 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1347 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1348 rect.width = 1000;
1349 rect.height = dc.GetCharHeight() + 6;
1350 Refresh( TRUE, &rect );
1351 }
1352