1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/treectrl.cpp 
   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 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  29 #include "wx/treectrl.h" 
  32     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly" 
  33     #include "wx/msw/missing.h" 
  34     #include "wx/dynarray.h" 
  37     #include "wx/settings.h" 
  40 #include "wx/dynlib.h" 
  41 #include "wx/msw/private.h" 
  43 #include "wx/imaglist.h" 
  44 #include "wx/msw/dragimag.h" 
  45 #include "wx/msw/uxtheme.h" 
  47 // macros to hide the cast ugliness 
  48 // -------------------------------- 
  50 // get HTREEITEM from wxTreeItemId 
  51 #define HITEM(item)     ((HTREEITEM)(((item).m_pItem))) 
  54 // older SDKs are missing these 
  55 #ifndef TVN_ITEMCHANGINGA 
  57 #define TVN_ITEMCHANGINGA (TVN_FIRST-16) 
  58 #define TVN_ITEMCHANGINGW (TVN_FIRST-17) 
  60 typedef struct tagNMTVITEMCHANGE
 
  73 // this helper class is used on vista systems for preventing unwanted 
  74 // item state changes in the vista tree control.  It is only effective in 
  75 // multi-select mode on vista systems. 
  77 // The vista tree control includes some new code that originally broke the 
  78 // multi-selection tree, causing seemingly spurious item selection state changes 
  79 // during Shift or Ctrl-click item selection. (To witness the original broken 
  80 // behaviour, simply make IsLocked() below always return false). This problem was 
  81 // solved by using the following class to 'unlock' an item's selection state. 
  83 class TreeItemUnlocker
 
  86     // unlock a single item 
  87     TreeItemUnlocker(HTREEITEM item
) { ms_unlockedItem 
= item
; } 
  89     // unlock all items, don't use unless absolutely necessary 
  90     TreeItemUnlocker() { ms_unlockedItem 
= (HTREEITEM
)-1; } 
  92     // lock everything back 
  93     ~TreeItemUnlocker() { ms_unlockedItem 
= NULL
; } 
  96     // check if the item state is currently locked 
  97     static bool IsLocked(HTREEITEM item
) 
  98         { return ms_unlockedItem 
!= (HTREEITEM
)-1 && item 
!= ms_unlockedItem
; } 
 101     static HTREEITEM ms_unlockedItem
; 
 104 HTREEITEM 
TreeItemUnlocker::ms_unlockedItem 
= NULL
; 
 106 // another helper class: set the variable to true during its lifetime and reset 
 107 // it to false when it is destroyed 
 109 // it is currently always used with wxTreeCtrl::m_changingSelection 
 113     TempSetter(bool& var
) : m_var(var
) 
 115         wxASSERT_MSG( !m_var
, "variable shouldn't be already set" ); 
 127     wxDECLARE_NO_COPY_CLASS(TempSetter
); 
 130 // ---------------------------------------------------------------------------- 
 132 // ---------------------------------------------------------------------------- 
 137 // Work around a problem with TreeView_GetItemRect() when using MinGW/Cygwin: 
 138 // it results in warnings about breaking strict aliasing rules because HITEM is 
 139 // passed via a RECT pointer, so use a union to avoid them and define our own 
 140 // version of the standard macro using it. 
 141 union TVGetItemRectParam
 
 148 wxTreeView_GetItemRect(HWND hwnd
, 
 150                        TVGetItemRectParam
& param
, 
 154     return ::SendMessage(hwnd
, TVM_GETITEMRECT
, fItemRect
, 
 155                         (LPARAM
)¶m
) == TRUE
; 
 158 } // anonymous namespace 
 160 // wrappers for TreeView_GetItem/TreeView_SetItem 
 161 static bool IsItemSelected(HWND hwndTV
, HTREEITEM hItem
) 
 164     tvi
.mask 
= TVIF_STATE 
| TVIF_HANDLE
; 
 165     tvi
.stateMask 
= TVIS_SELECTED
; 
 168     TreeItemUnlocker 
unlocker(hItem
); 
 170     if ( !TreeView_GetItem(hwndTV
, &tvi
) ) 
 172         wxLogLastError(wxT("TreeView_GetItem")); 
 175     return (tvi
.state 
& TVIS_SELECTED
) != 0; 
 178 static bool SelectItem(HWND hwndTV
, HTREEITEM hItem
, bool select 
= true) 
 181     tvi
.mask 
= TVIF_STATE 
| TVIF_HANDLE
; 
 182     tvi
.stateMask 
= TVIS_SELECTED
; 
 183     tvi
.state 
= select 
? TVIS_SELECTED 
: 0; 
 186     TreeItemUnlocker 
unlocker(hItem
); 
 188     if ( TreeView_SetItem(hwndTV
, &tvi
) == -1 ) 
 190         wxLogLastError(wxT("TreeView_SetItem")); 
 197 static inline void UnselectItem(HWND hwndTV
, HTREEITEM htItem
) 
 199     SelectItem(hwndTV
, htItem
, false); 
 202 static inline void ToggleItemSelection(HWND hwndTV
, HTREEITEM htItem
) 
 204     SelectItem(hwndTV
, htItem
, !IsItemSelected(hwndTV
, htItem
)); 
 207 // helper function which selects all items in a range and, optionally, 
 208 // deselects all the other ones 
 210 // returns true if the selection changed at all or false if nothing changed 
 212 // flags for SelectRange() 
 215     SR_SIMULATE 
= 1,        // don't do anything, just return true or false 
 216     SR_UNSELECT_OTHERS 
= 2  // deselect the items not in range 
 219 static bool SelectRange(HWND hwndTV
, 
 224     // find the first (or last) item and select it 
 225     bool changed 
= false; 
 227     HTREEITEM htItem 
= (HTREEITEM
)TreeView_GetRoot(hwndTV
); 
 229     while ( htItem 
&& cont 
) 
 231         if ( (htItem 
== htFirst
) || (htItem 
== htLast
) ) 
 233             if ( !IsItemSelected(hwndTV
, htItem
) ) 
 235                 if ( !(flags 
& SR_SIMULATE
) ) 
 237                     SelectItem(hwndTV
, htItem
); 
 245         else // not first or last 
 247             if ( flags 
& SR_UNSELECT_OTHERS 
) 
 249                 if ( IsItemSelected(hwndTV
, htItem
) ) 
 251                     if ( !(flags 
& SR_SIMULATE
) ) 
 252                         UnselectItem(hwndTV
, htItem
); 
 259         htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 262     // select the items in range 
 263     cont 
= htFirst 
!= htLast
; 
 264     while ( htItem 
&& cont 
) 
 266         if ( !IsItemSelected(hwndTV
, htItem
) ) 
 268             if ( !(flags 
& SR_SIMULATE
) ) 
 270                 SelectItem(hwndTV
, htItem
); 
 276         cont 
= (htItem 
!= htFirst
) && (htItem 
!= htLast
); 
 278         htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 281     // optionally deselect the rest 
 282     if ( flags 
& SR_UNSELECT_OTHERS 
) 
 286             if ( IsItemSelected(hwndTV
, htItem
) ) 
 288                 if ( !(flags 
& SR_SIMULATE
) ) 
 290                     UnselectItem(hwndTV
, htItem
); 
 296             htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 300     // seems to be necessary - otherwise the just selected items don't always 
 301     // appear as selected 
 302     if ( !(flags 
& SR_SIMULATE
) ) 
 304         UpdateWindow(hwndTV
); 
 310 // helper function which tricks the standard control into changing the focused 
 311 // item without changing anything else (if someone knows why Microsoft doesn't 
 312 // allow to do it by just setting TVIS_FOCUSED flag, please tell me!) 
 314 // returns true if the focus was changed, false if the given item was already 
 316 static bool SetFocus(HWND hwndTV
, HTREEITEM htItem
) 
 319     HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(hwndTV
); 
 321     if ( htItem 
== htFocus 
) 
 326         // remember the selection state of the item 
 327         bool wasSelected 
= IsItemSelected(hwndTV
, htItem
); 
 329         if ( htFocus 
&& IsItemSelected(hwndTV
, htFocus
) ) 
 331             // prevent the tree from unselecting the old focus which it 
 332             // would do by default (TreeView_SelectItem unselects the 
 334             TreeView_SelectItem(hwndTV
, 0); 
 335             SelectItem(hwndTV
, htFocus
); 
 338         TreeView_SelectItem(hwndTV
, htItem
); 
 342             // need to clear the selection which TreeView_SelectItem() gave 
 344             UnselectItem(hwndTV
, htItem
); 
 346         //else: was selected, still selected - ok 
 350         bool wasFocusSelected 
= IsItemSelected(hwndTV
, htFocus
); 
 352         // just clear the focus 
 353         TreeView_SelectItem(hwndTV
, 0); 
 355         if ( wasFocusSelected 
) 
 357             // restore the selection state 
 358             SelectItem(hwndTV
, htFocus
); 
 365 // ---------------------------------------------------------------------------- 
 367 // ---------------------------------------------------------------------------- 
 369 // a convenient wrapper around TV_ITEM struct which adds a ctor 
 371 #pragma warning( disable : 4097 ) // inheriting from typedef 
 374 struct wxTreeViewItem 
: public TV_ITEM
 
 376     wxTreeViewItem(const wxTreeItemId
& item
,    // the item handle 
 377                    UINT mask_
,                  // fields which are valid 
 378                    UINT stateMask_ 
= 0)         // for TVIF_STATE only 
 382         // hItem member is always valid 
 383         mask 
= mask_ 
| TVIF_HANDLE
; 
 384         stateMask 
= stateMask_
; 
 389 // ---------------------------------------------------------------------------- 
 390 // This class is our userdata/lParam for the TV_ITEMs stored in the treeview. 
 392 // We need this for a couple of reasons: 
 394 // 1) This class is needed for support of different images: the Win32 common 
 395 // control natively supports only 2 images (the normal one and another for the 
 396 // selected state). We wish to provide support for 2 more of them for folder 
 397 // items (i.e. those which have children): for expanded state and for expanded 
 398 // selected state. For this we use this structure to store the additional items 
 401 // 2) This class is also needed to hold the HITEM so that we can sort 
 402 // it correctly in the MSW sort callback. 
 404 // In addition it makes other workarounds such as this easier and helps 
 405 // simplify the code. 
 406 // ---------------------------------------------------------------------------- 
 408 class wxTreeItemParam
 
 415         for ( size_t n 
= 0; n 
< WXSIZEOF(m_images
); n
++ ) 
 421     // dtor deletes the associated data as well 
 422     virtual ~wxTreeItemParam() { delete m_data
; } 
 425         // get the real data associated with the item 
 426     wxTreeItemData 
*GetData() const { return m_data
; } 
 428     void SetData(wxTreeItemData 
*data
) { m_data 
= data
; } 
 430         // do we have such image? 
 431     bool HasImage(wxTreeItemIcon which
) const { return m_images
[which
] != -1; } 
 432         // get image, falling back to the other images if this one is not 
 434     int GetImage(wxTreeItemIcon which
) const 
 436         int image 
= m_images
[which
]; 
 441                 case wxTreeItemIcon_SelectedExpanded
: 
 442                     image 
= GetImage(wxTreeItemIcon_Expanded
); 
 447                 case wxTreeItemIcon_Selected
: 
 448                 case wxTreeItemIcon_Expanded
: 
 449                     image 
= GetImage(wxTreeItemIcon_Normal
); 
 452                 case wxTreeItemIcon_Normal
: 
 457                     wxFAIL_MSG( wxT("unsupported wxTreeItemIcon value") ); 
 463         // change the given image 
 464     void SetImage(int image
, wxTreeItemIcon which
) { m_images
[which
] = image
; } 
 467     const wxTreeItemId
& GetItem() const { return m_item
; } 
 469     void SetItem(const wxTreeItemId
& item
) { m_item 
= item
; } 
 472     // all the images associated with the item 
 473     int m_images
[wxTreeItemIcon_Max
]; 
 475     // item for sort callbacks 
 478     // the real client data 
 479     wxTreeItemData 
*m_data
; 
 481     wxDECLARE_NO_COPY_CLASS(wxTreeItemParam
); 
 484 // wxVirutalNode is used in place of a single root when 'hidden' root is 
 486 class wxVirtualNode 
: public wxTreeViewItem
 
 489     wxVirtualNode(wxTreeItemParam 
*param
) 
 490         : wxTreeViewItem(TVI_ROOT
, 0) 
 500     wxTreeItemParam 
*GetParam() const { return m_param
; } 
 501     void SetParam(wxTreeItemParam 
*param
) { delete m_param
; m_param 
= param
; } 
 504     wxTreeItemParam 
*m_param
; 
 506     wxDECLARE_NO_COPY_CLASS(wxVirtualNode
); 
 510 #pragma warning( default : 4097 ) 
 513 // a macro to get the virtual root, returns NULL if none 
 514 #define GET_VIRTUAL_ROOT() ((wxVirtualNode *)m_pVirtualRoot) 
 516 // returns true if the item is the virtual root 
 517 #define IS_VIRTUAL_ROOT(item) (HITEM(item) == TVI_ROOT) 
 519 // a class which encapsulates the tree traversal logic: it vists all (unless 
 520 // OnVisit() returns false) items under the given one 
 521 class wxTreeTraversal
 
 524     wxTreeTraversal(const wxTreeCtrl 
*tree
) 
 529     // give it a virtual dtor: not really needed as the class is never used 
 530     // polymorphically and not even allocated on heap at all, but this is safer 
 531     // (in case it ever is) and silences the compiler warnings for now 
 532     virtual ~wxTreeTraversal() { } 
 534     // do traverse the tree: visit all items (recursively by default) under the 
 535     // given one; return true if all items were traversed or false if the 
 536     // traversal was aborted because OnVisit returned false 
 537     bool DoTraverse(const wxTreeItemId
& root
, bool recursively 
= true); 
 539     // override this function to do whatever is needed for each item, return 
 540     // false to stop traversing 
 541     virtual bool OnVisit(const wxTreeItemId
& item
) = 0; 
 544     const wxTreeCtrl 
*GetTree() const { return m_tree
; } 
 547     bool Traverse(const wxTreeItemId
& root
, bool recursively
); 
 549     const wxTreeCtrl 
*m_tree
; 
 551     wxDECLARE_NO_COPY_CLASS(wxTreeTraversal
); 
 554 // internal class for getting the selected items 
 555 class TraverseSelections 
: public wxTreeTraversal
 
 558     TraverseSelections(const wxTreeCtrl 
*tree
, 
 559                        wxArrayTreeItemIds
& selections
) 
 560         : wxTreeTraversal(tree
), m_selections(selections
) 
 562             m_selections
.Empty(); 
 564             if (tree
->GetCount() > 0) 
 565                 DoTraverse(tree
->GetRootItem()); 
 568     virtual bool OnVisit(const wxTreeItemId
& item
) 
 570         const wxTreeCtrl 
* const tree 
= GetTree(); 
 572         // can't visit a virtual node. 
 573         if ( (tree
->GetRootItem() == item
) && tree
->HasFlag(wxTR_HIDE_ROOT
) ) 
 578         if ( ::IsItemSelected(GetHwndOf(tree
), HITEM(item
)) ) 
 580             m_selections
.Add(item
); 
 586     size_t GetCount() const { return m_selections
.GetCount(); } 
 589     wxArrayTreeItemIds
& m_selections
; 
 591     wxDECLARE_NO_COPY_CLASS(TraverseSelections
); 
 594 // internal class for counting tree items 
 595 class TraverseCounter 
: public wxTreeTraversal
 
 598     TraverseCounter(const wxTreeCtrl 
*tree
, 
 599                     const wxTreeItemId
& root
, 
 601         : wxTreeTraversal(tree
) 
 605             DoTraverse(root
, recursively
); 
 608     virtual bool OnVisit(const wxTreeItemId
& WXUNUSED(item
)) 
 615     size_t GetCount() const { return m_count
; } 
 620     wxDECLARE_NO_COPY_CLASS(TraverseCounter
); 
 623 // ---------------------------------------------------------------------------- 
 625 // ---------------------------------------------------------------------------- 
 627 // ---------------------------------------------------------------------------- 
 629 // ---------------------------------------------------------------------------- 
 631 // indices in gs_expandEvents table below 
 646 // handy table for sending events - it has to be initialized during run-time 
 647 // now so can't be const any more 
 648 static /* const */ wxEventType gs_expandEvents
[IDX_WHAT_MAX
][IDX_HOW_MAX
]; 
 651    but logically it's a const table with the following entries: 
 654     { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING }, 
 655     { wxEVT_COMMAND_TREE_ITEM_EXPANDED,  wxEVT_COMMAND_TREE_ITEM_EXPANDING  } 
 659 // ============================================================================ 
 661 // ============================================================================ 
 663 // ---------------------------------------------------------------------------- 
 665 // ---------------------------------------------------------------------------- 
 667 bool wxTreeTraversal::DoTraverse(const wxTreeItemId
& root
, bool recursively
) 
 669     if ( !OnVisit(root
) ) 
 672     return Traverse(root
, recursively
); 
 675 bool wxTreeTraversal::Traverse(const wxTreeItemId
& root
, bool recursively
) 
 677     wxTreeItemIdValue cookie
; 
 678     wxTreeItemId child 
= m_tree
->GetFirstChild(root
, cookie
); 
 679     while ( child
.IsOk() ) 
 681         // depth first traversal 
 682         if ( recursively 
&& !Traverse(child
, true) ) 
 685         if ( !OnVisit(child
) ) 
 688         child 
= m_tree
->GetNextChild(root
, cookie
); 
 694 // ---------------------------------------------------------------------------- 
 695 // construction and destruction 
 696 // ---------------------------------------------------------------------------- 
 698 void wxTreeCtrl::Init() 
 701     m_hasAnyAttr 
= false; 
 705     m_pVirtualRoot 
= NULL
; 
 706     m_dragStarted 
= false; 
 708     m_changingSelection 
= false; 
 709     m_triggerStateImageClick 
= false; 
 710     m_mouseUpDeselect 
= false; 
 712     // initialize the global array of events now as it can't be done statically 
 713     // with the wxEVT_XXX values being allocated during run-time only 
 714     gs_expandEvents
[IDX_COLLAPSE
][IDX_DONE
] = wxEVT_COMMAND_TREE_ITEM_COLLAPSED
; 
 715     gs_expandEvents
[IDX_COLLAPSE
][IDX_DOING
] = wxEVT_COMMAND_TREE_ITEM_COLLAPSING
; 
 716     gs_expandEvents
[IDX_EXPAND
][IDX_DONE
] = wxEVT_COMMAND_TREE_ITEM_EXPANDED
; 
 717     gs_expandEvents
[IDX_EXPAND
][IDX_DOING
] = wxEVT_COMMAND_TREE_ITEM_EXPANDING
; 
 720 bool wxTreeCtrl::Create(wxWindow 
*parent
, 
 725                         const wxValidator
& validator
, 
 726                         const wxString
& name
) 
 730     if ( (style 
& wxBORDER_MASK
) == wxBORDER_DEFAULT 
) 
 731         style 
|= wxBORDER_SUNKEN
; 
 733     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 737     DWORD wstyle 
= MSWGetStyle(m_windowStyle
, & exStyle
); 
 738     wstyle 
|= WS_TABSTOP 
| TVS_SHOWSELALWAYS
; 
 740     if ( !(m_windowStyle 
& wxTR_NO_LINES
) ) 
 741         wstyle 
|= TVS_HASLINES
; 
 742     if ( m_windowStyle 
& wxTR_HAS_BUTTONS 
) 
 743         wstyle 
|= TVS_HASBUTTONS
; 
 745     if ( m_windowStyle 
& wxTR_EDIT_LABELS 
) 
 746         wstyle 
|= TVS_EDITLABELS
; 
 748     if ( m_windowStyle 
& wxTR_LINES_AT_ROOT 
) 
 749         wstyle 
|= TVS_LINESATROOT
; 
 751     if ( m_windowStyle 
& wxTR_FULL_ROW_HIGHLIGHT 
) 
 753         if ( wxApp::GetComCtl32Version() >= 471 ) 
 754             wstyle 
|= TVS_FULLROWSELECT
; 
 757 #if !defined(__WXWINCE__) && defined(TVS_INFOTIP) 
 758     // Need so that TVN_GETINFOTIP messages will be sent 
 759     wstyle 
|= TVS_INFOTIP
; 
 762     // Create the tree control. 
 763     if ( !MSWCreateControl(WC_TREEVIEW
, wstyle
, pos
, size
) ) 
 766     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 767     SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); 
 769     wxSetCCUnicodeFormat(GetHwnd()); 
 771     if ( m_windowStyle 
& wxTR_TWIST_BUTTONS 
) 
 773         // Under Vista and later Explorer uses rotating ("twist") buttons 
 774         // instead of the default "+/-" ones so apply its theme to the tree 
 775         // control to implement this style. 
 776         if ( wxGetWinVersion() >= wxWinVersion_Vista 
) 
 778             if ( wxUxThemeEngine 
*theme 
= wxUxThemeEngine::GetIfActive() ) 
 780                 theme
->SetWindowTheme(GetHwnd(), L
"EXPLORER", NULL
); 
 788 wxTreeCtrl::~wxTreeCtrl() 
 790     // delete any attributes 
 793         WX_CLEAR_HASH_MAP(wxMapTreeAttr
, m_attrs
); 
 795         // prevent TVN_DELETEITEM handler from deleting the attributes again! 
 796         m_hasAnyAttr 
= false; 
 801     // delete user data to prevent memory leaks 
 802     // also deletes hidden root node storage. 
 806 // ---------------------------------------------------------------------------- 
 808 // ---------------------------------------------------------------------------- 
 810 /* static */ wxVisualAttributes
 
 811 wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant
) 
 813     wxVisualAttributes attrs 
