]> git.saurik.com Git - wxWidgets.git/blob - src/generic/treectrl.cpp
Added dummy DeleteChildren so the sample will at least link.
[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 wxTreeItemCmpFunc tree_ctrl_compare_func_2;
939
940 int tree_ctrl_compare_func_1( wxGenericTreeItem **line1, wxGenericTreeItem **line2 )
941 {
942 if (tree_ctrl_compare_func_2 == NULL)
943 {
944 return strcmp( (*line1)->GetText(), (*line2)->GetText() );
945 }
946 else
947 {
948 wxTreeItemData *data1 = (*line1)->GetData();
949 wxTreeItemData *data2 = (*line2)->GetData();
950 return tree_ctrl_compare_func_2( data1, data2 );
951 }
952 }
953
954 void wxTreeCtrl::SortChildren( const wxTreeItemId& item,
955 wxTreeItemCmpFunc *cmpFunction)
956 {
957 wxGenericTreeItem *gitem = item.m_pItem;
958
959 if (!gitem) return;
960
961 if (cmpFunction == NULL)
962 tree_ctrl_compare_func_2 = NULL;
963 else
964 tree_ctrl_compare_func_2 = *cmpFunction;
965
966 gitem->GetChildren().Sort( *tree_ctrl_compare_func_1 );
967
968 m_dirty = TRUE;
969 }
970
971 wxImageList *wxTreeCtrl::GetImageList() const
972 {
973 return m_imageListNormal;
974 }
975
976 wxImageList *wxTreeCtrl::GetStateImageList() const
977 {
978 return m_imageListState;
979 }
980
981 void wxTreeCtrl::SetImageList(wxImageList *imageList)
982 {
983 m_imageListNormal = imageList;
984 }
985
986 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
987 {
988 m_imageListState = imageList;
989 }
990
991 // -----------------------------------------------------------------------------
992 // helpers
993 // -----------------------------------------------------------------------------
994
995 void wxTreeCtrl::AdjustMyScrollbars()
996 {
997 if (m_anchor)
998 {
999 int x = 0;
1000 int y = 0;
1001 m_anchor->GetSize( x, y );
1002 y += 2*m_lineHeight;
1003 int x_pos = GetScrollPos( wxHORIZONTAL );
1004 int y_pos = GetScrollPos( wxVERTICAL );
1005 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
1006 }
1007 else
1008 {
1009 SetScrollbars( 0, 0, 0, 0 );
1010 }
1011 }
1012
1013 void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1014 {
1015 // render bold items in bold
1016 wxFont fontOld;
1017 wxFont fontNew;
1018
1019 if ( item->IsBold() )
1020 {
1021 fontOld = dc.GetFont();
1022 if (fontOld.Ok())
1023 {
1024 // @@ is there any better way to make a bold variant of old font?
1025 fontNew = wxFont( fontOld.GetPointSize(),
1026 fontOld.GetFamily(),
1027 fontOld.GetStyle(),
1028 wxBOLD,
1029 fontOld.GetUnderlined());
1030 dc.SetFont(fontNew);
1031 }
1032 else
1033 {
1034 wxFAIL_MSG("wxDC::GetFont() failed!");
1035 }
1036 }
1037
1038 long text_w = 0;
1039 long text_h = 0;
1040 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
1041
1042 int image_h = 0;
1043 int image_w = 0;
1044 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1045 {
1046 m_imageListNormal->GetSize( item->GetSelectedImage(), image_w, image_h );
1047 image_w += 4;
1048 }
1049 else if (item->GetImage() != -1)
1050 {
1051 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
1052 image_w += 4;
1053 }
1054
1055 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
1056
1057 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1058 {
1059 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1060 m_imageListNormal->Draw( item->GetSelectedImage(), dc,
1061 item->GetX(), item->GetY()-1,
1062 wxIMAGELIST_DRAW_TRANSPARENT );
1063 dc.DestroyClippingRegion();
1064 }
1065 else if (item->GetImage() != -1)
1066 {
1067 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1068 m_imageListNormal->Draw( item->GetImage(), dc,
1069 item->GetX(), item->GetY()-1,
1070 wxIMAGELIST_DRAW_TRANSPARENT );
1071 dc.DestroyClippingRegion();
1072 }
1073
1074 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY() );
1075
1076 // restore normal font for bold items
1077 if (fontOld.Ok())
1078 {
1079 dc.SetFont( fontOld);
1080 }
1081 }
1082
1083 void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1084 {
1085 int horizX = level*m_indent;
1086
1087 item->SetX( horizX+33 );
1088 item->SetY( y-m_lineHeight/3 );
1089 item->SetHeight( m_lineHeight );
1090
1091 item->SetCross( horizX+15, y );
1092
1093 int oldY = y;
1094
1095 int exposed_x = dc.LogicalToDeviceX( 0 );
1096 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
1097
1098 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
1099 {
1100 int startX = horizX;
1101 int endX = horizX + 10;
1102
1103 if (!item->HasChildren()) endX += 20;
1104
1105 dc.DrawLine( startX, y, endX, y );
1106
1107 if (item->HasPlus())
1108 {
1109 dc.DrawLine( horizX+20, y, horizX+30, y );
1110 dc.SetPen( *wxGREY_PEN );
1111 dc.SetBrush( *wxWHITE_BRUSH );
1112 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
1113 dc.SetPen( *wxBLACK_PEN );
1114 dc.DrawLine( horizX+13, y, horizX+18, y );
1115
1116 if (!item->IsExpanded())
1117 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
1118 }
1119
1120 if (item->HasHilight())
1121 {
1122 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
1123
1124 dc.SetBrush( *m_hilightBrush );
1125
1126 if (m_hasFocus)
1127 dc.SetPen( *wxBLACK_PEN );
1128 else
1129 dc.SetPen( *wxTRANSPARENT_PEN );
1130
1131 PaintItem(item, dc);
1132
1133 dc.SetPen( *wxBLACK_PEN );
1134 dc.SetTextForeground( *wxBLACK );
1135 dc.SetBrush( *wxWHITE_BRUSH );
1136 }
1137 else
1138 {
1139 dc.SetBrush( *wxWHITE_BRUSH );
1140 dc.SetPen( *wxTRANSPARENT_PEN );
1141
1142 PaintItem(item, dc);
1143
1144 dc.SetPen( *wxBLACK_PEN );
1145 }
1146 }
1147
1148 if ( item->IsExpanded() )
1149 {
1150 int semiOldY = y;
1151
1152 wxArrayTreeItems& children = item->GetChildren();
1153 size_t count = children.Count();
1154 for ( size_t n = 0; n < count; n++ )
1155 {
1156 y += m_lineHeight;
1157 semiOldY = y;
1158
1159 PaintLevel( children[n], dc, level+1, y );
1160 }
1161
1162 // it may happen that the item is expanded but has no items (when you
1163 // delete all its children for example) - don't draw the vertical line
1164 // in this case
1165 if ( count > 0 )
1166 dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1167 }
1168 }
1169
1170 // -----------------------------------------------------------------------------
1171 // wxWindows callbacks
1172 // -----------------------------------------------------------------------------
1173
1174 void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1175 {
1176 if ( !m_anchor )
1177 return;
1178
1179 wxPaintDC dc(this);
1180 PrepareDC( dc );
1181
1182 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1183
1184 dc.SetPen( m_dottedPen );
1185 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1186
1187 int y = m_lineHeight / 2 + 2;
1188 PaintLevel( m_anchor, dc, 0, y );
1189 }
1190
1191 void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1192 {
1193 m_hasFocus = TRUE;
1194 if ( m_current )
1195 RefreshLine( m_current );
1196 }
1197
1198 void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1199 {
1200 m_hasFocus = FALSE;
1201 if ( m_current )
1202 RefreshLine( m_current );
1203 }
1204
1205 void wxTreeCtrl::OnChar( wxKeyEvent &event )
1206 {
1207 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1208 te.m_code = event.KeyCode();
1209 te.SetEventObject( this );
1210 GetEventHandler()->ProcessEvent( te );
1211
1212 if (m_current == 0)
1213 {
1214 event.Skip();
1215 return;
1216 }
1217
1218 switch (event.KeyCode())
1219 {
1220 case '+':
1221 case WXK_ADD:
1222 if (m_current->HasPlus() && !IsExpanded(m_current))
1223 {
1224 Expand(m_current);
1225 }
1226 break;
1227
1228 case '-':
1229 case WXK_SUBTRACT:
1230 if (IsExpanded(m_current))
1231 {
1232 Collapse(m_current);
1233 }
1234 break;
1235
1236 case '*':
1237 case WXK_MULTIPLY:
1238 Toggle(m_current);
1239 break;
1240
1241 case ' ':
1242 case WXK_RETURN:
1243 {
1244 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1245 event.m_item = m_current;
1246 event.m_code = 0;
1247 event.SetEventObject( this );
1248 GetEventHandler()->ProcessEvent( event );
1249 }
1250 break;
1251
1252 case WXK_LEFT:
1253 case WXK_UP:
1254 {
1255 wxTreeItemId prev = GetPrevSibling( m_current );
1256 if (prev != 0)
1257 {
1258 SelectItem( prev );
1259 EnsureVisible( prev );
1260 }
1261 else
1262 {
1263 prev = GetParent( m_current );
1264 if (prev)
1265 {
1266 EnsureVisible( prev );
1267 SelectItem( prev );
1268 }
1269 }
1270 }
1271 break;
1272
1273 case WXK_RIGHT:
1274 // this works the same as the down arrow except that we also expand the
1275 // item if it wasn't expanded yet
1276 Expand(m_current);
1277 // fall through
1278
1279 case WXK_DOWN:
1280 {
1281 if (IsExpanded(m_current))
1282 {
1283 long cookie = 0;
1284 wxTreeItemId child = GetFirstChild( m_current, cookie );
1285 SelectItem( child );
1286 EnsureVisible( child );
1287 }
1288 else
1289 {
1290 wxTreeItemId next = GetNextSibling( m_current );
1291 if (next == 0)
1292 {
1293 wxTreeItemId current = m_current;
1294 while (current && !next)
1295 {
1296 current = GetParent( current );
1297 if (current) next = GetNextSibling( current );
1298 }
1299 }
1300 if (next != 0)
1301 {
1302 SelectItem( next );
1303 EnsureVisible( next );
1304 }
1305 }
1306 }
1307 break;
1308
1309 default:
1310 event.Skip();
1311 }
1312 }
1313
1314 wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& WXUNUSED(flags))
1315 {
1316 bool onButton = FALSE;
1317 return m_anchor->HitTest( point, onButton );
1318 }
1319
1320 void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1321 {
1322 if ( !(event.LeftDown() || event.LeftDClick()) )
1323 return;
1324
1325 if ( !m_anchor )
1326 return;
1327
1328 wxClientDC dc(this);
1329 PrepareDC(dc);
1330 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1331 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1332
1333 bool onButton = FALSE;
1334 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1335 if ( item == NULL )
1336 return;
1337
1338 if (!IsSelected(item)) SelectItem(item);
1339
1340 if ( event.LeftDClick() )
1341 {
1342 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1343 event.m_item = item;
1344 event.m_code = 0;
1345 event.SetEventObject( this );
1346 GetEventHandler()->ProcessEvent( event );
1347 }
1348
1349 if ( onButton )
1350 {
1351 Toggle( item );
1352 }
1353 }
1354
1355 void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1356 {
1357 if (!m_dirty) return;
1358
1359 m_dirty = FALSE;
1360
1361 CalculatePositions();
1362
1363 AdjustMyScrollbars();
1364 }
1365
1366 // -----------------------------------------------------------------------------
1367 // -----------------------------------------------------------------------------
1368 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item,
1369 wxDC &dc,
1370 int level,
1371 int &y )
1372 {
1373 int horizX = level*m_indent;
1374
1375 item->SetX( horizX+33 );
1376 item->SetY( y-m_lineHeight/3-2 );
1377 item->SetHeight( m_lineHeight );
1378
1379 if ( item->IsExpanded() )
1380 return;
1381
1382 wxArrayTreeItems& children = item->GetChildren();
1383 size_t count = children.Count();
1384 for ( size_t n = 0; n < count; n++ )
1385 {
1386 y += m_lineHeight;
1387 CalculateLevel( children[n], dc, level+1, y );
1388 }
1389 }
1390
1391 void wxTreeCtrl::CalculatePositions()
1392 {
1393 if ( !m_anchor )
1394 return;
1395
1396 wxClientDC dc(this);
1397 PrepareDC( dc );
1398
1399 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT ) );
1400
1401 dc.SetPen( m_dottedPen );
1402 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1403
1404 int y = m_lineHeight / 2 + 2;
1405 CalculateLevel( m_anchor, dc, 0, y );
1406 }
1407
1408 void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1409 {
1410 wxClientDC dc(this);
1411 PrepareDC(dc);
1412
1413 int cw = 0;
1414 int ch = 0;
1415 GetClientSize( &cw, &ch );
1416
1417 wxRect rect;
1418 rect.x = dc.LogicalToDeviceX( 0 );
1419 rect.width = cw;
1420 rect.y = dc.LogicalToDeviceY( item->GetY() );
1421 rect.height = ch;
1422
1423 Refresh( TRUE, &rect );
1424
1425 AdjustMyScrollbars();
1426 }
1427
1428 void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1429 {
1430 wxClientDC dc(this);
1431 PrepareDC( dc );
1432
1433 wxRect rect;
1434 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1435 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1436 rect.width = 1000;
1437 rect.height = dc.GetCharHeight() + 6;
1438 Refresh( TRUE, &rect );
1439 }
1440