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