= GetCompositeControlsDefaultAttributes(variant
); 
 815     // common controls have their own default font 
 816     attrs
.font 
= wxGetCCDefaultFont(); 
 822 // simple wrappers which add error checking in debug mode 
 824 bool wxTreeCtrl::DoGetItem(wxTreeViewItem 
*tvItem
) const 
 826     wxCHECK_MSG( tvItem
->hItem 
!= TVI_ROOT
, false, 
 827                  wxT("can't retrieve virtual root item") ); 
 829     if ( !TreeView_GetItem(GetHwnd(), tvItem
) ) 
 831         wxLogLastError(wxT("TreeView_GetItem")); 
 839 void wxTreeCtrl::DoSetItem(wxTreeViewItem 
*tvItem
) 
 841     TreeItemUnlocker 
unlocker(tvItem
->hItem
); 
 843     if ( TreeView_SetItem(GetHwnd(), tvItem
) == -1 ) 
 845         wxLogLastError(wxT("TreeView_SetItem")); 
 849 unsigned int wxTreeCtrl::GetCount() const 
 851     return (unsigned int)TreeView_GetCount(GetHwnd()); 
 854 unsigned int wxTreeCtrl::GetIndent() const 
 856     return TreeView_GetIndent(GetHwnd()); 
 859 void wxTreeCtrl::SetIndent(unsigned int indent
) 
 861     TreeView_SetIndent(GetHwnd(), indent
); 
 864 void wxTreeCtrl::SetAnyImageList(wxImageList 
*imageList
, int which
) 
 867     (void) TreeView_SetImageList(GetHwnd(), 
 868                                  imageList 
? imageList
->GetHIMAGELIST() : 0, 
 872 void wxTreeCtrl::SetImageList(wxImageList 
*imageList
) 
 874     if (m_ownsImageListNormal
) 
 875         delete m_imageListNormal
; 
 877     SetAnyImageList(m_imageListNormal 
= imageList
, TVSIL_NORMAL
); 
 878     m_ownsImageListNormal 
= false; 
 881 void wxTreeCtrl::SetStateImageList(wxImageList 
*imageList
) 
 883     if (m_ownsImageListState
) delete m_imageListState
; 
 884     SetAnyImageList(m_imageListState 
= imageList
, TVSIL_STATE
); 
 885     m_ownsImageListState 
= false; 
 888 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId
& item
, 
 889                                     bool recursively
) const 
 891     wxCHECK_MSG( item
.IsOk(), 0u, wxT("invalid tree item") ); 
 893     TraverseCounter 
counter(this, item
, recursively
); 
 894     return counter
.GetCount() - 1; 
 897 // ---------------------------------------------------------------------------- 
 899 // ---------------------------------------------------------------------------- 
 901 bool wxTreeCtrl::SetBackgroundColour(const wxColour 
&colour
) 
 903     if ( !wxWindowBase::SetBackgroundColour(colour
) ) 
 906     ::SendMessage(GetHwnd(), TVM_SETBKCOLOR
, 0, colour
.GetPixel()); 
 911 bool wxTreeCtrl::SetForegroundColour(const wxColour 
&colour
) 
 913     if ( !wxWindowBase::SetForegroundColour(colour
) ) 
 916     ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR
, 0, colour
.GetPixel()); 
 921 // ---------------------------------------------------------------------------- 
 923 // ---------------------------------------------------------------------------- 
 925 bool wxTreeCtrl::IsHiddenRoot(const wxTreeItemId
& item
) const 
 927     return HITEM(item
) == TVI_ROOT 
&& HasFlag(wxTR_HIDE_ROOT
); 
 930 wxString 
wxTreeCtrl::GetItemText(const wxTreeItemId
& item
) const 
 932     wxCHECK_MSG( item
.IsOk(), wxEmptyString
, wxT("invalid tree item") ); 
 934     wxChar buf
[512];  // the size is arbitrary... 
 936     wxTreeViewItem 
tvItem(item
, TVIF_TEXT
); 
 937     tvItem
.pszText 
= buf
; 
 938     tvItem
.cchTextMax 
= WXSIZEOF(buf
); 
 939     if ( !DoGetItem(&tvItem
) ) 
 941         // don't return some garbage which was on stack, but an empty string 
 945     return wxString(buf
); 
 948 void wxTreeCtrl::SetItemText(const wxTreeItemId
& item
, const wxString
& text
) 
 950     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
 952     if ( IS_VIRTUAL_ROOT(item
) ) 
 955     wxTreeViewItem 
tvItem(item
, TVIF_TEXT
); 
 956     tvItem
.pszText 
= (wxChar 
*)text
.wx_str();  // conversion is ok 
 959     // when setting the text of the item being edited, the text control should 
 960     // be updated to reflect the new text as well, otherwise calling 
 961     // SetItemText() in the OnBeginLabelEdit() handler doesn't have any effect 
 963     // don't use GetEditControl() here because m_textCtrl is not set yet 
 964     HWND hwndEdit 
= TreeView_GetEditControl(GetHwnd()); 
 967         if ( item 
== m_idEdited 
) 
 969             ::SetWindowText(hwndEdit
, text
.wx_str()); 
 974 int wxTreeCtrl::GetItemImage(const wxTreeItemId
& item
, 
 975                              wxTreeItemIcon which
) const 
 977     wxCHECK_MSG( item
.IsOk(), -1, wxT("invalid tree item") ); 
 979     if ( IsHiddenRoot(item
) ) 
 981         // no images for hidden root item 
 985     wxTreeItemParam 
*param 
= GetItemParam(item
); 
 987     return param 
