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/msw/private.h" 
  31 // include <commctrl.h> "properly" 
  32 #include "wx/msw/wrapcctl.h" 
  34 #include "wx/msw/missing.h" 
  36 // Set this to 1 to be _absolutely_ sure that repainting will work for all 
  37 // comctl32.dll versions 
  38 #define wxUSE_COMCTL32_SAFELY 0 
  42 #include "wx/dynarray.h" 
  43 #include "wx/imaglist.h" 
  44 #include "wx/settings.h" 
  45 #include "wx/treectrl.h" 
  46 #include "wx/msw/dragimag.h" 
  48 // macros to hide the cast ugliness 
  49 // -------------------------------- 
  51 // get HTREEITEM from wxTreeItemId 
  52 #define HITEM(item)     ((HTREEITEM)(((item).m_pItem))) 
  54 // the native control doesn't support multiple selections under MSW and we 
  55 // have 2 ways to emulate them: either using TVS_CHECKBOXES style and let 
  56 // checkboxes be the selection status (checked == selected) or by really 
  57 // emulating everything, i.e. intercepting mouse and key events &c. The first 
  58 // approach is much easier but doesn't work with comctl32.dll < 4.71 and also 
  60 #define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  66 // wrapper for TreeView_HitTest 
  67 static HTREEITEM 
GetItemFromPoint(HWND hwndTV
, int x
, int y
) 
  73     return (HTREEITEM
)TreeView_HitTest(hwndTV
, &tvht
); 
  76 #if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
  78 // wrappers for TreeView_GetItem/TreeView_SetItem 
  79 static bool IsItemSelected(HWND hwndTV
, HTREEITEM hItem
) 
  83     tvi
.mask 
= TVIF_STATE 
| TVIF_HANDLE
; 
  84     tvi
.stateMask 
= TVIS_SELECTED
; 
  87     if ( !TreeView_GetItem(hwndTV
, &tvi
) ) 
  89         wxLogLastError(wxT("TreeView_GetItem")); 
  92     return (tvi
.state 
& TVIS_SELECTED
) != 0; 
  95 static bool SelectItem(HWND hwndTV
, HTREEITEM hItem
, bool select 
= true) 
  98     tvi
.mask 
= TVIF_STATE 
| TVIF_HANDLE
; 
  99     tvi
.stateMask 
= TVIS_SELECTED
; 
 100     tvi
.state 
= select 
? TVIS_SELECTED 
: 0; 
 103     if ( TreeView_SetItem(hwndTV
, &tvi
) == -1 ) 
 105         wxLogLastError(wxT("TreeView_SetItem")); 
 112 static inline void UnselectItem(HWND hwndTV
, HTREEITEM htItem
) 
 114     SelectItem(hwndTV
, htItem
, false); 
 117 static inline void ToggleItemSelection(HWND hwndTV
, HTREEITEM htItem
) 
 119     SelectItem(hwndTV
, htItem
, !IsItemSelected(hwndTV
, htItem
)); 
 122 // helper function which selects all items in a range and, optionally, 
 123 // unselects all others 
 124 static void SelectRange(HWND hwndTV
, 
 127                         bool unselectOthers 
= true) 
 129     // find the first (or last) item and select it 
 131     HTREEITEM htItem 
= (HTREEITEM
)TreeView_GetRoot(hwndTV
); 
 132     while ( htItem 
&& cont 
) 
 134         if ( (htItem 
== htFirst
) || (htItem 
== htLast
) ) 
 136             if ( !IsItemSelected(hwndTV
, htItem
) ) 
 138                 SelectItem(hwndTV
, htItem
); 
 145             if ( unselectOthers 
&& IsItemSelected(hwndTV
, htItem
) ) 
 147                 UnselectItem(hwndTV
, htItem
); 
 151         htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 154     // select the items in range 
 155     cont 
= htFirst 
!= htLast
; 
 156     while ( htItem 
&& cont 
) 
 158         if ( !IsItemSelected(hwndTV
, htItem
) ) 
 160             SelectItem(hwndTV
, htItem
); 
 163         cont 
= (htItem 
!= htFirst
) && (htItem 
!= htLast
); 
 165         htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 169     if ( unselectOthers 
) 
 173             if ( IsItemSelected(hwndTV
, htItem
) ) 
 175                 UnselectItem(hwndTV
, htItem
); 
 178             htItem 
= (HTREEITEM
)TreeView_GetNextVisible(hwndTV
, htItem
); 
 182     // seems to be necessary - otherwise the just selected items don't always 
 183     // appear as selected 
 184     UpdateWindow(hwndTV
); 
 187 // helper function which tricks the standard control into changing the focused 
 188 // item without changing anything else (if someone knows why Microsoft doesn't 
 189 // allow to do it by just setting TVIS_FOCUSED flag, please tell me!) 
 190 static void SetFocus(HWND hwndTV
, HTREEITEM htItem
) 
 193     HTREEITEM htFocus 
= (HTREEITEM
)TreeView_GetSelection(hwndTV
); 
 198         if ( htItem 
!= htFocus 
) 
 200             // remember the selection state of the item 
 201             bool wasSelected 
= IsItemSelected(hwndTV
, htItem
); 
 203             if ( htFocus 
&& IsItemSelected(hwndTV
, htFocus
) ) 
 205                 // prevent the tree from unselecting the old focus which it 
 206                 // would do by default (TreeView_SelectItem unselects the 
 208                 TreeView_SelectItem(hwndTV
, 0); 
 209                 SelectItem(hwndTV
, htFocus
); 
 212             TreeView_SelectItem(hwndTV
, htItem
); 
 216                 // need to clear the selection which TreeView_SelectItem() gave 
 218                 UnselectItem(hwndTV
, htItem
); 
 220             //else: was selected, still selected - ok 
 222         //else: nothing to do, focus already there 
 228             bool wasFocusSelected 
= IsItemSelected(hwndTV
, htFocus
); 
 230             // just clear the focus 
 231             TreeView_SelectItem(hwndTV
, 0); 
 233             if ( wasFocusSelected 
) 
 235                 // restore the selection state 
 236                 SelectItem(hwndTV
, htFocus
); 
 239         //else: nothing to do, no focus already 
 243 #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
 245 // ---------------------------------------------------------------------------- 
 247 // ---------------------------------------------------------------------------- 
 249 // a convenient wrapper around TV_ITEM struct which adds a ctor 
 251 #pragma warning( disable : 4097 ) // inheriting from typedef 
 254 struct wxTreeViewItem 
: public TV_ITEM
 
 256     wxTreeViewItem(const wxTreeItemId
& item
,    // the item handle 
 257                    UINT mask_
,                  // fields which are valid 
 258                    UINT stateMask_ 
= 0)         // for TVIF_STATE only 
 262         // hItem member is always valid 
 263         mask 
= mask_ 
| TVIF_HANDLE
; 
 264         stateMask 
= stateMask_
; 
 269 // wxVirutalNode is used in place of a single root when 'hidden' root is 
 271 class wxVirtualNode 
: public wxTreeViewItem
 
 274     wxVirtualNode(wxTreeItemData 
*data
) 
 275         : wxTreeViewItem(TVI_ROOT
, 0) 
 285     wxTreeItemData 
*GetData() const { return m_data
; } 
 286     void SetData(wxTreeItemData 
*data
) { delete m_data
; m_data 
= data
; } 
 289     wxTreeItemData 
*m_data
; 
 291     DECLARE_NO_COPY_CLASS(wxVirtualNode
) 
 295 #pragma warning( default : 4097 ) 
 298 // a macro to get the virtual root, returns NULL if none 
 299 #define GET_VIRTUAL_ROOT() ((wxVirtualNode *)m_pVirtualRoot) 
 301 // returns true if the item is the virtual root 
 302 #define IS_VIRTUAL_ROOT(item) (HITEM(item) == TVI_ROOT) 
 304 // a class which encapsulates the tree traversal logic: it vists all (unless 
 305 // OnVisit() returns false) items under the given one 
 306 class wxTreeTraversal
 
 309     wxTreeTraversal(const wxTreeCtrl 
*tree
) 
 314     // do traverse the tree: visit all items (recursively by default) under the 
 315     // given one; return true if all items were traversed or false if the 
 316     // traversal was aborted because OnVisit returned false 
 317     bool DoTraverse(const wxTreeItemId
& root
, bool recursively 
= true); 
 319     // override this function to do whatever is needed for each item, return 
 320     // false to stop traversing 
 321     virtual bool OnVisit(const wxTreeItemId
& item
) = 0; 
 324     const wxTreeCtrl 
*GetTree() const { return m_tree
; } 
 327     bool Traverse(const wxTreeItemId
& root
, bool recursively
); 
 329     const wxTreeCtrl 
*m_tree
; 
 331     DECLARE_NO_COPY_CLASS(wxTreeTraversal
) 
 334 // internal class for getting the selected items 
 335 class TraverseSelections 
: public wxTreeTraversal
 
 338     TraverseSelections(const wxTreeCtrl 
*tree
, 
 339                        wxArrayTreeItemIds
& selections
) 
 340         : wxTreeTraversal(tree
), m_selections(selections
) 
 342             m_selections
.Empty(); 
 344             if (tree
->GetCount() > 0) 
 345                 DoTraverse(tree
->GetRootItem()); 
 348     virtual bool OnVisit(const wxTreeItemId
& item
) 
 350         // can't visit a virtual node. 
 351         if ( (GetTree()->GetRootItem() == item
) && (GetTree()->GetWindowStyle() & wxTR_HIDE_ROOT
)) 
 356 #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
 357         if ( GetTree()->IsItemChecked(item
) ) 
 359         if ( ::IsItemSelected(GetHwndOf(GetTree()), HITEM(item
)) ) 
 362             m_selections
.Add(item
); 
 368     size_t GetCount() const { return m_selections
.GetCount(); } 
 371     wxArrayTreeItemIds
& m_selections
; 
 373     DECLARE_NO_COPY_CLASS(TraverseSelections
) 
 376 // internal class for counting tree items 
 377 class TraverseCounter 
