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