&& param
->HasImage(which
) ? param
->GetImage(which
) : -1; 
 990 void wxTreeCtrl::SetItemImage(const wxTreeItemId
& item
, int image
, 
 991                               wxTreeItemIcon which
) 
 993     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
 994     wxCHECK_RET( which 
>= 0 && 
 995                  which 
< wxTreeItemIcon_Max
, 
 996                  wxT("invalid image index")); 
 999     if ( IsHiddenRoot(item
) ) 
1001         // no images for hidden root item 
1005     wxTreeItemParam 
*data 
= GetItemParam(item
); 
1009     data
->SetImage(image
, which
); 
1014 wxTreeItemParam 
*wxTreeCtrl::GetItemParam(const wxTreeItemId
& item
) const 
1016     wxCHECK_MSG( item
.IsOk(), NULL
, wxT("invalid tree item") ); 
1018     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
1020     // hidden root may still have data. 
1021     if ( IS_VIRTUAL_ROOT(item
) ) 
1023         return GET_VIRTUAL_ROOT()->GetParam(); 
1027     if ( !DoGetItem(&tvItem
) ) 
1032     return (wxTreeItemParam 
*)tvItem
.lParam
; 
1035 bool wxTreeCtrl::HandleTreeEvent(wxTreeEvent
& event
) const 
1037     if ( event
.m_item
.IsOk() ) 
1039         event
.SetClientObject(GetItemData(event
.m_item
)); 
1042     return HandleWindowEvent(event
); 
1045 wxTreeItemData 
*wxTreeCtrl::GetItemData(const wxTreeItemId
& item
) const 
1047     wxTreeItemParam 
*data 
= GetItemParam(item
); 
1049     return data 
? data
->GetData() : NULL
; 
1052 void wxTreeCtrl::SetItemData(const wxTreeItemId
& item
, wxTreeItemData 
*data
) 
1054     // first, associate this piece of data with this item 
1060     wxTreeItemParam 
*param 
= GetItemParam(item
); 
1062     wxCHECK_RET( param
, wxT("failed to change tree items data") ); 
1064     param
->SetData(data
); 
1067 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId
& item
, bool has
) 
1069     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1071     if ( IS_VIRTUAL_ROOT(item
) ) 
1074     wxTreeViewItem 
tvItem(item
, TVIF_CHILDREN
); 
1075     tvItem
.cChildren 
= (int)has
; 
1079 void wxTreeCtrl::SetItemBold(const wxTreeItemId
& item
, bool bold
) 
1081     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1083     if ( IS_VIRTUAL_ROOT(item
) ) 
1086     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_BOLD
); 
1087     tvItem
.state 
= bold 
? TVIS_BOLD 
: 0; 
1091 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId
& item
, bool highlight
) 
1093     if ( IS_VIRTUAL_ROOT(item
) ) 
1096     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_DROPHILITED
); 
1097     tvItem
.state 
= highlight 
? TVIS_DROPHILITED 
: 0; 
1101 void wxTreeCtrl::RefreshItem(const wxTreeItemId
& item
) 
1103     if ( IS_VIRTUAL_ROOT(item
) ) 
1107     if ( GetBoundingRect(item
, rect
) ) 
1113 wxColour 
wxTreeCtrl::GetItemTextColour(const wxTreeItemId
& item
) const 
1115     wxCHECK_MSG( item
.IsOk(), wxNullColour
, wxT("invalid tree item") ); 
1117     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1118     return it 
== m_attrs
.end() ? wxNullColour 
: it
->second
->GetTextColour(); 
1121 wxColour 
wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId
& item
) const 
1123     wxCHECK_MSG( item
.IsOk(), wxNullColour
, wxT("invalid tree item") ); 
1125     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1126     return it 
== m_attrs
.end() ? wxNullColour 
: it
->second
->GetBackgroundColour(); 
1129 wxFont 
wxTreeCtrl::GetItemFont(const wxTreeItemId
& item
) const 
1131     wxCHECK_MSG( item
.IsOk(), wxNullFont
, wxT("invalid tree item") ); 
1133     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1134     return it 
== m_attrs
.end() ? wxNullFont 
: it
->second
->GetFont(); 
1137 void wxTreeCtrl::SetItemTextColour(const wxTreeItemId
& item
, 
1138                                    const wxColour
& col
) 
1140     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1142     wxTreeItemAttr 
*attr
; 
1143     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1144     if ( it 
== m_attrs
.end() ) 
1146         m_hasAnyAttr 
= true; 
1148         m_attrs
[item
.m_pItem
] = 
1149         attr 
= new wxTreeItemAttr
; 
1156     attr
->SetTextColour(col
); 
1161 void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId
& item
, 
1162                                          const wxColour
& col
) 
1164     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1166     wxTreeItemAttr 
*attr
; 
1167     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1168     if ( it 
== m_attrs
.end() ) 
1170         m_hasAnyAttr 
= true; 
1172         m_attrs
[item
.m_pItem
] = 
1173         attr 
= new wxTreeItemAttr
; 
1175     else // already in the hash 
1180     attr
->SetBackgroundColour(col
); 
1185 void wxTreeCtrl::SetItemFont(const wxTreeItemId
& item
, const wxFont
& font
) 
1187     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1189     wxTreeItemAttr 
*attr
; 
1190     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1191     if ( it 
== m_attrs
.end() ) 
1193         m_hasAnyAttr 
= true; 
1195         m_attrs
[item
.m_pItem
] = 
1196         attr 
= new wxTreeItemAttr
; 
1198     else // already in the hash 
1203     attr
->SetFont(font
); 
1205     // Reset the item's text to ensure that the bounding rect will be adjusted 
1206     // for the new font. 
1207     SetItemText(item
, GetItemText(item
)); 
1212 // ---------------------------------------------------------------------------- 
1214 // ---------------------------------------------------------------------------- 
1216 bool wxTreeCtrl::IsVisible(const wxTreeItemId
& item
) const 
1218     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1220     if ( item 
== wxTreeItemId(TVI_ROOT
) ) 
1222         // virtual (hidden) root is never visible 
1226     // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect 
1227     TVGetItemRectParam param
; 
1229     // true means to get rect for just the text, not the whole line 
1230     if ( !wxTreeView_GetItemRect(GetHwnd(), HITEM(item
), param
, TRUE
) ) 
1232         // if TVM_GETITEMRECT returned false, then the item is definitely not 
1233         // visible (because its parent is not expanded) 
1237     // however if it returned true, the item might still be outside the 
1238     // currently visible part of the tree, test for it (notice that partly 
1239     // visible means visible here) 
1240     return param
.rect
.bottom 
> 0 && param
.rect
.top 
< GetClientSize().y
; 
1243 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId
& item
) const 
1245     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1247     if ( IS_VIRTUAL_ROOT(item
) ) 
1249         wxTreeItemIdValue cookie
; 
1250         return GetFirstChild(item
, cookie
).IsOk(); 
1253     wxTreeViewItem 
tvItem(item
, TVIF_CHILDREN
); 
1256     return tvItem
.cChildren 
!= 0; 
1259 bool wxTreeCtrl::IsExpanded(const wxTreeItemId
& item
) const 
1261     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1263     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_EXPANDED
); 
1266     return (tvItem
.state 
& TVIS_EXPANDED
) != 0; 
1269 bool wxTreeCtrl::IsSelected(const wxTreeItemId
& item
) const 
1271     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1273     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_SELECTED
); 
1276     return (tvItem
.state 
& TVIS_SELECTED
) != 0; 
1279 bool wxTreeCtrl::IsBold(const wxTreeItemId
& item
) const 
1281     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1283     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_BOLD
); 
1286     return (tvItem
.state 
& TVIS_BOLD
) != 0; 
1289 // ---------------------------------------------------------------------------- 
1291 // ---------------------------------------------------------------------------- 
1293 wxTreeItemId 
wxTreeCtrl::GetRootItem() const 
1295     // Root may be real (visible) or virtual (hidden). 
1296     if ( GET_VIRTUAL_ROOT() ) 
1299     return wxTreeItemId(TreeView_GetRoot(GetHwnd())); 
1302 wxTreeItemId 
wxTreeCtrl::GetSelection() const 
1304     wxCHECK_MSG( !HasFlag(wxTR_MULTIPLE
), wxTreeItemId(), 
1305                  wxT("this only works with single selection controls") ); 
1307     return GetFocusedItem(); 
1310 wxTreeItemId 
wxTreeCtrl::GetFocusedItem() const 
1312     return wxTreeItemId(TreeView_GetSelection(GetHwnd())); 
1315 wxTreeItemId 
wxTreeCtrl::GetItemParent(const wxTreeItemId
& item
) const 
1317     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1321     if ( IS_VIRTUAL_ROOT(item
) ) 
1323         // no parent for the virtual root 
1328         hItem 
= TreeView_GetParent(GetHwnd(), HITEM(item
)); 
1329         if ( !hItem 
&& HasFlag(wxTR_HIDE_ROOT
) ) 
1331             // the top level items should have the virtual root as their parent 
1336     return wxTreeItemId(hItem
); 
1339 wxTreeItemId 
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
, 
1340                                        wxTreeItemIdValue
& cookie
) const 
1342     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1344     // remember the last child returned in 'cookie' 
1345     cookie 
= TreeView_GetChild(GetHwnd(), HITEM(item
)); 
1347     return wxTreeItemId(cookie
); 
1350 wxTreeItemId 
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
), 
1351                                       wxTreeItemIdValue
& cookie
) const 
1353     wxTreeItemId 
fromCookie(cookie
); 
1355     HTREEITEM hitem 
= HITEM(fromCookie
); 
1357     hitem 
= TreeView_GetNextSibling(GetHwnd(), hitem
); 
1359     wxTreeItemId 
item(hitem
); 
1361     cookie 
= item
.m_pItem
; 
1366 wxTreeItemId 
wxTreeCtrl::GetLastChild(const wxTreeItemId
& item
) const 
1368     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1370     // can this be done more efficiently? 
1371     wxTreeItemIdValue cookie
; 
1373     wxTreeItemId childLast
, 
1374     child 
= GetFirstChild(item
, cookie
); 
1375     while ( child
.IsOk() ) 
1378         child 
= GetNextChild(item
, cookie
); 
1384 wxTreeItemId 
wxTreeCtrl::GetNextSibling(const wxTreeItemId
& item
) const 
1386     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1387     return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item
))); 
1390 wxTreeItemId 
wxTreeCtrl::GetPrevSibling(const wxTreeItemId
& item
) const 
1392     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1393     return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item
))); 
1396 wxTreeItemId 
wxTreeCtrl::GetFirstVisibleItem() const 
1398     return wxTreeItemId(TreeView_GetFirstVisible(GetHwnd())); 
1401 wxTreeItemId 
wxTreeCtrl::GetNextVisible(const wxTreeItemId
& item
) const 
1403     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1404     wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetNextVisible() for must be visible itself!")); 
1406     wxTreeItemId 
next(TreeView_GetNextVisible(GetHwnd(), HITEM(item
))); 
1407     if ( next
.IsOk() && !IsVisible(next
) ) 
1409         // Win32 considers that any non-collapsed item is visible while we want 
1410         // to return only really visible items 
1417 wxTreeItemId 
wxTreeCtrl::GetPrevVisible(const wxTreeItemId
& item
) const 
1419     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1420     wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetPrevVisible() for must be visible itself!")); 
1422     wxTreeItemId 
prev(TreeView_GetPrevVisible(GetHwnd(), HITEM(item
))); 
1423     if ( prev
.IsOk() && !IsVisible(prev
) ) 
1425         // just as above, Win32 function will happily return the previous item 
1426         // in the tree for the first visible item too 
1433 // ---------------------------------------------------------------------------- 
1434 // multiple selections emulation 
1435 // ---------------------------------------------------------------------------- 
1437 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds
& selections
) const 
1439     TraverseSelections 
selector(this, selections
); 
1441     return selector
.GetCount(); 
1444 // ---------------------------------------------------------------------------- 
1446 // ---------------------------------------------------------------------------- 
1448 wxTreeItemId 
wxTreeCtrl::DoInsertAfter(const wxTreeItemId
& parent
, 
1449                                        const wxTreeItemId
& hInsertAfter
, 
1450                                        const wxString
& text
, 
1451                                        int image
, int selectedImage
, 
1452                                        wxTreeItemData 
*data
) 
1454     wxCHECK_MSG( parent
.IsOk() || !TreeView_GetRoot(GetHwnd()), 
1456                  wxT("can't have more than one root in the tree") ); 
1458     TV_INSERTSTRUCT tvIns
; 
1459     tvIns
.hParent 
= HITEM(parent
); 
1460     tvIns
.hInsertAfter 
= HITEM(hInsertAfter
); 
1462     // this is how we insert the item as the first child: supply a NULL 
1464     if ( !tvIns
.hInsertAfter 
) 
1466         tvIns
.hInsertAfter 
= TVI_FIRST
; 
1470     if ( !text
.empty() ) 
1473         tvIns
.item
.pszText 
= (wxChar 
*)text
.wx_str();  // cast is ok 
1477         tvIns
.item
.pszText 
= NULL
; 
1478         tvIns
.item
.cchTextMax 
= 0; 
1481     // create the param which will store the other item parameters 
1482     wxTreeItemParam 
*param 
= new wxTreeItemParam
; 
1484     // we return the images on demand as they depend on whether the item is 
1485     // expanded or collapsed too in our case 
1486     mask 
|= TVIF_IMAGE 
| TVIF_SELECTEDIMAGE
; 
1487     tvIns
.item
.iImage 
= I_IMAGECALLBACK
; 
1488     tvIns
.item
.iSelectedImage 
= I_IMAGECALLBACK
; 
1490     param
->SetImage(image
, wxTreeItemIcon_Normal
); 
1491     param
->SetImage(selectedImage
, wxTreeItemIcon_Selected
); 
1494     tvIns
.item
.lParam 
= (LPARAM
)param
; 
1495     tvIns
.item
.mask 
= mask
; 
1497     // don't use the hack below for the children of hidden root: this results 
1498     // in a crash inside comctl32.dll when we call TreeView_GetItemRect() 
1499     const bool firstChild 
= !IsHiddenRoot(parent
) && 
1500                                 !TreeView_GetChild(GetHwnd(), HITEM(parent
)); 
1502     HTREEITEM id 
= TreeView_InsertItem(GetHwnd(), &tvIns
); 
1505         wxLogLastError(wxT("TreeView_InsertItem")); 
1508     // apparently some Windows versions (2000 and XP are reported to do this) 
1509     // sometimes don't refresh the tree after adding the first child and so we 
1510     // need this to make the "[+]" appear 
1513         TVGetItemRectParam param
; 
1515         wxTreeView_GetItemRect(GetHwnd(), HITEM(parent
), param
, FALSE
); 
1516         ::InvalidateRect(GetHwnd(), ¶m
.rect
, FALSE
); 
1519     // associate the application tree item with Win32 tree item handle 
1522     // setup wxTreeItemData 
1525         param
->SetData(data
); 
1529     return wxTreeItemId(id
); 
1532 wxTreeItemId 
wxTreeCtrl::AddRoot(const wxString
& text
, 
1533                                  int image
, int selectedImage
, 
1534                                  wxTreeItemData 
*data
) 
1536     if ( HasFlag(wxTR_HIDE_ROOT
) ) 
1538         wxASSERT_MSG( !m_pVirtualRoot
, wxT("tree can have only a single root") ); 
1540         // create a virtual root item, the parent for all the others 
1541         wxTreeItemParam 
*param 
= new wxTreeItemParam
; 
1542         param
->SetData(data
); 
1544         m_pVirtualRoot 
= new wxVirtualNode(param
); 
1549     return DoInsertAfter(wxTreeItemId(), wxTreeItemId(), 
1550                            text
, image
, selectedImage
, data
); 
1553 wxTreeItemId 
wxTreeCtrl::DoInsertItem(const wxTreeItemId
& parent
, 
1555                                       const wxString
& text
, 
1556                                       int image
, int selectedImage
, 
1557                                       wxTreeItemData 
*data
) 
1559     wxTreeItemId idPrev
; 
1560     if ( index 
== (size_t)-1 ) 
1562         // special value: append to the end 
1565     else // find the item from index 
1567         wxTreeItemIdValue cookie
; 
1568         wxTreeItemId idCur 
= GetFirstChild(parent
, cookie
); 
1569         while ( index 
!= 0 && idCur
.IsOk() ) 
1574             idCur 
= GetNextChild(parent
, cookie
); 
1577         // assert, not check: if the index is invalid, we will append the item 
1579         wxASSERT_MSG( index 
== 0, wxT("bad index in wxTreeCtrl::InsertItem") ); 
1582     return DoInsertAfter(parent
, idPrev
, text
, image
, selectedImage
, data
); 
1585 void wxTreeCtrl::Delete(const wxTreeItemId
& item
) 
1587     // unlock tree selections on vista, without this the 
1588     // tree ctrl will eventually crash after item deletion 
1589     TreeItemUnlocker unlock_all
; 
1591     if ( HasFlag(wxTR_MULTIPLE
) ) 
1593         bool selected 
= IsSelected(item
); 
1598             next 
= TreeView_GetNextVisible(GetHwnd(), HITEM(item
)); 
1602                 next 
= TreeView_GetPrevVisible(GetHwnd(), HITEM(item
)); 
1607             TempSetter 
set(m_changingSelection
); 
1608             if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item
)) ) 
1610                 wxLogLastError(wxT("TreeView_DeleteItem")); 
1620         if ( item 
== m_htSelStart 
) 
1621             m_htSelStart
.Unset(); 
1623         if ( item 
== m_htClickedItem 
) 
1624             m_htClickedItem
.Unset(); 
1628             wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this, next
); 
1630             if ( IsTreeEventAllowed(changingEvent
) ) 
1632                 wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, this, next
); 
1633                 (void)HandleTreeEvent(changedEvent
); 
1637                 DoUnselectItem(next
); 
1644         if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item
)) ) 
1646             wxLogLastError(wxT("TreeView_DeleteItem")); 
1651 // delete all children (but don't delete the item itself) 
1652 void wxTreeCtrl::DeleteChildren(const wxTreeItemId
& item
) 
1654     // unlock tree selections on vista for the duration of this call 
1655     TreeItemUnlocker unlock_all
; 
1657     wxTreeItemIdValue cookie
; 
1659     wxArrayTreeItemIds children
; 
1660     wxTreeItemId child 
= GetFirstChild(item
, cookie
); 
1661     while ( child
.IsOk() ) 
1663         children
.Add(child
); 
1665         child 
= GetNextChild(item
, cookie
); 
1668     size_t nCount 
= children
.Count(); 
1669     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
1671         Delete(children
[n
]); 
1675 void wxTreeCtrl::DeleteAllItems() 
1677     // unlock tree selections on vista for the duration of this call 
1678     TreeItemUnlocker unlock_all
; 
1680     // invalidate all the items we store as they're going to become invalid 
1682     m_htClickedItem 
= wxTreeItemId(); 
1684     // delete the "virtual" root item. 
1685     if ( GET_VIRTUAL_ROOT() ) 
1687         delete GET_VIRTUAL_ROOT(); 
1688         m_pVirtualRoot 
= NULL
; 
1691     // and all the real items 
1693     if ( !TreeView_DeleteAllItems(GetHwnd()) ) 
1695         wxLogLastError(wxT("TreeView_DeleteAllItems")); 
1699 void wxTreeCtrl::DoExpand(const wxTreeItemId
& item
, int flag
) 
1701     wxASSERT_MSG( flag 
== TVE_COLLAPSE 
|| 
1702                   flag 
== (TVE_COLLAPSE 
| TVE_COLLAPSERESET
) || 
1703                   flag 
== TVE_EXPAND   
|| 
1705                   wxT("Unknown flag in wxTreeCtrl::DoExpand") ); 
1707     // A hidden root can be neither expanded nor collapsed. 
1708     wxCHECK_RET( !IsHiddenRoot(item
), 
1709                  wxT("Can't expand/collapse hidden root node!") ); 
1711     // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must 
1712     // emulate them. This behaviour has changed slightly with comctl32.dll 
1713     // v 4.70 - now it does send them but only the first time. To maintain 
1714     // compatible behaviour and also in order to not have surprises with the 
1715     // future versions, don't rely on this and still do everything ourselves. 
1716     // To avoid that the messages be sent twice when the item is expanded for 
1717     // the first time we must clear TVIS_EXPANDEDONCE style manually. 
1719     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_EXPANDEDONCE
); 
1723     if ( IsExpanded(item
) ) 
1725         wxTreeEvent 
event(wxEVT_COMMAND_TREE_ITEM_COLLAPSING
, 
1726                           this, wxTreeItemId(item
)); 
1728         if ( !IsTreeEventAllowed(event
) ) 
1732     if ( TreeView_Expand(GetHwnd(), HITEM(item
), flag
) ) 
1734         if ( IsExpanded(item
) ) 
1737         wxTreeEvent 
event(wxEVT_COMMAND_TREE_ITEM_COLLAPSED
, this, item
); 
1738         (void)HandleTreeEvent(event
); 
1740     //else: change didn't took place, so do nothing at all 
1743 void wxTreeCtrl::Expand(const wxTreeItemId
& item
) 
1745     DoExpand(item
, TVE_EXPAND
); 
1748 void wxTreeCtrl::Collapse(const wxTreeItemId
& item
) 
1750     DoExpand(item
, TVE_COLLAPSE
); 
1753 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId
& item
) 
1755     DoExpand(item
, TVE_COLLAPSE 
| TVE_COLLAPSERESET
); 
1758 void wxTreeCtrl::Toggle(const wxTreeItemId
& item
) 
1760     DoExpand(item
, TVE_TOGGLE
); 
1763 void wxTreeCtrl::Unselect() 
1765     wxASSERT_MSG( !HasFlag(wxTR_MULTIPLE
), 
1766                   wxT("doesn't make sense, may be you want UnselectAll()?") ); 
1768     // the current focus 
1769     HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
1776     if ( HasFlag(wxTR_MULTIPLE
) ) 
1778         wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
1779                                   this, wxTreeItemId()); 
1780         changingEvent
.m_itemOld 
= htFocus
; 
1782         if ( IsTreeEventAllowed(changingEvent
) ) 
1786             wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
1787                                      this, wxTreeItemId()); 
1788             changedEvent
.m_itemOld 
= htFocus
; 
1789             (void)HandleTreeEvent(changedEvent
); 
1798 void wxTreeCtrl::DoUnselectAll() 
1800     wxArrayTreeItemIds selections
; 
1801     size_t count 
= GetSelections(selections
); 
1803     for ( size_t n 
= 0; n 
< count
; n
++ ) 
1805         DoUnselectItem(selections
[n
]); 
1808     m_htSelStart
.Unset(); 
1811 void wxTreeCtrl::UnselectAll() 
1813     if ( HasFlag(wxTR_MULTIPLE
) ) 
1815         HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
1816         if ( !htFocus 
) return; 
1818         wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this); 
1819         changingEvent
.m_itemOld 
= htFocus
; 
1821         if ( IsTreeEventAllowed(changingEvent
) ) 
1825             wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, this); 
1826             changedEvent
.m_itemOld 
= htFocus
; 
1827             (void)HandleTreeEvent(changedEvent
); 
1836 void wxTreeCtrl::DoSelectChildren(const wxTreeItemId
& parent
) 
1840     wxTreeItemIdValue cookie
; 
1841     wxTreeItemId child 
= GetFirstChild(parent
, cookie
); 
1842     while ( child
.IsOk() ) 
1844         DoSelectItem(child
, true); 
1845         child 
= GetNextChild(child
, cookie
); 
1849 void wxTreeCtrl::SelectChildren(const wxTreeItemId
& parent
) 
1851     wxCHECK_RET( HasFlag(wxTR_MULTIPLE
), 
1852                  "this only works with multiple selection controls" ); 
1854     HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
1856     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this); 
1857     changingEvent
.m_itemOld 
= htFocus
; 
1859     if ( IsTreeEventAllowed(changingEvent
) ) 
1861         DoSelectChildren(parent
); 
1863         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, this); 
1864         changedEvent
.m_itemOld 
= htFocus
; 
1865         (void)HandleTreeEvent(changedEvent
); 
1869 void wxTreeCtrl::DoSelectItem(const wxTreeItemId
& item
, bool select
) 
1871     TempSetter 
set(m_changingSelection
); 
1873     ::SelectItem(GetHwnd(), HITEM(item
), select
); 
1876 void wxTreeCtrl::SelectItem(const wxTreeItemId
& item
, bool select
) 
1878     wxCHECK_RET( !IsHiddenRoot(item
), wxT("can't select hidden root item") ); 
1880     if ( select 
== IsSelected(item
) ) 
1882         // nothing to do, the item is already in the requested state 
1886     if ( HasFlag(wxTR_MULTIPLE
) ) 
1888         wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this, item
); 
1890         if ( IsTreeEventAllowed(changingEvent
) ) 
1892             HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
1893             DoSelectItem(item
, select
); 
1897                 SetFocusedItem(item
); 
1900             wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
1902             (void)HandleTreeEvent(changedEvent
); 
1905     else // single selection 
1907         wxTreeItemId itemOld
, itemNew
; 
1910             itemOld 
= GetSelection(); 
1913         else // deselecting the currently selected item 
1916             // leave itemNew invalid 
1919         // Recent versions of comctl32.dll send TVN_SELCHANG{ED,ING} events 
1920         // when we call TreeView_SelectItem() but apparently some old ones did 
1921         // not so send the events ourselves and ignore those generated by 
1922         // TreeView_SelectItem() if m_changingSelection is set. 
1924             changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this, itemNew
); 
1925         changingEvent
.SetOldItem(itemOld
); 
1927         if ( IsTreeEventAllowed(changingEvent
) ) 
1929             TempSetter 
set(m_changingSelection
); 
1931             if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew
)) ) 
1933                 wxLogLastError(wxT("TreeView_SelectItem")); 
1937                 ::SetFocus(GetHwnd(), HITEM(item
)); 
1939                 wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
1941                 changedEvent
.SetOldItem(itemOld
); 
1942                 (void)HandleTreeEvent(changedEvent
); 
1945         //else: program vetoed the change 
1949 void wxTreeCtrl::EnsureVisible(const wxTreeItemId
& item
) 
1951     wxCHECK_RET( !IsHiddenRoot(item
), wxT("can't show hidden root item") ); 
1954     TreeView_EnsureVisible(GetHwnd(), HITEM(item
)); 
1957 void wxTreeCtrl::ScrollTo(const wxTreeItemId
& item
) 
1959     if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item
)) ) 
1961         wxLogLastError(wxT("TreeView_SelectSetFirstVisible")); 
1965 wxTextCtrl 
*wxTreeCtrl::GetEditControl() const 
1970 void wxTreeCtrl::DeleteTextCtrl() 
1974         // the HWND corresponding to this control is deleted by the tree 
1975         // control itself and we don't know when exactly this happens, so check 
1976         // if the window still exists before calling UnsubclassWin() 
1977         if ( !::IsWindow(GetHwndOf(m_textCtrl
)) ) 
1979             m_textCtrl
->SetHWND(0); 
1982         m_textCtrl
->UnsubclassWin(); 
1983         m_textCtrl
->SetHWND(0); 
1984         wxDELETE(m_textCtrl
); 
1990 wxTextCtrl 
*wxTreeCtrl::EditLabel(const wxTreeItemId
& item
, 
1991                                   wxClassInfo 
*textControlClass
) 
1993     wxASSERT( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)) ); 
1998     m_textCtrl 
= (wxTextCtrl 
*)textControlClass
->CreateObject(); 
1999     HWND hWnd 
= (HWND
) TreeView_EditLabel(GetHwnd(), HITEM(item
)); 
2001     // this is not an error - the TVN_BEGINLABELEDIT handler might have 
2005         wxDELETE(m_textCtrl
); 
2009     // textctrl is subclassed in MSWOnNotify 
2013 // End label editing, optionally cancelling the edit 
2014 void wxTreeCtrl::DoEndEditLabel(bool discardChanges
) 
2016     TreeView_EndEditLabelNow(GetHwnd(), discardChanges
); 
2021 wxTreeItemId 
wxTreeCtrl::DoTreeHitTest(const wxPoint
& point
, int& flags
) const 
2023     TV_HITTESTINFO hitTestInfo
; 
2024     hitTestInfo
.pt
.x 
= (int)point
.x
; 
2025     hitTestInfo
.pt
.y 
= (int)point
.y
; 
2027     (void) TreeView_HitTest(GetHwnd(), &hitTestInfo
); 
2032     #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \ 
2033                                     flags |= wxTREE_HITTEST_##flag 
2035     TRANSLATE_FLAG(ABOVE
); 
2036     TRANSLATE_FLAG(BELOW
); 
2037     TRANSLATE_FLAG(NOWHERE
); 
2038     TRANSLATE_FLAG(ONITEMBUTTON
); 
2039     TRANSLATE_FLAG(ONITEMICON
); 
2040     TRANSLATE_FLAG(ONITEMINDENT
); 
2041     TRANSLATE_FLAG(ONITEMLABEL
); 
2042     TRANSLATE_FLAG(ONITEMRIGHT
); 
2043     TRANSLATE_FLAG(ONITEMSTATEICON
); 
2044     TRANSLATE_FLAG(TOLEFT
); 
2045     TRANSLATE_FLAG(TORIGHT
); 
2047     #undef TRANSLATE_FLAG 
2049     return wxTreeItemId(hitTestInfo
.hItem
); 
2052 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId
& item
, 
2054                                  bool textOnly
) const 
2056     // Virtual root items have no bounding rectangle 
2057     if ( IS_VIRTUAL_ROOT(item
) ) 
2062     TVGetItemRectParam param
; 
2064     if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(item
), param
, textOnly
) ) 
2066         rect 
= wxRect(wxPoint(param
.rect
.left
, param
.rect
.top
), 
2067                       wxPoint(param
.rect
.right
, param
.rect
.bottom
)); 
2073         // couldn't retrieve rect: for example, item isn't visible 
2078 void wxTreeCtrl::ClearFocusedItem() 
2080     TempSetter 
set(m_changingSelection
); 
2082     if ( !TreeView_SelectItem(GetHwnd(), 0) ) 
2084         wxLogLastError(wxT("TreeView_SelectItem")); 
2088 void wxTreeCtrl::SetFocusedItem(const wxTreeItemId
& item
) 
2090     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
2092     TempSetter 
set(m_changingSelection
); 
2094     ::SetFocus(GetHwnd(), HITEM(item
)); 
2097 void wxTreeCtrl::DoUnselectItem(const wxTreeItemId
& item
) 
2099     TempSetter 
set(m_changingSelection
); 
2101     ::UnselectItem(GetHwnd(), HITEM(item
)); 
2104 void wxTreeCtrl::DoToggleItemSelection(const wxTreeItemId
& item
) 
2106     TempSetter 
set(m_changingSelection
); 
2108     ::ToggleItemSelection(GetHwnd(), HITEM(item
)); 
2111 // ---------------------------------------------------------------------------- 
2113 // ---------------------------------------------------------------------------- 
2115 // this is just a tiny namespace which is friend to wxTreeCtrl and so can use 
2116 // functions such as IsDataIndirect() 
2117 class wxTreeSortHelper
 