: public wxTreeTraversal
 
 380     TraverseCounter(const wxTreeCtrl 
*tree
, 
 381                     const wxTreeItemId
& root
, 
 383         : wxTreeTraversal(tree
) 
 387             DoTraverse(root
, recursively
); 
 390     virtual bool OnVisit(const wxTreeItemId
& WXUNUSED(item
)) 
 397     size_t GetCount() const { return m_count
; } 
 402     DECLARE_NO_COPY_CLASS(TraverseCounter
) 
 405 // ---------------------------------------------------------------------------- 
 406 // This class is needed for support of different images: the Win32 common 
 407 // control natively supports only 2 images (the normal one and another for the 
 408 // selected state). We wish to provide support for 2 more of them for folder 
 409 // items (i.e. those which have children): for expanded state and for expanded 
 410 // selected state. For this we use this structure to store the additional items 
 413 // There is only one problem with this: when we retrieve the item's data, we 
 414 // don't know whether we get a pointer to wxTreeItemData or 
 415 // wxTreeItemIndirectData. So we always set the item id to an invalid value 
 416 // in this class and the code using the client data checks for it and retrieves 
 417 // the real client data in this case. 
 418 // ---------------------------------------------------------------------------- 
 420 class wxTreeItemIndirectData 
: public wxTreeItemData
 
 423     // ctor associates this data with the item and the real item data becomes 
 424     // available through our GetData() method 
 425     wxTreeItemIndirectData(wxTreeCtrl 
*tree
, const wxTreeItemId
& item
) 
 427         for ( size_t n 
= 0; n 
< WXSIZEOF(m_images
); n
++ ) 
 433         m_data 
= tree
->GetItemData(item
); 
 435         // and set ourselves as the new one 
 436         tree
->SetIndirectItemData(item
, this); 
 438         // we must have the invalid value for the item 
 442     // dtor deletes the associated data as well 
 443     virtual ~wxTreeItemIndirectData() { delete m_data
; } 
 446         // get the real data associated with the item 
 447     wxTreeItemData 
*GetData() const { return m_data
; } 
 449     void SetData(wxTreeItemData 
*data
) { m_data 
= data
; } 
 451         // do we have such image? 
 452     bool HasImage(wxTreeItemIcon which
) const { return m_images
[which
] != -1; } 
 454     int GetImage(wxTreeItemIcon which
) const { return m_images
[which
]; } 
 456     void SetImage(int image
, wxTreeItemIcon which
) { m_images
[which
] = image
; } 
 459     // all the images associated with the item 
 460     int m_images
[wxTreeItemIcon_Max
]; 
 462     // the real client data 
 463     wxTreeItemData 
*m_data
; 
 465     DECLARE_NO_COPY_CLASS(wxTreeItemIndirectData
) 
 468 // ---------------------------------------------------------------------------- 
 470 // ---------------------------------------------------------------------------- 
 472 #if wxUSE_EXTENDED_RTTI 
 473 WX_DEFINE_FLAGS( wxTreeCtrlStyle 
) 
 475 wxBEGIN_FLAGS( wxTreeCtrlStyle 
) 
 476     // new style border flags, we put them first to 
 477     // use them for streaming out 
 478     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
 479     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
 480     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
 481     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
 482     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
 483     wxFLAGS_MEMBER(wxBORDER_NONE
) 
 485     // old style border flags 
 486     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
 487     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
 488     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
 489     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
 490     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
 491     wxFLAGS_MEMBER(wxBORDER
) 
 493     // standard window styles 
 494     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
 495     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
 496     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
 497     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
 498     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
 499     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
 500     wxFLAGS_MEMBER(wxVSCROLL
) 
 501     wxFLAGS_MEMBER(wxHSCROLL
) 
 503     wxFLAGS_MEMBER(wxTR_EDIT_LABELS
) 
 504     wxFLAGS_MEMBER(wxTR_NO_BUTTONS
) 
 505     wxFLAGS_MEMBER(wxTR_HAS_BUTTONS
) 
 506     wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS
) 
 507     wxFLAGS_MEMBER(wxTR_NO_LINES
) 
 508     wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT
) 
 509     wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT
) 
 510     wxFLAGS_MEMBER(wxTR_HIDE_ROOT
) 
 511     wxFLAGS_MEMBER(wxTR_ROW_LINES
) 
 512     wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT
) 
 513     wxFLAGS_MEMBER(wxTR_SINGLE
) 
 514     wxFLAGS_MEMBER(wxTR_MULTIPLE
) 
 515     wxFLAGS_MEMBER(wxTR_EXTENDED
) 
 516     wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE
) 
 518 wxEND_FLAGS( wxTreeCtrlStyle 
) 
 520 IMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl
, wxControl
,"wx/treectrl.h") 
 522 wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl
) 
 523     wxEVENT_PROPERTY( TextUpdated 
, wxEVT_COMMAND_TEXT_UPDATED 
, wxCommandEvent 
) 
 524     wxEVENT_RANGE_PROPERTY( TreeEvent 
, wxEVT_COMMAND_TREE_BEGIN_DRAG 
, wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK 
, wxTreeEvent 
) 
 525     wxPROPERTY_FLAGS( WindowStyle 
, wxTreeCtrlStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, EMPTY_MACROVALUE 
, 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 526 wxEND_PROPERTIES_TABLE() 
 528 wxBEGIN_HANDLERS_TABLE(wxTreeCtrl
) 
 529 wxEND_HANDLERS_TABLE() 
 531 wxCONSTRUCTOR_5( wxTreeCtrl 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle 
) 
 533 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl
, wxControl
) 
 536 // ---------------------------------------------------------------------------- 
 538 // ---------------------------------------------------------------------------- 
 540 // indices in gs_expandEvents table below 
 555 // handy table for sending events - it has to be initialized during run-time 
 556 // now so can't be const any more 
 557 static /* const */ wxEventType gs_expandEvents
[IDX_WHAT_MAX
][IDX_HOW_MAX
]; 
 560    but logically it's a const table with the following entries: 
 563     { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING }, 
 564     { wxEVT_COMMAND_TREE_ITEM_EXPANDED,  wxEVT_COMMAND_TREE_ITEM_EXPANDING  } 
 568 // ============================================================================ 
 570 // ============================================================================ 
 572 // ---------------------------------------------------------------------------- 
 574 // ---------------------------------------------------------------------------- 
 576 bool wxTreeTraversal::DoTraverse(const wxTreeItemId
& root
, bool recursively
) 
 578     if ( !OnVisit(root
) ) 
 581     return Traverse(root
, recursively
); 
 584 bool wxTreeTraversal::Traverse(const wxTreeItemId
& root
, bool recursively
) 
 586     wxTreeItemIdValue cookie
; 
 587     wxTreeItemId child 
= m_tree
->GetFirstChild(root
, cookie
); 
 588     while ( child
.IsOk() ) 
 590         // depth first traversal 
 591         if ( recursively 
&& !Traverse(child
, true) ) 
 594         if ( !OnVisit(child
) ) 
 597         child 
= m_tree
->GetNextChild(root
, cookie
); 
 603 // ---------------------------------------------------------------------------- 
 604 // construction and destruction 
 605 // ---------------------------------------------------------------------------- 
 607 void wxTreeCtrl::Init() 
 610     m_hasAnyAttr 
= false; 
 612     m_pVirtualRoot 
= NULL
; 
 614     // initialize the global array of events now as it can't be done statically 
 615     // with the wxEVT_XXX values being allocated during run-time only 
 616     gs_expandEvents
[IDX_COLLAPSE
][IDX_DONE
] = wxEVT_COMMAND_TREE_ITEM_COLLAPSED
; 
 617     gs_expandEvents
[IDX_COLLAPSE
][IDX_DOING
] = wxEVT_COMMAND_TREE_ITEM_COLLAPSING
; 
 618     gs_expandEvents
[IDX_EXPAND
][IDX_DONE
] = wxEVT_COMMAND_TREE_ITEM_EXPANDED
; 
 619     gs_expandEvents
[IDX_EXPAND
][IDX_DOING
] = wxEVT_COMMAND_TREE_ITEM_EXPANDING
; 
 622 bool wxTreeCtrl::Create(wxWindow 
*parent
, 
 627                         const wxValidator
& validator
, 
 628                         const wxString
& name
) 
 632     if ( (style 
& wxBORDER_MASK
) == wxBORDER_DEFAULT 
) 
 633         style 
|= wxBORDER_SUNKEN
; 
 635     if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) ) 
 639     DWORD wstyle 
= MSWGetStyle(m_windowStyle
, & exStyle
); 
 640     wstyle 
|= WS_TABSTOP 
| TVS_SHOWSELALWAYS
; 
 642     if ((m_windowStyle 
& wxTR_NO_LINES
) == 0) 
 643         wstyle 
|= TVS_HASLINES
; 
 644     if ( m_windowStyle 
& wxTR_HAS_BUTTONS 
) 
 645         wstyle 
|= TVS_HASBUTTONS
; 
 647     if ( m_windowStyle 
& wxTR_EDIT_LABELS 
) 
 648         wstyle 
|= TVS_EDITLABELS
; 
 650     if ( m_windowStyle 
& wxTR_LINES_AT_ROOT 
) 
 651         wstyle 
|= TVS_LINESATROOT
; 
 653     if ( m_windowStyle 
& wxTR_FULL_ROW_HIGHLIGHT 
) 
 655         if ( wxApp::GetComCtl32Version() >= 471 ) 
 656             wstyle 
|= TVS_FULLROWSELECT
; 
 659     // using TVS_CHECKBOXES for emulation of a multiselection tree control 
 660     // doesn't work without the new enough headers 
 661 #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE && \ 
 662     !defined( __GNUWIN32_OLD__ ) && \ 
 663     !defined( __BORLANDC__ ) && \ 
 664     !defined( __WATCOMC__ ) && \ 
 665     (!defined(__VISUALC__) || (__VISUALC__ > 1010)) 
 667     // we emulate the multiple selection tree controls by using checkboxes: set 
 668     // up the image list we need for this if we do have multiple selections 
 669     if ( m_windowStyle 
& wxTR_MULTIPLE 
) 
 670         wstyle 
|= TVS_CHECKBOXES
; 
 671 #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
 673 #if !defined(__WXWINCE__) && defined(TVS_INFOTIP) 
 674     // Need so that TVN_GETINFOTIP messages will be sent 
 675     wstyle 
|= TVS_INFOTIP
; 
 678     // Create the tree control. 
 679     if ( !MSWCreateControl(WC_TREEVIEW
, wstyle
, pos
, size
) ) 
 682 #if wxUSE_COMCTL32_SAFELY 
 683     wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 684     wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); 
 686     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 687     SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); 
 689     // This works around a bug in the Windows tree control whereby for some versions 
 690     // of comctrl32, setting any colour actually draws the background in black. 
 691     // This will initialise the background to the system colour. 
 692     // THIS FIX NOW REVERTED since it caused problems on _other_ systems. 
 693     // Assume the user has an updated comctl32.dll. 
 694     ::SendMessage(GetHwnd(), TVM_SETBKCOLOR
, 0,-1); 
 695     wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW
)); 
 696     SetForegroundColour(wxWindow::GetParent()->GetForegroundColour()); 
 700     // VZ: this is some experimental code which may be used to get the 
 701     //     TVS_CHECKBOXES style functionality for comctl32.dll < 4.71. 
 702     //     AFAIK, the standard DLL does about the same thing anyhow. 
 704     if ( m_windowStyle 
& wxTR_MULTIPLE 
) 
 708         // create the DC compatible with the current screen 
 709         HDC hdcMem 
= CreateCompatibleDC(NULL
); 
 711         // create a mono bitmap of the standard size 
 712         int x 
= ::GetSystemMetrics(SM_CXMENUCHECK
); 
 713         int y 
= ::GetSystemMetrics(SM_CYMENUCHECK
); 
 714         wxImageList 
imagelistCheckboxes(x
, y
, false, 2); 
 715         HBITMAP hbmpCheck 
= CreateBitmap(x
, y
,   // bitmap size 
 716                                          1,      // # of color planes 
 717                                          1,      // # bits needed for one pixel 
 718                                          0);     // array containing colour data 
 719         SelectObject(hdcMem
, hbmpCheck
); 
 721         // then draw a check mark into it 
 722         RECT rect 
= { 0, 0, x
, y 
}; 
 723         if ( !::DrawFrameControl(hdcMem
, &rect
, 
 725                                  DFCS_BUTTONCHECK 
| DFCS_CHECKED
) ) 
 727             wxLogLastError(wxT("DrawFrameControl(check)")); 
 730         bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
); 
 731         imagelistCheckboxes
