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