]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
775eadb61c8e72b6d23c3c469c3c4d5785c67bf9
[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 CalculatePositions();
783
784 RefreshSubtree(item);
785
786 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
787 ProcessEvent( event );
788 }
789
790 void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
791 {
792 wxGenericTreeItem *item = itemId.m_pItem;
793
794 if ( !item->IsExpanded() )
795 return;
796
797 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
798 event.m_item = item;
799 event.SetEventObject( this );
800 if ( ProcessEvent( event ) && event.m_code )
801 {
802 // cancelled by program
803 return;
804 }
805
806 item->Collapse();
807
808 wxArrayTreeItems& children = item->GetChildren();
809 size_t count = children.Count();
810 for ( size_t n = 0; n < count; n++ )
811 {
812 Collapse(children[n]);
813 }
814
815 CalculatePositions();
816
817 RefreshSubtree(item);
818
819 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
820 ProcessEvent( event );
821 }
822
823 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
824 {
825 Collapse(item);
826 DeleteChildren(item);
827 }
828
829 void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
830 {
831 wxGenericTreeItem *item = itemId.m_pItem;
832
833 if ( item->IsExpanded() )
834 Collapse(itemId);
835 else
836 Expand(itemId);
837 }
838
839 void wxTreeCtrl::Unselect()
840 {
841 if ( m_current )
842 {
843 m_current->SetHilight( FALSE );
844 RefreshLine( m_current );
845 }
846 }
847
848 void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
849 {
850 wxGenericTreeItem *item = itemId.m_pItem;
851
852 if ( m_current != item )
853 {
854 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
855 event.m_item = item;
856 event.m_itemOld = m_current;
857 event.SetEventObject( this );
858 if ( GetEventHandler()->ProcessEvent( event ) && event.WasVetoed() )
859 return;
860
861 if ( m_current )
862 {
863 m_current->SetHilight( FALSE );
864 RefreshLine( m_current );
865 }
866
867 m_current = item;
868 m_current->SetHilight( TRUE );
869 RefreshLine( m_current );
870
871 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
872 GetEventHandler()->ProcessEvent( event );
873 }
874 }
875
876 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
877 {
878 wxGenericTreeItem *gitem = item.m_pItem;
879
880 int item_y = gitem->GetY();
881
882 int start_x = 0;
883 int start_y = 0;
884 ViewStart( &start_x, &start_y );
885 start_y *= 10;
886
887 if (item_y < start_y+3)
888 {
889 int x = 0;
890 int y = 0;
891 m_anchor->GetSize( x, y );
892 y += 2*m_lineHeight;
893 int x_pos = GetScrollPos( wxHORIZONTAL );
894 SetScrollbars( 10, 10, x/10, y/10, x_pos, item_y/10 );
895 return;
896 }
897
898 int w = 0;
899 int h = 0;
900 GetClientSize( &w, &h );
901
902 if (item_y > start_y+h-26)
903 {
904 int x = 0;
905 int y = 0;
906 m_anchor->GetSize( x, y );
907 y += 2*m_lineHeight;
908 int x_pos = GetScrollPos( wxHORIZONTAL );
909 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-h+30)/10 );
910 return;
911 }
912 }
913
914 void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
915 {
916 wxFAIL_MSG("not implemented");
917 }
918
919 wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
920 wxClassInfo* WXUNUSED(textCtrlClass) )
921 {
922 wxFAIL_MSG("not implemented");
923
924 return (wxTextCtrl*)NULL;
925 }
926
927 wxTextCtrl *wxTreeCtrl::GetEditControl() const
928 {
929 wxFAIL_MSG("not implemented");
930
931 return (wxTextCtrl*)NULL;
932 }
933
934 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
935 {
936 wxFAIL_MSG("not implemented");
937 }
938
939 // FIXME: tree sorting functions are not reentrant and not MT-safe!
940 static wxTreeCtrl *s_treeBeingSorted = NULL;
941
942 static int tree_ctrl_compare_func(wxGenericTreeItem **item1,
943 wxGenericTreeItem **item2)
944 {
945 wxCHECK_MSG( s_treeBeingSorted, 0, "bug in wxTreeCtrl::SortChildren()" );
946
947 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
948 }
949
950 int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
951 const wxTreeItemId& item2)
952 {
953 return strcmp(GetItemText(item1), GetItemText(item2));
954 }
955
956 void wxTreeCtrl::SortChildren(const wxTreeItemId& itemId)
957 {
958 wxCHECK_RET( itemId.IsOk(), "invalid tree item" );
959
960 wxGenericTreeItem *item = itemId.m_pItem;
961
962 wxCHECK_RET( !s_treeBeingSorted,
963 "wxTreeCtrl::SortChildren is not reentrant" );
964
965 wxArrayTreeItems& children = item->GetChildren();
966 if ( children.Count() > 1 )
967 {
968 s_treeBeingSorted = this;
969 children.Sort(tree_ctrl_compare_func);
970 s_treeBeingSorted = NULL;
971
972 m_dirty = TRUE;
973 }
974 //else: don't make the tree dirty as nothing changed
975 }
976
977 wxImageList *wxTreeCtrl::GetImageList() const
978 {
979 return m_imageListNormal;
980 }
981
982 wxImageList *wxTreeCtrl::GetStateImageList() const
983 {
984 return m_imageListState;
985 }
986
987 void wxTreeCtrl::SetImageList(wxImageList *imageList)
988 {
989 m_imageListNormal = imageList;
990 }
991
992 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
993 {
994 m_imageListState = imageList;
995 }
996
997 // -----------------------------------------------------------------------------
998 // helpers
999 // -----------------------------------------------------------------------------
1000
1001 void wxTreeCtrl::AdjustMyScrollbars()
1002 {
1003 if (m_anchor)
1004 {
1005 int x = 0;
1006 int y = 0;
1007 m_anchor->GetSize( x, y );
1008 y += 2*m_lineHeight;
1009 int x_pos = GetScrollPos( wxHORIZONTAL );
1010 int y_pos = GetScrollPos( wxVERTICAL );
1011 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
1012 }
1013 else
1014 {
1015 SetScrollbars( 0, 0, 0, 0 );
1016 }
1017 }
1018
1019 void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1020 {
1021 // render bold items in bold
1022 wxFont fontOld;
1023 wxFont fontNew;
1024
1025 if ( item->IsBold() )
1026 {
1027 fontOld = dc.GetFont();
1028 if (fontOld.Ok())
1029 {
1030 // @@ is there any better way to make a bold variant of old font?
1031 fontNew = wxFont( fontOld.GetPointSize(),
1032 fontOld.GetFamily(),
1033 fontOld.GetStyle(),
1034 wxBOLD,
1035 fontOld.GetUnderlined());
1036 dc.SetFont(fontNew);
1037 }
1038 else
1039 {
1040 wxFAIL_MSG("wxDC::GetFont() failed!");
1041 }
1042 }
1043
1044 long text_w = 0;
1045 long text_h = 0;
1046 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
1047
1048 int image_h = 0;
1049 int image_w = 0;
1050 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1051 {
1052 m_imageListNormal->GetSize( item->GetSelectedImage(), image_w, image_h );
1053 image_w += 4;
1054 }
1055 else if (item->GetImage() != -1)
1056 {
1057 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
1058 image_w += 4;
1059 }
1060
1061 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
1062
1063 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1064 {
1065 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1066 m_imageListNormal->Draw( item->GetSelectedImage(), dc,
1067 item->GetX(), item->GetY()-1,
1068 wxIMAGELIST_DRAW_TRANSPARENT );
1069 dc.DestroyClippingRegion();
1070 }
1071 else if (item->GetImage() != -1)
1072 {
1073 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1074 m_imageListNormal->Draw( item->GetImage(), dc,
1075 item->GetX(), item->GetY()-1,
1076 wxIMAGELIST_DRAW_TRANSPARENT );
1077 dc.DestroyClippingRegion();
1078 }
1079
1080 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY() );
1081
1082 // restore normal font for bold items
1083 if (fontOld.Ok())
1084 {
1085 dc.SetFont( fontOld);
1086 }
1087 }
1088
1089 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1090 {
1091 int horizX = level*m_indent;
1092
1093 item->SetX( horizX+33 );
1094 item->SetY( y-m_lineHeight/3 );
1095 item->SetHeight( m_lineHeight );
1096
1097 item->SetCross( horizX+15, y );
1098
1099 int oldY = y;
1100
1101 int exposed_x = dc.LogicalToDeviceX( 0 );
1102 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
1103
1104 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
1105 {
1106 int startX = horizX;
1107 int endX = horizX + 10;
1108
1109 if (!item->HasChildren()) endX += 20;
1110
1111 dc.DrawLine( startX, y, endX, y );
1112
1113 if (item->HasPlus())
1114 {
1115 dc.DrawLine( horizX+20, y, horizX+30, y );
1116 dc.SetPen( *wxGREY_PEN );
1117 dc.SetBrush( *wxWHITE_BRUSH );
1118 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
1119 dc.SetPen( *wxBLACK_PEN );
1120 dc.DrawLine( horizX+13, y, horizX+18, y );
1121
1122 if (!item->IsExpanded())
1123 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
1124 }
1125
1126 if (item->HasHilight())
1127 {
1128 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
1129
1130 dc.SetBrush( *m_hilightBrush );
1131
1132 if (m_hasFocus)
1133 dc.SetPen( *wxBLACK_PEN );
1134 else
1135 dc.SetPen( *wxTRANSPARENT_PEN );
1136
1137 PaintItem(item, dc);
1138
1139 dc.SetPen( *wxBLACK_PEN );
1140 dc.SetTextForeground( *wxBLACK );
1141 dc.SetBrush( *wxWHITE_BRUSH );
1142 }
1143 else
1144 {
1145 dc.SetBrush( *wxWHITE_BRUSH );
1146 dc.SetPen( *wxTRANSPARENT_PEN );
1147
1148 PaintItem(item, dc);
1149
1150 dc.SetPen( *wxBLACK_PEN );
1151 }
1152 }
1153
1154 if ( item->IsExpanded() )
1155 {
1156 int semiOldY = y;
1157
1158 wxArrayTreeItems& children = item->GetChildren();
1159 size_t count = children.Count();
1160 for ( size_t n = 0; n < count; n++ )
1161 {
1162 y += m_lineHeight;
1163 semiOldY = y;
1164
1165 PaintLevel( children[n], dc, level+1, y );
1166 }
1167
1168 // it may happen that the item is expanded but has no items (when you
1169 // delete all its children for example) - don't draw the vertical line
1170 // in this case
1171 if ( count > 0 )
1172 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1173 }
1174 }
1175
1176 // -----------------------------------------------------------------------------
1177 // wxWindows callbacks
1178 // -----------------------------------------------------------------------------
1179
1180 void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1181 {
1182 if ( !m_anchor )
1183 return;
1184
1185 wxPaintDC dc(this);
1186 PrepareDC( dc );
1187
1188 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1189
1190 dc.SetPen( m_dottedPen );
1191 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1192
1193 int y = m_lineHeight / 2 + 2;
1194 PaintLevel( m_anchor, dc, 0, y );
1195 }
1196
1197 void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1198 {
1199 m_hasFocus = TRUE;
1200 if ( m_current )
1201 RefreshLine( m_current );
1202 }
1203
1204 void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1205 {
1206 m_hasFocus = FALSE;
1207 if ( m_current )
1208 RefreshLine( m_current );
1209 }
1210
1211 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1212 {
1213 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1214 te.m_code = event.KeyCode();
1215 te.SetEventObject( this );
1216 GetEventHandler()->ProcessEvent( te );
1217
1218 if (m_current == 0)
1219 {
1220 event.Skip();
1221 return;
1222 }
1223
1224 switch (event.KeyCode())
1225 {
1226 case '+':
1227 case WXK_ADD:
1228 if (m_current->HasPlus() && !IsExpanded(m_current))
1229 {
1230 Expand(m_current);
1231 }
1232 break;
1233
1234 case '-':
1235 case WXK_SUBTRACT:
1236 if (IsExpanded(m_current))
1237 {
1238 Collapse(m_current);
1239 }
1240 break;
1241
1242 case '*':
1243 case WXK_MULTIPLY:
1244 Toggle(m_current);
1245 break;
1246
1247 case ' ':
1248 case WXK_RETURN:
1249 {
1250 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1251 event.m_item = m_current;
1252 event.m_code = 0;
1253 event.SetEventObject( this );
1254 GetEventHandler()->ProcessEvent( event );
1255 }
1256 break;
1257
1258 case WXK_LEFT:
1259 case WXK_UP:
1260 {
1261 wxTreeItemId prev = GetPrevSibling( m_current );
1262 if (prev != 0)
1263 {
1264 SelectItem( prev );
1265 EnsureVisible( prev );
1266 }
1267 else
1268 {
1269 prev = GetParent( m_current );
1270 if (prev)
1271 {
1272 EnsureVisible( prev );
1273 SelectItem( prev );
1274 }
1275 }
1276 }
1277 break;
1278
1279 case WXK_RIGHT:
1280 // this works the same as the down arrow except that we also expand the
1281 // item if it wasn't expanded yet
1282 Expand(m_current);
1283 // fall through
1284
1285 case WXK_DOWN:
1286 {
1287 if (IsExpanded(m_current))
1288 {
1289 long cookie = 0;
1290 wxTreeItemId child = GetFirstChild( m_current, cookie );
1291 SelectItem( child );
1292 EnsureVisible( child );
1293 }
1294 else
1295 {
1296 wxTreeItemId next = GetNextSibling( m_current );
1297 if (next == 0)
1298 {
1299 wxTreeItemId current = m_current;
1300 while (current && !next)
1301 {
1302 current = GetParent( current );
1303 if (current) next = GetNextSibling( current );
1304 }
1305 }
1306 if (next != 0)
1307 {
1308 SelectItem( next );
1309 EnsureVisible( next );
1310 }
1311 }
1312 }
1313 break;
1314
1315 default:
1316 event.Skip();
1317 }
1318 }
1319
1320 wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& WXUNUSED(flags))
1321 {
1322 bool onButton = FALSE;
1323 return m_anchor->HitTest( point, onButton );
1324 }
1325
1326 void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1327 {
1328 if ( !(event.LeftDown() || event.LeftDClick()) )
1329 return;
1330
1331 if ( !m_anchor )
1332 return;
1333
1334 wxClientDC dc(this);
1335 PrepareDC(dc);
1336 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1337 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1338
1339 bool onButton = FALSE;
1340 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1341 if ( item == NULL )
1342 return;
1343
1344 if (!IsSelected(item)) SelectItem(item);
1345
1346 if ( event.LeftDClick() )
1347 {
1348 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1349 event.m_item = item;
1350 event.m_code = 0;
1351 event.SetEventObject( this );
1352 GetEventHandler()->ProcessEvent( event );
1353 }
1354
1355 if ( onButton )
1356 {
1357 Toggle( item );
1358 }
1359 }
1360
1361 void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1362 {
1363 if (!m_dirty) return;
1364
1365 m_dirty = FALSE;
1366
1367 CalculatePositions();
1368
1369 AdjustMyScrollbars();
1370 }
1371
1372 // -----------------------------------------------------------------------------
1373 // -----------------------------------------------------------------------------
1374 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1375 wxDC &dc,
1376 int level,
1377 int &y )
1378 {
1379 int horizX = level*m_indent;
1380
1381 item->SetX( horizX+33 );
1382 item->SetY( y-m_lineHeight/3-2 );
1383 item->SetHeight( m_lineHeight );
1384
1385 // if ( item->IsExpanded() )
1386 // return;
1387 if ( !item->IsExpanded() ) // Surely this is correct? JACS
1388 return;
1389
1390 wxArrayTreeItems& children = item->GetChildren();
1391 size_t count = children.Count();
1392 for ( size_t n = 0; n < count; n++ )
1393 {
1394 y += m_lineHeight;
1395 CalculateLevel( children[n], dc, level+1, y );
1396 }
1397 }
1398
1399 void wxTreeCtrl::CalculatePositions()
1400 {
1401 if ( !m_anchor )
1402 return;
1403
1404 wxClientDC dc(this);
1405 PrepareDC( dc );
1406
1407 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1408
1409 dc.SetPen( m_dottedPen );
1410 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1411
1412 int y = m_lineHeight / 2 + 2;
1413 CalculateLevel( m_anchor, dc, 0, y );
1414 }
1415
1416 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1417 {
1418 wxClientDC dc(this);
1419 PrepareDC(dc);
1420
1421 int cw = 0;
1422 int ch = 0;
1423 GetClientSize( &cw, &ch );
1424
1425 wxRect rect;
1426 rect.x = dc.LogicalToDeviceX( 0 );
1427 rect.width = cw;
1428 rect.y = dc.LogicalToDeviceY( item->GetY() );
1429 rect.height = ch;
1430
1431 Refresh( TRUE, &rect );
1432
1433 AdjustMyScrollbars();
1434 }
1435
1436 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1437 {
1438 wxClientDC dc(this);
1439 PrepareDC( dc );
1440
1441 wxRect rect;
1442 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1443 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1444 rect.width = 1000;
1445 rect.height = dc.GetCharHeight() + 6;
1446 Refresh( TRUE, &rect );
1447 }
1448