.Add(bmp
); 
 733         if ( !::DrawFrameControl(hdcMem
, &rect
, 
 737             wxLogLastError(wxT("DrawFrameControl(uncheck)")); 
 740         bmp
.SetHBITMAP((WXHBITMAP
)hbmpCheck
); 
 741         imagelistCheckboxes
.Add(bmp
); 
 747         SetStateImageList(&imagelistCheckboxes
); 
 751     wxSetCCUnicodeFormat(GetHwnd()); 
 756 wxTreeCtrl::~wxTreeCtrl() 
 758     // delete any attributes 
 761         WX_CLEAR_HASH_MAP(wxMapTreeAttr
, m_attrs
); 
 763         // prevent TVN_DELETEITEM handler from deleting the attributes again! 
 764         m_hasAnyAttr 
= false; 
 769     // delete user data to prevent memory leaks 
 770     // also deletes hidden root node storage. 
 774 // ---------------------------------------------------------------------------- 
 776 // ---------------------------------------------------------------------------- 
 778 /* static */ wxVisualAttributes
 
 779 wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant
) 
 781     wxVisualAttributes attrs 
= GetCompositeControlsDefaultAttributes(variant
); 
 783     // common controls have their own default font 
 784     attrs
.font 
= wxGetCCDefaultFont(); 
 790 // simple wrappers which add error checking in debug mode 
 792 bool wxTreeCtrl::DoGetItem(wxTreeViewItem
* tvItem
) const 
 794     wxCHECK_MSG( tvItem
->hItem 
!= TVI_ROOT
, false, 
 795                  _T("can't retrieve virtual root item") ); 
 797     if ( !TreeView_GetItem(GetHwnd(), tvItem
) ) 
 799         wxLogLastError(wxT("TreeView_GetItem")); 
 807 void wxTreeCtrl::DoSetItem(wxTreeViewItem
* tvItem
) 
 809     if ( TreeView_SetItem(GetHwnd(), tvItem
) == -1 ) 
 811         wxLogLastError(wxT("TreeView_SetItem")); 
 815 size_t wxTreeCtrl::GetCount() const 
 817     return (size_t)TreeView_GetCount(GetHwnd()); 
 820 unsigned int wxTreeCtrl::GetIndent() const 
 822     return TreeView_GetIndent(GetHwnd()); 
 825 void wxTreeCtrl::SetIndent(unsigned int indent
) 
 827     TreeView_SetIndent(GetHwnd(), indent
); 
 830 void wxTreeCtrl::SetAnyImageList(wxImageList 
*imageList
, int which
) 
 833     TreeView_SetImageList(GetHwnd(), 
 834                           imageList 
? imageList
->GetHIMAGELIST() : 0, 
 838 void wxTreeCtrl::SetImageList(wxImageList 
*imageList
) 
 840     if (m_ownsImageListNormal
) 
 841         delete m_imageListNormal
; 
 843     SetAnyImageList(m_imageListNormal 
= imageList
, TVSIL_NORMAL
); 
 844     m_ownsImageListNormal 
= false; 
 847 void wxTreeCtrl::SetStateImageList(wxImageList 
*imageList
) 
 849     if (m_ownsImageListState
) delete m_imageListState
; 
 850     SetAnyImageList(m_imageListState 
= imageList
, TVSIL_STATE
); 
 851     m_ownsImageListState 
= false; 
 854 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId
& item
, 
 855                                     bool recursively
) const 
 857     wxCHECK_MSG( item
.IsOk(), 0u, wxT("invalid tree item") ); 
 859     TraverseCounter 
counter(this, item
, recursively
); 
 860     return counter
.GetCount() - 1; 
 863 // ---------------------------------------------------------------------------- 
 865 // ---------------------------------------------------------------------------- 
 867 bool wxTreeCtrl::SetBackgroundColour(const wxColour 
&colour
) 
 869 #if !wxUSE_COMCTL32_SAFELY 
 870     if ( !wxWindowBase::SetBackgroundColour(colour
) ) 
 873     ::SendMessage(GetHwnd(), TVM_SETBKCOLOR
, 0, colour
.GetPixel()); 
 879 bool wxTreeCtrl::SetForegroundColour(const wxColour 
&colour
) 
 881 #if !wxUSE_COMCTL32_SAFELY 
 882     if ( !wxWindowBase::SetForegroundColour(colour
) ) 
 885     ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR
, 0, colour
.GetPixel()); 
 891 // ---------------------------------------------------------------------------- 
 893 // ---------------------------------------------------------------------------- 
 895 wxString 
wxTreeCtrl::GetItemText(const wxTreeItemId
& item
) const 
 897     wxCHECK_MSG( item
.IsOk(), wxEmptyString
, wxT("invalid tree item") ); 
 899     wxChar buf
[512];  // the size is arbitrary... 
 901     wxTreeViewItem 
tvItem(item
, TVIF_TEXT
); 
 902     tvItem
.pszText 
= buf
; 
 903     tvItem
.cchTextMax 
= WXSIZEOF(buf
); 
 904     if ( !DoGetItem(&tvItem
) ) 
 906         // don't return some garbage which was on stack, but an empty string 
 910     return wxString(buf
); 
 913 void wxTreeCtrl::SetItemText(const wxTreeItemId
& item
, const wxString
& text
) 
 915     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
 917     if ( IS_VIRTUAL_ROOT(item
) ) 
 920     wxTreeViewItem 
tvItem(item
, TVIF_TEXT
); 
 921     tvItem
.pszText 
= (wxChar 
*)text
.c_str();  // conversion is ok 
 924     // when setting the text of the item being edited, the text control should 
 925     // be updated to reflect the new text as well, otherwise calling 
 926     // SetItemText() in the OnBeginLabelEdit() handler doesn't have any effect 
 928     // don't use GetEditControl() here because m_textCtrl is not set yet 
 929     HWND hwndEdit 
= TreeView_GetEditControl(GetHwnd()); 
 932         if ( item 
== m_idEdited 
) 
 934             ::SetWindowText(hwndEdit
, text
); 
 939 int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId
& item
, 
 940                                        wxTreeItemIcon which
) const 
 942     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
 943     if ( !DoGetItem(&tvItem
) ) 
 948     return ((wxTreeItemIndirectData 
*)tvItem
.lParam
)->GetImage(which
); 
 951 void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId
& item
, 
 953                                         wxTreeItemIcon which
) const 
 955     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
 956     if ( !DoGetItem(&tvItem
) ) 
 961     wxTreeItemIndirectData 
*data 
= ((wxTreeItemIndirectData 
*)tvItem
.lParam
); 
 963     data
->SetImage(image
, which
); 
 965     // make sure that we have selected images as well 
 966     if ( which 
== wxTreeItemIcon_Normal 
&& 
 967          !data
->HasImage(wxTreeItemIcon_Selected
) ) 
 969         data
->SetImage(image
, wxTreeItemIcon_Selected
); 
 972     if ( which 
== wxTreeItemIcon_Expanded 
&& 
 973          !data
->HasImage(wxTreeItemIcon_SelectedExpanded
) ) 
 975         data
