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