1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin to be less MSW-specific on 10.10.98
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #pragma implementation "treectrl.h"
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
30 #include "wx/window.h"
31 #include "wx/msw/private.h"
33 // Mingw32 is a bit mental even though this is done in winundef
42 #if defined(__WIN95__)
45 #include "wx/dynarray.h"
46 #include "wx/imaglist.h"
47 #include "wx/treectrl.h"
48 #include "wx/settings.h"
51 #ifndef wxUSE_NORLANDER_HEADERS
52 #include "wx/msw/gnuwin32/extra.h"
56 #if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__) || defined(wxUSE_NORLANDER_HEADERS)
60 // Bug in headers, sometimes
62 #define TVIS_FOCUSED 0x0001
65 // ----------------------------------------------------------------------------
67 // ----------------------------------------------------------------------------
69 // a convenient wrapper around TV_ITEM struct which adds a ctor
70 #pragma warning( disable : 4097 )
71 struct wxTreeViewItem
: public TV_ITEM
73 wxTreeViewItem(const wxTreeItemId
& item
, // the item handle
74 UINT mask_
, // fields which are valid
75 UINT stateMask_
= 0) // for TVIF_STATE only
77 // hItem member is always valid
78 mask
= mask_
| TVIF_HANDLE
;
79 stateMask
= stateMask_
;
80 hItem
= (HTREEITEM
) (WXHTREEITEM
) item
;
83 #pragma warning( default : 4097 )
85 // a class which encapsulates the tree traversal logic: it vists all (unless
86 // OnVisit() returns FALSE) items under the given one
90 wxTreeTraversal(const wxTreeCtrl
*tree
)
95 // do traverse the tree: visit all items (recursively by default) under the
96 // given one; return TRUE if all items were traversed or FALSE if the
97 // traversal was aborted because OnVisit returned FALSE
98 bool DoTraverse(const wxTreeItemId
& root
, bool recursively
= TRUE
);
100 // override this function to do whatever is needed for each item, return
101 // FALSE to stop traversing
102 virtual bool OnVisit(const wxTreeItemId
& item
) = 0;
105 const wxTreeCtrl
*GetTree() const { return m_tree
; }
108 bool Traverse(const wxTreeItemId
& root
, bool recursively
);
110 const wxTreeCtrl
*m_tree
;
113 // internal class for getting the selected items
114 class TraverseSelections
: public wxTreeTraversal
117 TraverseSelections(const wxTreeCtrl
*tree
,
118 wxArrayTreeItemIds
& selections
)
119 : wxTreeTraversal(tree
), m_selections(selections
)
121 m_selections
.Empty();
123 DoTraverse(tree
->GetRootItem());
126 virtual bool OnVisit(const wxTreeItemId
& item
)
128 if ( GetTree()->IsItemChecked(item
) )
130 m_selections
.Add(item
);
137 wxArrayTreeItemIds
& m_selections
;
140 // internal class for counting tree items
141 class TraverseCounter
: public wxTreeTraversal
144 TraverseCounter(const wxTreeCtrl
*tree
,
145 const wxTreeItemId
& root
,
147 : wxTreeTraversal(tree
)
151 DoTraverse(root
, recursively
);
154 virtual bool OnVisit(const wxTreeItemId
& item
)
161 size_t GetCount() const { return m_count
; }
167 // ----------------------------------------------------------------------------
168 // This class is needed for support of different images: the Win32 common
169 // control natively supports only 2 images (the normal one and another for the
170 // selected state). We wish to provide support for 2 more of them for folder
171 // items (i.e. those which have children): for expanded state and for expanded
172 // selected state. For this we use this structure to store the additional items
175 // There is only one problem with this: when we retrieve the item's data, we
176 // don't know whether we get a pointer to wxTreeItemData or
177 // wxTreeItemIndirectData. So we have to maintain a list of all items which
178 // have indirect data inside the listctrl itself.
179 // ----------------------------------------------------------------------------
180 class wxTreeItemIndirectData
183 // ctor associates this data with the item and the real item data becomes
184 // available through our GetData() method
185 wxTreeItemIndirectData(wxTreeCtrl
*tree
, const wxTreeItemId
& item
)
187 for ( size_t n
= 0; n
< WXSIZEOF(m_images
); n
++ )
193 m_data
= tree
->GetItemData(item
);
195 // and set ourselves as the new one
196 tree
->SetIndirectItemData(item
, this);
199 // dtor deletes the associated data as well
200 ~wxTreeItemIndirectData() { delete m_data
; }
203 // get the real data associated with the item
204 wxTreeItemData
*GetData() const { return m_data
; }
206 void SetData(wxTreeItemData
*data
) { m_data
= data
; }
208 // do we have such image?
209 bool HasImage(wxTreeItemIcon which
) const { return m_images
[which
] != -1; }
211 int GetImage(wxTreeItemIcon which
) const { return m_images
[which
]; }
213 void SetImage(int image
, wxTreeItemIcon which
) { m_images
[which
] = image
; }
216 // all the images associated with the item
217 int m_images
[wxTreeItemIcon_Max
];
219 wxTreeItemData
*m_data
;
222 // ----------------------------------------------------------------------------
224 // ----------------------------------------------------------------------------
226 #if !USE_SHARED_LIBRARY
227 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl
, wxControl
)
230 // ----------------------------------------------------------------------------
232 // ----------------------------------------------------------------------------
234 // handy table for sending events
235 static const wxEventType g_events
[2][2] =
237 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED
, wxEVT_COMMAND_TREE_ITEM_COLLAPSING
},
238 { wxEVT_COMMAND_TREE_ITEM_EXPANDED
, wxEVT_COMMAND_TREE_ITEM_EXPANDING
}
241 // ============================================================================
243 // ============================================================================
245 // ----------------------------------------------------------------------------
247 // ----------------------------------------------------------------------------
249 bool wxTreeTraversal::DoTraverse(const wxTreeItemId
& root
, bool recursively
)
251 if ( !OnVisit(root
) )
254 return Traverse(root
, recursively
);
257 bool wxTreeTraversal::Traverse(const wxTreeItemId
& root
, bool recursively
)
260 wxTreeItemId child
= m_tree
->GetFirstChild(root
, cookie
);
261 while ( child
.IsOk() )
263 // depth first traversal
264 if ( recursively
&& !Traverse(child
, TRUE
) )
267 if ( !OnVisit(child
) )
270 child
= m_tree
->GetNextChild(root
, cookie
);
276 // ----------------------------------------------------------------------------
277 // construction and destruction
278 // ----------------------------------------------------------------------------
280 void wxTreeCtrl::Init()
282 m_imageListNormal
= NULL
;
283 m_imageListState
= NULL
;
287 bool wxTreeCtrl::Create(wxWindow
*parent
,
292 const wxValidator
& validator
,
293 const wxString
& name
)
297 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
300 DWORD wstyle
= WS_VISIBLE
| WS_CHILD
| WS_TABSTOP
|
301 TVS_HASLINES
| TVS_SHOWSELALWAYS
;
303 if ( m_windowStyle
& wxTR_HAS_BUTTONS
)
304 wstyle
|= TVS_HASBUTTONS
;
306 if ( m_windowStyle
& wxTR_EDIT_LABELS
)
307 wstyle
|= TVS_EDITLABELS
;
309 if ( m_windowStyle
& wxTR_LINES_AT_ROOT
)
310 wstyle
|= TVS_LINESATROOT
;
312 #if !defined( __GNUWIN32__ ) && !defined( __BORLANDC__ ) && !defined(wxUSE_NORLANDER_HEADERS)
313 // we emulate the multiple selection tree controls by using checkboxes: set
314 // up the image list we need for this if we do have multiple selections
315 #if !defined(__VISUALC__) || (__VISUALC__ != 1010)
316 if ( m_windowStyle
& wxTR_MULTIPLE
)
317 wstyle
|= TVS_CHECKBOXES
;
321 // Create the tree control.
322 if ( !MSWCreateControl(WC_TREEVIEW
, wstyle
) )
325 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
));
326 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
328 // VZ: this is some experimental code which may be used to get the
329 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
330 // AFAIK, the standard DLL does about the same thing anyhow.
332 if ( m_windowStyle
& wxTR_MULTIPLE
)
336 // create the DC compatible with the current screen
337 HDC hdcMem
= CreateCompatibleDC(NULL
);
339 // create a mono bitmap of the standard size
340 int x
= GetSystemMetrics(SM_CXMENUCHECK
);
341 int y
= GetSystemMetrics(SM_CYMENUCHECK
);
342 wxImageList
imagelistCheckboxes(x
, y
, FALSE
, 2);
343 HBITMAP hbmpCheck
= CreateBitmap(x
, y
, // bitmap size
344 1, // # of color planes
345 1, // # bits needed for one pixel
346 0); // array containing colour data
347 SelectObject(hdcMem
, hbmpCheck
);
349 // then draw a check mark into it
350 RECT rect
= { 0, 0, x
, y
};
351 if ( !::DrawFrameControl(hdcMem
, &rect
,
353 DFCS_BUTTONCHECK
| DFCS_CHECKED
) )
355 wxLogLastError(T("DrawFrameControl(check)"));
358 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
359 imagelistCheckboxes
.Add(bmp
);
361 if ( !::DrawFrameControl(hdcMem
, &rect
,
365 wxLogLastError(T("DrawFrameControl(uncheck)"));
368 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
369 imagelistCheckboxes
.Add(bmp
);
375 SetStateImageList(&imagelistCheckboxes
);
379 SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
);
384 wxTreeCtrl::~wxTreeCtrl()
388 // delete user data to prevent memory leaks
392 // ----------------------------------------------------------------------------
394 // ----------------------------------------------------------------------------
396 // simple wrappers which add error checking in debug mode
398 bool wxTreeCtrl::DoGetItem(wxTreeViewItem
* tvItem
) const
400 if ( !TreeView_GetItem(GetHwnd(), tvItem
) )
402 wxLogLastError("TreeView_GetItem");
410 void wxTreeCtrl::DoSetItem(wxTreeViewItem
* tvItem
)
412 if ( TreeView_SetItem(GetHwnd(), tvItem
) == -1 )
414 wxLogLastError("TreeView_SetItem");
418 size_t wxTreeCtrl::GetCount() const
420 return (size_t)TreeView_GetCount(GetHwnd());
423 unsigned int wxTreeCtrl::GetIndent() const
425 return TreeView_GetIndent(GetHwnd());
428 void wxTreeCtrl::SetIndent(unsigned int indent
)
430 TreeView_SetIndent(GetHwnd(), indent
);
433 wxImageList
*wxTreeCtrl::GetImageList() const
435 return m_imageListNormal
;
438 wxImageList
*wxTreeCtrl::GetStateImageList() const
440 return m_imageListNormal
;
443 void wxTreeCtrl::SetAnyImageList(wxImageList
*imageList
, int which
)
446 TreeView_SetImageList(GetHwnd(),
447 imageList
? imageList
->GetHIMAGELIST() : 0,
451 void wxTreeCtrl::SetImageList(wxImageList
*imageList
)
453 SetAnyImageList(m_imageListNormal
= imageList
, TVSIL_NORMAL
);
456 void wxTreeCtrl::SetStateImageList(wxImageList
*imageList
)
458 SetAnyImageList(m_imageListState
= imageList
, TVSIL_STATE
);
461 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId
& item
,
462 bool recursively
) const
464 TraverseCounter
counter(this, item
, recursively
);
466 return counter
.GetCount() - 1;
469 // ----------------------------------------------------------------------------
471 // ----------------------------------------------------------------------------
473 wxString
wxTreeCtrl::GetItemText(const wxTreeItemId
& item
) const
475 wxChar buf
[512]; // the size is arbitrary...
477 wxTreeViewItem
tvItem(item
, TVIF_TEXT
);
478 tvItem
.pszText
= buf
;
479 tvItem
.cchTextMax
= WXSIZEOF(buf
);
480 if ( !DoGetItem(&tvItem
) )
482 // don't return some garbage which was on stack, but an empty string
486 return wxString(buf
);
489 void wxTreeCtrl::SetItemText(const wxTreeItemId
& item
, const wxString
& text
)
491 wxTreeViewItem
tvItem(item
, TVIF_TEXT
);
492 tvItem
.pszText
= (wxChar
*)text
.c_str(); // conversion is ok
496 int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId
& item
,
497 wxTreeItemIcon which
) const
499 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
500 if ( !DoGetItem(&tvItem
) )
505 return ((wxTreeItemIndirectData
*)tvItem
.lParam
)->GetImage(which
);
508 void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId
& item
,
510 wxTreeItemIcon which
) const
512 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
513 if ( !DoGetItem(&tvItem
) )
518 wxTreeItemIndirectData
*data
= ((wxTreeItemIndirectData
*)tvItem
.lParam
);
520 data
->SetImage(image
, which
);
522 // make sure that we have selected images as well
523 if ( which
== wxTreeItemIcon_Normal
&&
524 !data
->HasImage(wxTreeItemIcon_Selected
) )
526 data
->SetImage(image
, wxTreeItemIcon_Selected
);
529 if ( which
== wxTreeItemIcon_Expanded
&&
530 !data
->HasImage(wxTreeItemIcon_SelectedExpanded
) )
532 data
->SetImage(image
, wxTreeItemIcon_SelectedExpanded
);
536 void wxTreeCtrl::DoSetItemImages(const wxTreeItemId
& item
,
540 wxTreeViewItem
tvItem(item
, TVIF_IMAGE
| TVIF_SELECTEDIMAGE
);
541 tvItem
.iSelectedImage
= imageSel
;
542 tvItem
.iImage
= image
;
546 int wxTreeCtrl::GetItemImage(const wxTreeItemId
& item
,
547 wxTreeItemIcon which
) const
549 if ( HasIndirectData(item
) )
551 return DoGetItemImageFromData(item
, which
);
558 wxFAIL_MSG( T("unknown tree item image type") );
560 case wxTreeItemIcon_Normal
:
564 case wxTreeItemIcon_Selected
:
565 mask
= TVIF_SELECTEDIMAGE
;
568 case wxTreeItemIcon_Expanded
:
569 case wxTreeItemIcon_SelectedExpanded
:
573 wxTreeViewItem
tvItem(item
, mask
);
576 return mask
== TVIF_IMAGE
? tvItem
.iImage
: tvItem
.iSelectedImage
;
579 void wxTreeCtrl::SetItemImage(const wxTreeItemId
& item
, int image
,
580 wxTreeItemIcon which
)
582 int imageNormal
, imageSel
;
586 wxFAIL_MSG( T("unknown tree item image type") );
588 case wxTreeItemIcon_Normal
:
590 imageSel
= GetItemSelectedImage(item
);
593 case wxTreeItemIcon_Selected
:
594 imageNormal
= GetItemImage(item
);
598 case wxTreeItemIcon_Expanded
:
599 case wxTreeItemIcon_SelectedExpanded
:
600 if ( !HasIndirectData(item
) )
602 // we need to get the old images first, because after we create
603 // the wxTreeItemIndirectData GetItemXXXImage() will use it to
605 imageNormal
= GetItemImage(item
);
606 imageSel
= GetItemSelectedImage(item
);
608 // if it doesn't have it yet, add it
609 wxTreeItemIndirectData
*data
= new
610 wxTreeItemIndirectData(this, item
);
612 // copy the data to the new location
613 data
->SetImage(imageNormal
, wxTreeItemIcon_Normal
);
614 data
->SetImage(imageSel
, wxTreeItemIcon_Selected
);
617 DoSetItemImageFromData(item
, image
, which
);
619 // reset the normal/selected images because we won't use them any
620 // more - now they're stored inside the indirect data
622 imageSel
= I_IMAGECALLBACK
;
626 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
627 // change both normal and selected image - otherwise the change simply
628 // doesn't take place!
629 DoSetItemImages(item
, imageNormal
, imageSel
);
632 wxTreeItemData
*wxTreeCtrl::GetItemData(const wxTreeItemId
& item
) const
634 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
635 if ( !DoGetItem(&tvItem
) )
640 if ( HasIndirectData(item
) )
642 return ((wxTreeItemIndirectData
*)tvItem
.lParam
)->GetData();
646 return (wxTreeItemData
*)tvItem
.lParam
;
650 void wxTreeCtrl::SetItemData(const wxTreeItemId
& item
, wxTreeItemData
*data
)
652 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
654 if ( HasIndirectData(item
) )
656 if ( DoGetItem(&tvItem
) )
658 ((wxTreeItemIndirectData
*)tvItem
.lParam
)->SetData(data
);
662 wxFAIL_MSG( T("failed to change tree items data") );
667 tvItem
.lParam
= (LPARAM
)data
;
672 void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId
& item
,
673 wxTreeItemIndirectData
*data
)
675 // this should never happen because it's unnecessary and will probably lead
676 // to crash too because the code elsewhere supposes that the pointer the
677 // wxTreeItemIndirectData has is a real wxItemData and not
678 // wxTreeItemIndirectData as well
679 wxASSERT_MSG( !HasIndirectData(item
), T("setting indirect data twice?") );
681 SetItemData(item
, (wxTreeItemData
*)data
);
683 m_itemsWithIndirectData
.Add(item
);
686 bool wxTreeCtrl::HasIndirectData(const wxTreeItemId
& item
) const
688 return m_itemsWithIndirectData
.Index(item
) != wxNOT_FOUND
;
691 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId
& item
, bool has
)
693 wxTreeViewItem
tvItem(item
, TVIF_CHILDREN
);
694 tvItem
.cChildren
= (int)has
;
698 void wxTreeCtrl::SetItemBold(const wxTreeItemId
& item
, bool bold
)
700 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_BOLD
);
701 tvItem
.state
= bold
? TVIS_BOLD
: 0;
705 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId
& item
, bool highlight
)
707 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_DROPHILITED
);
708 tvItem
.state
= highlight
? TVIS_DROPHILITED
: 0;
712 // ----------------------------------------------------------------------------
714 // ----------------------------------------------------------------------------
716 bool wxTreeCtrl::IsVisible(const wxTreeItemId
& item
) const
718 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
720 return SendMessage(GetHwnd(), TVM_GETITEMRECT
, FALSE
, (LPARAM
)&rect
) != 0;
724 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId
& item
) const
726 wxTreeViewItem
tvItem(item
, TVIF_CHILDREN
);
729 return tvItem
.cChildren
!= 0;
732 bool wxTreeCtrl::IsExpanded(const wxTreeItemId
& item
) const
734 // probably not a good idea to put it here
735 //wxASSERT( ItemHasChildren(item) );
737 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_EXPANDED
);
740 return (tvItem
.state
& TVIS_EXPANDED
) != 0;
743 bool wxTreeCtrl::IsSelected(const wxTreeItemId
& item
) const
745 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_SELECTED
);
748 return (tvItem
.state
& TVIS_SELECTED
) != 0;
751 bool wxTreeCtrl::IsBold(const wxTreeItemId
& item
) const
753 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_BOLD
);
756 return (tvItem
.state
& TVIS_BOLD
) != 0;
759 // ----------------------------------------------------------------------------
761 // ----------------------------------------------------------------------------
763 wxTreeItemId
wxTreeCtrl::GetRootItem() const
765 return wxTreeItemId((WXHTREEITEM
) TreeView_GetRoot(GetHwnd()));
768 wxTreeItemId
wxTreeCtrl::GetSelection() const
770 wxCHECK_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), (WXHTREEITEM
)0,
771 T("this only works with single selection controls") );
773 return wxTreeItemId((WXHTREEITEM
) TreeView_GetSelection(GetHwnd()));
776 wxTreeItemId
wxTreeCtrl::GetParent(const wxTreeItemId
& item
) const
778 return wxTreeItemId((WXHTREEITEM
) TreeView_GetParent(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
781 wxTreeItemId
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
,
784 // remember the last child returned in 'cookie'
785 _cookie
= (long)TreeView_GetChild(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
)item
);
787 return wxTreeItemId((WXHTREEITEM
)_cookie
);
790 wxTreeItemId
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
),
793 wxTreeItemId l
= wxTreeItemId((WXHTREEITEM
)TreeView_GetNextSibling(GetHwnd(),
794 (HTREEITEM
)(WXHTREEITEM
)_cookie
));
800 wxTreeItemId
wxTreeCtrl::GetLastChild(const wxTreeItemId
& item
) const
802 // can this be done more efficiently?
805 wxTreeItemId childLast
,
806 child
= GetFirstChild(item
, cookie
);
807 while ( child
.IsOk() )
810 child
= GetNextChild(item
, cookie
);
816 wxTreeItemId
wxTreeCtrl::GetNextSibling(const wxTreeItemId
& item
) const
818 return wxTreeItemId((WXHTREEITEM
) TreeView_GetNextSibling(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
821 wxTreeItemId
wxTreeCtrl::GetPrevSibling(const wxTreeItemId
& item
) const
823 return wxTreeItemId((WXHTREEITEM
) TreeView_GetPrevSibling(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
826 wxTreeItemId
wxTreeCtrl::GetFirstVisibleItem() const
828 return wxTreeItemId((WXHTREEITEM
) TreeView_GetFirstVisible(GetHwnd()));
831 wxTreeItemId
wxTreeCtrl::GetNextVisible(const wxTreeItemId
& item
) const
833 wxASSERT_MSG( IsVisible(item
), T("The item you call GetNextVisible() "
834 "for must be visible itself!"));
836 return wxTreeItemId((WXHTREEITEM
) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
839 wxTreeItemId
wxTreeCtrl::GetPrevVisible(const wxTreeItemId
& item
) const
841 wxASSERT_MSG( IsVisible(item
), T("The item you call GetPrevVisible() "
842 "for must be visible itself!"));
844 return wxTreeItemId((WXHTREEITEM
) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
847 // ----------------------------------------------------------------------------
848 // multiple selections emulation
849 // ----------------------------------------------------------------------------
851 bool wxTreeCtrl::IsItemChecked(const wxTreeItemId
& item
) const
853 // receive the desired information.
854 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
);
857 // state image indices are 1 based
858 return ((tvItem
.state
>> 12) - 1) == 1;
861 void wxTreeCtrl::SetItemCheck(const wxTreeItemId
& item
, bool check
)
863 // receive the desired information.
864 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
);
866 // state images are one-based
867 tvItem
.state
= (check
? 2 : 1) << 12;
872 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds
& selections
) const
874 TraverseSelections
selector(this, selections
);
876 return selections
.GetCount();
879 // ----------------------------------------------------------------------------
881 // ----------------------------------------------------------------------------
883 wxTreeItemId
wxTreeCtrl::DoInsertItem(const wxTreeItemId
& parent
,
884 wxTreeItemId hInsertAfter
,
885 const wxString
& text
,
886 int image
, int selectedImage
,
887 wxTreeItemData
*data
)
889 TV_INSERTSTRUCT tvIns
;
890 tvIns
.hParent
= (HTREEITEM
) (WXHTREEITEM
)parent
;
891 tvIns
.hInsertAfter
= (HTREEITEM
) (WXHTREEITEM
) hInsertAfter
;
893 // this is how we insert the item as the first child: supply a NULL
895 if ( !tvIns
.hInsertAfter
)
897 tvIns
.hInsertAfter
= TVI_FIRST
;
901 if ( !text
.IsEmpty() )
904 tvIns
.item
.pszText
= (wxChar
*)text
.c_str(); // cast is ok
910 tvIns
.item
.iImage
= image
;
912 if ( selectedImage
== -1 )
914 // take the same image for selected icon if not specified
915 selectedImage
= image
;
919 if ( selectedImage
!= -1 )
921 mask
|= TVIF_SELECTEDIMAGE
;
922 tvIns
.item
.iSelectedImage
= selectedImage
;
928 tvIns
.item
.lParam
= (LPARAM
)data
;
931 tvIns
.item
.mask
= mask
;
933 HTREEITEM id
= (HTREEITEM
) TreeView_InsertItem(GetHwnd(), &tvIns
);
936 wxLogLastError("TreeView_InsertItem");
941 // associate the application tree item with Win32 tree item handle
942 data
->SetId((WXHTREEITEM
)id
);
945 return wxTreeItemId((WXHTREEITEM
)id
);
948 // for compatibility only
949 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
950 const wxString
& text
,
951 int image
, int selImage
,
954 return DoInsertItem(parent
, (WXHTREEITEM
)insertAfter
, text
,
955 image
, selImage
, NULL
);
958 wxTreeItemId
wxTreeCtrl::AddRoot(const wxString
& text
,
959 int image
, int selectedImage
,
960 wxTreeItemData
*data
)
962 return DoInsertItem(wxTreeItemId((WXHTREEITEM
) 0), (WXHTREEITEM
) 0,
963 text
, image
, selectedImage
, data
);
966 wxTreeItemId
wxTreeCtrl::PrependItem(const wxTreeItemId
& parent
,
967 const wxString
& text
,
968 int image
, int selectedImage
,
969 wxTreeItemData
*data
)
971 return DoInsertItem(parent
, (WXHTREEITEM
) TVI_FIRST
,
972 text
, image
, selectedImage
, data
);
975 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
976 const wxTreeItemId
& idPrevious
,
977 const wxString
& text
,
978 int image
, int selectedImage
,
979 wxTreeItemData
*data
)
981 return DoInsertItem(parent
, idPrevious
, text
, image
, selectedImage
, data
);
984 wxTreeItemId
wxTreeCtrl::AppendItem(const wxTreeItemId
& parent
,
985 const wxString
& text
,
986 int image
, int selectedImage
,
987 wxTreeItemData
*data
)
989 return DoInsertItem(parent
, (WXHTREEITEM
) TVI_LAST
,
990 text
, image
, selectedImage
, data
);
993 void wxTreeCtrl::Delete(const wxTreeItemId
& item
)
995 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
) )
997 wxLogLastError("TreeView_DeleteItem");
1001 // delete all children (but don't delete the item itself)
1002 void wxTreeCtrl::DeleteChildren(const wxTreeItemId
& item
)
1006 wxArrayLong children
;
1007 wxTreeItemId child
= GetFirstChild(item
, cookie
);
1008 while ( child
.IsOk() )
1010 children
.Add((long)(WXHTREEITEM
)child
);
1012 child
= GetNextChild(item
, cookie
);
1015 size_t nCount
= children
.Count();
1016 for ( size_t n
= 0; n
< nCount
; n
++ )
1018 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM
)children
[n
]) )
1020 wxLogLastError("TreeView_DeleteItem");
1025 void wxTreeCtrl::DeleteAllItems()
1027 if ( !TreeView_DeleteAllItems(GetHwnd()) )
1029 wxLogLastError("TreeView_DeleteAllItems");
1033 void wxTreeCtrl::DoExpand(const wxTreeItemId
& item
, int flag
)
1035 wxASSERT_MSG( flag
== TVE_COLLAPSE
||
1036 flag
== (TVE_COLLAPSE
| TVE_COLLAPSERESET
) ||
1037 flag
== TVE_EXPAND
||
1039 T("Unknown flag in wxTreeCtrl::DoExpand") );
1041 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
1042 // emulate them. This behaviour has changed slightly with comctl32.dll
1043 // v 4.70 - now it does send them but only the first time. To maintain
1044 // compatible behaviour and also in order to not have surprises with the
1045 // future versions, don't rely on this and still do everything ourselves.
1046 // To avoid that the messages be sent twice when the item is expanded for
1047 // the first time we must clear TVIS_EXPANDEDONCE style manually.
1049 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_EXPANDEDONCE
);
1053 if ( TreeView_Expand(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
, flag
) != 0 )
1055 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
1056 event
.m_item
= item
;
1058 bool isExpanded
= IsExpanded(item
);
1060 event
.SetEventObject(this);
1062 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
1063 event
.SetEventType(g_events
[isExpanded
][TRUE
]);
1064 GetEventHandler()->ProcessEvent(event
);
1066 event
.SetEventType(g_events
[isExpanded
][FALSE
]);
1067 GetEventHandler()->ProcessEvent(event
);
1069 //else: change didn't took place, so do nothing at all
1072 void wxTreeCtrl::Expand(const wxTreeItemId
& item
)
1074 DoExpand(item
, TVE_EXPAND
);
1077 void wxTreeCtrl::Collapse(const wxTreeItemId
& item
)
1079 DoExpand(item
, TVE_COLLAPSE
);
1082 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId
& item
)
1084 DoExpand(item
, TVE_COLLAPSE
| TVE_COLLAPSERESET
);
1087 void wxTreeCtrl::Toggle(const wxTreeItemId
& item
)
1089 DoExpand(item
, TVE_TOGGLE
);
1092 void wxTreeCtrl::ExpandItem(const wxTreeItemId
& item
, int action
)
1094 DoExpand(item
, action
);
1097 void wxTreeCtrl::Unselect()
1099 wxASSERT_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), T("doesn't make sense") );
1101 // just remove the selection
1102 SelectItem(wxTreeItemId((WXHTREEITEM
) 0));
1105 void wxTreeCtrl::UnselectAll()
1107 if ( m_windowStyle
& wxTR_MULTIPLE
)
1109 wxArrayTreeItemIds selections
;
1110 size_t count
= GetSelections(selections
);
1111 for ( size_t n
= 0; n
< count
; n
++ )
1113 SetItemCheck(selections
[n
], FALSE
);
1118 // just remove the selection
1123 void wxTreeCtrl::SelectItem(const wxTreeItemId
& item
)
1125 if ( m_windowStyle
& wxTR_MULTIPLE
)
1127 // selecting the item means checking it
1132 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
1133 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
1134 // send them ourselves
1136 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
1137 event
.m_item
= item
;
1138 event
.SetEventObject(this);
1140 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING
);
1141 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
1143 if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
) )
1145 wxLogLastError("TreeView_SelectItem");
1149 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED
);
1150 (void)GetEventHandler()->ProcessEvent(event
);
1153 //else: program vetoed the change
1157 void wxTreeCtrl::EnsureVisible(const wxTreeItemId
& item
)
1160 TreeView_EnsureVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
);
1163 void wxTreeCtrl::ScrollTo(const wxTreeItemId
& item
)
1165 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
) )
1167 wxLogLastError("TreeView_SelectSetFirstVisible");
1171 wxTextCtrl
* wxTreeCtrl::GetEditControl() const
1176 void wxTreeCtrl::DeleteTextCtrl()
1180 m_textCtrl
->UnsubclassWin();
1181 m_textCtrl
->SetHWND(0);
1187 wxTextCtrl
* wxTreeCtrl::EditLabel(const wxTreeItemId
& item
,
1188 wxClassInfo
* textControlClass
)
1190 wxASSERT( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)) );
1192 HWND hWnd
= (HWND
) TreeView_EditLabel(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
);
1194 // this is not an error - the TVN_BEGINLABELEDIT handler might have
1203 m_textCtrl
= (wxTextCtrl
*)textControlClass
->CreateObject();
1204 m_textCtrl
->SetHWND((WXHWND
)hWnd
);
1205 m_textCtrl
->SubclassWin((WXHWND
)hWnd
);
1210 // End label editing, optionally cancelling the edit
1211 void wxTreeCtrl::EndEditLabel(const wxTreeItemId
& item
, bool discardChanges
)
1213 TreeView_EndEditLabelNow(GetHwnd(), discardChanges
);
1218 wxTreeItemId
wxTreeCtrl::HitTest(const wxPoint
& point
, int& flags
)
1220 TV_HITTESTINFO hitTestInfo
;
1221 hitTestInfo
.pt
.x
= (int)point
.x
;
1222 hitTestInfo
.pt
.y
= (int)point
.y
;
1224 TreeView_HitTest(GetHwnd(), &hitTestInfo
);
1229 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
1230 flags |= wxTREE_HITTEST_##flag
1232 TRANSLATE_FLAG(ABOVE
);
1233 TRANSLATE_FLAG(BELOW
);
1234 TRANSLATE_FLAG(NOWHERE
);
1235 TRANSLATE_FLAG(ONITEMBUTTON
);
1236 TRANSLATE_FLAG(ONITEMICON
);
1237 TRANSLATE_FLAG(ONITEMINDENT
);
1238 TRANSLATE_FLAG(ONITEMLABEL
);
1239 TRANSLATE_FLAG(ONITEMRIGHT
);
1240 TRANSLATE_FLAG(ONITEMSTATEICON
);
1241 TRANSLATE_FLAG(TOLEFT
);
1242 TRANSLATE_FLAG(TORIGHT
);
1244 #undef TRANSLATE_FLAG
1246 return wxTreeItemId((WXHTREEITEM
) hitTestInfo
.hItem
);
1249 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId
& item
,
1251 bool textOnly
) const
1254 if ( TreeView_GetItemRect(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
,
1257 rect
= wxRect(wxPoint(rc
.left
, rc
.top
), wxPoint(rc
.right
, rc
.bottom
));
1263 // couldn't retrieve rect: for example, item isn't visible
1268 // ----------------------------------------------------------------------------
1270 // ----------------------------------------------------------------------------
1272 static int CALLBACK
TreeView_CompareCallback(wxTreeItemData
*pItem1
,
1273 wxTreeItemData
*pItem2
,
1276 wxCHECK_MSG( pItem1
&& pItem2
, 0,
1277 T("sorting tree without data doesn't make sense") );
1279 return tree
->OnCompareItems(pItem1
->GetId(), pItem2
->GetId());
1282 int wxTreeCtrl::OnCompareItems(const wxTreeItemId
& item1
,
1283 const wxTreeItemId
& item2
)
1285 return wxStrcmp(GetItemText(item1
), GetItemText(item2
));
1288 void wxTreeCtrl::SortChildren(const wxTreeItemId
& item
)
1290 // rely on the fact that TreeView_SortChildren does the same thing as our
1291 // default behaviour, i.e. sorts items alphabetically and so call it
1292 // directly if we're not in derived class (much more efficient!)
1293 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl
) )
1295 TreeView_SortChildren(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
, 0);
1300 tvSort
.hParent
= (HTREEITEM
)(WXHTREEITEM
)item
;
1301 tvSort
.lpfnCompare
= (PFNTVCOMPARE
)TreeView_CompareCallback
;
1302 tvSort
.lParam
= (LPARAM
)this;
1303 TreeView_SortChildrenCB(GetHwnd(), &tvSort
, 0 /* reserved */);
1307 // ----------------------------------------------------------------------------
1309 // ----------------------------------------------------------------------------
1311 bool wxTreeCtrl::MSWCommand(WXUINT cmd
, WXWORD id
)
1313 if ( cmd
== EN_UPDATE
)
1315 wxCommandEvent
event(wxEVT_COMMAND_TEXT_UPDATED
, id
);
1316 event
.SetEventObject( this );
1317 ProcessCommand(event
);
1319 else if ( cmd
== EN_KILLFOCUS
)
1321 wxCommandEvent
event(wxEVT_KILL_FOCUS
, id
);
1322 event
.SetEventObject( this );
1323 ProcessCommand(event
);
1331 // command processed
1335 // process WM_NOTIFY Windows message
1336 bool wxTreeCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
1338 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
1339 wxEventType eventType
= wxEVT_NULL
;
1340 NMHDR
*hdr
= (NMHDR
*)lParam
;
1342 switch ( hdr
->code
)
1345 eventType
= wxEVT_COMMAND_TREE_BEGIN_DRAG
;
1348 case TVN_BEGINRDRAG
:
1350 if ( eventType
== wxEVT_NULL
)
1351 eventType
= wxEVT_COMMAND_TREE_BEGIN_RDRAG
;
1352 //else: left drag, already set above
1354 NM_TREEVIEW
*tv
= (NM_TREEVIEW
*)lParam
;
1356 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1357 event
.m_pointDrag
= wxPoint(tv
->ptDrag
.x
, tv
->ptDrag
.y
);
1361 case TVN_BEGINLABELEDIT
:
1363 eventType
= wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
;
1364 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1366 event
.m_item
= (WXHTREEITEM
) info
->item
.hItem
;
1367 event
.m_label
= info
->item
.pszText
;
1371 case TVN_DELETEITEM
:
1373 eventType
= wxEVT_COMMAND_TREE_DELETE_ITEM
;
1374 NM_TREEVIEW
*tv
= (NM_TREEVIEW
*)lParam
;
1376 event
.m_item
= (WXHTREEITEM
) tv
->itemOld
.hItem
;
1380 case TVN_ENDLABELEDIT
:
1382 eventType
= wxEVT_COMMAND_TREE_END_LABEL_EDIT
;
1383 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1385 event
.m_item
= (WXHTREEITEM
)info
->item
.hItem
;
1386 event
.m_label
= info
->item
.pszText
;
1390 case TVN_GETDISPINFO
:
1391 eventType
= wxEVT_COMMAND_TREE_GET_INFO
;
1394 case TVN_SETDISPINFO
:
1396 if ( eventType
== wxEVT_NULL
)
1397 eventType
= wxEVT_COMMAND_TREE_SET_INFO
;
1398 //else: get, already set above
1400 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1402 event
.m_item
= (WXHTREEITEM
) info
->item
.hItem
;
1406 case TVN_ITEMEXPANDING
:
1407 event
.m_code
= FALSE
;
1410 case TVN_ITEMEXPANDED
:
1412 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1414 bool expand
= FALSE
;
1415 switch ( tv
->action
)
1426 wxLogDebug(T("unexpected code %d in TVN_ITEMEXPAND "
1427 "message"), tv
->action
);
1430 bool ing
= (hdr
->code
== TVN_ITEMEXPANDING
);
1431 eventType
= g_events
[expand
][ing
];
1433 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1439 eventType
= wxEVT_COMMAND_TREE_KEY_DOWN
;
1440 TV_KEYDOWN
*info
= (TV_KEYDOWN
*)lParam
;
1442 event
.m_code
= wxCharCodeMSWToWX(info
->wVKey
);
1444 // a separate event for this case
1445 if ( info
->wVKey
== VK_SPACE
|| info
->wVKey
== VK_RETURN
)
1447 wxTreeEvent
event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED
,
1449 event2
.SetEventObject(this);
1451 GetEventHandler()->ProcessEvent(event2
);
1456 case TVN_SELCHANGED
:
1457 eventType
= wxEVT_COMMAND_TREE_SEL_CHANGED
;
1460 case TVN_SELCHANGING
:
1462 if ( eventType
== wxEVT_NULL
)
1463 eventType
= wxEVT_COMMAND_TREE_SEL_CHANGING
;
1464 //else: already set above
1466 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1468 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1469 event
.m_itemOld
= (WXHTREEITEM
) tv
->itemOld
.hItem
;
1474 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1477 event
.SetEventObject(this);
1478 event
.SetEventType(eventType
);
1480 bool processed
= GetEventHandler()->ProcessEvent(event
);
1483 switch ( hdr
->code
)
1485 case TVN_DELETEITEM
:
1487 // NB: we might process this message using wxWindows event
1488 // tables, but due to overhead of wxWin event system we
1489 // prefer to do it here ourself (otherwise deleting a tree
1490 // with many items is just too slow)
1491 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1493 wxTreeItemId item
= event
.m_item
;
1494 if ( HasIndirectData(item
) )
1496 wxTreeItemIndirectData
*data
= (wxTreeItemIndirectData
*)
1498 delete data
; // can't be NULL here
1500 m_itemsWithIndirectData
.Remove(item
);
1504 wxTreeItemData
*data
= (wxTreeItemData
*)tv
->itemOld
.lParam
;
1505 delete data
; // may be NULL, ok
1508 processed
= TRUE
; // Make sure we don't get called twice
1512 case TVN_BEGINLABELEDIT
:
1513 // return TRUE to cancel label editing
1514 *result
= !event
.IsAllowed();
1517 case TVN_ENDLABELEDIT
:
1518 // return TRUE to set the label to the new string
1519 *result
= event
.IsAllowed();
1521 // ensure that we don't have the text ctrl which is going to be
1526 case TVN_SELCHANGING
:
1527 case TVN_ITEMEXPANDING
:
1528 // return TRUE to prevent the action from happening
1529 *result
= !event
.IsAllowed();
1532 case TVN_GETDISPINFO
:
1533 // NB: so far the user can't set the image himself anyhow, so do it
1534 // anyway - but this may change later
1535 if ( /* !processed && */ 1 )
1537 wxTreeItemId item
= event
.m_item
;
1538 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1539 if ( info
->item
.mask
& TVIF_IMAGE
)
1542 DoGetItemImageFromData
1545 IsExpanded(item
) ? wxTreeItemIcon_Expanded
1546 : wxTreeItemIcon_Normal
1549 if ( info
->item
.mask
& TVIF_SELECTEDIMAGE
)
1551 info
->item
.iSelectedImage
=
1552 DoGetItemImageFromData
1555 IsExpanded(item
) ? wxTreeItemIcon_SelectedExpanded
1556 : wxTreeItemIcon_Selected
1563 // for the other messages the return value is ignored and there is
1564 // nothing special to do
1570 // ----------------------------------------------------------------------------
1572 // ----------------------------------------------------------------------------
1574 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent
, wxNotifyEvent
)
1576 wxTreeEvent::wxTreeEvent(wxEventType commandType
, int id
)
1577 : wxNotifyEvent(commandType
, id
)