->SetImage(image
, wxTreeItemIcon_SelectedExpanded
); 
 979 void wxTreeCtrl::DoSetItemImages(const wxTreeItemId
& item
, 
 983     wxTreeViewItem 
tvItem(item
, TVIF_IMAGE 
| TVIF_SELECTEDIMAGE
); 
 984     tvItem
.iSelectedImage 
= imageSel
; 
 985     tvItem
.iImage 
= image
; 
 989 int wxTreeCtrl::GetItemImage(const wxTreeItemId
& item
, 
 990                              wxTreeItemIcon which
) const 
 992     wxCHECK_MSG( item
.IsOk(), -1, wxT("invalid tree item") ); 
 994     if ( (HITEM(item
) == TVI_ROOT
) && (m_windowStyle 
& wxTR_HIDE_ROOT
) ) 
 996         // TODO: Maybe a hidden root can still provide images? 
1000     if ( HasIndirectData(item
) ) 
1002         return DoGetItemImageFromData(item
, which
); 
1009             wxFAIL_MSG( wxT("unknown tree item image type") ); 
1011         case wxTreeItemIcon_Normal
: 
1015         case wxTreeItemIcon_Selected
: 
1016             mask 
= TVIF_SELECTEDIMAGE
; 
1019         case wxTreeItemIcon_Expanded
: 
1020         case wxTreeItemIcon_SelectedExpanded
: 
1024     wxTreeViewItem 
tvItem(item
, mask
); 
1027     return mask 
== TVIF_IMAGE 
? tvItem
.iImage 
: tvItem
.iSelectedImage
; 
1030 void wxTreeCtrl::SetItemImage(const wxTreeItemId
& item
, int image
, 
1031                               wxTreeItemIcon which
) 
1033     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1035     if ( IS_VIRTUAL_ROOT(item
) ) 
1037         // TODO: Maybe a hidden root can still store images? 
1047             wxFAIL_MSG( wxT("unknown tree item image type") ); 
1050         case wxTreeItemIcon_Normal
: 
1052                 const int imageNormalOld 
= GetItemImage(item
); 
1053                 const int imageSelOld 
= 
1054                     GetItemImage(item
, wxTreeItemIcon_Selected
); 
1056                 // always set the normal image 
1057                 imageNormal 
= image
; 
1059                 // if the selected and normal images were the same, they should 
1060                 // be the same after the update, otherwise leave the selected 
1062                 imageSel 
= imageNormalOld 
== imageSelOld 
? image 
: imageSelOld
; 
1066         case wxTreeItemIcon_Selected
: 
1067             imageNormal 
= GetItemImage(item
); 
1071         case wxTreeItemIcon_Expanded
: 
1072         case wxTreeItemIcon_SelectedExpanded
: 
1073             if ( !HasIndirectData(item
) ) 
1075                 // we need to get the old images first, because after we create 
1076                 // the wxTreeItemIndirectData GetItemXXXImage() will use it to 
1078                 imageNormal 
= GetItemImage(item
); 
1079                 imageSel 
= GetItemImage(item
, wxTreeItemIcon_Selected
); 
1081                 // if it doesn't have it yet, add it 
1082                 wxTreeItemIndirectData 
*data 
= new 
1083                     wxTreeItemIndirectData(this, item
); 
1085                 // copy the data to the new location 
1086                 data
->SetImage(imageNormal
, wxTreeItemIcon_Normal
); 
1087                 data
->SetImage(imageSel
, wxTreeItemIcon_Selected
); 
1090             DoSetItemImageFromData(item
, image
, which
); 
1092             // reset the normal/selected images because we won't use them any 
1093             // more - now they're stored inside the indirect data 
1095             imageSel 
= I_IMAGECALLBACK
; 
1099     // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always 
1100     //     change both normal and selected image - otherwise the change simply 
1101     //     doesn't take place! 
1102     DoSetItemImages(item
, imageNormal
, imageSel
); 
1105 wxTreeItemData 
*wxTreeCtrl::GetItemData(const wxTreeItemId
& item
) const 
1107     wxCHECK_MSG( item
.IsOk(), NULL
, wxT("invalid tree item") ); 
1109     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
1111     // Hidden root may have data. 
1112     if ( IS_VIRTUAL_ROOT(item
) ) 
1114         return GET_VIRTUAL_ROOT()->GetData(); 
1118     if ( !DoGetItem(&tvItem
) ) 
1123     wxTreeItemData 
*data 
= (wxTreeItemData 
*)tvItem
.lParam
; 
1124     if ( IsDataIndirect(data
) ) 
1126         data 
= ((wxTreeItemIndirectData 
*)data
)->GetData(); 
1132 void wxTreeCtrl::SetItemData(const wxTreeItemId
& item
, wxTreeItemData 
*data
) 
1134     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1136     if ( IS_VIRTUAL_ROOT(item
) ) 
1138         GET_VIRTUAL_ROOT()->SetData(data
); 
1141     // first, associate this piece of data with this item 
1147     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
1149     if ( HasIndirectData(item
) ) 
1151         if ( DoGetItem(&tvItem
) ) 
1153             ((wxTreeItemIndirectData 
*)tvItem
.lParam
)->SetData(data
); 
1157             wxFAIL_MSG( wxT("failed to change tree items data") ); 
1162         tvItem
.lParam 
= (LPARAM
)data
; 
1167 void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId
& item
, 
1168                                      wxTreeItemIndirectData 
*data
) 
1170     // this should never happen because it's unnecessary and will probably lead 
1171     // to crash too because the code elsewhere supposes that the pointer the 
1172     // wxTreeItemIndirectData has is a real wxItemData and not 
1173     // wxTreeItemIndirectData as well 
1174     wxASSERT_MSG( !HasIndirectData(item
), wxT("setting indirect data twice?") ); 
1176     SetItemData(item
, data
); 
1179 bool wxTreeCtrl::HasIndirectData(const wxTreeItemId
& item
) const 
1181     // query the item itself 
1182     wxTreeViewItem 
tvItem(item
, TVIF_PARAM
); 
1183     if ( !DoGetItem(&tvItem
) ) 
1188     wxTreeItemData 
*data 
= (wxTreeItemData 
*)tvItem
.lParam
; 
1190     return data 
&& IsDataIndirect(data
); 
1193 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId
& item
, bool has
) 
1195     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1197     if ( IS_VIRTUAL_ROOT(item
) ) 
1200     wxTreeViewItem 
tvItem(item
, TVIF_CHILDREN
); 
1201     tvItem
.cChildren 
= (int)has
; 
1205 void wxTreeCtrl::SetItemBold(const wxTreeItemId
& item
, bool bold
) 
1207     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1209     if ( IS_VIRTUAL_ROOT(item
) ) 
1212     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_BOLD
); 
1213     tvItem
.state 
= bold 
? TVIS_BOLD 
: 0; 
1217 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId
& item
, bool highlight
) 
1219     if ( IS_VIRTUAL_ROOT(item
) ) 
1222     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_DROPHILITED
); 
1223     tvItem
.state 
= highlight 
? TVIS_DROPHILITED 
: 0; 
1227 void wxTreeCtrl::RefreshItem(const wxTreeItemId
& item
) 
1229     if ( IS_VIRTUAL_ROOT(item
) ) 
1233     if ( GetBoundingRect(item
, rect
) ) 
1239 wxColour 
wxTreeCtrl::GetItemTextColour(const wxTreeItemId
& item
) const 
1241     wxCHECK_MSG( item
.IsOk(), wxNullColour
, wxT("invalid tree item") ); 
1243     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1244     return it 
== m_attrs
.end() ? wxNullColour 
: it
->second
->GetTextColour(); 
1247 wxColour 
wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId
& item
) const 
1249     wxCHECK_MSG( item
.IsOk(), wxNullColour
, wxT("invalid tree item") ); 
1251     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1252     return it 
== m_attrs
.end() ? wxNullColour 
: it
->second
->GetBackgroundColour(); 
1255 wxFont 
wxTreeCtrl::GetItemFont(const wxTreeItemId
& item
) const 
1257     wxCHECK_MSG( item
.IsOk(), wxNullFont
, wxT("invalid tree item") ); 
1259     wxMapTreeAttr::const_iterator it 
= m_attrs
.find(item
.m_pItem
); 
1260     return it 
== m_attrs
.end() ? wxNullFont 
: it
->second
->GetFont(); 
1263 void wxTreeCtrl::SetItemTextColour(const wxTreeItemId
& item
, 
1264                                    const wxColour
& col
) 
1266     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1268     wxTreeItemAttr 
*attr
; 
1269     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1270     if ( it 
== m_attrs
.end() ) 
1272         m_hasAnyAttr 
= true; 
1274         m_attrs
[item
.m_pItem
] = 
1275         attr 
= new wxTreeItemAttr
; 
1282     attr
->SetTextColour(col
); 
1287 void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId
& item
, 
1288                                          const wxColour
& col
) 
1290     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1292     wxTreeItemAttr 
*attr
; 
1293     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1294     if ( it 
== m_attrs
.end() ) 
1296         m_hasAnyAttr 
= true; 
1298         m_attrs
[item
.m_pItem
] = 
1299         attr 
= new wxTreeItemAttr
; 
1301     else // already in the hash 
1306     attr
->SetBackgroundColour(col
); 
1311 void wxTreeCtrl::SetItemFont(const wxTreeItemId
& item
, const wxFont
& font
) 
1313     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1315     wxTreeItemAttr 
*attr
; 
1316     wxMapTreeAttr::iterator it 
= m_attrs
.find(item
.m_pItem
); 
1317     if ( it 
== m_attrs
.end() ) 
1319         m_hasAnyAttr 
= true; 
1321         m_attrs
[item
.m_pItem
] = 
1322         attr 
= new wxTreeItemAttr
; 
1324     else // already in the hash 
1329     attr
->SetFont(font
); 
1334 // ---------------------------------------------------------------------------- 
1336 // ---------------------------------------------------------------------------- 
1338 bool wxTreeCtrl::IsVisible(const wxTreeItemId
& item
) const 
1340     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1342     if ( item 
== wxTreeItemId(TVI_ROOT
) ) 
1344         // virtual (hidden) root is never visible 
1348     // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect 
1351     // this ugliness comes directly from MSDN - it *is* the correct way to pass 
1352     // the HTREEITEM with TVM_GETITEMRECT 
1353     *(HTREEITEM 
*)&rect 
= HITEM(item
); 
1355     // true means to get rect for just the text, not the whole line 
1356     if ( !::SendMessage(GetHwnd(), TVM_GETITEMRECT
, true, (LPARAM
)&rect
) ) 
1358         // if TVM_GETITEMRECT returned false, then the item is definitely not 
1359         // visible (because its parent is not expanded) 
1363     // however if it returned true, the item might still be outside the 
1364     // currently visible part of the tree, test for it (notice that partly 
1365     // visible means visible here) 
1366     return rect
.bottom 
> 0 && rect
.top 
< GetClientSize().y
; 
1369 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId
& item
) const 
1371     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1373     wxTreeViewItem 
tvItem(item
, TVIF_CHILDREN
); 
1376     return tvItem
.cChildren 
!= 0; 
1379 bool wxTreeCtrl::IsExpanded(const wxTreeItemId
& item
) const 
1381     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1383     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_EXPANDED
); 
1386     return (tvItem
.state 
& TVIS_EXPANDED
) != 0; 
1389 bool wxTreeCtrl::IsSelected(const wxTreeItemId
& item
) const 
1391     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1393     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_SELECTED
); 
1396     return (tvItem
.state 
& TVIS_SELECTED
) != 0; 
1399 bool wxTreeCtrl::IsBold(const wxTreeItemId
& item
) const 
1401     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1403     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_BOLD
); 
1406     return (tvItem
.state 
& TVIS_BOLD
) != 0; 
1409 // ---------------------------------------------------------------------------- 
1411 // ---------------------------------------------------------------------------- 
1413 wxTreeItemId 
wxTreeCtrl::GetRootItem() const 
1415     // Root may be real (visible) or virtual (hidden). 
1416     if ( GET_VIRTUAL_ROOT() ) 
1419     return wxTreeItemId(TreeView_GetRoot(GetHwnd())); 
1422 wxTreeItemId 
wxTreeCtrl::GetSelection() const 
1424     wxCHECK_MSG( !(m_windowStyle 
& wxTR_MULTIPLE
), wxTreeItemId(), 
1425                  wxT("this only works with single selection controls") ); 
1427     return wxTreeItemId(TreeView_GetSelection(GetHwnd())); 
1430 wxTreeItemId 
wxTreeCtrl::GetItemParent(const wxTreeItemId
& item
) const 
1432     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1436     if ( IS_VIRTUAL_ROOT(item
) ) 
1438         // no parent for the virtual root 
1443         hItem 
= TreeView_GetParent(GetHwnd(), HITEM(item
)); 
1444         if ( !hItem 
&& HasFlag(wxTR_HIDE_ROOT
) ) 
1446             // the top level items should have the virtual root as their parent 
1451     return wxTreeItemId(hItem
); 
1454 wxTreeItemId 
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
, 
1455                                        wxTreeItemIdValue
& cookie
) const 
1457     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1459     // remember the last child returned in 'cookie' 
1460     cookie 
= TreeView_GetChild(GetHwnd(), HITEM(item
)); 
1462     return wxTreeItemId(cookie
); 
1465 wxTreeItemId 
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
), 
1466                                       wxTreeItemIdValue
& cookie
) const 
1468     wxTreeItemId 
fromCookie(cookie
); 
1470     HTREEITEM hitem 
= HITEM(fromCookie
); 
1472     hitem 
= TreeView_GetNextSibling(GetHwnd(), hitem
); 
1474     wxTreeItemId 
item(hitem
); 
1476     cookie 
= item
.m_pItem
; 
1481 #if WXWIN_COMPATIBILITY_2_4 
1483 wxTreeItemId 
wxTreeCtrl::GetFirstChild(const wxTreeItemId
& item
, 
1486     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1488     cookie 
= (long)TreeView_GetChild(GetHwnd(), HITEM(item
)); 
1490     return wxTreeItemId((void *)cookie
); 
1493 wxTreeItemId 
wxTreeCtrl::GetNextChild(const wxTreeItemId
& WXUNUSED(item
), 
1496     wxTreeItemId 
fromCookie((void *)cookie
); 
1498     HTREEITEM hitem 
= HITEM(fromCookie
); 
1500     hitem 
= TreeView_GetNextSibling(GetHwnd(), hitem
); 
1502     wxTreeItemId 
item(hitem
); 
1504     cookie 
= (long)item
.m_pItem
; 
1509 #endif // WXWIN_COMPATIBILITY_2_4 
1511 wxTreeItemId 
wxTreeCtrl::GetLastChild(const wxTreeItemId
& item
) const 
1513     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1515     // can this be done more efficiently? 
1516     wxTreeItemIdValue cookie
; 
1518     wxTreeItemId childLast
, 
1519     child 
= GetFirstChild(item
, cookie
); 
1520     while ( child
.IsOk() ) 
1523         child 
= GetNextChild(item
, cookie
); 
1529 wxTreeItemId 
wxTreeCtrl::GetNextSibling(const wxTreeItemId
& item
) const 
1531     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1532     return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item
))); 
1535 wxTreeItemId 
wxTreeCtrl::GetPrevSibling(const wxTreeItemId
& item
) const 
1537     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1538     return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item
))); 
1541 wxTreeItemId 
wxTreeCtrl::GetFirstVisibleItem() const 
1543     return wxTreeItemId(TreeView_GetFirstVisible(GetHwnd())); 
1546 wxTreeItemId 
wxTreeCtrl::GetNextVisible(const wxTreeItemId
& item
) const 
1548     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1549     wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetNextVisible() for must be visible itself!")); 
1551     return wxTreeItemId(TreeView_GetNextVisible(GetHwnd(), HITEM(item
))); 
1554 wxTreeItemId 
wxTreeCtrl::GetPrevVisible(const wxTreeItemId
& item
) const 
1556     wxCHECK_MSG( item
.IsOk(), wxTreeItemId(), wxT("invalid tree item") ); 
1557     wxASSERT_MSG( IsVisible(item
), wxT("The item you call GetPrevVisible() for must be visible itself!")); 
1559     return wxTreeItemId(TreeView_GetPrevVisible(GetHwnd(), HITEM(item
))); 
1562 // ---------------------------------------------------------------------------- 
1563 // multiple selections emulation 
1564 // ---------------------------------------------------------------------------- 
1566 bool wxTreeCtrl::IsItemChecked(const wxTreeItemId
& item
) const 
1568     wxCHECK_MSG( item
.IsOk(), false, wxT("invalid tree item") ); 
1570     // receive the desired information. 
1571     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
); 
1574     // state image indices are 1 based 
1575     return ((tvItem
.state 
>> 12) - 1) == 1; 
1578 void wxTreeCtrl::SetItemCheck(const wxTreeItemId
& item
, bool check
) 
1580     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
1582     // receive the desired information. 
1583     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_STATEIMAGEMASK
); 
1587     // state images are one-based 
1588     tvItem
.state 
= (check 
? 2 : 1) << 12; 
1593 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds
& selections
) const 
1595     TraverseSelections 
selector(this, selections
); 
1597     return selector
.GetCount(); 
1600 // ---------------------------------------------------------------------------- 
1602 // ---------------------------------------------------------------------------- 
1604 wxTreeItemId 
wxTreeCtrl::DoInsertAfter(const wxTreeItemId
& parent
, 
1605                                        const wxTreeItemId
& hInsertAfter
, 
1606                                        const wxString
& text
, 
1607                                        int image
, int selectedImage
, 
1608                                        wxTreeItemData 
*data
) 
1610     wxCHECK_MSG( parent
.IsOk() || !TreeView_GetRoot(GetHwnd()), 
1612                  _T("can't have more than one root in the tree") ); 
1614     TV_INSERTSTRUCT tvIns
; 
1615     tvIns
.hParent 
= HITEM(parent
); 
1616     tvIns
.hInsertAfter 
= HITEM(hInsertAfter
); 
1618     // this is how we insert the item as the first child: supply a NULL 
1620     if ( !tvIns
.hInsertAfter 
) 
1622         tvIns
.hInsertAfter 
= TVI_FIRST
; 
1626     if ( !text
.empty() ) 
1629         tvIns
.item
.pszText 
= (wxChar 
*)text
.c_str();  // cast is ok 
1633         tvIns
.item
.pszText 
= NULL
; 
1634         tvIns
.item
.cchTextMax 
= 0; 
1640         tvIns
.item
.iImage 
= image
; 
1642         if ( selectedImage 
== -1 ) 
1644             // take the same image for selected icon if not specified 
1645             selectedImage 
= image
; 
1649     if ( selectedImage 
!= -1 ) 
1651         mask 
|= TVIF_SELECTEDIMAGE
; 
1652         tvIns
.item
.iSelectedImage 
= selectedImage
; 
1658         tvIns
.item
.lParam 
= (LPARAM
)data
; 
1661     tvIns
.item
.mask 
= mask
; 
1663     HTREEITEM id 
= (HTREEITEM
) TreeView_InsertItem(GetHwnd(), &tvIns
); 
1666         wxLogLastError(wxT("TreeView_InsertItem")); 
1671         // associate the application tree item with Win32 tree item handle 
1675     return wxTreeItemId(id
); 
1678 // for compatibility only 
1679 #if WXWIN_COMPATIBILITY_2_4 
1681 wxTreeItemId 
wxTreeCtrl::InsertItem(const wxTreeItemId
& parent
, 
1682                                     const wxString
& text
, 
1683                                     int image
, int selImage
, 
1686     return DoInsertAfter(parent
, 
1687                          wxTreeItemId(wxUIntToPtr(insertAfter
)), 
1694 wxImageList 
*wxTreeCtrl::GetImageList(int) const 
1696     return wxTreeCtrlBase::GetImageList(); 
1699 void wxTreeCtrl::SetImageList(wxImageList 
*imageList
, int) 
1701     SetImageList(imageList
); 
1704 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId
& item
) const 
1706     return GetItemImage(item
, wxTreeItemIcon_Selected
); 
1709 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId
& item
, int image
) 
1711     SetItemImage(item
, image
, wxTreeItemIcon_Selected
); 
1714 #endif // WXWIN_COMPATIBILITY_2_4 
1716 wxTreeItemId 
wxTreeCtrl::AddRoot(const wxString
& text
, 
1717                                  int image
, int selectedImage
, 
1718                                  wxTreeItemData 
*data
) 
1721     if ( m_windowStyle 
& wxTR_HIDE_ROOT 
) 
1723         // create a virtual root item, the parent for all the others 
1724         m_pVirtualRoot 
= new wxVirtualNode(data
); 
1729     return DoInsertAfter(wxTreeItemId(), wxTreeItemId(), 
1730                            text
, image
, selectedImage
, data
); 
1733 wxTreeItemId 
wxTreeCtrl::DoInsertItem(const wxTreeItemId
& parent
, 
1735                                       const wxString
& text
, 
1736                                       int image
, int selectedImage
, 
1737                                       wxTreeItemData 
*data
) 
1739     wxTreeItemId idPrev
; 
1740     if ( index 
== (size_t)-1 ) 
1742         // special value: append to the end 
1745     else // find the item from index 
1747         wxTreeItemIdValue cookie
; 
1748         wxTreeItemId idCur 
= GetFirstChild(parent
, cookie
); 
1749         while ( index 
!= 0 && idCur
.IsOk() ) 
1754             idCur 
= GetNextChild(parent
, cookie
); 
1757         // assert, not check: if the index is invalid, we will append the item 
1759         wxASSERT_MSG( index 
== 0, _T("bad index in wxTreeCtrl::InsertItem") ); 
1762     return DoInsertAfter(parent
, idPrev
, text
, image
, selectedImage
, data
); 
1765 void wxTreeCtrl::Delete(const wxTreeItemId
& item
) 
1767     if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item
)) ) 
1769         wxLogLastError(wxT("TreeView_DeleteItem")); 
1773 // delete all children (but don't delete the item itself) 
1774 void wxTreeCtrl::DeleteChildren(const wxTreeItemId
& item
) 
1776     wxTreeItemIdValue cookie
; 
1778     wxArrayTreeItemIds children
; 
1779     wxTreeItemId child 
= GetFirstChild(item
, cookie
); 
1780     while ( child
.IsOk() ) 
1782         children
.Add(child
); 
1784         child 
= GetNextChild(item
, cookie
); 
1787     size_t nCount 
= children
.Count(); 
1788     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
1790         if ( !TreeView_DeleteItem(GetHwnd(), HITEM(children
[n
])) ) 
1792             wxLogLastError(wxT("TreeView_DeleteItem")); 
1797 void wxTreeCtrl::DeleteAllItems() 
1799     // delete the "virtual" root item. 
1800     if ( GET_VIRTUAL_ROOT() ) 
1802         delete GET_VIRTUAL_ROOT(); 
1803         m_pVirtualRoot 
= NULL
; 
1806     // and all the real items 
1808     if ( !TreeView_DeleteAllItems(GetHwnd()) ) 
1810         wxLogLastError(wxT("TreeView_DeleteAllItems")); 
1814 void wxTreeCtrl::DoExpand(const wxTreeItemId
& item
, int flag
) 
1816     wxASSERT_MSG( flag 
== TVE_COLLAPSE 
|| 
1817                   flag 
== (TVE_COLLAPSE 
| TVE_COLLAPSERESET
) || 
1818                   flag 
== TVE_EXPAND   
|| 
1820                   wxT("Unknown flag in wxTreeCtrl::DoExpand") ); 
1822     // A hidden root can be neither expanded nor collapsed. 
1823     wxCHECK_RET( !(m_windowStyle 
& wxTR_HIDE_ROOT
) || (HITEM(item
) != TVI_ROOT
), 
1824                  wxT("Can't expand/collapse hidden root node!") ) 
1826     // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must 
1827     // emulate them. This behaviour has changed slightly with comctl32.dll 
1828     // v 4.70 - now it does send them but only the first time. To maintain 
1829     // compatible behaviour and also in order to not have surprises with the 
1830     // future versions, don't rely on this and still do everything ourselves. 
1831     // To avoid that the messages be sent twice when the item is expanded for 
1832     // the first time we must clear TVIS_EXPANDEDONCE style manually. 
1834     wxTreeViewItem 
tvItem(item
, TVIF_STATE
, TVIS_EXPANDEDONCE
); 
1838     if ( TreeView_Expand(GetHwnd(), HITEM(item
), flag
) != 0 ) 
1840         wxTreeEvent 
event(wxEVT_NULL
, m_windowId
); 
1841         event
.m_item 
= item
; 
1842         event
.SetEventObject(this); 
1844         // note that the {EXPAND|COLLAPS}ING event is sent by TreeView_Expand() 
1846         event
.SetEventType(gs_expandEvents
[IsExpanded(item
) ? IDX_EXPAND
 
1850         (void)GetEventHandler()->ProcessEvent(event
); 
1852     //else: change didn't took place, so do nothing at all 
1855 void wxTreeCtrl::Expand(const wxTreeItemId
& item
) 
1857     DoExpand(item
, TVE_EXPAND
); 
1860 void wxTreeCtrl::Collapse(const wxTreeItemId
& item
) 
1862     DoExpand(item
, TVE_COLLAPSE
); 
1865 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId
& item
) 
1867     DoExpand(item
, TVE_COLLAPSE 
| TVE_COLLAPSERESET
); 
1870 void wxTreeCtrl::Toggle(const wxTreeItemId
& item
) 
1872     DoExpand(item
, TVE_TOGGLE
); 
1875 #if WXWIN_COMPATIBILITY_2_4 
1877 void wxTreeCtrl::ExpandItem(const wxTreeItemId
& item
, int action
) 
1879     DoExpand(item
, action
); 
1884 void wxTreeCtrl::Unselect() 
1886     wxASSERT_MSG( !(m_windowStyle 
& wxTR_MULTIPLE
), 
1887                   wxT("doesn't make sense, may be you want UnselectAll()?") ); 
1889     // just remove the selection 
1890     SelectItem(wxTreeItemId()); 
1893 void wxTreeCtrl::UnselectAll() 
1895     if ( m_windowStyle 
& wxTR_MULTIPLE 
) 
1897         wxArrayTreeItemIds selections
; 
1898         size_t count 
= GetSelections(selections
); 
1899         for ( size_t n 
= 0; n 
< count
; n
++ ) 
1901 #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1902             SetItemCheck(HITEM(selections
[n
]), false); 
1903 #else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1904             ::UnselectItem(GetHwnd(), HITEM(selections
[n
])); 
1905 #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1908         m_htSelStart
.Unset(); 
1912         // just remove the selection 
1917 void wxTreeCtrl::SelectItem(const wxTreeItemId
& item
, bool select
) 
1919     if ( m_windowStyle 
& wxTR_MULTIPLE 
) 
1921 #if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1922         // selecting the item means checking it 
1923         SetItemCheck(item
, select
); 
1924 #else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1925         ::SelectItem(GetHwnd(), HITEM(item
), select
); 
1926 #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
1930         wxASSERT_MSG( select
, 
1931                       _T("SelectItem(false) works only for multiselect") ); 
1933         // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive 
1934         // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so 
1935         // send them ourselves 
1937         wxTreeEvent 
event(wxEVT_NULL
, m_windowId
); 
1938         event
.m_item 
= item
; 
1939         event
.SetEventObject(this); 
1941         event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING
); 
1942         if ( !GetEventHandler()->ProcessEvent(event
) || event
.IsAllowed() ) 
1944             if ( !TreeView_SelectItem(GetHwnd(), HITEM(item
)) ) 
1946                 wxLogLastError(wxT("TreeView_SelectItem")); 
1950                 event
.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED
); 
1951                 (void)GetEventHandler()->ProcessEvent(event
); 
1954         //else: program vetoed the change 
1958 void wxTreeCtrl::EnsureVisible(const wxTreeItemId
& item
) 
1961     TreeView_EnsureVisible(GetHwnd(), HITEM(item
)); 
1964 void wxTreeCtrl::ScrollTo(const wxTreeItemId
& item
) 
1966     if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item
)) ) 
1968         wxLogLastError(wxT("TreeView_SelectSetFirstVisible")); 
1972 wxTextCtrl 
*wxTreeCtrl::GetEditControl() const 
1977 void wxTreeCtrl::DeleteTextCtrl() 
1981         // the HWND corresponding to this control is deleted by the tree 
1982         // control itself and we don't know when exactly this happens, so check 
1983         // if the window still exists before calling UnsubclassWin() 
1984         if ( !::IsWindow(GetHwndOf(m_textCtrl
)) ) 
1986             m_textCtrl
->SetHWND(0); 
1989         m_textCtrl
->UnsubclassWin(); 
1990         m_textCtrl
->SetHWND(0); 
1998 wxTextCtrl
* wxTreeCtrl::EditLabel(const wxTreeItemId
& item
, 
1999                                   wxClassInfo
* textControlClass
) 
2001     wxASSERT( textControlClass
->IsKindOf(CLASSINFO(wxTextCtrl
)) ); 
2006     m_textCtrl 
= (wxTextCtrl 
*)textControlClass
->CreateObject(); 
2007     HWND hWnd 
= (HWND
) TreeView_EditLabel(GetHwnd(), HITEM(item
)); 
2009     // this is not an error - the TVN_BEGINLABELEDIT handler might have 
2018     // textctrl is subclassed in MSWOnNotify 
2022 // End label editing, optionally cancelling the edit 
2023 void wxTreeCtrl::DoEndEditLabel(bool discardChanges
) 
2025     TreeView_EndEditLabelNow(GetHwnd(), discardChanges
); 
2030 wxTreeItemId 
wxTreeCtrl::DoHitTest(const wxPoint
& point
, int& flags
) 
2032     TV_HITTESTINFO hitTestInfo
; 
2033     hitTestInfo
.pt
.x 
= (int)point
.x
; 
2034     hitTestInfo
.pt
.y 
= (int)point
.y
; 
2036     TreeView_HitTest(GetHwnd(), &hitTestInfo
); 
2041     #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \ 
2042                                     flags |= wxTREE_HITTEST_##flag 
2044     TRANSLATE_FLAG(ABOVE
); 
2045     TRANSLATE_FLAG(BELOW
); 
2046     TRANSLATE_FLAG(NOWHERE
); 
2047     TRANSLATE_FLAG(ONITEMBUTTON
); 
2048     TRANSLATE_FLAG(ONITEMICON
); 
2049     TRANSLATE_FLAG(ONITEMINDENT
); 
2050     TRANSLATE_FLAG(ONITEMLABEL
); 
2051     TRANSLATE_FLAG(ONITEMRIGHT
); 
2052     TRANSLATE_FLAG(ONITEMSTATEICON
); 
2053     TRANSLATE_FLAG(TOLEFT
); 
2054     TRANSLATE_FLAG(TORIGHT
); 
2056     #undef TRANSLATE_FLAG 
2058     return wxTreeItemId(hitTestInfo
.hItem
); 
2061 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId
& item
, 
2063                                  bool textOnly
) const 
2067     // Virtual root items have no bounding rectangle 
2068     if ( IS_VIRTUAL_ROOT(item
) ) 
2073     if ( TreeView_GetItemRect(GetHwnd(), HITEM(item
), 
2076         rect 
= wxRect(wxPoint(rc
.left
, rc
.top
), wxPoint(rc
.right
, rc
.bottom
)); 
2082         // couldn't retrieve rect: for example, item isn't visible 
2087 // ---------------------------------------------------------------------------- 
2089 // ---------------------------------------------------------------------------- 
2091 // this is just a tiny namespace which is friend to wxTreeCtrl and so can use 
2092 // functions such as IsDataIndirect() 
2093 class wxTreeSortHelper
 
2096     static int CALLBACK 
Compare(LPARAM data1
, LPARAM data2
, LPARAM tree
); 
2099     static wxTreeItemId 
GetIdFromData(wxTreeCtrl 
*tree
, LPARAM item
) 
2101         wxTreeItemData 
*data 
= (wxTreeItemData 
*)item
; 
2102         if ( tree
->IsDataIndirect(data
) ) 
2104             data 
= ((wxTreeItemIndirectData 
*)data
)->GetData(); 
2107         return data
->GetId(); 
2111 int CALLBACK 
wxTreeSortHelper::Compare(LPARAM pItem1
, 
2115     wxCHECK_MSG( pItem1 
&& pItem2
, 0, 
2116                  wxT("sorting tree without data doesn't make sense") ); 
2118     wxTreeCtrl 
*tree 
= (wxTreeCtrl 
*)htree
; 
2120     return tree
->OnCompareItems(GetIdFromData(tree
, pItem1
), 
2121                                 GetIdFromData(tree
, pItem2
)); 
2124 void wxTreeCtrl::SortChildren(const wxTreeItemId
& item
) 
2126     wxCHECK_RET( item
.IsOk(), wxT("invalid tree item") ); 
2128     // rely on the fact that TreeView_SortChildren does the same thing as our 
2129     // default behaviour, i.e. sorts items alphabetically and so call it 
2130     // directly if we're not in derived class (much more efficient!) 
2131     if ( GetClassInfo() == CLASSINFO(wxTreeCtrl
) ) 
2133         TreeView_SortChildren(GetHwnd(), HITEM(item
), 0); 
2138         tvSort
.hParent 
= HITEM(item
); 
2139         tvSort
.lpfnCompare 
= wxTreeSortHelper::Compare
; 
2140         tvSort
.lParam 
= (LPARAM
)this; 
2141         TreeView_SortChildrenCB(GetHwnd(), &tvSort
, 0 /* reserved */); 
2145 // ---------------------------------------------------------------------------- 
2147 // ---------------------------------------------------------------------------- 
2149 bool wxTreeCtrl::MSWCommand(WXUINT cmd
, WXWORD id
) 
2151     if ( cmd 
== EN_UPDATE 
) 
2153         wxCommandEvent 
event(wxEVT_COMMAND_TEXT_UPDATED
, id
); 
2154         event
.SetEventObject( this ); 
2155         ProcessCommand(event
); 
2157     else if ( cmd 
== EN_KILLFOCUS 
) 
2159         wxCommandEvent 
event(wxEVT_KILL_FOCUS
, id
); 
2160         event
.SetEventObject( this ); 
2161         ProcessCommand(event
); 
2169     // command processed 
2173 // we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we 
2174 // only do it during dragging, minimize wxWin overhead (this is important for 
2175 // WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly 
2176 // instead of passing by wxWin events 
2177 WXLRESULT 
wxTreeCtrl::MSWWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
2179     bool processed 
= false; 
2181     bool isMultiple 
= HasFlag(wxTR_MULTIPLE
); 
2183     // This message is sent after a right-click, or when the "menu" key is pressed 
2184     if ( nMsg 
== WM_CONTEXTMENU 
) 
2186         int x 
= GET_X_LPARAM(lParam
), 
2187             y 
= GET_Y_LPARAM(lParam
); 
2188         // Convert the screen point to a client point 
2189         wxPoint MenuPoint 
= ScreenToClient(wxPoint(x
, y
)); 
2191         wxTreeEvent 
event( wxEVT_COMMAND_TREE_ITEM_MENU
, GetId() ); 
2193         // can't use GetSelection() here as it would assert in multiselect mode 
2194         event
.m_item 
= wxTreeItemId(TreeView_GetSelection(GetHwnd())); 
2195         event
.SetEventObject( this ); 
2197         // Get the bounding rectangle for the item, including the non-text areas 
2199         GetBoundingRect(event
.m_item
, ItemRect
, false); 
2200         // If the point is inside the bounding rectangle, use it as the click position. 
2201         // This should be the case for WM_CONTEXTMENU as the result of a right-click 
2202         if (ItemRect
.Inside(MenuPoint
)) 
2204             event
.m_pointDrag 
= MenuPoint
; 
2206         // Use the Explorer standard of putting the menu at the left edge of the text, 
2207         // in the vertical middle of the text. Should be the case for the "menu" key 
2210             // Use the bounding rectangle of only the text part 
2211             GetBoundingRect(event
.m_item
, ItemRect
, true); 
2212             event
.m_pointDrag 
= wxPoint(ItemRect
.GetX(), ItemRect
.GetY() + ItemRect
.GetHeight() / 2); 
2215         if ( GetEventHandler()->ProcessEvent(event
) ) 
2217         //else: continue with generating wxEVT_CONTEXT_MENU in base class code 
2219     else if ( (nMsg 
>= WM_MOUSEFIRST
) && (nMsg 
<= WM_MOUSELAST
) ) 
2221         // we only process mouse messages here and these parameters have the 
2222         // same meaning for all of them 
2223         int x 
= GET_X_LPARAM(lParam
), 
2224             y 
= GET_Y_LPARAM(lParam
); 
2225         HTREEITEM htItem 
= GetItemFromPoint(GetHwnd(), x
, y
); 
2227         TV_HITTESTINFO tvht
; 
2231         TreeView_HitTest(GetHwnd(), &tvht
); 
2235             case WM_RBUTTONDOWN
: 
2236                 // if the item we are about to right click on is not already 
2237                 // selected or if we click outside of any item, remove the 
2238                 // entire previous selection 
2239                 if ( !htItem 
|| !::IsItemSelected(GetHwnd(), htItem
) ) 
2244                 // select item and set the focus to the 
2245                 // newly selected item 
2246                 ::SelectItem(GetHwnd(), htItem
); 
2247                 ::SetFocus(GetHwnd(), htItem
); 
2250 #if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
2251             case WM_LBUTTONDOWN
: 
2252                 if ( htItem 
&& isMultiple 
&& (tvht
.flags 
& TVHT_ONITEM
) != 0 ) 
2254                     m_htClickedItem 
= (WXHTREEITEM
) htItem
; 
2255                     m_ptClick 
= wxPoint(x
, y
); 
2257                     if ( wParam 
& MK_CONTROL 
) 
2261                         // toggle selected state 
2262                         ::ToggleItemSelection(GetHwnd(), htItem
); 
2264                         ::SetFocus(GetHwnd(), htItem
); 
2266                         // reset on any click without Shift 
2267                         m_htSelStart
.Unset(); 
2271                     else if ( wParam 
& MK_SHIFT 
) 
2273                         // this selects all items between the starting one and 
2276                         if ( !m_htSelStart 
) 
2278                             // take the focused item 
2279                             m_htSelStart 
= TreeView_GetSelection(GetHwnd()); 
2283                             SelectRange(GetHwnd(), HITEM(m_htSelStart
), htItem
, 
2284                                     !(wParam 
& MK_CONTROL
)); 
2286                             ::SelectItem(GetHwnd(), htItem
); 
2288                         ::SetFocus(GetHwnd(), htItem
); 
2292                     else // normal click 
2294                         // avoid doing anything if we click on the only 
2295                         // currently selected item 
2299                         wxArrayTreeItemIds selections
; 
2300                         size_t count 
= GetSelections(selections
); 
2303                              HITEM(selections
[0]) != htItem 
) 
2305                             // clear the previously selected items, if the 
2306                             // user clicked outside of the present selection. 
2307                             // otherwise, perform the deselection on mouse-up. 
2308                             // this allows multiple drag and drop to work. 
2310                             if (!IsItemSelected(GetHwnd(), htItem
)) 
2314                                 // prevent the click from starting in-place editing 
2315                                 // which should only happen if we click on the 
2316                                 // already selected item (and nothing else is 
2319                                 TreeView_SelectItem(GetHwnd(), 0); 
2320                                 ::SelectItem(GetHwnd(), htItem
); 
2322                             ::SetFocus(GetHwnd(), htItem
); 
2326                         // reset on any click without Shift 
2327                         m_htSelStart
.Unset(); 
2331 #endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
2335                 if ( m_htClickedItem 
) 
2337                     int cx 
= abs(m_ptClick
.x 
- x
); 
2338                     int cy 
= abs(m_ptClick
.y 
- y
); 
2340                     if ( cx 
> GetSystemMetrics( SM_CXDRAG 
) || cy 
> GetSystemMetrics( SM_CYDRAG 
) ) 
2342                         HWND pWnd 
= ::GetParent( GetHwnd() ); 
2347                             tv
.hdr
.hwndFrom 
= GetHwnd(); 
2348                             tv
.hdr
.idFrom 
= ::GetWindowLong( GetHwnd(), GWL_ID 
); 
2349                             tv
.hdr
.code 
= TVN_BEGINDRAG
; 
2351                             tv
.itemNew
.hItem 
= HITEM(m_htClickedItem
); 
2354                             ZeroMemory(&tviAux
, sizeof(tviAux
)); 
2355                             tviAux
.hItem 
= HITEM(m_htClickedItem
); 
2356                             tviAux
.mask 
= TVIF_STATE 
| TVIF_PARAM
; 
2357                             tviAux
.stateMask 
= 0xffffffff; 
2358                             TreeView_GetItem( GetHwnd(), &tviAux 
); 
2360                             tv
.itemNew
.state 
= tviAux
.state
; 
2361                             tv
.itemNew
.lParam 
= tviAux
.lParam
; 
2366                             ::SendMessage( pWnd
, WM_NOTIFY
, tv
.hdr
.idFrom
, (LPARAM
)&tv 
); 
2368                         m_htClickedItem
.Unset(); 
2371 #endif // __WXWINCE__ 
2375                     m_dragImage
->Move(wxPoint(x
, y
)); 
2378                         // highlight the item as target (hiding drag image is 
2379                         // necessary - otherwise the display will be corrupted) 
2380                         m_dragImage
->Hide(); 
2381                         TreeView_SelectDropTarget(GetHwnd(), htItem
); 
2382                         m_dragImage
->Show(); 
2389                 // facilitates multiple drag-and-drop 
2390                 if (htItem 
&& isMultiple
) 
2392                     wxArrayTreeItemIds selections
; 
2393                     size_t count 
= GetSelections(selections
); 
2396                         !(wParam 
& MK_CONTROL
) && 
2397                         !(wParam 
& MK_SHIFT
)) 
2400                         TreeView_SelectItem(GetHwnd(), htItem
); 
2401                         ::SelectItem(GetHwnd(), htItem
); 
2402                         ::SetFocus(GetHwnd(), htItem
); 
2404                     m_htClickedItem
.Unset(); 
2412                     m_dragImage
->EndDrag(); 
2416                     // generate the drag end event 
2417                     wxTreeEvent 
event(wxEVT_COMMAND_TREE_END_DRAG
, m_windowId
); 
2419                     event
.m_item 
= htItem
; 
2420                     event
.m_pointDrag 
= wxPoint(x
, y
); 
2421                     event
.SetEventObject(this); 
2423                     (void)GetEventHandler()->ProcessEvent(event
); 
2425                     // if we don't do it, the tree seems to think that 2 items 
2426                     // are selected simultaneously which is quite weird 
2427                     TreeView_SelectDropTarget(GetHwnd(), 0); 
2432 #if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
2433     else if ( (nMsg 
== WM_SETFOCUS 
|| nMsg 
== WM_KILLFOCUS
) && isMultiple 
) 
2435         // the tree control greys out the selected item when it loses focus and 
2436         // paints it as selected again when it regains it, but it won't do it 
2437         // for the other items itself - help it 
2438         wxArrayTreeItemIds selections
; 
2439         size_t count 
= GetSelections(selections
); 
2441         for ( size_t n 
= 0; n 
< count
; n
++ ) 
2443             // TreeView_GetItemRect() will return false if item is not visible, 
2444             // which may happen perfectly well 
2445             if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections
[n
]), 
2448                 ::InvalidateRect(GetHwnd(), &rect
, FALSE
); 
2452     else if ( nMsg 
== WM_KEYDOWN 
&& isMultiple 
) 
2454         bool bCtrl 
= wxIsCtrlDown(), 
2455              bShift 
= wxIsShiftDown(); 
2457         HTREEITEM htSel 
= (HTREEITEM
)TreeView_GetSelection(GetHwnd()); 
2463                     ::ToggleItemSelection(GetHwnd(), htSel
); 
2469                     ::SelectItem(GetHwnd(), htSel
); 
2477                 if ( !bCtrl 
&& !bShift 
) 
2479                     // no modifiers, just clear selection and then let the default 
2480                     // processing to take place 
2485                     (void)wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
2487                     HTREEITEM htNext 
= (HTREEITEM
) 
2488                         TreeView_GetNextItem
 