2120     static int CALLBACK 
Compare(LPARAM data1
, LPARAM data2
, LPARAM tree
); 
2123     static wxTreeItemId 
GetIdFromData(LPARAM lParam
) 
2125         return ((wxTreeItemParam
*)lParam
)->GetItem(); 
2129 int CALLBACK 
wxTreeSortHelper::Compare(LPARAM pItem1
, 
2133     wxCHECK_MSG( pItem1 
&& pItem2
, 0, 
2134                  wxT("sorting tree without data doesn't make sense") ); 
2136     wxTreeCtrl 
*tree 
= (wxTreeCtrl 
*)htree
; 
2138     return tree
->OnCompareItems(GetIdFromData(pItem1
), 
2139                                 GetIdFromData(pItem2
)); 
2142 void wxTreeCtrl::SortChildren(const wxTreeItemId
& item
) 
2144     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
2146     // rely on the fact that TreeView_SortChildren does the same thing as our 
2147     // default behaviour, i.e. sorts items alphabetically and so call it 
2148     // directly if we're not in derived class (much more efficient!) 
2149     // RN: Note that if you find you're code doesn't sort as expected this 
2150     //     may be why as if you don't use the DECLARE_CLASS/IMPLEMENT_CLASS 
2151     //     combo for your derived wxTreeCtrl if will sort without 
2153     if ( GetClassInfo() == CLASSINFO(wxTreeCtrl
) ) 
2155         TreeView_SortChildren(GetHwnd(), HITEM(item
), 0); 
2160         tvSort
.hParent 
= HITEM(item
); 
2161         tvSort
.lpfnCompare 
= wxTreeSortHelper::Compare
; 
2162         tvSort
.lParam 
= (LPARAM
)this; 
2163         TreeView_SortChildrenCB(GetHwnd(), &tvSort
, 0 /* reserved */); 
2167 // ---------------------------------------------------------------------------- 
2169 // ---------------------------------------------------------------------------- 
2171 bool wxTreeCtrl::MSWShouldPreProcessMessage(WXMSG
* msg
) 
2173     if ( msg
->message 
== WM_KEYDOWN 
) 
2175         // Only eat VK_RETURN if not being used by the application in 
2176         // conjunction with modifiers 
2177         if ( (msg
->wParam 
== VK_RETURN
) && !wxIsAnyModifierDown() ) 
2179             // we need VK_RETURN to generate wxEVT_COMMAND_TREE_ITEM_ACTIVATED 
2184     return wxTreeCtrlBase::MSWShouldPreProcessMessage(msg
); 
2187 bool wxTreeCtrl::MSWCommand(WXUINT cmd
, WXWORD id_
) 
2189     const int id 
= (signed short)id_
; 
2191     if ( cmd 
== EN_UPDATE 
) 
2193         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, id
); 
2194         event
.SetEventObject( this ); 
2195         ProcessCommand(event
); 
2197     else if ( cmd 
== EN_KILLFOCUS 
) 
2199         wxCommandEvent 
event(wxEVT_KILL_FOCUS
, id
); 
2200         event
.SetEventObject( this ); 
2201         ProcessCommand(event
); 
2209     // command processed 
2213 bool wxTreeCtrl::MSWIsOnItem(unsigned flags
) const 
2215     unsigned mask 
= TVHT_ONITEM
; 
2216     if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT
) ) 
2217         mask 
|= TVHT_ONITEMINDENT 
| TVHT_ONITEMRIGHT
; 
2219     return (flags 
& mask
) != 0; 
2222 bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey
) 
2224     const bool bCtrl 
= wxIsCtrlDown(); 
2225     const bool bShift 
= wxIsShiftDown(); 
2226     const HTREEITEM htSel 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
2235             if ( vkey 
!= VK_RETURN 
&& bCtrl 
) 
2237                 wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2239                 changingEvent
.m_itemOld 
= htSel
; 
2241                 if ( IsTreeEventAllowed(changingEvent
) ) 
2243                     DoToggleItemSelection(wxTreeItemId(htSel
)); 
2245                     wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2247                     changedEvent
.m_itemOld 
= htSel
; 
2248                     (void)HandleTreeEvent(changedEvent
); 
2253                 wxArrayTreeItemIds selections
; 
2254                 size_t count 
= GetSelections(selections
); 
2256                 if ( count 
!= 1 || HITEM(selections
[0]) != htSel 
) 
2258                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2260                     changingEvent
.m_itemOld 
= htSel
; 
2262                     if ( IsTreeEventAllowed(changingEvent
) ) 
2265                         DoSelectItem(wxTreeItemId(htSel
)); 
2267                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2269                         changedEvent
.m_itemOld 
= htSel
; 
2270                         (void)HandleTreeEvent(changedEvent
); 
2278             if ( !bCtrl 
&& !bShift 
) 
2280                 wxArrayTreeItemIds selections
; 
2285                     next 
= vkey 
== VK_UP
 
