]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/treectrl.cpp
latest CW additions
[wxWidgets.git] / src / generic / treectrl.cpp
... / ...
CommitLineData
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
44class WXDLLEXPORT wxGenericTreeItem;
45
46WX_DEFINE_ARRAY(wxGenericTreeItem *, wxArrayTreeItems);
47
48// -----------------------------------------------------------------------------
49// private classes
50// -----------------------------------------------------------------------------
51
52// a tree item
53class WXDLLEXPORT wxGenericTreeItem
54{
55public:
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
126private:
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
157IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
158
159wxTreeEvent::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
170wxGenericTreeItem::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
195wxGenericTreeItem::~wxGenericTreeItem()
196{
197 delete m_data;
198
199 wxASSERT_MSG( m_children.IsEmpty(),
200 "please call DeleteChildren() before deleting the item" );
201}
202
203void 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
221void wxGenericTreeItem::SetText( const wxString &text, wxDC& dc )
222{
223 m_text = text;
224
225 dc.GetTextExtent( m_text, &m_width, &m_height );
226}
227
228void 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
247size_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
262void wxGenericTreeItem::SetCross( int x, int y )
263{
264 m_xCross = x;
265 m_yCross = y;
266}
267
268void 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
284wxGenericTreeItem *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
328IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxScrolledWindow)
329
330BEGIN_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)
337END_EVENT_TABLE()
338
339// -----------------------------------------------------------------------------
340// construction/destruction
341// -----------------------------------------------------------------------------
342void 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 m_dragCount = 0;
364}
365
366bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
367 const wxPoint& pos, const wxSize& size,
368 long style,
369 const wxValidator &validator,
370 const wxString& name )
371{
372 Init();
373
374 wxScrolledWindow::Create( parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name );
375
376 SetValidator( validator );
377
378 SetBackgroundColour( *wxWHITE );
379 m_dottedPen = wxPen( *wxBLACK, 0, 0 );
380
381 return TRUE;
382}
383
384wxTreeCtrl::~wxTreeCtrl()
385{
386 wxDELETE( m_hilightBrush );
387
388 DeleteAllItems();
389}
390
391// -----------------------------------------------------------------------------
392// accessors
393// -----------------------------------------------------------------------------
394
395size_t wxTreeCtrl::GetCount() const
396{
397 return m_anchor == NULL ? 0u : m_anchor->GetChildrenCount();
398}
399
400void wxTreeCtrl::SetIndent(unsigned int indent)
401{
402 m_indent = indent;
403 Refresh();
404}
405
406size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
407{
408 wxCHECK_MSG( item.IsOk(), 0u, "invalid tree item" );
409
410 return item.m_pItem->GetChildrenCount(recursively);
411}
412
413// -----------------------------------------------------------------------------
414// functions to work with tree items
415// -----------------------------------------------------------------------------
416
417wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
418{
419 wxCHECK_MSG( item.IsOk(), "", "invalid tree item" );
420
421 return item.m_pItem->GetText();
422}
423
424int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
425{
426 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
427
428 return item.m_pItem->GetImage();
429}
430
431int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
432{
433 wxCHECK_MSG( item.IsOk(), -1, "invalid tree item" );
434
435 return item.m_pItem->GetSelectedImage();
436}
437
438wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
439{
440 wxCHECK_MSG( item.IsOk(), NULL, "invalid tree item" );
441
442 return item.m_pItem->GetData();
443}
444
445void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
446{
447 wxCHECK_RET( item.IsOk(), "invalid tree item" );
448
449 wxClientDC dc(this);
450 item.m_pItem->SetText(text, dc);
451}
452
453void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
454{
455 wxCHECK_RET( item.IsOk(), "invalid tree item" );
456
457 item.m_pItem->SetImage(image);
458}
459
460void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
461{
462 wxCHECK_RET( item.IsOk(), "invalid tree item" );
463
464 item.m_pItem->SetSelectedImage(image);
465}
466
467void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
468{
469 wxCHECK_RET( item.IsOk(), "invalid tree item" );
470
471 item.m_pItem->SetData(data);
472}
473
474void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
475{
476 wxCHECK_RET( item.IsOk(), "invalid tree item" );
477
478 item.m_pItem->SetHasPlus(has);
479}
480
481void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
482{
483 wxCHECK_RET( item.IsOk(), "invalid tree item" );
484
485 // avoid redrawing the tree if no real change
486 wxGenericTreeItem *pItem = item.m_pItem;
487 if ( pItem->IsBold() != bold )
488 {
489 pItem->SetBold(bold);
490 RefreshLine(pItem);
491 }
492}
493
494// -----------------------------------------------------------------------------
495// item status inquiries
496// -----------------------------------------------------------------------------
497
498bool wxTreeCtrl::IsVisible(const wxTreeItemId& WXUNUSED(item)) const
499{
500 wxFAIL_MSG("not implemented");
501
502 return TRUE;
503}
504
505bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
506{
507 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
508
509 return !item.m_pItem->GetChildren().IsEmpty();
510}
511
512bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
513{
514 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
515
516 return item.m_pItem->IsExpanded();
517}
518
519bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
520{
521 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
522
523 return item.m_pItem->HasHilight();
524}
525
526bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
527{
528 wxCHECK_MSG( item.IsOk(), FALSE, "invalid tree item" );
529
530 return item.m_pItem->IsBold();
531}
532
533// -----------------------------------------------------------------------------
534// navigation
535// -----------------------------------------------------------------------------
536
537wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
538{
539 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
540
541 return item.m_pItem->GetParent();
542}
543
544wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item, long& cookie) const
545{
546 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
547
548 cookie = 0;
549 return GetNextChild(item, cookie);
550}
551
552wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& item, long& cookie) const
553{
554 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
555
556 wxArrayTreeItems& children = item.m_pItem->GetChildren();
557 if ( (size_t)cookie < children.Count() )
558 {
559 return children.Item(cookie++);
560 }
561 else
562 {
563 // there are no more of them
564 return wxTreeItemId();
565 }
566}
567
568wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
569{
570 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
571
572 wxArrayTreeItems& children = item.m_pItem->GetChildren();
573 return (children.IsEmpty() ? wxTreeItemId() : wxTreeItemId(children.Last()));
574}
575
576wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
577{
578 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
579
580 wxGenericTreeItem *i = item.m_pItem;
581 wxGenericTreeItem *parent = i->GetParent();
582 if ( parent == NULL )
583 {
584 // root item doesn't have any siblings
585 return wxTreeItemId();
586 }
587
588 wxArrayTreeItems& siblings = parent->GetChildren();
589 int index = siblings.Index(i);
590 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
591
592 size_t n = (size_t)(index + 1);
593 return n == siblings.Count() ? wxTreeItemId() : wxTreeItemId(siblings[n]);
594}
595
596wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
597{
598 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
599
600 wxGenericTreeItem *i = item.m_pItem;
601 wxGenericTreeItem *parent = i->GetParent();
602 if ( parent == NULL )
603 {
604 // root item doesn't have any siblings
605 return wxTreeItemId();
606 }
607
608 wxArrayTreeItems& siblings = parent->GetChildren();
609 int index = siblings.Index(i);
610 wxASSERT( index != wxNOT_FOUND ); // I'm not a child of my parent?
611
612 return index == 0 ? wxTreeItemId()
613 : wxTreeItemId(siblings[(size_t)(index - 1)]);
614}
615
616wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
617{
618 wxFAIL_MSG("not implemented");
619
620 return wxTreeItemId();
621}
622
623wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
624{
625 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
626
627 wxFAIL_MSG("not implemented");
628
629 return wxTreeItemId();
630}
631
632wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
633{
634 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), "invalid tree item" );
635
636 wxFAIL_MSG("not implemented");
637
638 return wxTreeItemId();
639}
640
641// -----------------------------------------------------------------------------
642// operations
643// -----------------------------------------------------------------------------
644
645wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parentId,
646 size_t previous,
647 const wxString& text,
648 int image, int selImage,
649 wxTreeItemData *data)
650{
651 wxGenericTreeItem *parent = parentId.m_pItem;
652 if ( !parent )
653 {
654 // should we give a warning here?
655 return AddRoot(text, image, selImage, data);
656 }
657
658 wxClientDC dc(this);
659 wxGenericTreeItem *item = new wxGenericTreeItem(parent,
660 text, dc,
661 image, selImage,
662 data);
663
664 if ( data != NULL )
665 {
666 data->m_pItem = item;
667 }
668
669 parent->Insert( item, previous );
670
671 m_dirty = TRUE;
672
673 return item;
674}
675
676wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
677 int image, int selImage,
678 wxTreeItemData *data)
679{
680 wxCHECK_MSG( !m_anchor, wxTreeItemId(), "tree can have only one root" );
681
682 wxClientDC dc(this);
683 m_anchor = new wxGenericTreeItem((wxGenericTreeItem *)NULL, text, dc,
684 image, selImage, data);
685 if ( data != NULL )
686 {
687 data->m_pItem = m_anchor;
688 }
689
690 AdjustMyScrollbars();
691 Refresh();
692
693 return m_anchor;
694}
695
696wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
697 const wxString& text,
698 int image, int selImage,
699 wxTreeItemData *data)
700{
701 return DoInsertItem(parent, 0u, text, image, selImage, data);
702}
703
704wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parentId,
705 const wxTreeItemId& idPrevious,
706 const wxString& text,
707 int image, int selImage,
708 wxTreeItemData *data)
709{
710 wxGenericTreeItem *parent = parentId.m_pItem;
711 if ( !parent )
712 {
713 // should we give a warning here?
714 return AddRoot(text, image, selImage, data);
715 }
716
717 int index = parent->GetChildren().Index(idPrevious.m_pItem);
718 wxASSERT_MSG( index != wxNOT_FOUND,
719 "previous item in wxTreeCtrl::InsertItem() is not a sibling" );
720 return DoInsertItem(parentId, (size_t)index, text, image, selImage, data);
721}
722
723wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parentId,
724 const wxString& text,
725 int image, int selImage,
726 wxTreeItemData *data)
727{
728 wxGenericTreeItem *parent = parentId.m_pItem;
729 if ( !parent )
730 {
731 // should we give a warning here?
732 return AddRoot(text, image, selImage, data);
733 }
734
735 return DoInsertItem(parent, parent->GetChildren().Count(), text,
736 image, selImage, data);
737}
738
739void wxTreeCtrl::SendDeleteEvent(wxGenericTreeItem *item)
740{
741 wxTreeEvent event( wxEVT_COMMAND_TREE_DELETE_ITEM, GetId() );
742 event.m_item = item;
743 event.SetEventObject( this );
744 ProcessEvent( event );
745}
746
747void wxTreeCtrl::DeleteChildren(const wxTreeItemId& itemId)
748{
749 wxGenericTreeItem *item = itemId.m_pItem;
750 item->DeleteChildren(this);
751
752 m_dirty = TRUE;
753}
754
755void wxTreeCtrl::Delete(const wxTreeItemId& itemId)
756{
757 wxGenericTreeItem *item = itemId.m_pItem;
758 wxGenericTreeItem *parent = item->GetParent();
759
760 if ( parent )
761 {
762 parent->GetChildren().Remove(item);
763 }
764
765 item->DeleteChildren(this);
766 SendDeleteEvent(item);
767 delete item;
768
769 m_dirty = TRUE;
770}
771
772void wxTreeCtrl::DeleteAllItems()
773{
774 if ( m_anchor )
775 {
776 m_anchor->DeleteChildren(this);
777 delete m_anchor;
778
779 m_anchor = NULL;
780
781 m_dirty = TRUE;
782 }
783}
784
785void wxTreeCtrl::Expand(const wxTreeItemId& itemId)
786{
787 wxGenericTreeItem *item = itemId.m_pItem;
788
789 if ( !item->HasPlus() )
790 return;
791
792 if ( item->IsExpanded() )
793 return;
794
795 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_EXPANDING, GetId() );
796 event.m_item = item;
797 event.SetEventObject( this );
798 if ( ProcessEvent( event ) && event.m_code )
799 {
800 // cancelled by program
801 return;
802 }
803
804 item->Expand();
805 CalculatePositions();
806
807 RefreshSubtree(item);
808
809 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_EXPANDED);
810 ProcessEvent( event );
811}
812
813void wxTreeCtrl::Collapse(const wxTreeItemId& itemId)
814{
815 wxGenericTreeItem *item = itemId.m_pItem;
816
817 if ( !item->IsExpanded() )
818 return;
819
820 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, GetId() );
821 event.m_item = item;
822 event.SetEventObject( this );
823 if ( ProcessEvent( event ) && event.m_code )
824 {
825 // cancelled by program
826 return;
827 }
828
829 item->Collapse();
830
831 wxArrayTreeItems& children = item->GetChildren();
832 size_t count = children.Count();
833 for ( size_t n = 0; n < count; n++ )
834 {
835 Collapse(children[n]);
836 }
837
838 CalculatePositions();
839
840 RefreshSubtree(item);
841
842 event.SetEventType(wxEVT_COMMAND_TREE_ITEM_COLLAPSED);
843 ProcessEvent( event );
844}
845
846void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
847{
848 Collapse(item);
849 DeleteChildren(item);
850}
851
852void wxTreeCtrl::Toggle(const wxTreeItemId& itemId)
853{
854 wxGenericTreeItem *item = itemId.m_pItem;
855
856 if ( item->IsExpanded() )
857 Collapse(itemId);
858 else
859 Expand(itemId);
860}
861
862void wxTreeCtrl::Unselect()
863{
864 if ( m_current )
865 {
866 m_current->SetHilight( FALSE );
867 RefreshLine( m_current );
868 }
869}
870
871void wxTreeCtrl::SelectItem(const wxTreeItemId& itemId)
872{
873 wxGenericTreeItem *item = itemId.m_pItem;
874
875 if ( m_current != item )
876 {
877 wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, GetId() );
878 event.m_item = item;
879 event.m_itemOld = m_current;
880 event.SetEventObject( this );
881 if ( GetEventHandler()->ProcessEvent( event ) && event.WasVetoed() )
882 return;
883
884 if ( m_current )
885 {
886 m_current->SetHilight( FALSE );
887 RefreshLine( m_current );
888 }
889
890 m_current = item;
891 m_current->SetHilight( TRUE );
892 RefreshLine( m_current );
893
894 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
895 GetEventHandler()->ProcessEvent( event );
896 }
897}
898
899void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
900{
901 wxGenericTreeItem *gitem = item.m_pItem;
902
903 int item_y = gitem->GetY();
904
905 int start_x = 0;
906 int start_y = 0;
907 ViewStart( &start_x, &start_y );
908 start_y *= 10;
909
910 int client_h = 0;
911 int client_w = 0;
912 GetClientSize( &client_w, &client_h );
913
914 if (item_y < start_y+3)
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 if (item_y > start_y+client_h-16)
926 {
927 int x = 0;
928 int y = 0;
929 m_anchor->GetSize( x, y );
930 y += 2*m_lineHeight;
931 int x_pos = GetScrollPos( wxHORIZONTAL );
932 SetScrollbars( 10, 10, x/10, y/10, x_pos, (item_y-client_h/2)/10 );
933 return;
934 }
935}
936
937void wxTreeCtrl::ScrollTo(const wxTreeItemId& WXUNUSED(item))
938{
939 wxFAIL_MSG("not implemented");
940}
941
942wxTextCtrl *wxTreeCtrl::EditLabel( const wxTreeItemId& WXUNUSED(item),
943 wxClassInfo* WXUNUSED(textCtrlClass) )
944{
945 wxFAIL_MSG("not implemented");
946
947 return (wxTextCtrl*)NULL;
948}
949
950wxTextCtrl *wxTreeCtrl::GetEditControl() const
951{
952 wxFAIL_MSG("not implemented");
953
954 return (wxTextCtrl*)NULL;
955}
956
957void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool WXUNUSED(discardChanges))
958{
959 wxFAIL_MSG("not implemented");
960}
961
962// FIXME: tree sorting functions are not reentrant and not MT-safe!
963static wxTreeCtrl *s_treeBeingSorted = NULL;
964
965static int tree_ctrl_compare_func(wxGenericTreeItem **item1,
966 wxGenericTreeItem **item2)
967{
968 wxCHECK_MSG( s_treeBeingSorted, 0, "bug in wxTreeCtrl::SortChildren()" );
969
970 return s_treeBeingSorted->OnCompareItems(*item1, *item2);
971}
972
973int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
974 const wxTreeItemId& item2)
975{
976 return strcmp(GetItemText(item1), GetItemText(item2));
977}
978
979void wxTreeCtrl::SortChildren(const wxTreeItemId& itemId)
980{
981 wxCHECK_RET( itemId.IsOk(), "invalid tree item" );
982
983 wxGenericTreeItem *item = itemId.m_pItem;
984
985 wxCHECK_RET( !s_treeBeingSorted,
986 "wxTreeCtrl::SortChildren is not reentrant" );
987
988 wxArrayTreeItems& children = item->GetChildren();
989 if ( children.Count() > 1 )
990 {
991 s_treeBeingSorted = this;
992 children.Sort(tree_ctrl_compare_func);
993 s_treeBeingSorted = NULL;
994
995 m_dirty = TRUE;
996 }
997 //else: don't make the tree dirty as nothing changed
998}
999
1000wxImageList *wxTreeCtrl::GetImageList() const
1001{
1002 return m_imageListNormal;
1003}
1004
1005wxImageList *wxTreeCtrl::GetStateImageList() const
1006{
1007 return m_imageListState;
1008}
1009
1010void wxTreeCtrl::SetImageList(wxImageList *imageList)
1011{
1012 m_imageListNormal = imageList;
1013}
1014
1015void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
1016{
1017 m_imageListState = imageList;
1018}
1019
1020// -----------------------------------------------------------------------------
1021// helpers
1022// -----------------------------------------------------------------------------
1023
1024void wxTreeCtrl::AdjustMyScrollbars()
1025{
1026 if (m_anchor)
1027 {
1028 int x = 0;
1029 int y = 0;
1030 m_anchor->GetSize( x, y );
1031 y += 2*m_lineHeight;
1032 int x_pos = GetScrollPos( wxHORIZONTAL );
1033 int y_pos = GetScrollPos( wxVERTICAL );
1034 SetScrollbars( 10, 10, x/10, y/10, x_pos, y_pos );
1035 }
1036 else
1037 {
1038 SetScrollbars( 0, 0, 0, 0 );
1039 }
1040}
1041
1042void wxTreeCtrl::PaintItem(wxGenericTreeItem *item, wxDC& dc)
1043{
1044 /* render bold items in bold */
1045 wxFont fontOld;
1046 wxFont fontNew;
1047
1048 if (item->IsBold())
1049 {
1050 fontOld = dc.GetFont();
1051 if (fontOld.Ok())
1052 {
1053 /* @@ is there any better way to make a bold variant of old font? */
1054 fontNew = wxFont( fontOld.GetPointSize(),
1055 fontOld.GetFamily(),
1056 fontOld.GetStyle(),
1057 wxBOLD,
1058 fontOld.GetUnderlined());
1059 dc.SetFont(fontNew);
1060 }
1061 else
1062 {
1063 wxFAIL_MSG("wxDC::GetFont() failed!");
1064 }
1065 }
1066
1067 long text_w = 0;
1068 long text_h = 0;
1069 dc.GetTextExtent( item->GetText(), &text_w, &text_h );
1070
1071 int image_h = 0;
1072 int image_w = 0;
1073 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1074 {
1075 m_imageListNormal->GetSize( item->GetSelectedImage(), image_w, image_h );
1076 image_w += 4;
1077 }
1078 else if (item->GetImage() != -1)
1079 {
1080 m_imageListNormal->GetSize( item->GetImage(), image_w, image_h );
1081 image_w += 4;
1082 }
1083
1084 dc.DrawRectangle( item->GetX()-2, item->GetY()-2, image_w+text_w+4, text_h+4 );
1085
1086 if ((item->IsExpanded()) && (item->GetSelectedImage() != -1))
1087 {
1088 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1089 m_imageListNormal->Draw( item->GetSelectedImage(), dc,
1090 item->GetX(), item->GetY()-1,
1091 wxIMAGELIST_DRAW_TRANSPARENT );
1092 dc.DestroyClippingRegion();
1093 }
1094 else if (item->GetImage() != -1)
1095 {
1096 dc.SetClippingRegion( item->GetX(), item->GetY(), image_w-2, text_h );
1097 m_imageListNormal->Draw( item->GetImage(), dc,
1098 item->GetX(), item->GetY()-1,
1099 wxIMAGELIST_DRAW_TRANSPARENT );
1100 dc.DestroyClippingRegion();
1101 }
1102
1103 dc.SetBackgroundMode(wxTRANSPARENT);
1104 dc.DrawText( item->GetText(), image_w + item->GetX(), item->GetY() );
1105
1106 /* restore normal font for bold items */
1107 if (fontOld.Ok())
1108 {
1109 dc.SetFont( fontOld);
1110 }
1111}
1112
1113void wxTreeCtrl::PaintLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1114{
1115 int horizX = level*m_indent;
1116
1117 item->SetX( horizX+33 );
1118 item->SetY( y-m_lineHeight/3 );
1119 item->SetHeight( m_lineHeight );
1120
1121 item->SetCross( horizX+15, y );
1122
1123 int oldY = y;
1124
1125 int exposed_x = dc.LogicalToDeviceX( 0 );
1126 int exposed_y = dc.LogicalToDeviceY( item->GetY()-2 );
1127
1128 if (IsExposed( exposed_x, exposed_y, 10000, m_lineHeight+4 )) // 10000 = very much
1129 {
1130 int startX = horizX;
1131 int endX = horizX + 10;
1132
1133 if (!item->HasChildren()) endX += 20;
1134
1135 dc.DrawLine( startX, y, endX, y );
1136
1137 if (item->HasPlus())
1138 {
1139 dc.DrawLine( horizX+20, y, horizX+30, y );
1140 dc.SetPen( *wxGREY_PEN );
1141 dc.SetBrush( *wxWHITE_BRUSH );
1142 dc.DrawRectangle( horizX+10, y-4, 11, 9 );
1143 dc.SetPen( *wxBLACK_PEN );
1144 dc.DrawLine( horizX+13, y, horizX+18, y );
1145
1146 if (!item->IsExpanded())
1147 {
1148 dc.DrawLine( horizX+15, y-2, horizX+15, y+3 );
1149 }
1150 }
1151
1152 if (item->HasHilight())
1153 {
1154 dc.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
1155
1156 dc.SetBrush( *m_hilightBrush );
1157
1158 if (m_hasFocus)
1159 dc.SetPen( *wxBLACK_PEN );
1160 else
1161 dc.SetPen( *wxTRANSPARENT_PEN );
1162
1163 PaintItem(item, dc);
1164
1165 dc.SetPen( *wxBLACK_PEN );
1166 dc.SetTextForeground( *wxBLACK );
1167 dc.SetBrush( *wxWHITE_BRUSH );
1168 }
1169 else
1170 {
1171 dc.SetBrush( *wxWHITE_BRUSH );
1172 dc.SetPen( *wxTRANSPARENT_PEN );
1173
1174 PaintItem(item, dc);
1175
1176 dc.SetPen( *wxBLACK_PEN );
1177 }
1178 }
1179
1180 if (item->IsExpanded())
1181 {
1182 int semiOldY = y;
1183
1184 wxArrayTreeItems& children = item->GetChildren();
1185 size_t count = children.Count();
1186 for ( size_t n = 0; n < count; n++ )
1187 {
1188 y += m_lineHeight;
1189 semiOldY = y;
1190 PaintLevel( children[n], dc, level+1, y );
1191 }
1192
1193 /* it may happen that the item is expanded but has no items (when you
1194 * delete all its children for example) - don't draw the vertical line
1195 * in this case */
1196 if (count > 0) dc.DrawLine( horizX+15, oldY+5, horizX+15, semiOldY );
1197 }
1198}
1199
1200// -----------------------------------------------------------------------------
1201// wxWindows callbacks
1202// -----------------------------------------------------------------------------
1203
1204void wxTreeCtrl::OnPaint( wxPaintEvent &WXUNUSED(event) )
1205{
1206 if ( !m_anchor )
1207 return;
1208
1209 wxPaintDC dc(this);
1210 PrepareDC( dc );
1211
1212 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ) );
1213
1214 dc.SetPen( m_dottedPen );
1215 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1216
1217 int y = m_lineHeight / 2 + 2;
1218 PaintLevel( m_anchor, dc, 0, y );
1219}
1220
1221void wxTreeCtrl::OnSetFocus( wxFocusEvent &WXUNUSED(event) )
1222{
1223 m_hasFocus = TRUE;
1224
1225 if (m_current) RefreshLine( m_current );
1226}
1227
1228void wxTreeCtrl::OnKillFocus( wxFocusEvent &WXUNUSED(event) )
1229{
1230 m_hasFocus = FALSE;
1231
1232 if (m_current) RefreshLine( m_current );
1233}
1234
1235void wxTreeCtrl::OnChar( wxKeyEvent &event )
1236{
1237 wxTreeEvent te( wxEVT_COMMAND_TREE_KEY_DOWN, GetId() );
1238 te.m_code = event.KeyCode();
1239 te.SetEventObject( this );
1240 GetEventHandler()->ProcessEvent( te );
1241
1242 if (m_current == 0)
1243 {
1244 event.Skip();
1245 return;
1246 }
1247
1248 switch (event.KeyCode())
1249 {
1250 case '+':
1251 case WXK_ADD:
1252 if (m_current->HasPlus() && !IsExpanded(m_current))
1253 {
1254 Expand(m_current);
1255 }
1256 break;
1257
1258 case '-':
1259 case WXK_SUBTRACT:
1260 if (IsExpanded(m_current))
1261 {
1262 Collapse(m_current);
1263 }
1264 break;
1265
1266 case '*':
1267 case WXK_MULTIPLY:
1268 Toggle(m_current);
1269 break;
1270
1271 case ' ':
1272 case WXK_RETURN:
1273 {
1274 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1275 event.m_item = m_current;
1276 event.m_code = 0;
1277 event.SetEventObject( this );
1278 GetEventHandler()->ProcessEvent( event );
1279 }
1280 break;
1281
1282 // up goes to the previous sibling or to the last of its children if
1283 // it's expanded
1284 case WXK_UP:
1285 {
1286 wxTreeItemId prev = GetPrevSibling( m_current );
1287 if (!prev)
1288 {
1289 prev = GetParent( m_current );
1290 long cockie = 0;
1291 wxTreeItemId current = m_current;
1292 if (current == GetFirstChild( prev, cockie ))
1293 {
1294 // otherwise we return to where we came from
1295 SelectItem( prev );
1296 EnsureVisible( prev );
1297 break;
1298 }
1299 }
1300 if (prev)
1301 {
1302 while (IsExpanded(prev))
1303 {
1304 int c = (int)GetChildrenCount( prev, FALSE );
1305 long cockie = 0;
1306 prev = GetFirstChild( prev, cockie );
1307 for (int i = 0; i < c-1; i++)
1308 prev = GetNextSibling( prev );
1309 }
1310 SelectItem( prev );
1311 EnsureVisible( prev );
1312 }
1313 }
1314 break;
1315
1316 // left arrow goes to the parent
1317 case WXK_LEFT:
1318 {
1319 wxTreeItemId prev = GetParent( m_current );
1320 if (prev)
1321 {
1322 EnsureVisible( prev );
1323 SelectItem( prev );
1324 }
1325 }
1326 break;
1327
1328 case WXK_RIGHT:
1329 // this works the same as the down arrow except that we also expand the
1330 // item if it wasn't expanded yet
1331 Expand(m_current);
1332 // fall through
1333
1334 case WXK_DOWN:
1335 {
1336 if (IsExpanded(m_current))
1337 {
1338 long cookie = 0;
1339 wxTreeItemId child = GetFirstChild( m_current, cookie );
1340 SelectItem( child );
1341 EnsureVisible( child );
1342 }
1343 else
1344 {
1345 wxTreeItemId next = GetNextSibling( m_current );
1346 if (next == 0)
1347 {
1348 wxTreeItemId current = m_current;
1349 while (current && !next)
1350 {
1351 current = GetParent( current );
1352 if (current) next = GetNextSibling( current );
1353 }
1354 }
1355 if (next != 0)
1356 {
1357 SelectItem( next );
1358 EnsureVisible( next );
1359 }
1360 }
1361 }
1362 break;
1363
1364 // <End> selects the last visible tree item
1365 case WXK_END:
1366 {
1367 wxTreeItemId last = GetRootItem();
1368
1369 while ( last.IsOk() && IsExpanded(last) )
1370 {
1371 wxTreeItemId lastChild = GetLastChild(last);
1372
1373 // it may happen if the item was expanded but then all of
1374 // its children have been deleted - so IsExpanded() returned
1375 // TRUE, but GetLastChild() returned invalid item
1376 if ( !lastChild )
1377 break;
1378
1379 last = lastChild;
1380 }
1381
1382 if ( last.IsOk() )
1383 {
1384 EnsureVisible( last );
1385 SelectItem( last );
1386 }
1387 }
1388 break;
1389
1390 // <Home> selects the root item
1391 case WXK_HOME:
1392 {
1393 wxTreeItemId prev = GetRootItem();
1394 if (prev)
1395 {
1396 EnsureVisible( prev );
1397 SelectItem( prev );
1398 }
1399 }
1400 break;
1401
1402 default:
1403 event.Skip();
1404 }
1405}
1406
1407wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& WXUNUSED(flags))
1408{
1409 bool onButton = FALSE;
1410 return m_anchor->HitTest( point, onButton );
1411}
1412
1413void wxTreeCtrl::OnMouse( wxMouseEvent &event )
1414{
1415 if (!event.LeftIsDown()) m_dragCount = 0;
1416
1417 if ( !(event.LeftDown() || event.LeftDClick() || event.Dragging()) ) return;
1418
1419 if ( !m_anchor ) return;
1420
1421 wxClientDC dc(this);
1422 PrepareDC(dc);
1423 long x = dc.DeviceToLogicalX( (long)event.GetX() );
1424 long y = dc.DeviceToLogicalY( (long)event.GetY() );
1425
1426 bool onButton = FALSE;
1427 wxGenericTreeItem *item = m_anchor->HitTest( wxPoint(x,y), onButton );
1428
1429 if (item == NULL) return; /* we hit the blank area */
1430
1431 if (event.Dragging())
1432 {
1433 if (m_dragCount == 2) /* small drag latency (3?) */
1434 {
1435 m_dragCount = 0;
1436
1437 wxTreeEvent nevent(wxEVT_COMMAND_TREE_BEGIN_DRAG, GetId());
1438 nevent.m_item = m_current;
1439 nevent.SetEventObject(this);
1440 GetEventHandler()->ProcessEvent(nevent);
1441 }
1442 else
1443 {
1444 m_dragCount++;
1445 }
1446 return;
1447 }
1448
1449 if (!IsSelected(item)) SelectItem(item); /* we dont support multiple selections, BTW */
1450
1451 if (event.LeftDClick())
1452 {
1453 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, GetId() );
1454 event.m_item = item;
1455 event.m_code = 0;
1456 event.SetEventObject( this );
1457 GetEventHandler()->ProcessEvent( event );
1458 }
1459
1460 if (onButton)
1461 {
1462 Toggle( item );
1463 }
1464}
1465
1466void wxTreeCtrl::OnIdle( wxIdleEvent &WXUNUSED(event) )
1467{
1468 /* after all changes have been done to the tree control,
1469 * we actually redraw the tree when everything is over */
1470
1471 if (!m_dirty) return;
1472
1473 m_dirty = FALSE;
1474
1475 CalculatePositions();
1476
1477 AdjustMyScrollbars();
1478}
1479
1480// -----------------------------------------------------------------------------
1481
1482void wxTreeCtrl::CalculateLevel( wxGenericTreeItem *item, wxDC &dc, int level, int &y )
1483{
1484 int horizX = level*m_indent;
1485
1486 item->SetX( horizX+33 );
1487 item->SetY( y-m_lineHeight/3-2 );
1488 item->SetHeight( m_lineHeight );
1489
1490 if ( !item->IsExpanded() )
1491 {
1492 /* we dont need to calculate collapsed branches */
1493 return;
1494 }
1495
1496 wxArrayTreeItems& children = item->GetChildren();
1497 size_t count = children.Count();
1498 for ( size_t n = 0; n < count; n++ )
1499 {
1500 y += m_lineHeight;
1501 CalculateLevel( children[n], dc, level+1, y ); /* recurse */
1502 }
1503}
1504
1505void wxTreeCtrl::CalculatePositions()
1506{
1507 if ( !m_anchor ) return;
1508
1509 wxClientDC dc(this);
1510 PrepareDC( dc );
1511
1512 dc.SetFont( wxSystemSettings::GetSystemFont( wxSYS_DEFAULT_GUI_FONT ) );
1513
1514 dc.SetPen( m_dottedPen );
1515 m_lineHeight = (int)(dc.GetCharHeight() + 4);
1516
1517 int y = m_lineHeight / 2 + 2;
1518 CalculateLevel( m_anchor, dc, 0, y ); /* start recursion */
1519}
1520
1521void wxTreeCtrl::RefreshSubtree(wxGenericTreeItem *item)
1522{
1523 wxClientDC dc(this);
1524 PrepareDC(dc);
1525
1526 int cw = 0;
1527 int ch = 0;
1528 GetClientSize( &cw, &ch );
1529
1530 wxRect rect;
1531 rect.x = dc.LogicalToDeviceX( 0 );
1532 rect.width = cw;
1533 rect.y = dc.LogicalToDeviceY( item->GetY() );
1534 rect.height = ch;
1535
1536 Refresh( TRUE, &rect );
1537
1538 AdjustMyScrollbars();
1539}
1540
1541void wxTreeCtrl::RefreshLine( wxGenericTreeItem *item )
1542{
1543 wxClientDC dc(this);
1544 PrepareDC( dc );
1545
1546 wxRect rect;
1547 rect.x = dc.LogicalToDeviceX( item->GetX() - 2 );
1548 rect.y = dc.LogicalToDeviceY( item->GetY() - 2 );
1549 rect.width = 1000;
1550 rect.height = dc.GetCharHeight() + 6;
1551
1552 Refresh( TRUE, &rect );
1553}
1554