2492                             wParam 
== VK_UP 
? TVGN_PREVIOUSVISIBLE
 
2498                         // at the top/bottom 
2504                         if ( !m_htSelStart 
) 
2505                             m_htSelStart 
= htSel
; 
2507                         SelectRange(GetHwnd(), HITEM(m_htSelStart
), htNext
); 
2511                         // without changing selection 
2512                         ::SetFocus(GetHwnd(), htNext
); 
2523                 // TODO: handle Shift/Ctrl with these keys 
2524                 if ( !bCtrl 
&& !bShift 
) 
2528                     m_htSelStart
.Unset(); 
2532 #endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 
2533     else if ( nMsg 
== WM_COMMAND 
) 
2535         // if we receive a EN_KILLFOCUS command from the in-place edit control 
2536         // used for label editing, make sure to end editing 
2539         UnpackCommand(wParam
, lParam
, &id
, &hwnd
, &cmd
); 
2541         if ( cmd 
== EN_KILLFOCUS 
) 
2543             if ( m_textCtrl 
&& m_textCtrl
->GetHandle() == hwnd 
) 
2553         rc 
= wxControl::MSWWindowProc(nMsg
, wParam
, lParam
); 
2559 wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg
, WXWPARAM wParam
, WXLPARAM lParam
) 
2561     // default WM_RBUTTONDOWN handler enters modal loop inside DefWindowProc() 
2562     // waiting for WM_RBUTTONUP and then sends the resulting WM_CONTEXTMENU to 
2563     // the parent window, not us, which completely breaks everything so simply 
2564     // don't let it see this message at all 
2565     if ( nMsg 
== WM_RBUTTONDOWN 
) 
2568     // but because of the above we don't get NM_RCLICK which is normally 
2569     // generated by tree window proc when the modal loop mentioned above ends 
2570     // because the mouse is released -- synthesize it ourselves instead 
2571     if ( nMsg 
== WM_RBUTTONUP 
) 
2574         hdr
.hwndFrom 
= GetHwnd(); 
2575         hdr
.idFrom 
= GetId(); 
2576         hdr
.code 
= NM_RCLICK
; 
2579         MSWOnNotify(GetId(), (LPARAM
)&hdr
, &rc
); 
2581         // continue as usual 
2584     if ( nMsg 
== WM_CHAR 
) 
2586         // also don't let the control process Space and Return keys because it 
2587         // doesn't do anything useful with them anyhow but always beeps 
2588         // annoyingly when it receives them and there is no way to turn it off 
2589         // simply if you just process TREEITEM_ACTIVATED event to which Space 
2590         // and Enter presses are mapped in your code 
2591         if ( wParam 
== VK_SPACE 
|| wParam 
== VK_RETURN 
) 
2595     return wxControl::MSWDefWindowProc(nMsg
, wParam
, lParam
); 
2598 // process WM_NOTIFY Windows message 
2599 bool wxTreeCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM 
*result
) 
2601     wxTreeEvent 
event(wxEVT_NULL
, m_windowId
); 
2602     wxEventType eventType 
= wxEVT_NULL
; 
2603     NMHDR 
*hdr 
= (NMHDR 
*)lParam
; 
2605     switch ( hdr
->code 
) 
2608             eventType 
= wxEVT_COMMAND_TREE_BEGIN_DRAG
; 
2611         case TVN_BEGINRDRAG
: 
2613                 if ( eventType 
== wxEVT_NULL 
) 
2614                     eventType 
= wxEVT_COMMAND_TREE_BEGIN_RDRAG
; 
2615                 //else: left drag, already set above 
2617                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
2619                 event
.m_item 
= tv
->itemNew
.hItem
; 
2620                 event
.m_pointDrag 
= wxPoint(tv
->ptDrag
.x
, tv
->ptDrag
.y
); 
2622                 // don't allow dragging by default: the user code must 
2623                 // explicitly say that it wants to allow it to avoid breaking 
2629         case TVN_BEGINLABELEDIT
: 
2631                 eventType 
= wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT
; 
2632                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
2634                 // although the user event handler may still veto it, it is 
2635                 // important to set it now so that calls to SetItemText() from 
2636                 // the event handler would change the text controls contents 
2638                 event
.m_item 
= info
->item
.hItem
; 
2639                 event
.m_label 
= info
->item
.pszText
; 
2640                 event
.m_editCancelled 
= false; 
2644         case TVN_DELETEITEM
: 
2646                 eventType 
= wxEVT_COMMAND_TREE_DELETE_ITEM
; 
2647                 NM_TREEVIEW 
*tv 
= (NM_TREEVIEW 
*)lParam
; 
2649                 event
.m_item 
= tv
->itemOld
.hItem
; 
2653                     wxMapTreeAttr::iterator it 
= m_attrs
.find(tv
->itemOld
.hItem
); 
2654                     if ( it 
!= m_attrs
.end() ) 
2663         case TVN_ENDLABELEDIT
: 
2665                 eventType 
= wxEVT_COMMAND_TREE_END_LABEL_EDIT
; 
2666                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
2668                 event
.m_item 
= info
->item
.hItem
; 
2669                 event
.m_label 
= info
->item
.pszText
; 
2670                 event
.m_editCancelled 
= info
->item
.pszText 
== NULL
; 
2675         // These *must* not be removed or TVN_GETINFOTIP will 
2676         // not be processed each time the mouse is moved 
2677         // and the tooltip will only ever update once. 
2686 #ifdef TVN_GETINFOTIP 
2687         case TVN_GETINFOTIP
: 
2689                 eventType 
= wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP
; 
2690                 NMTVGETINFOTIP 
*info 
= (NMTVGETINFOTIP
*)lParam
; 
2692                 // Which item are we trying to get a tooltip for? 
2693                 event
.m_item 
= info
->hItem
; 
2700         case TVN_GETDISPINFO
: 
2701             eventType 
= wxEVT_COMMAND_TREE_GET_INFO
; 
2704         case TVN_SETDISPINFO
: 
2706                 if ( eventType 
== wxEVT_NULL 
) 
2707                     eventType 
= wxEVT_COMMAND_TREE_SET_INFO
; 
2708                 //else: get, already set above 
2710                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
2712                 event
.m_item 
= info
->item
.hItem
; 
2716         case TVN_ITEMEXPANDING
: 
2717         case TVN_ITEMEXPANDED
: 
2719                 NM_TREEVIEW
* tv 
= (NM_TREEVIEW
*)lParam
; 
2722                 switch ( tv
->action 
) 
2725                         wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv
->action
); 
2733                         what 
= IDX_COLLAPSE
; 
2737                 int how 
= hdr
->code 
== TVN_ITEMEXPANDING 
? IDX_DOING
 
