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"
50 #include "wx/msw/gnuwin32/extra.h"
53 #if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__)
57 // Bug in headers, sometimes
59 #define TVIS_FOCUSED 0x0001
62 // ----------------------------------------------------------------------------
64 // ----------------------------------------------------------------------------
66 // a convenient wrapper around TV_ITEM struct which adds a ctor
67 struct wxTreeViewItem
: public TV_ITEM
69 wxTreeViewItem(const wxTreeItemId
& item
, // the item handle
70 UINT mask_
, // fields which are valid
71 UINT stateMask_
= 0) // for TVIF_STATE only
73 // hItem member is always valid
74 mask
= mask_
| TVIF_HANDLE
;
75 stateMask
= stateMask_
;
76 hItem
= (HTREEITEM
) (WXHTREEITEM
) item
;
80 // a class which encapsulates the tree traversal logic: it vists all (unless
81 // OnVisit() returns FALSE) items under the given one
85 wxTreeTraversal(const wxTreeCtrl
*tree
)
90 // do traverse the tree: visit all items (recursively by default) under the
91 // given one; return TRUE if all items were traversed or FALSE if the
92 // traversal was aborted because OnVisit returned FALSE
93 bool DoTraverse(const wxTreeItemId
& root
, bool recursively
= TRUE
);
95 // override this function to do whatever is needed for each item, return
96 // FALSE to stop traversing
97 virtual bool OnVisit(const wxTreeItemId
& item
) = 0;
100 const wxTreeCtrl
*GetTree() const { return m_tree
; }
103 bool Traverse(const wxTreeItemId
& root
, bool recursively
);
105 const wxTreeCtrl
*m_tree
;
108 // ----------------------------------------------------------------------------
110 // ----------------------------------------------------------------------------
112 #if !USE_SHARED_LIBRARY
113 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl
, wxControl
)
116 // ----------------------------------------------------------------------------
118 // ----------------------------------------------------------------------------
120 // handy table for sending events
121 static const wxEventType g_events
[2][2] =
123 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED
, wxEVT_COMMAND_TREE_ITEM_COLLAPSING
},
124 { wxEVT_COMMAND_TREE_ITEM_EXPANDED
, wxEVT_COMMAND_TREE_ITEM_EXPANDING
}
127 // ============================================================================
129 // ============================================================================
131 // ----------------------------------------------------------------------------
133 // ----------------------------------------------------------------------------
135 bool wxTreeTraversal::DoTraverse(const wxTreeItemId
& root
, bool recursively
)
137 if ( !OnVisit(root
) )
140 return Traverse(root
, recursively
);
143 bool wxTreeTraversal::Traverse(const wxTreeItemId
& root
, bool recursively
)
146 wxTreeItemId child
= m_tree
->GetFirstChild(root
, cookie
);
147 while ( child
.IsOk() )
149 // depth first traversal
150 if ( recursively
&& !Traverse(child
, TRUE
) )
153 if ( !OnVisit(child
) )
156 child
= m_tree
->GetNextChild(root
, cookie
);
162 // ----------------------------------------------------------------------------
163 // construction and destruction
164 // ----------------------------------------------------------------------------
166 void wxTreeCtrl::Init()
168 m_imageListNormal
= NULL
;
169 m_imageListState
= NULL
;
173 bool wxTreeCtrl::Create(wxWindow
*parent
,
178 const wxValidator
& validator
,
179 const wxString
& name
)
183 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
186 DWORD wstyle
= WS_VISIBLE
| WS_CHILD
| WS_TABSTOP
|
187 TVS_HASLINES
| TVS_SHOWSELALWAYS
;
189 if ( m_windowStyle
& wxTR_HAS_BUTTONS
)
190 wstyle
|= TVS_HASBUTTONS
;
192 if ( m_windowStyle
& wxTR_EDIT_LABELS
)
193 wstyle
|= TVS_EDITLABELS
;
195 if ( m_windowStyle
& wxTR_LINES_AT_ROOT
)
196 wstyle
|= TVS_LINESATROOT
;
199 // we emulate the multiple selection tree controls by using checkboxes: set
200 // up the image list we need for this if we do have multiple selections
201 if ( m_windowStyle
& wxTR_MULTIPLE
)
202 wstyle
|= TVS_CHECKBOXES
;
205 // Create the tree control.
206 if ( !MSWCreateControl(WC_TREEVIEW
, wstyle
) )
209 // the treectrl with any other background looks ugly because the items
210 // background is white anyhow
211 SetBackgroundColour(*wxWHITE
);
213 // VZ: this is some experimental code which may be used to get the
214 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
215 // AFAIK, the standard DLL does about the same thing anyhow.
217 if ( m_windowStyle
& wxTR_MULTIPLE
)
221 // create the DC compatible with the current screen
222 HDC hdcMem
= CreateCompatibleDC(NULL
);
224 // create a mono bitmap of the standard size
225 int x
= GetSystemMetrics(SM_CXMENUCHECK
);
226 int y
= GetSystemMetrics(SM_CYMENUCHECK
);
227 wxImageList
imagelistCheckboxes(x
, y
, FALSE
, 2);
228 HBITMAP hbmpCheck
= CreateBitmap(x
, y
, // bitmap size
229 1, // # of color planes
230 1, // # bits needed for one pixel
231 0); // array containing colour data
232 SelectObject(hdcMem
, hbmpCheck
);
234 // then draw a check mark into it
235 RECT rect
= { 0, 0, x
, y
};
236 if ( !::DrawFrameControl(hdcMem
, &rect
,
238 DFCS_BUTTONCHECK
| DFCS_CHECKED
) )
240 wxLogLastError(_T("DrawFrameControl(check)"));
243 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
244 imagelistCheckboxes
.Add(bmp
);
246 if ( !::DrawFrameControl(hdcMem
, &rect
,
250 wxLogLastError(_T("DrawFrameControl(uncheck)"));
253 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
254 imagelistCheckboxes
.Add(bmp
);
260 SetStateImageList(&imagelistCheckboxes
);
264 SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
);
269 wxTreeCtrl::~wxTreeCtrl()
273 // delete user data to prevent memory leaks
277 // ----------------------------------------------------------------------------
279 // ----------------------------------------------------------------------------
281 // simple wrappers which add error checking in debug mode
283 bool wxTreeCtrl::DoGetItem(wxTreeViewItem
* tvItem
) const
285 if ( !TreeView_GetItem(GetHwnd(), tvItem
) )
287 wxLogLastError("TreeView_GetItem");
295 void wxTreeCtrl::DoSetItem(wxTreeViewItem
* tvItem
)
297 if ( TreeView_SetItem(GetHwnd(), tvItem
) == -1 )
299 wxLogLastError("TreeView_SetItem");
303 size_t wxTreeCtrl::GetCount() const
305 return (size_t)TreeView_GetCount(GetHwnd());
308 unsigned int wxTreeCtrl::GetIndent() const
310 return TreeView_GetIndent(GetHwnd());
313 void wxTreeCtrl::SetIndent(unsigned int indent
)
315 TreeView_SetIndent(GetHwnd(), indent
);
318 wxImageList
*wxTreeCtrl::GetImageList() const
320 return m_imageListNormal
;
323 wxImageList
*wxTreeCtrl::GetStateImageList() const
325 return m_imageListNormal
;
328 void wxTreeCtrl::SetAnyImageList(wxImageList
*imageList
, int which
)
331 TreeView_SetImageList(GetHwnd(),
332 imageList
? imageList
->GetHIMAGELIST() : 0,
336 void wxTreeCtrl::SetImageList(wxImageList
*imageList
)
338 SetAnyImageList(m_imageListNormal
= imageList
, TVSIL_NORMAL
);
341 void wxTreeCtrl::SetStateImageList(wxImageList
*imageList
)
343 SetAnyImageList(m_imageListState
= imageList
, TVSIL_STATE
);
346 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId
& item
,
347 bool recursively
) const
349 class TraverseCounter
: public wxTreeTraversal
352 TraverseCounter(const wxTreeCtrl
*tree
,
353 const wxTreeItemId
& root
,
355 : wxTreeTraversal(tree
)
359 DoTraverse(root
, recursively
);
362 virtual bool OnVisit(const wxTreeItemId
& item
)
369 size_t GetCount() const { return m_count
; }
373 } counter(this, item
, recursively
);
375 return counter
.GetCount();
378 // ----------------------------------------------------------------------------
380 // ----------------------------------------------------------------------------
382 wxString
wxTreeCtrl::GetItemText(const wxTreeItemId
& item
) const
384 wxChar buf
[512]; // the size is arbitrary...
386 wxTreeViewItem
tvItem(item
, TVIF_TEXT
);
387 tvItem
.pszText
= buf
;
388 tvItem
.cchTextMax
= WXSIZEOF(buf
);
389 if ( !DoGetItem(&tvItem
) )
391 // don't return some garbage which was on stack, but an empty string
395 return wxString(buf
);
398 void wxTreeCtrl::SetItemText(const wxTreeItemId
& item
, const wxString
& text
)
400 wxTreeViewItem
tvItem(item
, TVIF_TEXT
);
401 tvItem
.pszText
= (wxChar
*)text
.c_str(); // conversion is ok
405 void wxTreeCtrl::DoSetItemImages(const wxTreeItemId
& item
,
409 wxTreeViewItem
tvItem(item
, TVIF_IMAGE
| TVIF_SELECTEDIMAGE
);
410 tvItem
.iSelectedImage
= imageSel
;
411 tvItem
.iImage
= image
;
415 int wxTreeCtrl::GetItemImage(const wxTreeItemId
& item
) const
417 wxTreeViewItem
tvItem(item
, TVIF_IMAGE
);
420 return tvItem
.iImage
;
423 void wxTreeCtrl::SetItemImage(const wxTreeItemId
& item
, int image
)
425 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
426 // change both normal and selected image - otherwise the change simply
427 // doesn't take place!
428 DoSetItemImages(item
, image
, GetItemSelectedImage(item
));
431 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId
& item
) const
433 wxTreeViewItem
tvItem(item
, TVIF_SELECTEDIMAGE
);
436 return tvItem
.iSelectedImage
;
439 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId
& item
, int image
)
441 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
442 // change both normal and selected image - otherwise the change simply
443 // doesn't take place!
444 DoSetItemImages(item
, GetItemImage(item
), image
);
447 wxTreeItemData
*wxTreeCtrl::GetItemData(const wxTreeItemId
& item
) const
449 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
450 if ( !DoGetItem(&tvItem
) )
455 return (wxTreeItemData
*)tvItem
.lParam
;
458 void wxTreeCtrl::SetItemData(const wxTreeItemId
& item
, wxTreeItemData
*data
)
460 wxTreeViewItem
tvItem(item
, TVIF_PARAM
);
461 tvItem
.lParam
= (LPARAM
)data
;
465 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId
& item
, bool has
)
467 wxTreeViewItem
tvItem(item
, TVIF_CHILDREN
);
468 tvItem
.cChildren
= (int)has
;
472 void wxTreeCtrl::SetItemBold(const wxTreeItemId
& item
, bool bold
)
474 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_BOLD
);
475 tvItem
.state
= bold
? TVIS_BOLD
: 0;
479 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId
& item
, bool highlight
)
481 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_DROPHILITED
);
482 tvItem
.state
= highlight
? TVIS_DROPHILITED
: 0;
486 // ----------------------------------------------------------------------------
488 // ----------------------------------------------------------------------------
490 bool wxTreeCtrl::IsVisible(const wxTreeItemId
& item
) const
492 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
494 return SendMessage(GetHwnd(), TVM_GETITEMRECT
, FALSE
, (LPARAM
)&rect
) != 0;
498 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId
& item
) const
500 wxTreeViewItem
tvItem(item
, TVIF_CHILDREN
);
503 return tvItem
.cChildren
!= 0;
506 bool wxTreeCtrl::IsExpanded(const wxTreeItemId
& item
) const
508 // probably not a good idea to put it here
509 //wxASSERT( ItemHasChildren(item) );
511 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_EXPANDED
);
514 return (tvItem
.state
& TVIS_EXPANDED
) != 0;
517 bool wxTreeCtrl::IsSelected(const wxTreeItemId
& item
) const
519 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_SELECTED
);
522 return (tvItem
.state
& TVIS_SELECTED
) != 0;
525 bool wxTreeCtrl::IsBold(const wxTreeItemId
& item
) const
527 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_BOLD
);
530 return (tvItem
.state
& TVIS_BOLD
) != 0;
533 // ----------------------------------------------------------------------------
535 // ----------------------------------------------------------------------------
537 wxTreeItemId
wxTreeCtrl::GetRootItem() const
539 return wxTreeItemId((WXHTREEITEM
) TreeView_GetRoot(GetHwnd()));
542 wxTreeItemId
wxTreeCtrl::GetSelection() const
544 wxCHECK_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), (WXHTREEITEM
)0,
545 _T("this only works with single selection controls") );
547 return wxTreeItemId((WXHTREEITEM
) TreeView_GetSelection(GetHwnd()));
550 wxTreeItemId
wxTreeCtrl::GetParent(const wxTreeItemId
& item
) const
552 return wxTreeItemId((WXHTREEITEM
) TreeView_GetParent(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
555 wxTreeItemId
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
,
558 // remember the last child returned in 'cookie'
559 _cookie
= (long)TreeView_GetChild(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
)item
);
561 return wxTreeItemId((WXHTREEITEM
)_cookie
);
564 wxTreeItemId
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
),
567 wxTreeItemId l
= wxTreeItemId((WXHTREEITEM
)TreeView_GetNextSibling(GetHwnd(),
568 (HTREEITEM
)(WXHTREEITEM
)_cookie
));
574 wxTreeItemId
wxTreeCtrl::GetLastChild(const wxTreeItemId
& item
) const
576 // can this be done more efficiently?
579 wxTreeItemId childLast
,
580 child
= GetFirstChild(item
, cookie
);
581 while ( child
.IsOk() )
584 child
= GetNextChild(item
, cookie
);
590 wxTreeItemId
wxTreeCtrl::GetNextSibling(const wxTreeItemId
& item
) const
592 return wxTreeItemId((WXHTREEITEM
) TreeView_GetNextSibling(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
595 wxTreeItemId
wxTreeCtrl::GetPrevSibling(const wxTreeItemId
& item
) const
597 return wxTreeItemId((WXHTREEITEM
) TreeView_GetPrevSibling(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
600 wxTreeItemId
wxTreeCtrl::GetFirstVisibleItem() const
602 return wxTreeItemId((WXHTREEITEM
) TreeView_GetFirstVisible(GetHwnd()));
605 wxTreeItemId
wxTreeCtrl::GetNextVisible(const wxTreeItemId
& item
) const
607 wxASSERT_MSG( IsVisible(item
), _T("The item you call GetNextVisible() "
608 "for must be visible itself!"));
610 return wxTreeItemId((WXHTREEITEM
) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
613 wxTreeItemId
wxTreeCtrl::GetPrevVisible(const wxTreeItemId
& item
) const
615 wxASSERT_MSG( IsVisible(item
), _T("The item you call GetPrevVisible() "
616 "for must be visible itself!"));
618 return wxTreeItemId((WXHTREEITEM
) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
));
621 // ----------------------------------------------------------------------------
622 // multiple selections emulation
623 // ----------------------------------------------------------------------------
625 bool wxTreeCtrl::IsItemChecked(const wxTreeItemId
& item
) const
627 // receive the desired information.
628 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
);
631 // state image indices are 1 based
632 return ((tvItem
.state
>> 12) - 1) == 1;
635 void wxTreeCtrl::SetItemCheck(const wxTreeItemId
& item
, bool check
)
637 // receive the desired information.
638 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
);
640 // state images are one-based
641 tvItem
.state
= (check
? 2 : 1) << 12;
646 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds
& selections
) const
648 class TraverseSelections
: public wxTreeTraversal
651 TraverseSelections(const wxTreeCtrl
*tree
,
652 wxArrayTreeItemIds
& selections
)
653 : wxTreeTraversal(tree
), m_selections(selections
)
655 m_selections
.Empty();
657 DoTraverse(tree
->GetRootItem());
660 virtual bool OnVisit(const wxTreeItemId
& item
)
662 if ( GetTree()->IsItemChecked(item
) )
664 m_selections
.Add(item
);
671 wxArrayTreeItemIds
& m_selections
;
672 } selector(this, selections
);
674 return selections
.GetCount();
677 // ----------------------------------------------------------------------------
679 // ----------------------------------------------------------------------------
681 wxTreeItemId
wxTreeCtrl::DoInsertItem(const wxTreeItemId
& parent
,
682 wxTreeItemId hInsertAfter
,
683 const wxString
& text
,
684 int image
, int selectedImage
,
685 wxTreeItemData
*data
)
687 TV_INSERTSTRUCT tvIns
;
688 tvIns
.hParent
= (HTREEITEM
) (WXHTREEITEM
)parent
;
689 tvIns
.hInsertAfter
= (HTREEITEM
) (WXHTREEITEM
) hInsertAfter
;
691 // This is how we insert the item as the first child: supply a NULL hInsertAfter
692 if (tvIns
.hInsertAfter
== (HTREEITEM
) 0)
694 tvIns
.hInsertAfter
= TVI_FIRST
;
698 if ( !text
.IsEmpty() )
701 tvIns
.item
.pszText
= (wxChar
*)text
.c_str(); // cast is ok
707 tvIns
.item
.iImage
= image
;
709 if ( selectedImage
== -1 )
711 // take the same image for selected icon if not specified
712 selectedImage
= image
;
716 if ( selectedImage
!= -1 )
718 mask
|= TVIF_SELECTEDIMAGE
;
719 tvIns
.item
.iSelectedImage
= selectedImage
;
725 tvIns
.item
.lParam
= (LPARAM
)data
;
728 tvIns
.item
.mask
= mask
;
730 HTREEITEM id
= (HTREEITEM
) TreeView_InsertItem(GetHwnd(), &tvIns
);
733 wxLogLastError("TreeView_InsertItem");
738 // associate the application tree item with Win32 tree item handle
739 data
->SetId((WXHTREEITEM
)id
);
742 return wxTreeItemId((WXHTREEITEM
)id
);
745 // for compatibility only
746 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
747 const wxString
& text
,
748 int image
, int selImage
,
751 return DoInsertItem(parent
, (WXHTREEITEM
)insertAfter
, text
,
752 image
, selImage
, NULL
);
755 wxTreeItemId
wxTreeCtrl::AddRoot(const wxString
& text
,
756 int image
, int selectedImage
,
757 wxTreeItemData
*data
)
759 return DoInsertItem(wxTreeItemId((WXHTREEITEM
) 0), (WXHTREEITEM
) 0,
760 text
, image
, selectedImage
, data
);
763 wxTreeItemId
wxTreeCtrl::PrependItem(const wxTreeItemId
& parent
,
764 const wxString
& text
,
765 int image
, int selectedImage
,
766 wxTreeItemData
*data
)
768 return DoInsertItem(parent
, (WXHTREEITEM
) TVI_FIRST
,
769 text
, image
, selectedImage
, data
);
772 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
773 const wxTreeItemId
& idPrevious
,
774 const wxString
& text
,
775 int image
, int selectedImage
,
776 wxTreeItemData
*data
)
778 return DoInsertItem(parent
, idPrevious
, text
, image
, selectedImage
, data
);
781 wxTreeItemId
wxTreeCtrl::AppendItem(const wxTreeItemId
& parent
,
782 const wxString
& text
,
783 int image
, int selectedImage
,
784 wxTreeItemData
*data
)
786 return DoInsertItem(parent
, (WXHTREEITEM
) TVI_LAST
,
787 text
, image
, selectedImage
, data
);
790 void wxTreeCtrl::Delete(const wxTreeItemId
& item
)
792 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
) )
794 wxLogLastError("TreeView_DeleteItem");
798 // delete all children (but don't delete the item itself)
799 void wxTreeCtrl::DeleteChildren(const wxTreeItemId
& item
)
803 wxArrayLong children
;
804 wxTreeItemId child
= GetFirstChild(item
, cookie
);
805 while ( child
.IsOk() )
807 children
.Add((long)(WXHTREEITEM
)child
);
809 child
= GetNextChild(item
, cookie
);
812 size_t nCount
= children
.Count();
813 for ( size_t n
= 0; n
< nCount
; n
++ )
815 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM
)children
[n
]) )
817 wxLogLastError("TreeView_DeleteItem");
822 void wxTreeCtrl::DeleteAllItems()
824 if ( !TreeView_DeleteAllItems(GetHwnd()) )
826 wxLogLastError("TreeView_DeleteAllItems");
830 void wxTreeCtrl::DoExpand(const wxTreeItemId
& item
, int flag
)
832 wxASSERT_MSG( flag
== TVE_COLLAPSE
||
833 flag
== (TVE_COLLAPSE
| TVE_COLLAPSERESET
) ||
834 flag
== TVE_EXPAND
||
836 _T("Unknown flag in wxTreeCtrl::DoExpand") );
838 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
839 // emulate them. This behaviour has changed slightly with comctl32.dll
840 // v 4.70 - now it does send them but only the first time. To maintain
841 // compatible behaviour and also in order to not have surprises with the
842 // future versions, don't rely on this and still do everything ourselves.
843 // To avoid that the messages be sent twice when the item is expanded for
844 // the first time we must clear TVIS_EXPANDEDONCE style manually.
846 wxTreeViewItem
tvItem(item
, TVIF_STATE
, TVIS_EXPANDEDONCE
);
850 if ( TreeView_Expand(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
, flag
) != 0 )
852 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
855 bool isExpanded
= IsExpanded(item
);
857 event
.SetEventObject(this);
859 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
860 event
.SetEventType(g_events
[isExpanded
][TRUE
]);
861 GetEventHandler()->ProcessEvent(event
);
863 event
.SetEventType(g_events
[isExpanded
][FALSE
]);
864 GetEventHandler()->ProcessEvent(event
);
866 //else: change didn't took place, so do nothing at all
869 void wxTreeCtrl::Expand(const wxTreeItemId
& item
)
871 DoExpand(item
, TVE_EXPAND
);
874 void wxTreeCtrl::Collapse(const wxTreeItemId
& item
)
876 DoExpand(item
, TVE_COLLAPSE
);
879 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId
& item
)
881 DoExpand(item
, TVE_COLLAPSE
| TVE_COLLAPSERESET
);
884 void wxTreeCtrl::Toggle(const wxTreeItemId
& item
)
886 DoExpand(item
, TVE_TOGGLE
);
889 void wxTreeCtrl::ExpandItem(const wxTreeItemId
& item
, int action
)
891 DoExpand(item
, action
);
894 void wxTreeCtrl::Unselect()
896 wxASSERT_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), _T("doesn't make sense") );
898 // just remove the selection
899 SelectItem(wxTreeItemId((WXHTREEITEM
) 0));
902 void wxTreeCtrl::UnselectAll()
904 if ( m_windowStyle
& wxTR_MULTIPLE
)
906 wxArrayTreeItemIds selections
;
907 size_t count
= GetSelections(selections
);
908 for ( size_t n
= 0; n
< count
; n
++ )
910 SetItemCheck(selections
[n
], FALSE
);
915 // just remove the selection
920 void wxTreeCtrl::SelectItem(const wxTreeItemId
& item
)
922 if ( m_windowStyle
& wxTR_MULTIPLE
)
924 // selecting the item means checking it
929 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
930 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
931 // send them ourselves
933 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
935 event
.SetEventObject(this);
937 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING
);
938 if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() )
940 if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
) )
942 wxLogLastError("TreeView_SelectItem");
946 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED
);
947 (void)GetEventHandler()->ProcessEvent(event
);
950 //else: program vetoed the change
954 void wxTreeCtrl::EnsureVisible(const wxTreeItemId
& item
)
957 TreeView_EnsureVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
);
960 void wxTreeCtrl::ScrollTo(const wxTreeItemId
& item
)
962 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
) )
964 wxLogLastError("TreeView_SelectSetFirstVisible");
968 wxTextCtrl
* wxTreeCtrl::GetEditControl() const
973 void wxTreeCtrl::DeleteTextCtrl()
977 m_textCtrl
->UnsubclassWin();
978 m_textCtrl
->SetHWND(0);
984 wxTextCtrl
* wxTreeCtrl::EditLabel(const wxTreeItemId
& item
,
985 wxClassInfo
* textControlClass
)
987 wxASSERT( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)) );
989 HWND hWnd
= (HWND
) TreeView_EditLabel(GetHwnd(), (HTREEITEM
) (WXHTREEITEM
) item
);
991 // this is not an error - the TVN_BEGINLABELEDIT handler might have
1000 m_textCtrl
= (wxTextCtrl
*)textControlClass
->CreateObject();
1001 m_textCtrl
->SetHWND((WXHWND
)hWnd
);
1002 m_textCtrl
->SubclassWin((WXHWND
)hWnd
);
1007 // End label editing, optionally cancelling the edit
1008 void wxTreeCtrl::EndEditLabel(const wxTreeItemId
& item
, bool discardChanges
)
1010 TreeView_EndEditLabelNow(GetHwnd(), discardChanges
);
1015 wxTreeItemId
wxTreeCtrl::HitTest(const wxPoint
& point
, int& flags
)
1017 TV_HITTESTINFO hitTestInfo
;
1018 hitTestInfo
.pt
.x
= (int)point
.x
;
1019 hitTestInfo
.pt
.y
= (int)point
.y
;
1021 TreeView_HitTest(GetHwnd(), &hitTestInfo
);
1026 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
1027 flags |= wxTREE_HITTEST_##flag
1029 TRANSLATE_FLAG(ABOVE
);
1030 TRANSLATE_FLAG(BELOW
);
1031 TRANSLATE_FLAG(NOWHERE
);
1032 TRANSLATE_FLAG(ONITEMBUTTON
);
1033 TRANSLATE_FLAG(ONITEMICON
);
1034 TRANSLATE_FLAG(ONITEMINDENT
);
1035 TRANSLATE_FLAG(ONITEMLABEL
);
1036 TRANSLATE_FLAG(ONITEMRIGHT
);
1037 TRANSLATE_FLAG(ONITEMSTATEICON
);
1038 TRANSLATE_FLAG(TOLEFT
);
1039 TRANSLATE_FLAG(TORIGHT
);
1041 #undef TRANSLATE_FLAG
1043 return wxTreeItemId((WXHTREEITEM
) hitTestInfo
.hItem
);
1046 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId
& item
,
1048 bool textOnly
) const
1051 if ( TreeView_GetItemRect(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
,
1054 rect
= wxRect(wxPoint(rc
.left
, rc
.top
), wxPoint(rc
.right
, rc
.bottom
));
1060 // couldn't retrieve rect: for example, item isn't visible
1065 // ----------------------------------------------------------------------------
1067 // ----------------------------------------------------------------------------
1069 static int CALLBACK
TreeView_CompareCallback(wxTreeItemData
*pItem1
,
1070 wxTreeItemData
*pItem2
,
1073 wxCHECK_MSG( pItem1
&& pItem2
, 0,
1074 _T("sorting tree without data doesn't make sense") );
1076 return tree
->OnCompareItems(pItem1
->GetId(), pItem2
->GetId());
1079 int wxTreeCtrl::OnCompareItems(const wxTreeItemId
& item1
,
1080 const wxTreeItemId
& item2
)
1082 return wxStrcmp(GetItemText(item1
), GetItemText(item2
));
1085 void wxTreeCtrl::SortChildren(const wxTreeItemId
& item
)
1087 // rely on the fact that TreeView_SortChildren does the same thing as our
1088 // default behaviour, i.e. sorts items alphabetically and so call it
1089 // directly if we're not in derived class (much more efficient!)
1090 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl
) )
1092 TreeView_SortChildren(GetHwnd(), (HTREEITEM
)(WXHTREEITEM
)item
, 0);
1097 tvSort
.hParent
= (HTREEITEM
)(WXHTREEITEM
)item
;
1098 tvSort
.lpfnCompare
= (PFNTVCOMPARE
)TreeView_CompareCallback
;
1099 tvSort
.lParam
= (LPARAM
)this;
1100 TreeView_SortChildrenCB(GetHwnd(), &tvSort
, 0 /* reserved */);
1104 // ----------------------------------------------------------------------------
1106 // ----------------------------------------------------------------------------
1108 bool wxTreeCtrl::MSWCommand(WXUINT cmd
, WXWORD id
)
1110 if ( cmd
== EN_UPDATE
)
1112 wxCommandEvent
event(wxEVT_COMMAND_TEXT_UPDATED
, id
);
1113 event
.SetEventObject( this );
1114 ProcessCommand(event
);
1116 else if ( cmd
== EN_KILLFOCUS
)
1118 wxCommandEvent
event(wxEVT_KILL_FOCUS
, id
);
1119 event
.SetEventObject( this );
1120 ProcessCommand(event
);
1128 // command processed
1132 // process WM_NOTIFY Windows message
1133 bool wxTreeCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
1135 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
1136 wxEventType eventType
= wxEVT_NULL
;
1137 NMHDR
*hdr
= (NMHDR
*)lParam
;
1139 switch ( hdr
->code
)
1142 eventType
= wxEVT_COMMAND_TREE_BEGIN_DRAG
;
1145 case TVN_BEGINRDRAG
:
1147 if ( eventType
== wxEVT_NULL
)
1148 eventType
= wxEVT_COMMAND_TREE_BEGIN_RDRAG
;
1149 //else: left drag, already set above
1151 NM_TREEVIEW
*tv
= (NM_TREEVIEW
*)lParam
;
1153 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1154 event
.m_pointDrag
= wxPoint(tv
->ptDrag
.x
, tv
->ptDrag
.y
);
1158 case TVN_BEGINLABELEDIT
:
1160 eventType
= wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
;
1161 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1163 event
.m_item
= (WXHTREEITEM
) info
->item
.hItem
;
1164 event
.m_label
= info
->item
.pszText
;
1168 case TVN_DELETEITEM
:
1170 eventType
= wxEVT_COMMAND_TREE_DELETE_ITEM
;
1171 NM_TREEVIEW
*tv
= (NM_TREEVIEW
*)lParam
;
1173 event
.m_item
= (WXHTREEITEM
) tv
->itemOld
.hItem
;
1177 case TVN_ENDLABELEDIT
:
1179 eventType
= wxEVT_COMMAND_TREE_END_LABEL_EDIT
;
1180 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1182 event
.m_item
= (WXHTREEITEM
)info
->item
.hItem
;
1183 event
.m_label
= info
->item
.pszText
;
1187 case TVN_GETDISPINFO
:
1188 eventType
= wxEVT_COMMAND_TREE_GET_INFO
;
1191 case TVN_SETDISPINFO
:
1193 if ( eventType
== wxEVT_NULL
)
1194 eventType
= wxEVT_COMMAND_TREE_SET_INFO
;
1195 //else: get, already set above
1197 TV_DISPINFO
*info
= (TV_DISPINFO
*)lParam
;
1199 event
.m_item
= (WXHTREEITEM
) info
->item
.hItem
;
1203 case TVN_ITEMEXPANDING
:
1204 event
.m_code
= FALSE
;
1207 case TVN_ITEMEXPANDED
:
1209 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1211 bool expand
= FALSE
;
1212 switch ( tv
->action
)
1223 wxLogDebug(_T("unexpected code %d in TVN_ITEMEXPAND "
1224 "message"), tv
->action
);
1227 bool ing
= (hdr
->code
== TVN_ITEMEXPANDING
);
1228 eventType
= g_events
[expand
][ing
];
1230 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1236 eventType
= wxEVT_COMMAND_TREE_KEY_DOWN
;
1237 TV_KEYDOWN
*info
= (TV_KEYDOWN
*)lParam
;
1239 event
.m_code
= wxCharCodeMSWToWX(info
->wVKey
);
1241 // a separate event for this case
1242 if ( info
->wVKey
== VK_SPACE
|| info
->wVKey
== VK_RETURN
)
1244 wxTreeEvent
event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED
,
1246 event2
.SetEventObject(this);
1248 GetEventHandler()->ProcessEvent(event2
);
1253 case TVN_SELCHANGED
:
1254 eventType
= wxEVT_COMMAND_TREE_SEL_CHANGED
;
1257 case TVN_SELCHANGING
:
1259 if ( eventType
== wxEVT_NULL
)
1260 eventType
= wxEVT_COMMAND_TREE_SEL_CHANGING
;
1261 //else: already set above
1263 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1265 event
.m_item
= (WXHTREEITEM
) tv
->itemNew
.hItem
;
1266 event
.m_itemOld
= (WXHTREEITEM
) tv
->itemOld
.hItem
;
1271 return wxControl::MSWOnNotify(idCtrl
, lParam
, result
);
1274 event
.SetEventObject(this);
1275 event
.SetEventType(eventType
);
1277 bool processed
= GetEventHandler()->ProcessEvent(event
);
1280 switch ( hdr
->code
)
1282 case TVN_DELETEITEM
:
1284 // NB: we might process this message using wxWindows event
1285 // tables, but due to overhead of wxWin event system we
1286 // prefer to do it here ourself (otherwise deleting a tree
1287 // with many items is just too slow)
1288 NM_TREEVIEW
* tv
= (NM_TREEVIEW
*)lParam
;
1289 wxTreeItemData
*data
= (wxTreeItemData
*)tv
->itemOld
.lParam
;
1290 delete data
; // may be NULL, ok
1292 processed
= TRUE
; // Make sure we don't get called twice
1296 case TVN_BEGINLABELEDIT
:
1297 // return TRUE to cancel label editing
1298 *result
= !event
.IsAllowed();
1301 case TVN_ENDLABELEDIT
:
1302 // return TRUE to set the label to the new string
1303 *result
= event
.IsAllowed();
1305 // ensure that we don't have the text ctrl which is going to be
1310 case TVN_SELCHANGING
:
1311 case TVN_ITEMEXPANDING
:
1312 // return TRUE to prevent the action from happening
1313 *result
= !event
.IsAllowed();
1317 // for the other messages the return value is ignored and there is
1318 // nothing special to do
1324 // ----------------------------------------------------------------------------
1326 // ----------------------------------------------------------------------------
1328 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent
, wxNotifyEvent
)
1330 wxTreeEvent::wxTreeEvent(wxEventType commandType
, int id
)
1331 : wxNotifyEvent(commandType
, id
)