1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxTreeCtrl. See also Robert's generic wxTreeCtrl.
4 // Author: David Webster
8 // Copyright: (c) David
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
15 #include "wx/window.h"
16 #include "wx/os2/private.h"
19 #include "wx/dynarray.h"
20 #include "wx/imaglist.h"
21 #include "wx/treectrl.h"
22 #include "wx/settings.h"
24 // Bug in headers, sometimes
26 #define TVIS_FOCUSED 0x0001
29 // ----------------------------------------------------------------------------
31 // ----------------------------------------------------------------------------
33 struct wxTreeViewItem
// ??? : public TV_ITEM
35 wxTreeViewItem(const wxTreeItemId
& item
, // the item handle
36 UINT mask_
, // fields which are valid
37 UINT stateMask_
= 0) // for TVIF_STATE only
39 // hItem member is always valid
40 mask
= mask_
; // | TVIF_HANDLE;
41 stateMask
= stateMask_
;
42 hItem
= /*(HTREEITEM)*/ (WXHTREEITEM
) item
;
50 // a class which encapsulates the tree traversal logic: it vists all (unless
51 // OnVisit() returns FALSE) items under the given one
55 wxTreeTraversal(const wxTreeCtrl
*tree
)
60 // do traverse the tree: visit all items (recursively by default) under the
61 // given one; return TRUE if all items were traversed or FALSE if the
62 // traversal was aborted because OnVisit returned FALSE
63 bool DoTraverse(const wxTreeItemId
& root
, bool recursively
= TRUE
);
65 // override this function to do whatever is needed for each item, return
66 // FALSE to stop traversing
67 virtual bool OnVisit(const wxTreeItemId
& item
) = 0;
70 const wxTreeCtrl
*GetTree() const { return m_tree
; }
73 bool Traverse(const wxTreeItemId
& root
, bool recursively
);
75 const wxTreeCtrl
*m_tree
;
78 // internal class for getting the selected items
79 class TraverseSelections
: public wxTreeTraversal
82 TraverseSelections(const wxTreeCtrl
*tree
,
83 wxArrayTreeItemIds
& selections
)
84 : wxTreeTraversal(tree
), m_selections(selections
)
88 DoTraverse(tree
->GetRootItem());
91 virtual bool OnVisit(const wxTreeItemId
& item
)
93 if ( GetTree()->IsItemChecked(item
) )
95 m_selections
.Add(item
);
102 wxArrayTreeItemIds
& m_selections
;
105 // internal class for counting tree items
106 class TraverseCounter
: public wxTreeTraversal
109 TraverseCounter(const wxTreeCtrl
*tree
,
110 const wxTreeItemId
& root
,
112 : wxTreeTraversal(tree
)
116 DoTraverse(root
, recursively
);
119 virtual bool OnVisit(const wxTreeItemId
& item
)
126 size_t GetCount() const { return m_count
; }
132 // ----------------------------------------------------------------------------
133 // This class is needed for support of different images: the Win32 common
134 // control natively supports only 2 images (the normal one and another for the
135 // selected state). We wish to provide support for 2 more of them for folder
136 // items (i.e. those which have children): for expanded state and for expanded
137 // selected state. For this we use this structure to store the additional items
140 // There is only one problem with this: when we retrieve the item's data, we
141 // don't know whether we get a pointer to wxTreeItemData or
142 // wxTreeItemIndirectData. So we have to maintain a list of all items which
143 // have indirect data inside the listctrl itself.
144 // ----------------------------------------------------------------------------
145 class wxTreeItemIndirectData
148 // ctor associates this data with the item and the real item data becomes
149 // available through our GetData() method
150 wxTreeItemIndirectData(wxTreeCtrl
*tree
, const wxTreeItemId
& item
)
152 for ( size_t n
= 0; n
< WXSIZEOF(m_images
); n
++ )
158 m_data
= tree
->GetItemData(item
);
160 // and set ourselves as the new one
161 tree
->SetIndirectItemData(item
, this);
164 // dtor deletes the associated data as well
165 ~wxTreeItemIndirectData() { delete m_data
; }
168 // get the real data associated with the item
169 wxTreeItemData
*GetData() const { return m_data
; }
171 void SetData(wxTreeItemData
*data
) { m_data
= data
; }
173 // do we have such image?
174 bool HasImage(wxTreeItemIcon which
) const { return m_images
[which
] != -1; }
176 int GetImage(wxTreeItemIcon which
) const { return m_images
[which
]; }
178 void SetImage(int image
, wxTreeItemIcon which
) { m_images
[which
] = image
; }
181 // all the images associated with the item
182 int m_images
[wxTreeItemIcon_Max
];
184 wxTreeItemData
*m_data
;
187 // ----------------------------------------------------------------------------
189 // ----------------------------------------------------------------------------
191 #if !USE_SHARED_LIBRARY
192 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl
, wxControl
)
195 // ----------------------------------------------------------------------------
197 // ----------------------------------------------------------------------------
199 // handy table for sending events
200 static const wxEventType g_events
[2][2] =
202 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED
, wxEVT_COMMAND_TREE_ITEM_COLLAPSING
},
203 { wxEVT_COMMAND_TREE_ITEM_EXPANDED
, wxEVT_COMMAND_TREE_ITEM_EXPANDING
}
206 // ============================================================================
208 // ============================================================================
210 // ----------------------------------------------------------------------------
212 // ----------------------------------------------------------------------------
214 bool wxTreeTraversal::DoTraverse(const wxTreeItemId
& root
, bool recursively
)
216 if ( !OnVisit(root
) )
219 return Traverse(root
, recursively
);
222 bool wxTreeTraversal::Traverse(const wxTreeItemId
& root
, bool recursively
)
225 wxTreeItemId child
= m_tree
->GetFirstChild(root
, cookie
);
226 while ( child
.IsOk() )
228 // depth first traversal
229 if ( recursively
&& !Traverse(child
, TRUE
) )
232 if ( !OnVisit(child
) )
235 child
= m_tree
->GetNextChild(root
, cookie
);
241 // ----------------------------------------------------------------------------
242 // construction and destruction
243 // ----------------------------------------------------------------------------
245 void wxTreeCtrl::Init()
247 m_imageListNormal
= NULL
;
248 m_imageListState
= NULL
;
252 bool wxTreeCtrl::Create(wxWindow
*parent
,
257 const wxValidator
& validator
,
258 const wxString
& name
)
262 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
266 DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
267 TVS_HASLINES | TVS_SHOWSELALWAYS;
269 if ( m_windowStyle & wxTR_HAS_BUTTONS )
270 wstyle |= TVS_HASBUTTONS;
272 if ( m_windowStyle & wxTR_EDIT_LABELS )
273 wstyle |= TVS_EDITLABELS;
275 if ( m_windowStyle & wxTR_LINES_AT_ROOT )
276 wstyle |= TVS_LINESATROOT;
278 if ( m_windowStyle & wxTR_MULTIPLE )
279 wstyle |= TVS_CHECKBOXES;
281 // Create the tree control.
282 if ( !OS2CreateControl(WC_TREEVIEW, wstyle) )
285 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW
));
286 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
288 // VZ: this is some experimental code which may be used to get the
289 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
290 // AFAIK, the standard DLL does about the same thing anyhow.
292 if ( m_windowStyle
& wxTR_MULTIPLE
)
296 // create the DC compatible with the current screen
297 HDC hdcMem
= CreateCompatibleDC(NULL
);
299 // create a mono bitmap of the standard size
300 int x
= GetSystemMetrics(SM_CXMENUCHECK
);
301 int y
= GetSystemMetrics(SM_CYMENUCHECK
);
302 wxImageList
imagelistCheckboxes(x
, y
, FALSE
, 2);
303 HBITMAP hbmpCheck
= CreateBitmap(x
, y
, // bitmap size
304 1, // # of color planes
305 1, // # bits needed for one pixel
306 0); // array containing colour data
307 SelectObject(hdcMem
, hbmpCheck
);
309 // then draw a check mark into it
310 RECT rect
= { 0, 0, x
, y
};
311 if ( !::DrawFrameControl(hdcMem
, &rect
,
313 DFCS_BUTTONCHECK
| DFCS_CHECKED
) )
315 wxLogLastError(wxT("DrawFrameControl(check)"));
318 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
319 imagelistCheckboxes
.Add(bmp
);
321 if ( !::DrawFrameControl(hdcMem
, &rect
,
325 wxLogLastError(wxT("DrawFrameControl(uncheck)"));
328 bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
);
329 imagelistCheckboxes
.Add(bmp
);
335 SetStateImageList(&imagelistCheckboxes
);
339 SetSize(pos
.x
, pos
.y
, size
.x
, size
.y
);
344 wxTreeCtrl::~wxTreeCtrl()
348 // delete user data to prevent memory leaks
352 // ----------------------------------------------------------------------------
354 // ----------------------------------------------------------------------------
356 // simple wrappers which add error checking in debug mode
358 bool wxTreeCtrl::DoGetItem(wxTreeViewItem
* tvItem
) const
362 if ( !TreeView_GetItem(GetHwnd(), tvItem) )
364 wxLogLastError("TreeView_GetItem");
372 void wxTreeCtrl::DoSetItem(wxTreeViewItem
* tvItem
)
376 if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
378 wxLogLastError("TreeView_SetItem");
383 size_t wxTreeCtrl::GetCount() const
385 // return (size_t)TreeView_GetCount(GetHwnd());
389 unsigned int wxTreeCtrl::GetIndent() const
391 // return TreeView_GetIndent(GetHwnd());
395 void wxTreeCtrl::SetIndent(unsigned int indent
)
397 // TreeView_SetIndent(GetHwnd(), indent);
400 wxImageList
*wxTreeCtrl::GetImageList() const
402 return m_imageListNormal
;
405 wxImageList
*wxTreeCtrl::GetStateImageList() const
407 return m_imageListNormal
;
410 void wxTreeCtrl::SetAnyImageList(wxImageList
*imageList
, int which
)
415 TreeView_SetImageList(GetHwnd(),
416 imageList ? imageList->GetHIMAGELIST() : 0,
421 void wxTreeCtrl::SetImageList(wxImageList
*imageList
)
423 // SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
426 void wxTreeCtrl::SetStateImageList(wxImageList
*imageList
)
428 // SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
431 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId
& item
,
432 bool recursively
) const
434 TraverseCounter
counter(this, item
, recursively
);
436 return counter
.GetCount() - 1;
439 // ----------------------------------------------------------------------------
441 // ----------------------------------------------------------------------------
443 wxString
wxTreeCtrl::GetItemText(const wxTreeItemId
& item
) const
445 wxChar buf
[512]; // the size is arbitrary...
448 wxTreeViewItem tvItem(item, TVIF_TEXT);
449 tvItem.pszText = buf;
450 tvItem.cchTextMax = WXSIZEOF(buf);
451 if ( !DoGetItem(&tvItem) )
453 // don't return some garbage which was on stack, but an empty string
457 return wxString(buf
);
460 void wxTreeCtrl::SetItemText(const wxTreeItemId
& item
, const wxString
& text
)
464 wxTreeViewItem tvItem(item, TVIF_TEXT);
465 tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok
470 int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId
& item
,
471 wxTreeItemIcon which
) const
475 wxTreeViewItem tvItem(item, TVIF_PARAM);
476 if ( !DoGetItem(&tvItem) )
480 return ((wxTreeItemIndirectData *)tvItem.lParam)->GetImage(which);
485 void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId
& item
,
487 wxTreeItemIcon which
) const
491 wxTreeViewItem tvItem(item, TVIF_PARAM);
492 if ( !DoGetItem(&tvItem) )
497 wxTreeItemIndirectData *data = ((wxTreeItemIndirectData *)tvItem.lParam);
499 data->SetImage(image, which);
501 // make sure that we have selected images as well
502 if ( which == wxTreeItemIcon_Normal &&
503 !data->HasImage(wxTreeItemIcon_Selected) )
505 data->SetImage(image, wxTreeItemIcon_Selected);
508 if ( which == wxTreeItemIcon_Expanded &&
509 !data->HasImage(wxTreeItemIcon_SelectedExpanded) )
511 data->SetImage(image, wxTreeItemIcon_SelectedExpanded);
516 void wxTreeCtrl::DoSetItemImages(const wxTreeItemId
& item
,
522 wxTreeViewItem tvItem(item, TVIF_IMAGE | TVIF_SELECTEDIMAGE);
523 tvItem.iSelectedImage = imageSel;
524 tvItem.iImage = image;
529 int wxTreeCtrl::GetItemImage(const wxTreeItemId
& item
,
530 wxTreeItemIcon which
) const
532 if ( HasIndirectData(item
) )
534 return DoGetItemImageFromData(item
, which
);
541 wxFAIL_MSG( wxT("unknown tree item image type") );
543 case wxTreeItemIcon_Normal
:
544 // mask = TVIF_IMAGE;
547 case wxTreeItemIcon_Selected
:
548 // mask = TVIF_SELECTEDIMAGE;
551 case wxTreeItemIcon_Expanded
:
552 case wxTreeItemIcon_SelectedExpanded
:
556 wxTreeViewItem
tvItem(item
, mask
);
559 // return mask == TVIF_IMAGE ? tvItem.iImage : tvItem.iSelectedImage;
563 void wxTreeCtrl::SetItemImage(const wxTreeItemId
& item
, int image
,
564 wxTreeItemIcon which
)
566 int imageNormal
, imageSel
;
570 wxFAIL_MSG( wxT("unknown tree item image type") );
572 case wxTreeItemIcon_Normal
:
574 imageSel
= GetItemSelectedImage(item
);
577 case wxTreeItemIcon_Selected
:
578 imageNormal
= GetItemImage(item
);
582 case wxTreeItemIcon_Expanded
:
583 case wxTreeItemIcon_SelectedExpanded
:
584 if ( !HasIndirectData(item
) )
586 // we need to get the old images first, because after we create
587 // the wxTreeItemIndirectData GetItemXXXImage() will use it to
589 imageNormal
= GetItemImage(item
);
590 imageSel
= GetItemSelectedImage(item
);
592 // if it doesn't have it yet, add it
593 wxTreeItemIndirectData
*data
= new
594 wxTreeItemIndirectData(this, item
);
596 // copy the data to the new location
597 data
->SetImage(imageNormal
, wxTreeItemIcon_Normal
);
598 data
->SetImage(imageSel
, wxTreeItemIcon_Selected
);
601 DoSetItemImageFromData(item
, image
, which
);
603 // reset the normal/selected images because we won't use them any
604 // more - now they're stored inside the indirect data
605 // imageSel = I_IMAGECALLBACK;
609 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
610 // change both normal and selected image - otherwise the change simply
611 // doesn't take place!
612 DoSetItemImages(item
, imageNormal
, imageSel
);
615 wxTreeItemData
*wxTreeCtrl::GetItemData(const wxTreeItemId
& item
) const
619 wxTreeViewItem tvItem(item, TVIF_PARAM);
620 if ( !DoGetItem(&tvItem) )
625 if ( HasIndirectData(item) )
627 return ((wxTreeItemIndirectData *)tvItem.lParam)->GetData();
631 return (wxTreeItemData *)tvItem.lParam;
637 void wxTreeCtrl::SetItemData(const wxTreeItemId
& item
, wxTreeItemData
*data
)
641 wxTreeViewItem tvItem(item, TVIF_PARAM);
643 if ( HasIndirectData(item) )
645 if ( DoGetItem(&tvItem) )
647 ((wxTreeItemIndirectData *)tvItem.lParam)->SetData(data);
651 wxFAIL_MSG( wxT("failed to change tree items data") );
656 tvItem.lParam = (MPARAM)data;
662 void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId
& item
,
663 wxTreeItemIndirectData
*data
)
665 // this should never happen because it's unnecessary and will probably lead
666 // to crash too because the code elsewhere supposes that the pointer the
667 // wxTreeItemIndirectData has is a real wxItemData and not
668 // wxTreeItemIndirectData as well
669 wxASSERT_MSG( !HasIndirectData(item
), wxT("setting indirect data twice?") );
671 SetItemData(item
, (wxTreeItemData
*)data
);
673 m_itemsWithIndirectData
.Add(item
);
676 bool wxTreeCtrl::HasIndirectData(const wxTreeItemId
& item
) const
678 return m_itemsWithIndirectData
.Index(item
) != wxNOT_FOUND
;
681 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId
& item
, bool has
)
685 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
686 tvItem.cChildren = (int)has;
691 void wxTreeCtrl::SetItemBold(const wxTreeItemId
& item
, bool bold
)
695 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
696 tvItem.state = bold ? TVIS_BOLD : 0;
701 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId
& item
, bool highlight
)
705 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
706 tvItem.state = highlight ? TVIS_DROPHILITED : 0;
711 // ----------------------------------------------------------------------------
713 // ----------------------------------------------------------------------------
715 bool wxTreeCtrl::IsVisible(const wxTreeItemId
& item
) const
717 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
721 return SendMessage(GetHwnd(), TVM_GETITEMRECT, FALSE, (LPARAM)&rect) != 0;
726 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId
& item
) const
730 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
733 return tvItem.cChildren != 0;
738 bool wxTreeCtrl::IsExpanded(const wxTreeItemId
& item
) const
740 // probably not a good idea to put it here
741 //wxASSERT( ItemHasChildren(item) );
745 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
748 return (tvItem.state & TVIS_EXPANDED) != 0;
753 bool wxTreeCtrl::IsSelected(const wxTreeItemId
& item
) const
757 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
760 return (tvItem.state & TVIS_SELECTED) != 0;
765 bool wxTreeCtrl::IsBold(const wxTreeItemId
& item
) const
769 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
772 return (tvItem.state & TVIS_BOLD) != 0;
777 // ----------------------------------------------------------------------------
779 // ----------------------------------------------------------------------------
781 wxTreeItemId
wxTreeCtrl::GetRootItem() const
783 // return wxTreeItemId((WXHTREEITEM) TreeView_GetRoot(GetHwnd()));
787 wxTreeItemId
wxTreeCtrl::GetSelection() const
789 wxCHECK_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), (WXHTREEITEM
)0,
790 wxT("this only works with single selection controls") );
792 // return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd()));
796 wxTreeItemId
wxTreeCtrl::GetParent(const wxTreeItemId
& item
) const
798 // return wxTreeItemId((WXHTREEITEM) TreeView_GetParent(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
802 wxTreeItemId
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
,
807 // remember the last child returned in 'cookie'
808 _cookie = (long)TreeView_GetChild(GetHwnd(), (HTREEITEM) (WXHTREEITEM)item);
810 return wxTreeItemId((WXHTREEITEM)_cookie);
815 wxTreeItemId
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
),
818 wxTreeItemId l
= 0; //wxTreeItemId((WXHTREEITEM)TreeView_GetNextSibling(GetHwnd(),
819 // (HTREEITEM)(WXHTREEITEM)_cookie));
825 wxTreeItemId
wxTreeCtrl::GetLastChild(const wxTreeItemId
& item
) const
827 // can this be done more efficiently?
830 wxTreeItemId childLast
,
831 child
= GetFirstChild(item
, cookie
);
832 while ( child
.IsOk() )
835 child
= GetNextChild(item
, cookie
);
841 wxTreeItemId
wxTreeCtrl::GetNextSibling(const wxTreeItemId
& item
) const
843 // return wxTreeItemId((WXHTREEITEM) TreeView_GetNextSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
847 wxTreeItemId
wxTreeCtrl::GetPrevSibling(const wxTreeItemId
& item
) const
849 // return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
853 wxTreeItemId
wxTreeCtrl::GetFirstVisibleItem() const
855 // return wxTreeItemId((WXHTREEITEM) TreeView_GetFirstVisible(GetHwnd()));
859 wxTreeItemId
wxTreeCtrl::GetNextVisible(const wxTreeItemId
& item
) const
861 wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetNextVisible() "
862 "for must be visible itself!"));
864 // return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
868 wxTreeItemId
wxTreeCtrl::GetPrevVisible(const wxTreeItemId
& item
) const
870 wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetPrevVisible() "
871 "for must be visible itself!"));
873 // return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
877 // ----------------------------------------------------------------------------
878 // multiple selections emulation
879 // ----------------------------------------------------------------------------
881 bool wxTreeCtrl::IsItemChecked(const wxTreeItemId
& item
) const
883 // receive the desired information.
886 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
889 // state image indices are 1 based
890 return ((tvItem.state >> 12) - 1) == 1;
895 void wxTreeCtrl::SetItemCheck(const wxTreeItemId
& item
, bool check
)
897 // receive the desired information.
900 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
902 // state images are one-based
903 tvItem.state = (check ? 2 : 1) << 12;
909 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds
& selections
) const
911 TraverseSelections
selector(this, selections
);
913 return selections
.GetCount();
916 // ----------------------------------------------------------------------------
918 // ----------------------------------------------------------------------------
920 wxTreeItemId
wxTreeCtrl::DoInsertItem(const wxTreeItemId
& parent
,
921 wxTreeItemId hInsertAfter
,
922 const wxString
& text
,
923 int image
, int selectedImage
,
924 wxTreeItemData
*data
)
928 TV_INSERTSTRUCT tvIns;
929 tvIns.hParent = (HTREEITEM) (WXHTREEITEM)parent;
930 tvIns.hInsertAfter = (HTREEITEM) (WXHTREEITEM) hInsertAfter;
932 // this is how we insert the item as the first child: supply a NULL
934 if ( !tvIns.hInsertAfter )
936 tvIns.hInsertAfter = TVI_FIRST;
940 if ( !text.IsEmpty() )
943 tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
949 tvIns.item.iImage = image;
951 if ( selectedImage == -1 )
953 // take the same image for selected icon if not specified
954 selectedImage = image;
958 if ( selectedImage != -1 )
960 mask |= TVIF_SELECTEDIMAGE;
961 tvIns.item.iSelectedImage = selectedImage;
967 tvIns.item.lParam = (LPARAM)data;
970 tvIns.item.mask = mask;
972 HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns);
975 wxLogLastError("TreeView_InsertItem");
980 // associate the application tree item with Win32 tree item handle
981 data->SetId((WXHTREEITEM)id);
984 return wxTreeItemId((WXHTREEITEM)id);
989 // for compatibility only
990 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
991 const wxString
& text
,
992 int image
, int selImage
,
995 return DoInsertItem(parent
, (WXHTREEITEM
)insertAfter
, text
,
996 image
, selImage
, NULL
);
999 wxTreeItemId
wxTreeCtrl::AddRoot(const wxString
& text
,
1000 int image
, int selectedImage
,
1001 wxTreeItemData
*data
)
1003 return DoInsertItem(wxTreeItemId((WXHTREEITEM
) 0), (WXHTREEITEM
) 0,
1004 text
, image
, selectedImage
, data
);
1007 wxTreeItemId
wxTreeCtrl::PrependItem(const wxTreeItemId
& parent
,
1008 const wxString
& text
,
1009 int image
, int selectedImage
,
1010 wxTreeItemData
*data
)
1014 return DoInsertItem(parent, (WXHTREEITEM) TVI_FIRST,
1015 text, image, selectedImage, data);
1020 wxTreeItemId
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
,
1021 const wxTreeItemId
& idPrevious
,
1022 const wxString
& text
,
1023 int image
, int selectedImage
,
1024 wxTreeItemData
*data
)
1026 return DoInsertItem(parent
, idPrevious
, text
, image
, selectedImage
, data
);
1029 wxTreeItemId
wxTreeCtrl::AppendItem(const wxTreeItemId
& parent
,
1030 const wxString
& text
,
1031 int image
, int selectedImage
,
1032 wxTreeItemData
*data
)
1036 return DoInsertItem(parent, (WXHTREEITEM) TVI_LAST,
1037 text, image, selectedImage, data);
1042 void wxTreeCtrl::Delete(const wxTreeItemId
& item
)
1046 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item) )
1048 wxLogLastError("TreeView_DeleteItem");
1053 // delete all children (but don't delete the item itself)
1054 void wxTreeCtrl::DeleteChildren(const wxTreeItemId
& item
)
1058 wxArrayLong children
;
1059 wxTreeItemId child
= GetFirstChild(item
, cookie
);
1060 while ( child
.IsOk() )
1062 children
.Add((long)(WXHTREEITEM
)child
);
1064 child
= GetNextChild(item
, cookie
);
1067 size_t nCount
= children
.Count();
1070 for ( size_t n = 0; n < nCount; n++ )
1072 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)children[n]) )
1074 wxLogLastError("TreeView_DeleteItem");
1080 void wxTreeCtrl::DeleteAllItems()
1084 if ( !TreeView_DeleteAllItems(GetHwnd()) )
1086 wxLogLastError("TreeView_DeleteAllItems");
1091 void wxTreeCtrl::DoExpand(const wxTreeItemId
& item
, int flag
)
1095 wxASSERT_MSG( flag == TVE_COLLAPSE ||
1096 flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
1097 flag == TVE_EXPAND ||
1099 wxT("Unknown flag in wxTreeCtrl::DoExpand") );
1101 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
1102 // emulate them. This behaviour has changed slightly with comctl32.dll
1103 // v 4.70 - now it does send them but only the first time. To maintain
1104 // compatible behaviour and also in order to not have surprises with the
1105 // future versions, don't rely on this and still do everything ourselves.
1106 // To avoid that the messages be sent twice when the item is expanded for
1107 // the first time we must clear TVIS_EXPANDEDONCE style manually.
1109 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
1113 if ( TreeView_Expand(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item, flag) != 0 )
1115 wxTreeEvent event(wxEVT_NULL, m_windowId);
1116 event.m_item = item;
1118 bool isExpanded = IsExpanded(item);
1120 event.SetEventObject(this);
1122 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
1123 event.SetEventType(g_events[isExpanded][TRUE]);
1124 GetEventHandler()->ProcessEvent(event);
1126 event.SetEventType(g_events[isExpanded][FALSE]);
1127 GetEventHandler()->ProcessEvent(event);
1129 //else: change didn't took place, so do nothing at all
1133 void wxTreeCtrl::Expand(const wxTreeItemId
& item
)
1135 // DoExpand(item, TVE_EXPAND);
1138 void wxTreeCtrl::Collapse(const wxTreeItemId
& item
)
1140 // DoExpand(item, TVE_COLLAPSE);
1143 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId
& item
)
1145 // DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
1148 void wxTreeCtrl::Toggle(const wxTreeItemId
& item
)
1150 // DoExpand(item, TVE_TOGGLE);
1153 void wxTreeCtrl::ExpandItem(const wxTreeItemId
& item
, int action
)
1155 // DoExpand(item, action);
1158 void wxTreeCtrl::Unselect()
1160 wxASSERT_MSG( !(m_windowStyle
& wxTR_MULTIPLE
), wxT("doesn't make sense") );
1162 // just remove the selection
1163 // SelectItem(wxTreeItemId((WXHTREEITEM) 0));
1166 void wxTreeCtrl::UnselectAll()
1168 if ( m_windowStyle
& wxTR_MULTIPLE
)
1170 wxArrayTreeItemIds selections
;
1171 size_t count
= GetSelections(selections
);
1172 for ( size_t n
= 0; n
< count
; n
++ )
1174 SetItemCheck(selections
[n
], FALSE
);
1179 // just remove the selection
1184 void wxTreeCtrl::SelectItem(const wxTreeItemId
& item
)
1186 if ( m_windowStyle
& wxTR_MULTIPLE
)
1188 // selecting the item means checking it
1193 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
1194 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
1195 // send them ourselves
1197 wxTreeEvent
event(wxEVT_NULL
, m_windowId
);
1198 event
.m_item
= item
;
1199 event
.SetEventObject(this);
1201 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING
);
1204 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
1206 if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
1208 wxLogLastError("TreeView_SelectItem");
1212 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
1213 (void)GetEventHandler()->ProcessEvent(event);
1216 //else: program vetoed the change
1221 void wxTreeCtrl::EnsureVisible(const wxTreeItemId
& item
)
1224 // TreeView_EnsureVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
1227 void wxTreeCtrl::ScrollTo(const wxTreeItemId
& item
)
1231 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
1233 wxLogLastError("TreeView_SelectSetFirstVisible");
1238 wxTextCtrl
* wxTreeCtrl::GetEditControl() const
1243 void wxTreeCtrl::DeleteTextCtrl()
1247 m_textCtrl
->UnsubclassWin();
1248 m_textCtrl
->SetHWND(0);
1254 wxTextCtrl
* wxTreeCtrl::EditLabel(const wxTreeItemId
& item
,
1255 wxClassInfo
* textControlClass
)
1257 wxASSERT( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)) );
1261 HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
1263 // this is not an error - the TVN_BEGINLABELEDIT handler might have
1272 m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
1273 m_textCtrl->SetHWND((WXHWND)hWnd);
1274 m_textCtrl->SubclassWin((WXHWND)hWnd);
1279 // End label editing, optionally cancelling the edit
1280 void wxTreeCtrl::EndEditLabel(const wxTreeItemId
& item
, bool discardChanges
)
1282 // TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
1287 wxTreeItemId
wxTreeCtrl::HitTest(const wxPoint
& point
, int& flags
)
1291 TV_HITTESTINFO hitTestInfo;
1292 hitTestInfo.pt.x = (int)point.x;
1293 hitTestInfo.pt.y = (int)point.y;
1295 TreeView_HitTest(GetHwnd(), &hitTestInfo);
1300 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
1301 flags |= wxTREE_HITTEST_##flag
1303 TRANSLATE_FLAG(ABOVE);
1304 TRANSLATE_FLAG(BELOW);
1305 TRANSLATE_FLAG(NOWHERE);
1306 TRANSLATE_FLAG(ONITEMBUTTON);
1307 TRANSLATE_FLAG(ONITEMICON);
1308 TRANSLATE_FLAG(ONITEMINDENT);
1309 TRANSLATE_FLAG(ONITEMLABEL);
1310 TRANSLATE_FLAG(ONITEMRIGHT);
1311 TRANSLATE_FLAG(ONITEMSTATEICON);
1312 TRANSLATE_FLAG(TOLEFT);
1313 TRANSLATE_FLAG(TORIGHT);
1315 #undef TRANSLATE_FLAG
1317 return wxTreeItemId((WXHTREEITEM) hitTestInfo.hItem);
1322 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId
& item
,
1324 bool textOnly
) const
1329 if ( TreeView_GetItemRect(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item,
1332 rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
1338 // couldn't retrieve rect: for example, item isn't visible
1345 // ----------------------------------------------------------------------------
1347 // ----------------------------------------------------------------------------
1349 static int TreeView_CompareCallback(wxTreeItemData
*pItem1
,
1350 wxTreeItemData
*pItem2
,
1353 wxCHECK_MSG( pItem1
&& pItem2
, 0,
1354 wxT("sorting tree without data doesn't make sense") );
1356 return tree
->OnCompareItems(pItem1
->GetId(), pItem2
->GetId());
1359 int wxTreeCtrl::OnCompareItems(const wxTreeItemId
& item1
,
1360 const wxTreeItemId
& item2
)
1362 return wxStrcmp(GetItemText(item1
), GetItemText(item2
));
1365 void wxTreeCtrl::SortChildren(const wxTreeItemId
& item
)
1367 // rely on the fact that TreeView_SortChildren does the same thing as our
1368 // default behaviour, i.e. sorts items alphabetically and so call it
1369 // directly if we're not in derived class (much more efficient!)
1372 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
1374 TreeView_SortChildren(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item, 0);
1379 tvSort.hParent = (HTREEITEM)(WXHTREEITEM)item;
1380 tvSort.lpfnCompare = (PFNTVCOMPARE)TreeView_CompareCallback;
1381 tvSort.lParam = (LPARAM)this;
1382 TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0);
1387 // ----------------------------------------------------------------------------
1389 // ----------------------------------------------------------------------------
1391 bool wxTreeCtrl::OS2Command(WXUINT cmd
, WXWORD id
)
1395 if ( cmd == EN_UPDATE )
1397 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
1398 event.SetEventObject( this );
1399 ProcessCommand(event);
1401 else if ( cmd == EN_KILLFOCUS )
1403 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
1404 event.SetEventObject( this );
1405 ProcessCommand(event);
1413 // command processed
1419 // process WM_NOTIFY Windows message
1420 bool wxTreeCtrl::OS2OnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
1424 wxTreeEvent event(wxEVT_NULL, m_windowId);
1425 wxEventType eventType = wxEVT_NULL;
1426 NMHDR *hdr = (NMHDR *)lParam;
1428 switch ( hdr->code )
1432 if ( wxControl::MSWOnNotify(idCtrl, lParam, result) )
1435 TV_HITTESTINFO tvhti;
1436 ::GetCursorPos(&(tvhti.pt));
1437 ::ScreenToClient(GetHwnd(),&(tvhti.pt));
1438 if ( TreeView_HitTest(GetHwnd(),&tvhti) )
1440 if( tvhti.flags & TVHT_ONITEM )
1442 event.m_item = (WXHTREEITEM) tvhti.hItem;
1443 eventType=wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK;
1450 eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
1453 case TVN_BEGINRDRAG:
1455 if ( eventType == wxEVT_NULL )
1456 eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
1457 //else: left drag, already set above
1459 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
1461 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1462 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
1466 case TVN_BEGINLABELEDIT:
1468 eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
1469 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1471 event.m_item = (WXHTREEITEM) info->item.hItem;
1472 event.m_label = info->item.pszText;
1476 case TVN_DELETEITEM:
1478 eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
1479 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
1481 event.m_item = (WXHTREEITEM) tv->itemOld.hItem;
1485 case TVN_ENDLABELEDIT:
1487 eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
1488 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1490 event.m_item = (WXHTREEITEM)info->item.hItem;
1491 event.m_label = info->item.pszText;
1495 case TVN_GETDISPINFO:
1496 eventType = wxEVT_COMMAND_TREE_GET_INFO;
1499 case TVN_SETDISPINFO:
1501 if ( eventType == wxEVT_NULL )
1502 eventType = wxEVT_COMMAND_TREE_SET_INFO;
1503 //else: get, already set above
1505 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1507 event.m_item = (WXHTREEITEM) info->item.hItem;
1511 case TVN_ITEMEXPANDING:
1512 event.m_code = FALSE;
1515 case TVN_ITEMEXPANDED:
1517 NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
1519 bool expand = FALSE;
1520 switch ( tv->action )
1531 wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND "
1532 "message"), tv->action);
1535 bool ing = ((int)hdr->code == TVN_ITEMEXPANDING);
1536 eventType = g_events[expand][ing];
1538 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1544 eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
1545 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
1547 event.m_code = wxCharCodeMSWToWX(info->wVKey);
1549 // a separate event for this case
1550 if ( info->wVKey == VK_SPACE || info->wVKey == VK_RETURN )
1552 wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
1554 event2.SetEventObject(this);
1556 GetEventHandler()->ProcessEvent(event2);
1561 case TVN_SELCHANGED:
1562 eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
1565 case TVN_SELCHANGING:
1567 if ( eventType == wxEVT_NULL )
1568 eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
1569 //else: already set above
1571 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1573 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1574 event.m_itemOld = (WXHTREEITEM) tv->itemOld.hItem;
1579 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1582 event.SetEventObject(this);
1583 event.SetEventType(eventType);
1585 bool processed = GetEventHandler()->ProcessEvent(event);
1588 switch ( hdr->code )
1590 case TVN_DELETEITEM:
1592 // NB: we might process this message using wxWindows event
1593 // tables, but due to overhead of wxWin event system we
1594 // prefer to do it here ourself (otherwise deleting a tree
1595 // with many items is just too slow)
1596 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1598 wxTreeItemId item = event.m_item;
1599 if ( HasIndirectData(item) )
1601 wxTreeItemIndirectData *data = (wxTreeItemIndirectData *)
1603 delete data; // can't be NULL here
1605 m_itemsWithIndirectData.Remove(item);
1609 wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam;
1610 delete data; // may be NULL, ok
1613 processed = TRUE; // Make sure we don't get called twice
1617 case TVN_BEGINLABELEDIT:
1618 // return TRUE to cancel label editing
1619 *result = !event.IsAllowed();
1622 case TVN_ENDLABELEDIT:
1623 // return TRUE to set the label to the new string
1624 *result = event.IsAllowed();
1626 // ensure that we don't have the text ctrl which is going to be
1631 case TVN_SELCHANGING:
1632 case TVN_ITEMEXPANDING:
1633 // return TRUE to prevent the action from happening
1634 *result = !event.IsAllowed();
1637 case TVN_GETDISPINFO:
1638 // NB: so far the user can't set the image himself anyhow, so do it
1639 // anyway - but this may change later
1640 if ( 1 // !processed && )
1642 wxTreeItemId item = event.m_item;
1643 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1644 if ( info->item.mask & TVIF_IMAGE )
1647 DoGetItemImageFromData
1650 IsExpanded(item) ? wxTreeItemIcon_Expanded
1651 : wxTreeItemIcon_Normal
1654 if ( info->item.mask & TVIF_SELECTEDIMAGE )
1656 info->item.iSelectedImage =
1657 DoGetItemImageFromData
1660 IsExpanded(item) ? wxTreeItemIcon_SelectedExpanded
1661 : wxTreeItemIcon_Selected
1668 // for the other messages the return value is ignored and there is
1669 // nothing special to do
1677 // ----------------------------------------------------------------------------
1679 // ----------------------------------------------------------------------------
1681 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent
, wxNotifyEvent
)
1683 wxTreeEvent::wxTreeEvent(wxEventType commandType
, int id
)
1684 : wxNotifyEvent(commandType
, id
)