2740                 eventType 
= gs_expandEvents
[what
][how
]; 
2742                 event
.m_item 
= tv
->itemNew
.hItem
; 
2748                 eventType 
= wxEVT_COMMAND_TREE_KEY_DOWN
; 
2749                 TV_KEYDOWN 
*info 
= (TV_KEYDOWN 
*)lParam
; 
2751                 // fabricate the lParam and wParam parameters sufficiently 
2752                 // similar to the ones from a "real" WM_KEYDOWN so that 
2753                 // CreateKeyEvent() works correctly 
2754                 const bool isAltDown 
= ::GetKeyState(VK_MENU
) < 0; 
2755                 WXLPARAM lParam 
= (isAltDown 
? KF_ALTDOWN 
: 0) << 16; 
2757                 WXWPARAM wParam 
= info
->wVKey
; 
2759                 int keyCode 
= wxCharCodeMSWToWX(info
->wVKey
); 
2762                     // wxCharCodeMSWToWX() returns 0 to indicate that this is a 
2767                 event
.m_evtKey 
= CreateKeyEvent(wxEVT_KEY_DOWN
, 
2772                 // a separate event for Space/Return 
2773                 if ( !wxIsCtrlDown() && !wxIsShiftDown() && !isAltDown 
&& 
2774                      ((info
->wVKey 
== VK_SPACE
) || (info
->wVKey 
== VK_RETURN
)) ) 
2776                     wxTreeEvent 
event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED
, 
2778                     event2
.SetEventObject(this); 
2779                     if ( !(GetWindowStyle() & wxTR_MULTIPLE
) ) 
2781                         event2
.m_item 
= GetSelection(); 
2783                     //else: don't know how to get it 
2785                     (void)GetEventHandler()->ProcessEvent(event2
); 
2790         // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of 
2791         //     TVN_SELCHANGEDW in Unicode mode under Win98. Therefore 
2792         //     we have to handle both messages: 
2793         case TVN_SELCHANGEDA
: 
2794         case TVN_SELCHANGEDW
: 
2795             eventType 
= wxEVT_COMMAND_TREE_SEL_CHANGED
; 
2798         case TVN_SELCHANGINGA
: 
2799         case TVN_SELCHANGINGW
: 
2801                 if ( eventType 
== wxEVT_NULL 
) 
2802                     eventType 
= wxEVT_COMMAND_TREE_SEL_CHANGING
; 
2803                 //else: already set above 
2805                 if (hdr
->code 
== TVN_SELCHANGINGW 
|| 
2806                     hdr
->code 
== TVN_SELCHANGEDW
) 
2808                     NM_TREEVIEWW
* tv 
= (NM_TREEVIEWW 
*)lParam
; 
2809                     event
.m_item 
= tv
->itemNew
.hItem
; 
2810                     event
.m_itemOld 
= tv
->itemOld
.hItem
; 
2814                     NM_TREEVIEWA
* tv 
= (NM_TREEVIEWA 
*)lParam
; 
2815                     event
.m_item 
= tv
->itemNew
.hItem
; 
2816                     event
.m_itemOld 
= tv
->itemOld
.hItem
; 
2821             // instead of explicitly checking for _WIN32_IE, check if the 
2822             // required symbols are available in the headers 
2823 #if defined(CDDS_PREPAINT) && !wxUSE_COMCTL32_SAFELY 
2826                 LPNMTVCUSTOMDRAW lptvcd 
= (LPNMTVCUSTOMDRAW
)lParam
; 
2827                 NMCUSTOMDRAW
& nmcd 
= lptvcd
->nmcd
; 
2828                 switch ( nmcd
.dwDrawStage 
) 
2831                         // if we've got any items with non standard attributes, 
2832                         // notify us before painting each item 
2833                         *result 
= m_hasAnyAttr 
? CDRF_NOTIFYITEMDRAW
 
2837                     case CDDS_ITEMPREPAINT
: 
2839                             wxMapTreeAttr::iterator
 
2840                                 it 
= m_attrs
.find((void *)nmcd
.dwItemSpec
); 
2842                             if ( it 
== m_attrs
.end() ) 
2844                                 // nothing to do for this item 
2845                                 *result 
= CDRF_DODEFAULT
; 
2849                             wxTreeItemAttr 
* const attr 
= it
->second
; 
2851                             // selection colours should override ours, 
2852                             // otherwise it is too confusing ot the user 
2853                             if ( !(nmcd
.uItemState 
& CDIS_SELECTED
) ) 
2856                                 if ( attr
->HasBackgroundColour() ) 
2858                                     colBack 
= attr
->GetBackgroundColour(); 
2859                                     lptvcd
->clrTextBk 
= wxColourToRGB(colBack
); 
2863                             // but we still want to keep the special foreground 
2864                             // colour when we don't have focus (we can't keep 
2865                             // it when we do, it would usually be unreadable on 
2866                             // the almost inverted bg colour...) 
2867                             if ( !(nmcd
.uItemState 
& CDIS_SELECTED
) || 
2868                                     FindFocus() != this ) 
2871                                 if ( attr
->HasTextColour() ) 
2873                                     colText 
= attr
->GetTextColour(); 
2874                                     lptvcd
->clrText 
= wxColourToRGB(colText
); 
2878                             if ( attr
->HasFont() ) 
2880                                 HFONT hFont 
= GetHfontOf(attr
->GetFont()); 
2882                                 ::SelectObject(nmcd
.hdc
, hFont
); 
2884                                 *result 
= CDRF_NEWFONT
; 
2886                             else // no specific font 
2888                                 *result 
= CDRF_DODEFAULT
; 
2894                         *result 
= CDRF_DODEFAULT
; 
2898             // we always process it 
2900 #endif // have owner drawn support in headers 
2904                 DWORD pos 
= GetMessagePos(); 
2906                 point
.x 
= LOWORD(pos
); 
2907                 point
.y 
= HIWORD(pos
); 
2908                 ::MapWindowPoints(HWND_DESKTOP
, GetHwnd(), &point
, 1); 
2910                 wxTreeItemId item 
= HitTest(wxPoint(point
.x
, point
.y
), flags
); 
2911                 if (flags 
& wxTREE_HITTEST_ONITEMSTATEICON
) 
2913                     event
.m_item 
= item
; 
2914                     eventType 
= wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK
; 
2922                 TV_HITTESTINFO tvhti
; 
2923                 ::GetCursorPos(&tvhti
.pt
); 
2924                 ::ScreenToClient(GetHwnd(), &tvhti
.pt
); 
2925                 if ( TreeView_HitTest(GetHwnd(), &tvhti
) ) 
2927                     if ( tvhti
.flags 
& TVHT_ONITEM 
) 
2929                         event
.m_item 
= tvhti
.hItem
; 
2930                         eventType 
= (int)hdr
->code 
== NM_DBLCLK
 
2931                                     ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED
 
2932                                     : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK
; 
2934                         event
.m_pointDrag
.x 
= tvhti
.pt
.x
; 
2935                         event
.m_pointDrag
.y 
= tvhti
.pt
.y
; 
2944             return wxControl::MSWOnNotify(idCtrl
, lParam
, result
); 
2947     event
.SetEventObject(this); 
2948     event
.SetEventType(eventType
); 
2950     bool processed 
= GetEventHandler()->ProcessEvent(event
); 
2953     switch ( hdr
->code 
) 
2956             // we translate NM_DBLCLK into ACTIVATED event, so don't interpret 
2957             // the return code of this event handler as the return value for 
2958             // NM_DBLCLK - otherwise, double clicking the item to toggle its 
2959             // expanded status would never work 
2964         case TVN_BEGINRDRAG
: 
2965             if ( event
.IsAllowed() ) 
2967                 // normally this is impossible because the m_dragImage is 
2968                 // deleted once the drag operation is over 
2969                 wxASSERT_MSG( !m_dragImage
, _T("starting to drag once again?") ); 
2971                 m_dragImage 
= new wxDragImage(*this, event
.m_item
); 
2972                 m_dragImage
->BeginDrag(wxPoint(0,0), this); 
2973                 m_dragImage
->Show(); 
2977         case TVN_DELETEITEM
: 
2979                 // NB: we might process this message using wxWidgets event 
2980                 //     tables, but due to overhead of wxWin event system we 
2981                 //     prefer to do it here ourself (otherwise deleting a tree 
2982                 //     with many items is just too slow) 
2983                 NM_TREEVIEW
* tv 
= (NM_TREEVIEW 
*)lParam
; 
2985                 wxTreeItemId item 
= event
.m_item
; 
2986                 if ( HasIndirectData(item
) ) 
2988                     wxTreeItemIndirectData 
*data 
= (wxTreeItemIndirectData 
*) 
2990                     delete data
; // can't be NULL here 
2994                     wxTreeItemData 
*data 
= (wxTreeItemData 
*)tv
->itemOld
.lParam
; 
2995                     delete data
; // may be NULL, ok 
2998                 processed 
= true; // Make sure we don't get called twice 
3002         case TVN_BEGINLABELEDIT
: 
3003             // return true to cancel label editing 
3004             *result 
= !event
.IsAllowed(); 
3006             // set ES_WANTRETURN ( like we do in BeginLabelEdit ) 
3007             if ( event
.IsAllowed() ) 
3009                 HWND hText 
= TreeView_GetEditControl(GetHwnd()); 
3012                     // MBN: if m_textCtrl already has an HWND, it is a stale 
3013                     // pointer from a previous edit (because the user 
3014                     // didn't modify the label before dismissing the control, 
3015                     // and TVN_ENDLABELEDIT was not sent), so delete it 
3016                     if(m_textCtrl 
&& m_textCtrl
->GetHWND() != 0) 
3019                         m_textCtrl 
= new wxTextCtrl(); 
3020                     m_textCtrl
->SetParent(this); 
3021                     m_textCtrl
->SetHWND((WXHWND
)hText
); 
3022                     m_textCtrl
->SubclassWin((WXHWND
)hText
); 
3024                     // set wxTE_PROCESS_ENTER style for the text control to 
3025                     // force it to process the Enter presses itself, otherwise 
3026                     // they could be stolen from it by the dialog 
3028                     m_textCtrl
->SetWindowStyle(m_textCtrl
->GetWindowStyle() 
3029                                                | wxTE_PROCESS_ENTER
); 
3032             else // we had set m_idEdited before 
3038         case TVN_ENDLABELEDIT
: 
3039             // return true to set the label to the new string: note that we 
3040             // also must pretend that we did process the message or it is going 
3041             // to be passed to DefWindowProc() which will happily return false 
3042             // cancelling the label change 
3043             *result 
= event
.IsAllowed(); 
3046             // ensure that we don't have the text ctrl which is going to be 
3052 #ifdef TVN_GETINFOTIP 
3053          case TVN_GETINFOTIP
: 
3055                 // If the user permitted a tooltip change, change it 
3056                 if (event
.IsAllowed()) 
3058                     SetToolTip(event
.m_label
); 
3065         case TVN_SELCHANGING
: 
3066         case TVN_ITEMEXPANDING
: 
3067             // return true to prevent the action from happening 
3068             *result 
= !event
.IsAllowed(); 
3071         case TVN_ITEMEXPANDED
: 
3072             // the item is not refreshed properly after expansion when it has 
3073             // an image depending on the expanded/collapsed state - bug in 
3074             // comctl32.dll or our code? 
3076                 NM_TREEVIEW
* tv 
= (NM_TREEVIEW 
*)lParam
; 
3077                 wxTreeItemId 
id(tv
->itemNew
.hItem
); 
3079                 int image 
= GetItemImage(id
, wxTreeItemIcon_Expanded
); 
3087         case TVN_GETDISPINFO
: 
3088             // NB: so far the user can't set the image himself anyhow, so do it 
3089             //     anyway - but this may change later 
3090             //if ( /* !processed && */ 1 ) 
3092                 wxTreeItemId item 
= event
.m_item
; 
3093                 TV_DISPINFO 
*info 
= (TV_DISPINFO 
*)lParam
; 
3094                 if ( info
->item
.mask 
& TVIF_IMAGE 
) 
3097                         DoGetItemImageFromData
 
3100                          IsExpanded(item
) ? wxTreeItemIcon_Expanded
 
3101                                           : wxTreeItemIcon_Normal
 
3104                 if ( info
->item
.mask 
& TVIF_SELECTEDIMAGE 
) 
3106                     info
->item
.iSelectedImage 
= 
3107                         DoGetItemImageFromData
 
3110                          IsExpanded(item
) ? wxTreeItemIcon_SelectedExpanded
 
3111                                           : wxTreeItemIcon_Selected
 
3118             // for the other messages the return value is ignored and there is 
3119             // nothing special to do 
3124 // ---------------------------------------------------------------------------- 
3126 // ---------------------------------------------------------------------------- 
3128 // why do they define INDEXTOSTATEIMAGEMASK but not the inverse? 
3129 #define STATEIMAGEMASKTOINDEX(state) (((state) & TVIS_STATEIMAGEMASK) >> 12) 
3131 void wxTreeCtrl::SetState(const wxTreeItemId
& node
, int state
) 
3134     tvi
.hItem 
= (HTREEITEM
)node
.m_pItem
; 
3135     tvi
.mask 
= TVIF_STATE
; 
3136     tvi
.stateMask 
= TVIS_STATEIMAGEMASK
; 
3138     // Select the specified state, or -1 == cycle to the next one. 
3141         TreeView_GetItem(GetHwnd(), &tvi
); 
3143         state 
= STATEIMAGEMASKTOINDEX(tvi
.state
) + 1; 
3144         if ( state 
== m_imageListState
->GetImageCount() ) 
3148     wxCHECK_RET( state 
< m_imageListState
->GetImageCount(), 
3149                  _T("wxTreeCtrl::SetState(): item index out of bounds") ); 
3151     tvi
.state 
= INDEXTOSTATEIMAGEMASK(state
); 
3153     TreeView_SetItem(GetHwnd(), &tvi
); 
3156 int wxTreeCtrl::GetState(const wxTreeItemId
& node
) 
3159     tvi
.hItem 
= (HTREEITEM
)node
.m_pItem
; 
3160     tvi
.mask 
= TVIF_STATE
; 
3161     tvi
.stateMask 
= TVIS_STATEIMAGEMASK
; 
3162     TreeView_GetItem(GetHwnd(), &tvi
); 
3164     return STATEIMAGEMASKTOINDEX(tvi
.state
); 
3167 #endif // wxUSE_TREECTRL