2286                             ? TreeView_GetPrevVisible(GetHwnd(), htSel
) 
2287                             : TreeView_GetNextVisible(GetHwnd(), htSel
); 
2291                     next 
= GetRootItem(); 
2293                     if ( IsHiddenRoot(next
) ) 
2294                         next 
= TreeView_GetChild(GetHwnd(), HITEM(next
)); 
2302                 wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2304                 changingEvent
.m_itemOld 
= htSel
; 
2306                 if ( IsTreeEventAllowed(changingEvent
) ) 
2310                     SetFocusedItem(next
); 
2312                     wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2314                     changedEvent
.m_itemOld 
= htSel
; 
2315                     (void)HandleTreeEvent(changedEvent
); 
2320                 wxTreeItemId next 
= vkey 
== VK_UP
 
2321                     ? TreeView_GetPrevVisible(GetHwnd(), htSel
) 
2322                     : TreeView_GetNextVisible(GetHwnd(), htSel
); 
2329                 if ( !m_htSelStart 
) 
2331                     m_htSelStart 
= htSel
; 
2334                 if ( bShift 
&& SelectRange(GetHwnd(), HITEM(m_htSelStart
), HITEM(next
), 
2335                      SR_UNSELECT_OTHERS 
| SR_SIMULATE
) ) 
2337                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this, next
); 
2338                     changingEvent
.m_itemOld 
= htSel
; 
2340                     if ( IsTreeEventAllowed(changingEvent
) ) 
2342                         SelectRange(GetHwnd(), HITEM(m_htSelStart
), HITEM(next
), 
2343                                     SR_UNSELECT_OTHERS
); 
2345                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, this, next
); 
2346                         changedEvent
.m_itemOld 
= htSel
; 
2347                         (void)HandleTreeEvent(changedEvent
); 
2351                 SetFocusedItem(next
); 
2356             if ( HasChildren(htSel
) && IsExpanded(htSel
) ) 
2362                 wxTreeItemId next 
= GetItemParent(htSel
); 
2364                 if ( next
.IsOk() && !IsHiddenRoot(next
) ) 
2366                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2368                     changingEvent
.m_itemOld 
= htSel
; 
2370                     if ( IsTreeEventAllowed(changingEvent
) ) 
2374                         SetFocusedItem(next
); 
2376                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2378                         changedEvent
.m_itemOld 
= htSel
; 
2379                         (void)HandleTreeEvent(changedEvent
); 
2386             if ( !IsVisible(htSel
) ) 
2388                 EnsureVisible(htSel
); 
2391             if ( !HasChildren(htSel
) ) 
2394             if ( !IsExpanded(htSel
) ) 
2400                 wxTreeItemId next 
= TreeView_GetChild(GetHwnd(), htSel
); 
2402                 wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, this, next
); 
2403                 changingEvent
.m_itemOld 
= htSel
; 
2405                 if ( IsTreeEventAllowed(changingEvent
) ) 
2409                     SetFocusedItem(next
); 
2411                     wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, this, next
); 
2412                     changedEvent
.m_itemOld 
= htSel
; 
2413                     (void)HandleTreeEvent(changedEvent
); 
2421                 wxTreeItemId next 
= GetRootItem(); 
2423                 if ( IsHiddenRoot(next
) ) 
2425                     next 
= TreeView_GetChild(GetHwnd(), HITEM(next
)); 
2431                 if ( vkey 
== VK_END 
) 
2435                         wxTreeItemId nextTemp 
= TreeView_GetNextVisible( 
2436                                                     GetHwnd(), HITEM(next
)); 
2438                         if ( !nextTemp
.IsOk() ) 
2445                 if ( htSel 
== HITEM(next
) ) 
2450                     if ( !m_htSelStart 
) 
2452                         m_htSelStart 
= htSel
; 
2455                     if ( SelectRange(GetHwnd(), 
2456                                      HITEM(m_htSelStart
), HITEM(next
), 
2457                                      SR_UNSELECT_OTHERS 
| SR_SIMULATE
) ) 
2459                         wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2461                         changingEvent
.m_itemOld 
= htSel
; 
2463                         if ( IsTreeEventAllowed(changingEvent
) ) 
2465                             SelectRange(GetHwnd(), 
2466                                         HITEM(m_htSelStart
), HITEM(next
), 
2467                                         SR_UNSELECT_OTHERS
); 
2468                             SetFocusedItem(next
); 
2470                             wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2472                             changedEvent
.m_itemOld 
= htSel
; 
2473                             (void)HandleTreeEvent(changedEvent
); 
2479                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2481                     changingEvent
.m_itemOld 
= htSel
; 
2483                     if ( IsTreeEventAllowed(changingEvent
) ) 
2487                         SetFocusedItem(next
); 
2489                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2491                         changedEvent
.m_itemOld 
= htSel
; 
2492                         (void)HandleTreeEvent(changedEvent
); 
2502                 wxTreeItemId firstVisible 
= GetFirstVisibleItem(); 
2503                 size_t visibleCount 
= TreeView_GetVisibleCount(GetHwnd()); 
2504                 wxTreeItemId nextAdjacent 
= (vkey 
== VK_PRIOR
) ? 
2505                     TreeView_GetPrevVisible(GetHwnd(), HITEM(firstVisible
)) : 
2506                     TreeView_GetNextVisible(GetHwnd(), HITEM(firstVisible
)); 
2508                 if ( !nextAdjacent 
) 
2513                 wxTreeItemId nextStart 
= firstVisible
; 
2515                 for ( size_t n 
= 1; n 
< visibleCount
; n
++ ) 
2517                     wxTreeItemId nextTemp 
= (vkey 
== VK_PRIOR
) ? 
2518                         TreeView_GetPrevVisible(GetHwnd(), HITEM(nextStart
)) : 
2519                         TreeView_GetNextVisible(GetHwnd(), HITEM(nextStart
)); 
2521                     if ( nextTemp
.IsOk() ) 
2523                         nextStart 
= nextTemp
; 
2531                 EnsureVisible(nextStart
); 
2533                 if ( vkey 
== VK_NEXT 
) 
2535                     wxTreeItemId nextEnd 
= nextStart
; 
2537                     for ( size_t n 
= 1; n 
< visibleCount
; n
++ ) 
2539                         wxTreeItemId nextTemp 
= 
2540                             TreeView_GetNextVisible(GetHwnd(), HITEM(nextEnd
)); 
2542                         if ( nextTemp
.IsOk() ) 
2552                     EnsureVisible(nextEnd
); 
2557                 size_t visibleCount 
= TreeView_GetVisibleCount(GetHwnd()); 
2558                 wxTreeItemId nextAdjacent 
= (vkey 
== VK_PRIOR
) ? 
2559                     TreeView_GetPrevVisible(GetHwnd(), htSel
) : 
2560                     TreeView_GetNextVisible(GetHwnd(), htSel
); 
2562                 if ( !nextAdjacent 
) 
2567                 wxTreeItemId 
next(htSel
); 
2569                 for ( size_t n 
= 1; n 
< visibleCount
; n
++ ) 
2571                     wxTreeItemId nextTemp 
= vkey 
== VK_PRIOR 
? 
2572                         TreeView_GetPrevVisible(GetHwnd(), HITEM(next
)) : 
2573                         TreeView_GetNextVisible(GetHwnd(), HITEM(next
)); 
2575                     if ( !nextTemp
.IsOk() ) 
2581                 wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2583                 changingEvent
.m_itemOld 
= htSel
; 
2585                 if ( IsTreeEventAllowed(changingEvent
) ) 
2588                     m_htSelStart
.Unset(); 
2590                     SetFocusedItem(next
); 
2592                     wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2594                     changedEvent
.m_itemOld 
= htSel
; 
2595                     (void)HandleTreeEvent(changedEvent
); 
2607 bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam
, WXLPARAM lParam
) 
2609     wxTreeEvent 
keyEvent(wxEVT_COMMAND_TREE_KEY_DOWN
, this); 
2610     keyEvent
.m_evtKey 
= CreateKeyEvent(wxEVT_KEY_DOWN
, wParam
, lParam
); 
2612     bool processed 
= HandleTreeEvent(keyEvent
); 
2614     // generate a separate event for Space/Return 
2615     if ( !wxIsCtrlDown() && !wxIsShiftDown() && !wxIsAltDown() && 
2616          ((wParam 
== VK_SPACE
) || (wParam 
== VK_RETURN
)) ) 
2618         const HTREEITEM htSel 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
2621             wxTreeEvent 
activatedEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED
, 
2623             (void)HandleTreeEvent(activatedEvent
); 
2630 // we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we 
2631 // only do it during dragging, minimize wxWin overhead (this is important for 
2632 // WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly 
2633 // instead of passing by wxWin events 
2635 wxTreeCtrl::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
2637     bool processed 
= false; 
2639     bool isMultiple 
= HasFlag(wxTR_MULTIPLE
); 
2641     if ( nMsg 
== WM_CONTEXTMENU 
) 
2643         int x 
= GET_X_LPARAM(lParam
), 
2644             y 
= GET_Y_LPARAM(lParam
); 
2646         // the item for which the menu should be shown 
2649         // the position where the menu should be shown in client coordinates 
2650         // (so that it can be passed directly to PopupMenu()) 
2653         if ( x 
== -1 || y 
== -1 ) 
2655             // this means that the event was generated from keyboard (e.g. with 
2656             // Shift-F10 or special Windows menu key) 
2658             // use the Explorer standard of putting the menu at the left edge 
2659             // of the text, in the vertical middle of the text 
2660             item 
= wxTreeItemId(TreeView_GetSelection(GetHwnd())); 
2663                 // Use the bounding rectangle of only the text part 
2665                 GetBoundingRect(item
, rect
, true); 
2666                 pt 
= wxPoint(rect
.GetX(), rect
.GetY() + rect
.GetHeight() / 2); 
2669         else // event from mouse, use mouse position 
2671             pt 
= ScreenToClient(wxPoint(x
, y
)); 
2673             TV_HITTESTINFO tvhti
; 
2677             if ( TreeView_HitTest(GetHwnd(), &tvhti
) ) 
2678                 item 
= wxTreeItemId(tvhti
.hItem
); 
2684             wxTreeEvent 
event(wxEVT_COMMAND_TREE_ITEM_MENU
, this, item
); 
2686             event
.m_pointDrag 
= pt
; 
2688             if ( HandleTreeEvent(event
) ) 
2690             //else: continue with generating wxEVT_CONTEXT_MENU in base class code 
2693     else if ( (nMsg 
>= WM_MOUSEFIRST
) && (nMsg 
<= WM_MOUSELAST
) ) 
2695         // we only process mouse messages here and these parameters have the 
2696         // same meaning for all of them 
2697         int x 
= GET_X_LPARAM(lParam
), 
2698             y 
= GET_Y_LPARAM(lParam
); 
2700         TV_HITTESTINFO tvht
; 
2704         HTREEITEM htOldItem 
= TreeView_GetSelection(GetHwnd()); 
2705         HTREEITEM htItem 
= TreeView_HitTest(GetHwnd(), &tvht
); 
2709             case WM_LBUTTONDOWN
: 
2713                 m_htClickedItem
.Unset(); 
2715                 if ( !MSWIsOnItem(tvht
.flags
) ) 
2717                     if ( tvht
.flags 
& TVHT_ONITEMBUTTON 
) 
2719                         // either it's going to be handled by user code or 
2720                         // we're going to use it ourselves to toggle the 
2721                         // branch, in either case don't pass it to the base 
2722                         // class which would generate another mouse click event 
2723                         // for it even though it's already handled here 
2727                         if ( !HandleMouseEvent(nMsg
, x
, y
, wParam
) ) 
2729                             if ( !IsExpanded(htItem
) ) 
2740                     m_focusLost 
= false; 
2746                 m_htClickedItem 
= (WXHTREEITEM
) htItem
; 
2747                 m_ptClick 
= wxPoint(x
, y
); 
2749                 if ( wParam 
& MK_CONTROL 
) 
2751                     if ( HandleMouseEvent(nMsg
, x
, y
, wParam
) ) 
2753                         m_htClickedItem
.Unset(); 
2757                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2759                     changingEvent
.m_itemOld 
= htOldItem
; 
2761                     if ( IsTreeEventAllowed(changingEvent
) ) 
2763                         // toggle selected state 
2764                         DoToggleItemSelection(wxTreeItemId(htItem
)); 
2766                         SetFocusedItem(wxTreeItemId(htItem
)); 
2768                         // reset on any click without Shift 
2769                         m_htSelStart
.Unset(); 
2771                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2773                         changedEvent
.m_itemOld 
= htOldItem
; 
2774                         (void)HandleTreeEvent(changedEvent
); 
2777                 else if ( wParam 
& MK_SHIFT 
) 
2779                     if ( HandleMouseEvent(nMsg
, x
, y
, wParam
) ) 
2781                         m_htClickedItem
.Unset(); 
2786                     bool willChange 
= true; 
2788                     if ( !(wParam 
& MK_CONTROL
) ) 
2790                         srFlags 
|= SR_UNSELECT_OTHERS
; 
2793                     if ( !m_htSelStart 
) 
2795                         // take the focused item 
2796                         m_htSelStart 
= htOldItem
; 
2800                         willChange 
= SelectRange(GetHwnd(), HITEM(m_htSelStart
), 
2801                                                  htItem
, srFlags 
| SR_SIMULATE
); 
2806                         wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2808                         changingEvent
.m_itemOld 
= htOldItem
; 
2810                         if ( IsTreeEventAllowed(changingEvent
) ) 
2812                             // this selects all items between the starting one 
2816                                 SelectRange(GetHwnd(), HITEM(m_htSelStart
), 
2821                                 DoSelectItem(wxTreeItemId(htItem
)); 
2824                             SetFocusedItem(wxTreeItemId(htItem
)); 
2826                             wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2828                             changedEvent
.m_itemOld 
= htOldItem
; 
2829                             (void)HandleTreeEvent(changedEvent
); 
2833                 else // normal click 
2835                     // avoid doing anything if we click on the only 
2836                     // currently selected item 
2838                     wxArrayTreeItemIds selections
; 
2839                     size_t count 
= GetSelections(selections
); 
2843                          HITEM(selections
[0]) != htItem 
) 
2845                         if ( HandleMouseEvent(nMsg
, x
, y
, wParam
) ) 
2847                             m_htClickedItem
.Unset(); 
2851                         // clear the previously selected items, if the user 
2852                         // clicked outside of the present selection, otherwise, 
2853                         // perform the deselection on mouse-up, this allows 
2854                         // multiple drag and drop to work. 
2855                         if ( !IsItemSelected(GetHwnd(), htItem
)) 
2857                             wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2859                             changingEvent
.m_itemOld 
= htOldItem
; 
2861                             if ( IsTreeEventAllowed(changingEvent
) ) 
2864                                 DoSelectItem(wxTreeItemId(htItem
)); 
2865                                 SetFocusedItem(wxTreeItemId(htItem
)); 
2867                                 wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2869                                 changedEvent
.m_itemOld 
= htOldItem
; 
2870                                 (void)HandleTreeEvent(changedEvent
); 
2875                             SetFocusedItem(wxTreeItemId(htItem
)); 
2876                             m_mouseUpDeselect 
= true; 
2879                     else // click on a single selected item 
2881                         // don't interfere with the default processing in 
2882                         // WM_MOUSEMOVE handler below as the default window 
2883                         // proc will start the drag itself if we let have 
2885                         m_htClickedItem
.Unset(); 
2887                         // prevent in-place editing from starting if focus lost 
2888                         // since previous click 
2892                             DoSelectItem(wxTreeItemId(htItem
)); 
2893                             SetFocusedItem(wxTreeItemId(htItem
)); 
2901                     // reset on any click without Shift 
2902                     m_htSelStart
.Unset(); 
2905                 m_focusLost 
= false; 
2907                 // we consumed the event so we need to trigger state image 
2912                     wxTreeItemId item 
= HitTest(wxPoint(x
, y
), htFlags
); 
2914                     if ( htFlags 
& wxTREE_HITTEST_ONITEMSTATEICON 
) 
2916                         m_triggerStateImageClick 
= true; 
2921             case WM_RBUTTONDOWN
: 
2928                 if ( HandleMouseEvent(nMsg
, x
, y
, wParam
) || !htItem 
) 
2933                 // default handler removes the highlight from the currently 
2934                 // focused item when right mouse button is pressed on another 
2935                 // one but keeps the remaining items highlighted, which is 
2936                 // confusing, so override this default behaviour 
2937                 if ( !IsItemSelected(GetHwnd(), htItem
) ) 
2939                     wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
2941                     changingEvent
.m_itemOld 
= htOldItem
; 
2943                     if ( IsTreeEventAllowed(changingEvent
) ) 
2946                         DoSelectItem(wxTreeItemId(htItem
)); 
2947                         SetFocusedItem(wxTreeItemId(htItem
)); 
2949                         wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
2951                         changedEvent
.m_itemOld 
= htOldItem
; 
2952                         (void)HandleTreeEvent(changedEvent
); 
2960                 if ( m_htClickedItem 
) 
2962                     int cx 
= abs(m_ptClick
.x 
- x
); 
2963                     int cy 
= abs(m_ptClick
.y 
- y
); 
2965                     if ( cx 
> ::GetSystemMetrics(SM_CXDRAG
) || 
2966                             cy 
> ::GetSystemMetrics(SM_CYDRAG
) ) 
2971                         tv
.hdr
.hwndFrom 
= GetHwnd(); 
2972                         tv
.hdr
.idFrom 
= ::GetWindowLong(GetHwnd(), GWL_ID
); 
2973                         tv
.hdr
.code 
= TVN_BEGINDRAG
; 
2975                         tv
.itemNew
.hItem 
= HITEM(m_htClickedItem
); 
2979                         wxZeroMemory(tviAux
); 
2981                         tviAux
.hItem 
= HITEM(m_htClickedItem
); 
2982                         tviAux
.mask 
= TVIF_STATE 
| TVIF_PARAM
; 
2983                         tviAux
.stateMask 
= 0xffffffff; 
2984                         TreeView_GetItem(GetHwnd(), &tviAux
); 
2986                         tv
.itemNew
.state 
= tviAux
.state
; 
2987                         tv
.itemNew
.lParam 
= tviAux
.lParam
; 
2992                         // do it before SendMessage() call below to avoid 
2993                         // reentrancies here if there is another WM_MOUSEMOVE 
2994                         // in the queue already 
2995                         m_htClickedItem
.Unset(); 
2997                         ::SendMessage(GetHwndOf(GetParent()), WM_NOTIFY
, 
2998                                       tv
.hdr
.idFrom
, (LPARAM
)&tv 
); 
3000                         // don't pass it to the default window proc, it would 
3001                         // start dragging again 
3005 #endif // __WXWINCE__ 
3010                     m_dragImage
->Move(wxPoint(x
, y
)); 
3013                         // highlight the item as target (hiding drag image is 
3014                         // necessary - otherwise the display will be corrupted) 
3015                         m_dragImage
->Hide(); 
3016                         TreeView_SelectDropTarget(GetHwnd(), htItem
); 
3017                         m_dragImage
->Show(); 
3020 #endif // wxUSE_DRAGIMAGE 
3026                     // deselect other items if needed 
3029                         if ( m_mouseUpDeselect 
) 
3031                             m_mouseUpDeselect 
= false; 
3033                             wxTreeEvent 
changingEvent(wxEVT_COMMAND_TREE_SEL_CHANGING
, 
3035                             changingEvent
.m_itemOld 
= htOldItem
; 
3037                             if ( IsTreeEventAllowed(changingEvent
) ) 
3040                                 DoSelectItem(wxTreeItemId(htItem
)); 
3041                                 SetFocusedItem(wxTreeItemId(htItem
)); 
3043                                 wxTreeEvent 
changedEvent(wxEVT_COMMAND_TREE_SEL_CHANGED
, 
3045                                 changedEvent
.m_itemOld 
= htOldItem
; 
3046                                 (void)HandleTreeEvent(changedEvent
); 
3051                     m_htClickedItem
.Unset(); 
3053                     if ( m_triggerStateImageClick 
) 
3055                         if ( tvht
.flags 
& TVHT_ONITEMSTATEICON 
) 
3057                             wxTreeEvent 
event(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
, 
3059                             (void)HandleTreeEvent(event
); 
3061                             m_triggerStateImageClick 
= false; 
3066                     if ( !m_dragStarted 
&& MSWIsOnItem(tvht
.flags
) ) 
3078                     m_dragImage
->EndDrag(); 
3079                     wxDELETE(m_dragImage
); 
3081                     // generate the drag end event 
3082                     wxTreeEvent 
event(wxEVT_COMMAND_TREE_END_DRAG
, 
3084                     event
.m_pointDrag 
= wxPoint(x
, y
); 
3085                     (void)HandleTreeEvent(event
); 
3087                     // if we don't do it, the tree seems to think that 2 items 
3088                     // are selected simultaneously which is quite weird 
3089                     TreeView_SelectDropTarget(GetHwnd(), 0); 
3091 #endif // wxUSE_DRAGIMAGE 
3093                 if ( isMultiple 
&& nMsg 
== WM_RBUTTONUP 
) 
3097                     nmhdr
.hwndFrom 
= GetHwnd(); 
3098                     nmhdr
.idFrom 
= ::GetWindowLong(GetHwnd(), GWL_ID
); 
3099                     nmhdr
.code 
= NM_RCLICK
; 
3100                     ::SendMessage(::GetParent(GetHwnd()), WM_NOTIFY
, 
3101                                   nmhdr
.idFrom
, (LPARAM
)&nmhdr
); 
3105                 m_dragStarted 
= false; 
3110     else if ( (nMsg 
== WM_SETFOCUS 
|| nMsg 
== WM_KILLFOCUS
) ) 
3114             // the tree control greys out the selected item when it loses focus 
3115             // and paints it as selected again when it regains it, but it won't 
3116             // do it for the other items itself - help it 
3117             wxArrayTreeItemIds selections
; 
3118             size_t count 
= GetSelections(selections
); 
3119             TVGetItemRectParam param
; 
3121             for ( size_t n 
= 0; n 
< count
; n
++ ) 
3123                 // TreeView_GetItemRect() will return false if item is not 
3124                 // visible, which may happen perfectly well 
3125                 if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(selections
[n
]), 
3128                     ::InvalidateRect(GetHwnd(), ¶m
.rect
, FALSE
); 
3133         if ( nMsg 
== WM_KILLFOCUS 
) 
3138     else if ( (nMsg 
== WM_KEYDOWN 
|| nMsg 
== WM_SYSKEYDOWN
) && isMultiple 
) 
3140         // normally we want to generate wxEVT_KEY_DOWN events from TVN_KEYDOWN 
3141         // notification but for the keys which can be used to change selection 
3142         // we need to do it from here so as to not apply the default behaviour 
3143         // if the events are handled by the user code 
3156                 if ( !HandleKeyDown(wParam
, lParam
) && 
3157                         !MSWHandleTreeKeyDownEvent(wParam
, lParam
) ) 
3159                     // use the key to update the selection if it was left 
3161                     MSWHandleSelectionKey(wParam
); 
3164                 // pretend that we did process it in any case as we already 
3165                 // generated an event for it 
3168             //default: for all the other keys leave processed as false so that 
3169             //         the tree control generates a TVN_KEYDOWN for us 
3173     else if ( nMsg 
== WM_COMMAND 
) 
3175         // if we receive a EN_KILLFOCUS command from the in-place edit control 
3176         // used for label editing, make sure to end editing 
3179         UnpackCommand(wParam
, lParam
, &id
, &hwnd
, &cmd
); 
3181         if ( cmd 
== EN_KILLFOCUS 
) 
3183             if ( m_textCtrl 
&& m_textCtrl
->GetHandle() == hwnd 
) 
3193         rc 
= wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
3199 wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
3201     if ( nMsg 
== WM_CHAR 
) 
3203         // don't let the control process Space and Return keys because it 
3204         // doesn't do anything useful with them anyhow but always beeps 
3205         // annoyingly when it receives them and there is no way to turn it off 
3206         // simply if you just process TREEITEM_ACTIVATED event to which Space 
3207         // and Enter presses are mapped in your code 
3208         if ( wParam 
== VK_SPACE 
|| wParam 
== VK_RETURN 
) 
3212     else if ( nMsg 
== WM_KEYDOWN 
) 
3214         if ( wParam 
== VK_ESCAPE 
) 
3218                 m_dragImage
->EndDrag(); 
3219                 wxDELETE(m_dragImage
); 
3221                 // if we don't do it, the tree seems to think that 2 items 
3222                 // are selected simultaneously which is quite weird 
3223                 TreeView_SelectDropTarget(GetHwnd(), 0); 
3227 #endif // wxUSE_DRAGIMAGE 
3229     return wxControl::MSWDefWindowProc(nMsg
, wParam
, lParam
); 
3232 // process WM_NOTIFY Windows message 
3233 bool wxTreeCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
3235     wxTreeEvent 
event(wxEVT_NULL
, this); 
3236     wxEventType eventType 
= wxEVT_NULL
; 
3237     NMHDR 
*hdr 
= (NMHDR 
*)lParam
; 
3239     switch ( hdr
->code 
) 
3242             eventType 
= wxEVT_COMMAND_TREE_BEGIN_DRAG
; 
3245         case TVN_BEGINRDRAG
: 
3247                 if ( eventType 
== wxEVT_NULL 
) 
3248                     eventType 
= wxEVT_COMMAND_TREE_BEGIN_RDRAG
; 
3249                 //else: left drag, already set above 
3251                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
3253                 event
.m_item 
= tv
->itemNew
.hItem
; 
3254                 event
.m_pointDrag 
= wxPoint(tv
->ptDrag
.x
, tv
->ptDrag
.y
); 
3256                 // don't allow dragging by default: the user code must 
3257                 // explicitly say that it wants to allow it to avoid breaking 
3263         case TVN_BEGINLABELEDIT
: 
3265                 eventType 
= wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
; 
3266                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
3268                 // although the user event handler may still veto it, it is 
3269                 // important to set it now so that calls to SetItemText() from 
3270                 // the event handler would change the text controls contents 
3272                 event
.m_item 
= info
->item
.hItem
; 
3273                 event
.m_label 
= info
->item
.pszText
; 
3274                 event
.m_editCancelled 
= false; 
3278         case TVN_DELETEITEM
: 
3280                 eventType 
= wxEVT_COMMAND_TREE_DELETE_ITEM
; 
3281                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
3283                 event
.m_item 
= tv
->itemOld
.hItem
; 
3287                     wxMapTreeAttr::iterator it 
= m_attrs
.find(tv
->itemOld
.hItem
); 
3288                     if ( it 
!= m_attrs
.end() ) 
3297         case TVN_ENDLABELEDIT
: 
3299                 eventType 
= wxEVT_COMMAND_TREE_END_LABEL_EDIT
; 
3300                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
3302                 event
.m_item 
= info
->item
.hItem
; 
3303                 event
.m_label 
= info
->item
.pszText
; 
3304                 event
.m_editCancelled 
= info
->item
.pszText 
== NULL
; 
3309         // These *must* not be removed or TVN_GETINFOTIP will 
3310         // not be processed each time the mouse is moved 
3311         // and the tooltip will only ever update once. 
3320 #ifdef TVN_GETINFOTIP 
3321         case TVN_GETINFOTIP
: 
3323                 eventType 
= wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
; 
3324                 NMTVGETINFOTIP 
*info 
= (NMTVGETINFOTIP
*)lParam
; 
3326                 // Which item are we trying to get a tooltip for? 
3327                 event
.m_item 
= info
->hItem
; 
3331 #endif // TVN_GETINFOTIP 
3332 #endif // !__WXWINCE__ 
3334         case TVN_GETDISPINFO
: 
3335             eventType 
= wxEVT_COMMAND_TREE_GET_INFO
; 
3338         case TVN_SETDISPINFO
: 
3340                 if ( eventType 
== wxEVT_NULL 
) 
3341                     eventType 
= wxEVT_COMMAND_TREE_SET_INFO
; 
3342                 //else: get, already set above 
3344                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
3346                 event
.m_item 
= info
->item
.hItem
; 
3350         case TVN_ITEMEXPANDING
: 
3351         case TVN_ITEMEXPANDED
: 
3353                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW
*)lParam
; 
3356                 switch ( tv
->action 
) 
3359                         wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv
->action
); 
3367                         what 
= IDX_COLLAPSE
; 
3371                 int how 
= hdr
->code 
== TVN_ITEMEXPANDING 
? IDX_DOING
 
3374                 eventType 
= gs_expandEvents
[what
][how
]; 
3376                 event
.m_item 
= tv
->itemNew
.hItem
; 
3382                 TV_KEYDOWN 
*info 
= (TV_KEYDOWN 
*)lParam
; 
3384                 // fabricate the lParam and wParam parameters sufficiently 
3385                 // similar to the ones from a "real" WM_KEYDOWN so that 
3386                 // CreateKeyEvent() works correctly 
3387                 return MSWHandleTreeKeyDownEvent( 
3388                         info
->wVKey
, (wxIsAltDown() ? KF_ALTDOWN 
: 0) << 16); 
3392         // Vista's tree control has introduced some problems with our 
3393         // multi-selection tree.  When TreeView_SelectItem() is called, 
3394         // the wrong items are deselected. 
3396         // Fortunately, Vista provides a new notification, TVN_ITEMCHANGING 
3397         // that can be used to regulate this incorrect behaviour.  The 
3398         // following messages will allow only the unlocked item's selection 
3401         case TVN_ITEMCHANGINGA
: 
3402         case TVN_ITEMCHANGINGW
: 
3404                 // we only need to handles these in multi-select trees 
3405                 if ( HasFlag(wxTR_MULTIPLE
) ) 
3407                     // get info about the item about to be changed 
3408                     NMTVITEMCHANGE
* info 
= (NMTVITEMCHANGE
*)lParam
; 
3409                     if (TreeItemUnlocker::IsLocked(info
->hItem
)) 
3411                         // item's state is locked, don't allow the change 
3412                         // returning 1 will disallow the change 
3418                 // allow the state change 
3422         // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of 
3423         //     TVN_SELCHANGEDW in Unicode mode under Win98. Therefore 
3424         //     we have to handle both messages: 
3425         case TVN_SELCHANGEDA
: 
3426         case TVN_SELCHANGEDW
: 
3427             if ( !m_changingSelection 
) 
3429                 eventType 
= wxEVT_COMMAND_TREE_SEL_CHANGED
; 
3433         case TVN_SELCHANGINGA
: 
3434         case TVN_SELCHANGINGW
: 
3435             if ( !m_changingSelection 
) 
3437                 if ( eventType 
== wxEVT_NULL 
) 
3438                     eventType 
= wxEVT_COMMAND_TREE_SEL_CHANGING
; 
3439                 //else: already set above 
3441                 if (hdr
->code 
== TVN_SELCHANGINGW 
|| 
3442                     hdr
->code 
== TVN_SELCHANGEDW
) 
3444                     NM_TREEVIEWW 
*tv 
= (NM_TREEVIEWW 
*)lParam
; 
3445                     event
.m_item 
= tv
->itemNew
.hItem
; 
3446                     event
.m_itemOld 
= tv
->itemOld
.hItem
; 
3450                     NM_TREEVIEWA 
*tv 
= (NM_TREEVIEWA 
*)lParam
; 
3451                     event
.m_item 
= tv
->itemNew
.hItem
; 
3452                     event
.m_itemOld 
= tv
->itemOld
.hItem
; 
3456             // we receive this message from WM_LBUTTONDOWN handler inside 
3457             // comctl32.dll and so before the click is passed to 
3458             // DefWindowProc() which sets the focus to the window which was 
3459             // clicked and this can lead to unexpected event sequences: for 
3460             // example, we may get a "selection change" event from the tree 
3461             // before getting a "kill focus" event for the text control which 
3462             // had the focus previously, thus breaking user code doing input 
3465             // to avoid such surprises, we force the generation of focus events 
3466             // now, before we generate the selection change ones 
3467             if ( !m_changingSelection 
) 
3471         // instead of explicitly checking for _WIN32_IE, check if the 
3472         // required symbols are available in the headers 
3473 #if defined(CDDS_PREPAINT) 
3476                 LPNMTVCUSTOMDRAW lptvcd 
= (LPNMTVCUSTOMDRAW
)lParam
; 
3477                 NMCUSTOMDRAW
& nmcd 
= lptvcd
->nmcd
; 
3478                 switch ( nmcd
.dwDrawStage 
) 
3481                         // if we've got any items with non standard attributes, 
3482                         // notify us before painting each item 
3483                         *result 
= m_hasAnyAttr 
? CDRF_NOTIFYITEMDRAW
 
3486                         // windows in TreeCtrl use one-based index for item state images, 
3487                         // 0 indexed image is not being used, we're using zero-based index, 
3488                         // so we have to add temp image (of zero index) to state image list 
3489                         // before we draw any item, then after items are drawn we have to 
3490                         // delete it (in POSTPAINT notify) 
3491                         if (m_imageListState 
&& m_imageListState
->GetImageCount() > 0) 
3493                             typedef BOOL (wxSTDCALL 
*ImageList_Copy_t
) 
3494                                 (HIMAGELIST
, int, HIMAGELIST
, int, UINT
); 
3495                             static ImageList_Copy_t s_pfnImageList_Copy 
= NULL
; 
3496                             static bool loaded 
= false; 
3500                                 wxLoadedDLL 
dllComCtl32(wxT("comctl32.dll")); 
3501                                 if ( dllComCtl32
.IsLoaded() ) 
3502                                     wxDL_INIT_FUNC(s_pfn
, ImageList_Copy
, dllComCtl32
); 
3505                             if ( !s_pfnImageList_Copy 
) 
3507                                 // this code is broken with ImageList_Copy() 
3508                                 // but I don't care enough about Win95 support 
3509                                 // to write it now -- if anybody does, please 
3511                                 wxFAIL_MSG("TODO: implement this for Win95"); 
3516                                 hImageList 
= GetHimagelistOf(m_imageListState
); 
3518                             // add temporary image 
3520                             m_imageListState
->GetSize(0, width
, height
); 
3522                             HBITMAP hbmpTemp 
= ::CreateBitmap(width
, height
, 1, 1, NULL
); 
3523                             int index 
= ::ImageList_Add(hImageList
, hbmpTemp
, hbmpTemp
); 
3524                             ::DeleteObject(hbmpTemp
); 
3528                                 // move images to right 
3529                                 for ( int i 
= index
; i 
> 0; i
-- ) 
3531                                     (*s_pfnImageList_Copy
)(hImageList
, i
, 
3536                                 // we must remove the image in POSTPAINT notify 
3537                                 *result 
|= CDRF_NOTIFYPOSTPAINT
; 
3542                     case CDDS_POSTPAINT
: 
3543                         // we are deleting temp image of 0 index, which was 
3544                         // added before items were drawn (in PREPAINT notify) 
3545                         if (m_imageListState 
&& m_imageListState
->GetImageCount() > 0) 
3546                             m_imageListState
->Remove(0); 
3549                     case CDDS_ITEMPREPAINT
: 
3551                             wxMapTreeAttr::iterator
 
3552                                 it 
= m_attrs
.find((void *)nmcd
.dwItemSpec
); 
3554                             if ( it 
== m_attrs
.end() ) 
3556                                 // nothing to do for this item 
3557                                 *result 
= CDRF_DODEFAULT
; 
3561                             wxTreeItemAttr 
* const attr 
= it
->second
; 
3563                             wxTreeViewItem 
tvItem((void *)nmcd
.dwItemSpec
, 
3564                                                   TVIF_STATE
, TVIS_DROPHILITED
); 
3566                             const UINT tvItemState 
= tvItem
.state
; 
3568                             // selection colours should override ours, 
3569                             // otherwise it is too confusing to the user 
3570                             if ( !(nmcd
.uItemState 
& CDIS_SELECTED
) && 
3571                                  !(tvItemState 
& TVIS_DROPHILITED
) ) 
3574                                 if ( attr
->HasBackgroundColour() ) 
3576                                     colBack 
= attr
->GetBackgroundColour(); 
3577                                     lptvcd
->clrTextBk 
= wxColourToRGB(colBack
); 
3581                             // but we still want to keep the special foreground 
3582                             // colour when we don't have focus (we can't keep 
3583                             // it when we do, it would usually be unreadable on 
3584                             // the almost inverted bg colour...) 
3585                             if ( ( !(nmcd
.uItemState 
& CDIS_SELECTED
) || 
3586                                     FindFocus() != this ) && 
3587                                  !(tvItemState 
& TVIS_DROPHILITED
) ) 
3590                                 if ( attr
->HasTextColour() ) 
3592                                     colText 
= attr
->GetTextColour(); 
3593                                     lptvcd
->clrText 
= wxColourToRGB(colText
); 
3597                             if ( attr
->HasFont() ) 
3599                                 HFONT hFont 
= GetHfontOf(attr
->GetFont()); 
3601                                 ::SelectObject(nmcd
.hdc
, hFont
); 
3603                                 *result 
= CDRF_NEWFONT
; 
3605                             else // no specific font 
3607                                 *result 
= CDRF_DODEFAULT
; 
3613                         *result 
= CDRF_DODEFAULT
; 
3617             // we always process it 
3619 #endif // have owner drawn support in headers 
3623                 DWORD pos 
= GetMessagePos(); 
3625                 point
.x 
= LOWORD(pos
); 
3626                 point
.y 
= HIWORD(pos
); 
3627                 ::MapWindowPoints(HWND_DESKTOP
, GetHwnd(), &point
, 1); 
3629                 wxTreeItemId item 
= HitTest(wxPoint(point
.x
, point
.y
), htFlags
); 
3631                 if ( htFlags 
& wxTREE_HITTEST_ONITEMSTATEICON 
) 
3633                     event
.m_item 
= item
; 
3634                     eventType 
= wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
; 
3643                 TV_HITTESTINFO tvhti
; 
3644                 ::GetCursorPos(&tvhti
.pt
); 
3645                 ::ScreenToClient(GetHwnd(), &tvhti
.pt
); 
3646                 if ( TreeView_HitTest(GetHwnd(), &tvhti
) ) 
3648                     if ( MSWIsOnItem(tvhti
.flags
) ) 
3650                         event
.m_item 
= tvhti
.hItem
; 
3651                         eventType 
= (int)hdr
->code 
== NM_DBLCLK
 
3652                                     ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED
 
3653                                     : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
; 
3655                         event
.m_pointDrag
.x 
= tvhti
.pt
.x
; 
3656                         event
.m_pointDrag
.y 
= tvhti
.pt
.y
; 
3665             return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
3668     event
.SetEventType(eventType
); 
3670     bool processed 
= HandleTreeEvent(event
); 
3673     switch ( hdr
->code 
) 
3676             // we translate NM_DBLCLK into ACTIVATED event and if the user 
3677             // handled the activation of the item we shouldn't proceed with 
3678             // also using the same double click for toggling the item expanded 
3679             // state -- but OTOH do let the user to expand/collapse the item by 
3680             // double clicking on it if the activation is not handled specially 
3681             *result 
= processed
; 
3685             // prevent tree control from sending WM_CONTEXTMENU to our parent 
3686             // (which it does if NM_RCLICK is not handled) because we want to 
3687             // send it to the control itself 
3691             ::SendMessage(GetHwnd(), WM_CONTEXTMENU
, 
3692                           (WPARAM
)GetHwnd(), ::GetMessagePos()); 
3696         case TVN_BEGINRDRAG
: 
3698             if ( event
.IsAllowed() ) 
3700                 // normally this is impossible because the m_dragImage is 
3701                 // deleted once the drag operation is over 
3702                 wxASSERT_MSG( !m_dragImage
, wxT("starting to drag once again?") ); 
3704                 m_dragImage 
= new wxDragImage(*this, event
.m_item
); 
3705                 m_dragImage
->BeginDrag(wxPoint(0,0), this); 
3706                 m_dragImage
->Show(); 
3708                 m_dragStarted 
= true; 
3710 #endif // wxUSE_DRAGIMAGE 
3713         case TVN_DELETEITEM
: 
3715                 // NB: we might process this message using wxWidgets event 
3716                 //     tables, but due to overhead of wxWin event system we 
3717                 //     prefer to do it here ourself (otherwise deleting a tree 
3718                 //     with many items is just too slow) 
3719                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
3721                 wxTreeItemParam 
*param 
= 
3722                         (wxTreeItemParam 
*)tv
->itemOld
.lParam
; 
3725                 processed 
= true; // Make sure we don't get called twice 
3729         case TVN_BEGINLABELEDIT
: 
3730             // return true to cancel label editing 
3731             *result 
= !event
.IsAllowed(); 
3733             // set ES_WANTRETURN ( like we do in BeginLabelEdit ) 
3734             if ( event
.IsAllowed() ) 
3736                 HWND hText 
= TreeView_GetEditControl(GetHwnd()); 
3739                     // MBN: if m_textCtrl already has an HWND, it is a stale 
3740                     // pointer from a previous edit (because the user 
3741                     // didn't modify the label before dismissing the control, 
3742                     // and TVN_ENDLABELEDIT was not sent), so delete it 
3743                     if ( m_textCtrl 
&& m_textCtrl
->GetHWND() ) 
3746                         m_textCtrl 
= new wxTextCtrl(); 
3747                     m_textCtrl
->SetParent(this); 
3748                     m_textCtrl
->SetHWND((WXHWND
)hText
); 
3749                     m_textCtrl
->SubclassWin((WXHWND
)hText
); 
3751                     // set wxTE_PROCESS_ENTER style for the text control to 
3752                     // force it to process the Enter presses itself, otherwise 
3753                     // they could be stolen from it by the dialog 
3755                     m_textCtrl
->SetWindowStyle(m_textCtrl
->GetWindowStyle() 
3756                                                | wxTE_PROCESS_ENTER
); 
3759             else // we had set m_idEdited before 
3765         case TVN_ENDLABELEDIT
: 
3766             // return true to set the label to the new string: note that we 
3767             // also must pretend that we did process the message or it is going 
3768             // to be passed to DefWindowProc() which will happily return false 
3769             // cancelling the label change 
3770             *result 
= event
.IsAllowed(); 
3773             // ensure that we don't have the text ctrl which is going to be 
3779 #ifdef TVN_GETINFOTIP 
3780          case TVN_GETINFOTIP
: 
3782                 // If the user permitted a tooltip change, change it 
3783                 if (event
.IsAllowed()) 
3785                     SetToolTip(event
.m_label
); 
3792         case TVN_SELCHANGING
: 
3793         case TVN_ITEMEXPANDING
: 
3794             // return true to prevent the action from happening 
3795             *result 
= !event
.IsAllowed(); 
3798         case TVN_ITEMEXPANDED
: 
3800                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
3801                 const wxTreeItemId 
id(tv
->itemNew
.hItem
); 
3803                 if ( tv
->action 
== TVE_COLLAPSE 
) 
3805                     if ( wxApp::GetComCtl32Version() >= 600 ) 
3807                         // for some reason the item selection rectangle depends 
3808                         // on whether it is expanded or collapsed (at least 
3809                         // with comctl32.dll v6): it is wider (by 3 pixels) in 
3810                         // the expanded state, so when the item collapses and 
3811                         // then is deselected the rightmost 3 pixels of the 
3812                         // previously drawn selection are left on the screen 
3814                         // it's not clear if it's a bug in comctl32.dll or in 
3815                         // our code (because it does not happen in Explorer but 
3816                         // OTOH we don't do anything which could result in this 
3817                         // AFAICS) but we do need to work around it to avoid 
3824                     // the item is also not refreshed properly after expansion when 
3825                     // it has an image depending on the expanded/collapsed state: 
3826                     // again, it's not clear if the bug is in comctl32.dll or our 
3828                     int image 
= GetItemImage(id
, wxTreeItemIcon_Expanded
); 
3837         case TVN_GETDISPINFO
: 
3838             // NB: so far the user can't set the image himself anyhow, so do it 
3839             //     anyway - but this may change later 
3840             //if ( /* !processed && */ ) 
3842                 wxTreeItemId item 
= event
.m_item
; 
3843                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
3845                 const wxTreeItemParam 
* const param 
= GetItemParam(item
); 
3849                 if ( info
->item
.mask 
& TVIF_IMAGE 
) 
3854                          IsExpanded(item
) ? wxTreeItemIcon_Expanded
 
3855                                           : wxTreeItemIcon_Normal
 
3858                 if ( info
->item
.mask 
& TVIF_SELECTEDIMAGE 
) 
3860                     info
->item
.iSelectedImage 
= 
3863                          IsExpanded(item
) ? wxTreeItemIcon_SelectedExpanded
 
3864                                           : wxTreeItemIcon_Selected
 
3871             // for the other messages the return value is ignored and there is 
3872             // nothing special to do 
3877 // ---------------------------------------------------------------------------- 
3879 // ---------------------------------------------------------------------------- 
3881 // why do they define INDEXTOSTATEIMAGEMASK but not the inverse? 
3882 #define STATEIMAGEMASKTOINDEX(state) (((state) & TVIS_STATEIMAGEMASK) >> 12) 
3884 int wxTreeCtrl::DoGetItemState(const wxTreeItemId
& item
) const 
3886     wxCHECK_MSG( item
.IsOk(), wxTREE_ITEMSTATE_NONE
, wxT("invalid tree item") ); 
3888     // receive the desired information 
3889     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
); 
3892     // state images are one-based 
3893     return STATEIMAGEMASKTOINDEX(tvItem
.state
) - 1; 
3896 void wxTreeCtrl::DoSetItemState(const wxTreeItemId
& item
, int state
) 
3898     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
3900     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
); 
3902     // state images are one-based 
3903     // 0 if no state image display (wxTREE_ITEMSTATE_NONE = -1) 
3904     tvItem
.state 
= INDEXTOSTATEIMAGEMASK(state 
+ 1); 
3909 #endif // wxUSE_TREECTRL