1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
7 // Copyright: (c) 1998 Robert Roebling, Julian Smart and Markus Holzem
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
12 #pragma implementation "treectrl.h"
15 #include "wx/treectrl.h"
16 #include "wx/settings.h"
18 //-----------------------------------------------------------------------------
20 //-----------------------------------------------------------------------------
22 IMPLEMENT_DYNAMIC_CLASS(wxTreeItem
, wxObject
)
24 wxTreeItem::wxTreeItem(void)
36 //-----------------------------------------------------------------------------
38 //-----------------------------------------------------------------------------
40 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent
,wxCommandEvent
)
42 wxTreeEvent::wxTreeEvent( wxEventType commandType
, int id
) :
43 wxCommandEvent( commandType
, id
)
49 //-----------------------------------------------------------------------------
51 //-----------------------------------------------------------------------------
53 IMPLEMENT_DYNAMIC_CLASS(wxGenericTreeItem
,wxObject
)
55 wxGenericTreeItem::wxGenericTreeItem( wxGenericTreeItem
*parent
)
62 wxGenericTreeItem::wxGenericTreeItem( wxGenericTreeItem
*parent
, const wxTreeItem
& item
, wxDC
*dc
)
70 void wxGenericTreeItem::SetItem( const wxTreeItem
&item
, wxDC
*dc
)
72 if ((item
.m_mask
& wxTREE_MASK_HANDLE
) == wxTREE_MASK_HANDLE
)
73 m_itemId
= item
.m_itemId
;
74 if ((item
.m_mask
& wxTREE_MASK_STATE
) == wxTREE_MASK_STATE
)
75 m_state
= item
.m_state
;
76 if ((item
.m_mask
& wxTREE_MASK_TEXT
) == wxTREE_MASK_TEXT
)
78 if ((item
.m_mask
& wxTREE_MASK_IMAGE
) == wxTREE_MASK_IMAGE
)
79 m_image
= item
.m_image
;
80 if ((item
.m_mask
& wxTREE_MASK_SELECTED_IMAGE
) == wxTREE_MASK_SELECTED_IMAGE
)
81 m_selectedImage
= item
.m_selectedImage
;
82 if ((item
.m_mask
& wxTREE_MASK_CHILDREN
) == wxTREE_MASK_CHILDREN
)
83 m_hasChildren
= (item
.m_children
> 0);
84 if ((item
.m_mask
& wxTREE_MASK_DATA
) == wxTREE_MASK_DATA
)
88 dc
->GetTextExtent( m_text
, &lw
, &lh
);
93 void wxGenericTreeItem::SetText( const wxString
&text
, wxDC
*dc
)
98 dc
->GetTextExtent( m_text
, &lw
, &lh
);
103 void wxGenericTreeItem::Reset(void)
109 m_selectedImage
= -1;
111 m_hasChildren
= FALSE
;
120 m_children
.DeleteContents( TRUE
);
124 void wxGenericTreeItem::GetItem( wxTreeItem
&item
) const
126 if ((item
.m_mask
& wxTREE_MASK_STATE
) == wxTREE_MASK_STATE
)
127 item
.m_state
= m_state
;
128 if ((item
.m_mask
& wxTREE_MASK_TEXT
) == wxTREE_MASK_TEXT
)
129 item
.m_text
= m_text
;
130 if ((item
.m_mask
& wxTREE_MASK_IMAGE
) == wxTREE_MASK_IMAGE
)
131 item
.m_image
= m_image
;
132 if ((item
.m_mask
& wxTREE_MASK_SELECTED_IMAGE
) == wxTREE_MASK_SELECTED_IMAGE
)
133 item
.m_selectedImage
= m_selectedImage
;
134 if ((item
.m_mask
& wxTREE_MASK_CHILDREN
) == wxTREE_MASK_CHILDREN
)
135 item
.m_children
= (int)m_hasChildren
;
136 if ((item
.m_mask
& wxTREE_MASK_DATA
) == wxTREE_MASK_DATA
)
137 item
.m_data
= m_data
;
140 bool wxGenericTreeItem::HasChildren(void)
142 return m_hasChildren
;
145 bool wxGenericTreeItem::HasPlus(void)
147 return (m_hasChildren
&& (m_children
.Number() == 0));
150 int wxGenericTreeItem::NumberOfVisibleDescendents(void)
152 int ret
= m_children
.Number();
153 wxNode
*node
= m_children
.First();
156 wxGenericTreeItem
*item
= (wxGenericTreeItem
*)node
->Data();
157 ret
+= item
->NumberOfVisibleDescendents();
163 int wxGenericTreeItem::NumberOfVisibleChildren(void)
165 return m_children
.Number();
168 wxGenericTreeItem
*wxGenericTreeItem::FindItem( long itemId
) const
170 if (m_itemId
== itemId
) return (wxGenericTreeItem
*)(this);
171 wxNode
*node
= m_children
.First();
174 wxGenericTreeItem
*item
= (wxGenericTreeItem
*)node
->Data();
175 wxGenericTreeItem
*res
= item
->FindItem( itemId
);
176 if (res
) return (wxGenericTreeItem
*)(res
);
182 void wxGenericTreeItem::AddChild( wxGenericTreeItem
*child
)
184 m_children
.Append( child
);
187 void wxGenericTreeItem::SetCross( int x
, int y
)
193 void wxGenericTreeItem::GetSize( int &x
, int &y
)
195 if (y
< m_y
+ 10) y
= m_y
+10;
196 int width
= m_x
+ m_width
;
197 if (width
> x
) x
= width
;
198 wxNode
*node
= m_children
.First();
201 wxGenericTreeItem
*item
= (wxGenericTreeItem
*)node
->Data();
202 item
->GetSize( x
, y
);
207 long wxGenericTreeItem::HitTest( const wxPoint
& point
, int &flags
)
209 if (m_parent
&& ((point
.y
> m_y
) && (point
.y
< m_y
+m_height
)))
211 if ((point
.x
> m_xCross
-5) &&
212 (point
.x
< m_xCross
+5) &&
213 (point
.y
> m_yCross
-5) &&
214 (point
.y
< m_yCross
+5) &&
217 flags
= wxTREE_HITTEST_ONITEMBUTTON
;
220 if ((point
.x
> m_x
) && (point
.x
< m_x
+m_width
))
222 flags
= wxTREE_HITTEST_ONITEMLABEL
;
227 flags
= wxTREE_HITTEST_ONITEMRIGHT
;
230 flags
= wxTREE_HITTEST_ONITEMINDENT
;
235 wxNode
*node
= m_children
.First();
238 wxGenericTreeItem
*child
= (wxGenericTreeItem
*)node
->Data();
239 long res
= child
->HitTest( point
, flags
);
240 if (res
!= -1) return res
;
247 void wxGenericTreeItem::PrepareEvent( wxTreeEvent
&event
)
249 event
.m_item
.m_itemId
= m_itemId
;
250 event
.m_item
.m_state
= m_state
;
251 event
.m_item
.m_text
= m_text
;
252 event
.m_item
.m_image
= m_image
;
253 event
.m_item
.m_selectedImage
= m_selectedImage
;
254 event
.m_item
.m_children
= (int)m_hasChildren
;
255 event
.m_item
.m_data
= m_data
;
258 event
.m_pointDrag
.x
= 0;
259 event
.m_pointDrag
.y
= 0;
262 void wxGenericTreeItem::SendKeyDown( wxWindow
*target
)
264 wxTreeEvent
event( wxEVT_COMMAND_TREE_KEY_DOWN
, target
->GetId() );
265 PrepareEvent( event
);
266 event
.SetEventObject( target
);
267 target
->ProcessEvent( event
);
270 void wxGenericTreeItem::SendSelected( wxWindow
*target
)
272 wxTreeEvent
event( wxEVT_COMMAND_TREE_SEL_CHANGED
, target
->GetId() );
273 PrepareEvent( event
);
274 event
.SetEventObject( target
);
275 target
->ProcessEvent( event
);
278 void wxGenericTreeItem::SendDelete( wxWindow
*target
)
280 wxTreeEvent
event( wxEVT_COMMAND_TREE_DELETE_ITEM
,
282 PrepareEvent( event
);
283 event
.SetEventObject( target
);
284 target
->ProcessEvent( event
);
287 void wxGenericTreeItem::SendExpand( wxWindow
*target
)
289 wxTreeEvent
event( wxEVT_COMMAND_TREE_ITEM_EXPANDED
,
291 PrepareEvent( event
);
292 event
.SetEventObject( target
);
293 target
->ProcessEvent( event
);
296 void wxGenericTreeItem::SetHilight( bool set
)
301 bool wxGenericTreeItem::HasHilight(void)
306 //-----------------------------------------------------------------------------
308 //-----------------------------------------------------------------------------
310 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl
,wxScrolledWindow
313 BEGIN_EVENT_TABLE(wxTreeCtrl
,wxScrolledWindow
)
314 EVT_PAINT (wxTreeCtrl::OnPaint
)
315 EVT_MOUSE_EVENTS (wxTreeCtrl::OnMouse
)
316 EVT_CHAR (wxTreeCtrl::OnChar
)
317 EVT_SET_FOCUS (wxTreeCtrl::OnSetFocus
)
318 EVT_KILL_FOCUS (wxTreeCtrl::OnKillFocus
)
321 wxTreeCtrl::wxTreeCtrl(void)
333 m_hilightBrush
= new wxBrush( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT
), wxSOLID
);
336 wxTreeCtrl::wxTreeCtrl(wxWindow
*parent
, const wxWindowID id
,
339 const long style
, const wxString
& name
)
351 m_hilightBrush
= new wxBrush( wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT
), wxSOLID
);
352 Create( parent
, id
, pos
, size
, style
, name
);
355 wxTreeCtrl::~wxTreeCtrl(void)
357 if (m_dc
) delete m_dc
;
360 bool wxTreeCtrl::Create(wxWindow
*parent
, const wxWindowID id
,
364 , const wxString
& name
)
366 wxScrolledWindow::Create( parent
, id
, pos
, size
, style
, name
);
367 SetBackgroundColour( *wxWHITE
);
368 m_dottedPen
= wxPen( *wxBLACK
, 0, 0 );
372 int wxTreeCtrl::GetCount(void) const
374 if (!m_anchor
) return 0;
375 return m_anchor
->NumberOfVisibleDescendents();
378 long wxTreeCtrl::InsertItem( const long parent
, const wxString
& label
, const int image
,
379 const int selImage
, const long WXUNUSED(insertAfter
) )
381 wxGenericTreeItem
*p
= NULL
;
384 if (m_anchor
) return -1;
388 p
= FindItem( parent
);
393 item
.m_mask
= wxTREE_MASK_HANDLE
;
394 item
.m_itemId
= m_lastId
;
395 if (!label
.IsNull() || (label
== ""))
398 item
.m_mask
|= wxTREE_MASK_TEXT
;
402 item
.m_image
= image
;
403 item
.m_mask
|= wxTREE_MASK_IMAGE
;
407 item
.m_selectedImage
= selImage
;
408 item
.m_mask
|= wxTREE_MASK_SELECTED_IMAGE
;
412 wxGenericTreeItem
*new_child
= new wxGenericTreeItem( p
, item
, &dc
);
414 p
->AddChild( new_child
);
416 m_anchor
= new_child
;
420 CalculatePositions();
423 GetClientSize( NULL
, &ch
);
426 rect
.x
= 0; rect
.y
= 0;
427 rect
.width
= 10000; rect
.height
= ch
;
430 if (p
->m_children
.Number() == 1)
432 rect
.y
= dc
.LogicalToDeviceY( p
->m_y
);
436 wxNode
*node
= p
->m_children
.Member( new_child
)->Previous();
437 wxGenericTreeItem
* last_child
= (wxGenericTreeItem
*)node
->Data();
438 rect
.y
= dc
.LogicalToDeviceY( last_child
->m_y
);
443 dc
.GetDeviceOrigin( &doX
, &doY
);
444 rect
.height
= ch
-rect
.y
-doY
;
446 AdjustMyScrollbars();
448 if (rect
.height
> 0) Refresh( FALSE
, &rect
);
452 AdjustMyScrollbars();
460 long wxTreeCtrl::InsertItem( const long parent
, wxTreeItem
&info
, const long WXUNUSED(insertAfter
) )
462 int oldMask
= info
.m_mask
;
463 wxGenericTreeItem
*p
= NULL
;
466 if (m_anchor
) return -1;
470 p
= FindItem( parent
);
473 printf( "TreeItem not found.\n" );
478 if ((info
.m_mask
& wxTREE_MASK_HANDLE
) == 0)
481 info
.m_itemId
= m_lastId
;
482 info
.m_mask
|= wxTREE_MASK_HANDLE
;
491 wxGenericTreeItem
*new_child
= new wxGenericTreeItem( p
, info
, &dc
);
493 p
->AddChild( new_child
);
495 m_anchor
= new_child
;
499 CalculatePositions();
502 GetClientSize( NULL
, &ch
);
505 rect
.x
= 0; rect
.y
= 0;
506 rect
.width
= 10000; rect
.height
= ch
;
509 if (p
->m_children
.Number() == 1)
511 rect
.y
= dc
.LogicalToDeviceY( p
->m_y
);
515 wxNode
*node
= p
->m_children
.Member( new_child
)->Previous();
516 wxGenericTreeItem
* last_child
= (wxGenericTreeItem
*)node
->Data();
517 rect
.y
= dc
.LogicalToDeviceY( last_child
->m_y
);
522 dc
.GetDeviceOrigin( &doX
, &doY
);
523 rect
.height
= ch
-rect
.y
-doY
;
525 AdjustMyScrollbars();
527 if (rect
.height
> 0) Refresh( FALSE
, &rect
);
531 AdjustMyScrollbars();
536 info
.m_mask
= oldMask
;
540 bool wxTreeCtrl::ExpandItem( const long item
, const int action
)
542 wxGenericTreeItem
*i
= FindItem( item
);
543 if (!i
) return FALSE
;
546 case wxTREE_EXPAND_EXPAND
:
548 i
->SendExpand( this );
551 case wxTREE_EXPAND_COLLAPSE_RESET
:
552 case wxTREE_EXPAND_COLLAPSE
:
554 wxNode
*node
= i
->m_children
.First();
557 wxGenericTreeItem
*child
= (wxGenericTreeItem
*)node
->Data();
558 child
->SendDelete( this );
560 node
= i
->m_children
.First();
565 GetClientSize( &cw
, &ch
);
571 rect
.y
= dc
.LogicalToDeviceY( i
->m_y
);
574 dc
.GetDeviceOrigin( NULL
, &doY
);
575 rect
.height
= ch
-rect
.y
-doY
;
576 Refresh( TRUE
, &rect
);
578 AdjustMyScrollbars();
581 case wxTREE_EXPAND_TOGGLE
:
584 ExpandItem( item
, wxTREE_EXPAND_EXPAND
);
586 ExpandItem( item
, wxTREE_EXPAND_COLLAPSE
);
593 bool wxTreeCtrl::DeleteAllItems(void)
601 bool wxTreeCtrl::GetItem( wxTreeItem
&info
) const
603 wxGenericTreeItem
*i
= FindItem( info
.m_itemId
);
604 if (!i
) return FALSE
;
609 long wxTreeCtrl::GetItemData( const long item
) const
611 wxGenericTreeItem
*i
= FindItem( item
);
616 wxString
wxTreeCtrl::GetItemText( const long item
) const
618 wxGenericTreeItem
*i
= FindItem( item
);
623 long wxTreeCtrl::GetParent( const long item
) const
625 wxGenericTreeItem
*i
= FindItem( item
);
629 return i
->m_parent
->m_itemId
;
632 long wxTreeCtrl::GetRootItem(void) const
634 if (m_anchor
) return m_anchor
->m_itemId
;
638 long wxTreeCtrl::GetSelection(void) const
643 bool wxTreeCtrl::SelectItem( const long WXUNUSED(item
) ) const
648 bool wxTreeCtrl::ItemHasChildren( const long item
) const
650 wxGenericTreeItem
*i
= FindItem( item
);
651 if (!i
) return FALSE
;
652 return i
->m_hasChildren
;
655 void wxTreeCtrl::SetIndent( const int indent
)
661 int wxTreeCtrl::GetIndent(void) const
666 bool wxTreeCtrl::SetItem( wxTreeItem
&info
)
668 wxGenericTreeItem
*i
= FindItem( info
.m_itemId
);
669 if (!i
) return FALSE
;
671 i
->SetItem( info
, &dc
);
675 bool wxTreeCtrl::SetItemData( const long item
, const long data
)
677 wxGenericTreeItem
*i
= FindItem( item
);
678 if (!i
) return FALSE
;
683 bool wxTreeCtrl::SetItemText( const long item
, const wxString
&text
)
685 wxGenericTreeItem
*i
= FindItem( item
);
686 if (!i
) return FALSE
;
688 i
->SetText( text
, &dc
);
692 long wxTreeCtrl::HitTest( const wxPoint
& point
, int &flags
)
695 if (!m_anchor
) return -1;
696 return m_anchor
->HitTest( point
, flags
);
699 void wxTreeCtrl::AdjustMyScrollbars(void)
705 m_anchor
->GetSize( x
, y
);
707 int x_pos
= GetScrollPos( wxHORIZONTAL
);
708 int y_pos
= GetScrollPos( wxVERTICAL
);
709 SetScrollbars( 10, 10, x
/10, y
/10, x_pos
, y_pos
);
713 SetScrollbars( 0, 0, 0, 0 );
717 void wxTreeCtrl::PaintLevel( wxGenericTreeItem
*item
, wxPaintDC
&dc
, int level
, int &y
)
719 int horizX
= level
*m_indent
+10;
721 wxNode
*node
= item
->m_children
.First();
724 wxGenericTreeItem
*child
= (wxGenericTreeItem
*)node
->Data();
725 dc
.SetPen( m_dottedPen
);
727 child
->SetCross( horizX
+15, y
);
731 if (level
!= 0) oldY
-= (m_lineHeight
-5);
732 dc
.DrawLine( horizX
, oldY
, horizX
, y
);
735 child
->m_x
= horizX
+33;
736 child
->m_y
= y
-m_lineHeight
/3;
737 child
->m_height
= m_lineHeight
;
739 if (IsExposed( 0, child
->m_y
-2, 10000, m_lineHeight
+4 ))
744 if (!(node
->Previous()) && (level
== 0))
746 if (!child
->HasChildren())
748 dc
.DrawLine( startX
, y
, endX
, y
);
750 if (child
->HasChildren())
752 dc
.DrawLine( horizX
+20, y
, horizX
+30, y
);
753 dc
.SetPen( *wxGREY_PEN
);
754 dc
.DrawRectangle( horizX
+10, y
-4, 11, 9 );
755 dc
.SetPen( *wxBLACK_PEN
);
756 dc
.DrawLine( horizX
+13, y
, horizX
+17, y
);
757 if (child
->HasPlus())
758 dc
.DrawLine( horizX
+15, y
-2, horizX
+15, y
+2 );
761 if (child
->HasHilight())
763 dc
.SetTextForeground( wxSystemSettings::GetSystemColour( wxSYS_COLOUR_HIGHLIGHTTEXT
) );
764 #if 0 // VZ: this code leaves horizontal stripes when item is unselected
765 dc
.SetBrush( *m_hilightBrush
);
767 dc
.SetPen( wxBLACK_PEN
);
769 dc
.SetPen( wxTRANSPARENT_PEN
);
771 dc
.GetTextExtent( child
->m_text
, &tw
, &th
);
772 dc
.DrawRectangle( child
->m_x
-2, child
->m_y
-2, tw
+4, th
+4 );
774 int modeOld
= dc
.GetBackgroundMode();
775 dc
.SetTextBackground( *wxBLACK
);
776 dc
.SetBackgroundMode(wxSOLID
);
779 dc
.DrawText( child
->m_text
, child
->m_x
, child
->m_y
);
781 #if 0 // VZ: same as above
782 dc
.SetPen( *wxBLACK_PEN
);
784 dc
.SetBackgroundMode(modeOld
);
785 dc
.SetTextBackground( *wxWHITE
);
786 dc
.SetBrush( *wxWHITE_BRUSH
);
788 dc
.SetTextForeground( *wxBLACK
);
791 dc
.DrawText( child
->m_text
, child
->m_x
, child
->m_y
);
795 if (child
->NumberOfVisibleChildren() > 0)
796 PaintLevel( child
, dc
, level
+1, y
);
801 void wxTreeCtrl::OnPaint( const wxPaintEvent
&WXUNUSED(event
) )
803 if (!m_anchor
) return;
807 m_dc
= new wxPaintDC(this);
811 m_dc
->SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT
) );
813 m_dc
->SetPen( m_dottedPen
);
814 m_lineHeight
= (int)(m_dc
->GetCharHeight() + 4);
816 int y
= m_lineHeight
/ 2 + 2;
817 PaintLevel( m_anchor
, *m_dc
, 0, y
);
820 void wxTreeCtrl::OnSetFocus( const wxFocusEvent
&WXUNUSED(event
) )
823 if (m_current
) RefreshLine( m_current
);
826 void wxTreeCtrl::OnKillFocus( const wxFocusEvent
&WXUNUSED(event
) )
829 if (m_current
) RefreshLine( m_current
);
832 void wxTreeCtrl::OnChar( wxKeyEvent
&event
)
837 void wxTreeCtrl::OnMouse( const wxMouseEvent
&event
)
839 if (!event
.LeftDown() &&
840 !event
.LeftDClick()) return;
844 long x
= dc
.DeviceToLogicalX( (long)event
.GetX() );
845 long y
= dc
.DeviceToLogicalY( (long)event
.GetY() );
848 long id
= HitTest( wxPoint(x
,y
), flag
);
851 wxGenericTreeItem
*item
= FindItem( id
);
854 if ((flag
!= wxTREE_HITTEST_ONITEMBUTTON
) &&
855 (flag
!= wxTREE_HITTEST_ONITEMLABEL
)) return;
857 if (m_current
!= item
)
861 m_current
->SetHilight( FALSE
);
862 RefreshLine( m_current
);
865 m_current
->SetHilight( TRUE
);
866 RefreshLine( m_current
);
867 m_current
->SendSelected( this );
870 if (event
.LeftDClick())
871 m_current
->SendKeyDown( this );
873 if (flag
== wxTREE_HITTEST_ONITEMBUTTON
)
875 ExpandItem( item
->m_itemId
, wxTREE_EXPAND_TOGGLE
);
880 void wxTreeCtrl::CalculateLevel( wxGenericTreeItem
*item
, wxPaintDC
&dc
, int level
, int &y
)
882 int horizX
= level
*m_indent
+10;
883 wxNode
*node
= item
->m_children
.First();
886 wxGenericTreeItem
*child
= (wxGenericTreeItem
*)node
->Data();
887 dc
.SetPen( m_dottedPen
);
892 if (!node
->Previous() && (level
== 0))
894 if (!child
->HasChildren())
897 child
->m_x
= horizX
+33;
898 child
->m_y
= y
-m_lineHeight
/3-2;
899 child
->m_height
= m_lineHeight
;
902 if (child
->NumberOfVisibleChildren() > 0)
903 CalculateLevel( child
, dc
, level
+1, y
);
909 void wxTreeCtrl::CalculatePositions(void)
917 dc
.SetFont( wxSystemSettings::GetSystemFont( wxSYS_SYSTEM_FONT
) );
919 dc
.SetPen( m_dottedPen
);
920 m_lineHeight
= (int)(dc
.GetCharHeight() + 4);
922 int y
= m_lineHeight
/ 2 + 2;
923 CalculateLevel( m_anchor
, dc
, 0, y
);
926 wxGenericTreeItem
*wxTreeCtrl::FindItem( long itemId
) const
928 if (!m_anchor
) return NULL
;
929 return m_anchor
->FindItem( itemId
);
932 void wxTreeCtrl::RefreshLine( wxGenericTreeItem
*item
)
938 rect
.x
= dc
.LogicalToDeviceX( item
->m_x
-2 );
939 rect
.y
= dc
.LogicalToDeviceY( item
->m_y
-2 );
941 rect
.height
= dc
.GetCharHeight()+4;
942 Refresh( TRUE
, &rect
);