1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/treectrl.cpp
3 // Purpose:     wxTreeCtrl
4 // Author:      Julian Smart
5 // Modified by: Vadim Zeitlin to be less MSW-specific on 10.10.98
6 // Created:     1997
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #ifdef __BORLANDC__
23     #pragma hdrstop
24 #endif
25 
26 #if wxUSE_TREECTRL
27 
28 #include "wx/treectrl.h"
29 
30 #ifndef WX_PRECOMP
31     #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
32     #include "wx/msw/missing.h"
33     #include "wx/dynarray.h"
34     #include "wx/log.h"
35     #include "wx/app.h"
36     #include "wx/settings.h"
37 #endif
38 
39 #include "wx/dynlib.h"
40 #include "wx/msw/private.h"
41 
42 #include "wx/imaglist.h"
43 #include "wx/msw/dragimag.h"
44 #include "wx/msw/uxtheme.h"
45 
46 // macros to hide the cast ugliness
47 // --------------------------------
48 
49 // get HTREEITEM from wxTreeItemId
50 #define HITEM(item)     ((HTREEITEM)(((item).m_pItem)))
51 
52 
53 // older SDKs are missing these
54 #ifndef TVN_ITEMCHANGINGA
55 
56 #define TVN_ITEMCHANGINGA (TVN_FIRST-16)
57 #define TVN_ITEMCHANGINGW (TVN_FIRST-17)
58 
59 typedef struct tagNMTVITEMCHANGE
60 {
61     NMHDR hdr;
62     UINT uChanged;
63     HTREEITEM hItem;
64     UINT uStateNew;
65     UINT uStateOld;
66     LPARAM lParam;
67 } NMTVITEMCHANGE;
68 
69 #endif
70 
71 
72 // this helper class is used on vista systems for preventing unwanted
73 // item state changes in the vista tree control.  It is only effective in
74 // multi-select mode on vista systems.
75 
76 // The vista tree control includes some new code that originally broke the
77 // multi-selection tree, causing seemingly spurious item selection state changes
78 // during Shift or Ctrl-click item selection. (To witness the original broken
79 // behaviour, simply make IsLocked() below always return false). This problem was
80 // solved by using the following class to 'unlock' an item's selection state.
81 
82 class TreeItemUnlocker
83 {
84 public:
85     // unlock a single item
TreeItemUnlocker(HTREEITEM item)86     TreeItemUnlocker(HTREEITEM item)
87     {
88         m_oldUnlockedItem = ms_unlockedItem;
89         ms_unlockedItem = item;
90     }
91 
92     // unlock all items, don't use unless absolutely necessary
TreeItemUnlocker()93     TreeItemUnlocker()
94     {
95         m_oldUnlockedItem = ms_unlockedItem;
96         ms_unlockedItem = (HTREEITEM)-1;
97     }
98 
99     // lock everything back
~TreeItemUnlocker()100     ~TreeItemUnlocker() { ms_unlockedItem = m_oldUnlockedItem; }
101 
102 
103     // check if the item state is currently locked
IsLocked(HTREEITEM item)104     static bool IsLocked(HTREEITEM item)
105         { return ms_unlockedItem != (HTREEITEM)-1 && item != ms_unlockedItem; }
106 
107 private:
108     static HTREEITEM ms_unlockedItem;
109     HTREEITEM m_oldUnlockedItem;
110 
111     wxDECLARE_NO_COPY_CLASS(TreeItemUnlocker);
112 };
113 
114 HTREEITEM TreeItemUnlocker::ms_unlockedItem = NULL;
115 
116 // another helper class: set the variable to true during its lifetime and reset
117 // it to false when it is destroyed
118 //
119 // it is currently always used with wxTreeCtrl::m_changingSelection
120 class TempSetter
121 {
122 public:
TempSetter(bool & var)123     TempSetter(bool& var) : m_var(var)
124     {
125         wxASSERT_MSG( !m_var, "variable shouldn't be already set" );
126         m_var = true;
127     }
128 
~TempSetter()129     ~TempSetter()
130     {
131         m_var = false;
132     }
133 
134 private:
135     bool& m_var;
136 
137     wxDECLARE_NO_COPY_CLASS(TempSetter);
138 };
139 
140 // ----------------------------------------------------------------------------
141 // private functions
142 // ----------------------------------------------------------------------------
143 
144 namespace
145 {
146 
147 // Work around a problem with TreeView_GetItemRect() when using MinGW/Cygwin:
148 // it results in warnings about breaking strict aliasing rules because HITEM is
149 // passed via a RECT pointer, so use a union to avoid them and define our own
150 // version of the standard macro using it.
151 union TVGetItemRectParam
152 {
153     RECT rect;
154     HTREEITEM hItem;
155 };
156 
157 inline bool
wxTreeView_GetItemRect(HWND hwnd,HTREEITEM hItem,TVGetItemRectParam & param,BOOL fItemRect)158 wxTreeView_GetItemRect(HWND hwnd,
159                        HTREEITEM hItem,
160                        TVGetItemRectParam& param,
161                        BOOL fItemRect)
162 {
163     param.hItem = hItem;
164     return ::SendMessage(hwnd, TVM_GETITEMRECT, fItemRect,
165                         (LPARAM)&param) == TRUE;
166 }
167 
168 } // anonymous namespace
169 
170 // wrappers for TreeView_GetItem/TreeView_SetItem
IsItemSelected(HWND hwndTV,HTREEITEM hItem)171 static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
172 {
173     TV_ITEM tvi;
174     tvi.mask = TVIF_STATE | TVIF_HANDLE;
175     tvi.stateMask = TVIS_SELECTED;
176     tvi.hItem = hItem;
177 
178     TreeItemUnlocker unlocker(hItem);
179 
180     if ( !TreeView_GetItem(hwndTV, &tvi) )
181     {
182         wxLogLastError(wxT("TreeView_GetItem"));
183     }
184 
185     return (tvi.state & TVIS_SELECTED) != 0;
186 }
187 
SelectItem(HWND hwndTV,HTREEITEM hItem,bool select=true)188 static bool SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true)
189 {
190     TV_ITEM tvi;
191     tvi.mask = TVIF_STATE | TVIF_HANDLE;
192     tvi.stateMask = TVIS_SELECTED;
193     tvi.state = select ? TVIS_SELECTED : 0;
194     tvi.hItem = hItem;
195 
196     TreeItemUnlocker unlocker(hItem);
197 
198     if ( TreeView_SetItem(hwndTV, &tvi) == -1 )
199     {
200         wxLogLastError(wxT("TreeView_SetItem"));
201         return false;
202     }
203 
204     return true;
205 }
206 
UnselectItem(HWND hwndTV,HTREEITEM htItem)207 static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem)
208 {
209     SelectItem(hwndTV, htItem, false);
210 }
211 
ToggleItemSelection(HWND hwndTV,HTREEITEM htItem)212 static inline void ToggleItemSelection(HWND hwndTV, HTREEITEM htItem)
213 {
214     SelectItem(hwndTV, htItem, !IsItemSelected(hwndTV, htItem));
215 }
216 
217 // helper function which selects all items in a range and, optionally,
218 // deselects all the other ones
219 //
220 // returns true if the selection changed at all or false if nothing changed
221 
222 // flags for SelectRange()
223 enum
224 {
225     SR_SIMULATE = 1,        // don't do anything, just return true or false
226     SR_UNSELECT_OTHERS = 2  // deselect the items not in range
227 };
228 
SelectRange(HWND hwndTV,HTREEITEM htFirst,HTREEITEM htLast,int flags)229 static bool SelectRange(HWND hwndTV,
230                         HTREEITEM htFirst,
231                         HTREEITEM htLast,
232                         int flags)
233 {
234     // find the first (or last) item and select it
235     bool changed = false;
236     bool cont = true;
237     HTREEITEM htItem = (HTREEITEM)TreeView_GetRoot(hwndTV);
238 
239     while ( htItem && cont )
240     {
241         if ( (htItem == htFirst) || (htItem == htLast) )
242         {
243             if ( !IsItemSelected(hwndTV, htItem) )
244             {
245                 if ( !(flags & SR_SIMULATE) )
246                 {
247                     SelectItem(hwndTV, htItem);
248                 }
249 
250                 changed = true;
251             }
252 
253             cont = false;
254         }
255         else // not first or last
256         {
257             if ( flags & SR_UNSELECT_OTHERS )
258             {
259                 if ( IsItemSelected(hwndTV, htItem) )
260                 {
261                     if ( !(flags & SR_SIMULATE) )
262                         UnselectItem(hwndTV, htItem);
263 
264                     changed = true;
265                 }
266             }
267         }
268 
269         htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
270     }
271 
272     // select the items in range
273     cont = htFirst != htLast;
274     while ( htItem && cont )
275     {
276         if ( !IsItemSelected(hwndTV, htItem) )
277         {
278             if ( !(flags & SR_SIMULATE) )
279             {
280                 SelectItem(hwndTV, htItem);
281             }
282 
283             changed = true;
284         }
285 
286         cont = (htItem != htFirst) && (htItem != htLast);
287 
288         htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
289     }
290 
291     // optionally deselect the rest
292     if ( flags & SR_UNSELECT_OTHERS )
293     {
294         while ( htItem )
295         {
296             if ( IsItemSelected(hwndTV, htItem) )
297             {
298                 if ( !(flags & SR_SIMULATE) )
299                 {
300                     UnselectItem(hwndTV, htItem);
301                 }
302 
303                 changed = true;
304             }
305 
306             htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
307         }
308     }
309 
310     // seems to be necessary - otherwise the just selected items don't always
311     // appear as selected
312     if ( !(flags & SR_SIMULATE) )
313     {
314         UpdateWindow(hwndTV);
315     }
316 
317     return changed;
318 }
319 
320 // helper function which tricks the standard control into changing the focused
321 // item without changing anything else (if someone knows why Microsoft doesn't
322 // allow to do it by just setting TVIS_FOCUSED flag, please tell me!)
323 //
324 // returns true if the focus was changed, false if the given item was already
325 // the focused one
SetFocus(HWND hwndTV,HTREEITEM htItem)326 static bool SetFocus(HWND hwndTV, HTREEITEM htItem)
327 {
328     // the current focus
329     HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV);
330 
331     if ( htItem == htFocus )
332         return false;
333 
334     if ( htItem )
335     {
336         // remember the selection state of the item
337         bool wasSelected = IsItemSelected(hwndTV, htItem);
338 
339         if ( htFocus && IsItemSelected(hwndTV, htFocus) )
340         {
341             // prevent the tree from unselecting the old focus which it
342             // would do by default (TreeView_SelectItem unselects the
343             // focused item)
344             TreeView_SelectItem(hwndTV, 0);
345             SelectItem(hwndTV, htFocus);
346         }
347 
348         TreeView_SelectItem(hwndTV, htItem);
349 
350         if ( !wasSelected )
351         {
352             // need to clear the selection which TreeView_SelectItem() gave
353             // us
354             UnselectItem(hwndTV, htItem);
355         }
356         //else: was selected, still selected - ok
357     }
358     else // reset focus
359     {
360         bool wasFocusSelected = IsItemSelected(hwndTV, htFocus);
361 
362         // just clear the focus
363         TreeView_SelectItem(hwndTV, 0);
364 
365         if ( wasFocusSelected )
366         {
367             // restore the selection state
368             SelectItem(hwndTV, htFocus);
369         }
370     }
371 
372     return true;
373 }
374 
375 // ----------------------------------------------------------------------------
376 // private classes
377 // ----------------------------------------------------------------------------
378 
379 // a convenient wrapper around TV_ITEM struct which adds a ctor
380 #ifdef __VISUALC__
381 #pragma warning( disable : 4097 ) // inheriting from typedef
382 #endif
383 
384 struct wxTreeViewItem : public TV_ITEM
385 {
wxTreeViewItemwxTreeViewItem386     wxTreeViewItem(const wxTreeItemId& item,    // the item handle
387                    UINT mask_,                  // fields which are valid
388                    UINT stateMask_ = 0)         // for TVIF_STATE only
389     {
390         wxZeroMemory(*this);
391 
392         // hItem member is always valid
393         mask = mask_ | TVIF_HANDLE;
394         stateMask = stateMask_;
395         hItem = HITEM(item);
396     }
397 };
398 
399 // ----------------------------------------------------------------------------
400 // This class is our userdata/lParam for the TV_ITEMs stored in the treeview.
401 //
402 // We need this for a couple of reasons:
403 //
404 // 1) This class is needed for support of different images: the Win32 common
405 // control natively supports only 2 images (the normal one and another for the
406 // selected state). We wish to provide support for 2 more of them for folder
407 // items (i.e. those which have children): for expanded state and for expanded
408 // selected state. For this we use this structure to store the additional items
409 // images.
410 //
411 // 2) This class is also needed to hold the HITEM so that we can sort
412 // it correctly in the MSW sort callback.
413 //
414 // In addition it makes other workarounds such as this easier and helps
415 // simplify the code.
416 // ----------------------------------------------------------------------------
417 
418 class wxTreeItemParam
419 {
420 public:
wxTreeItemParam()421     wxTreeItemParam()
422     {
423         m_data = NULL;
424 
425         for ( size_t n = 0; n < WXSIZEOF(m_images); n++ )
426         {
427             m_images[n] = -1;
428         }
429     }
430 
431     // dtor deletes the associated data as well
~wxTreeItemParam()432     virtual ~wxTreeItemParam() { delete m_data; }
433 
434     // accessors
435         // get the real data associated with the item
GetData() const436     wxTreeItemData *GetData() const { return m_data; }
437         // change it
SetData(wxTreeItemData * data)438     void SetData(wxTreeItemData *data) { m_data = data; }
439 
440         // do we have such image?
HasImage(wxTreeItemIcon which) const441     bool HasImage(wxTreeItemIcon which) const { return m_images[which] != -1; }
442         // get image, falling back to the other images if this one is not
443         // specified
GetImage(wxTreeItemIcon which) const444     int GetImage(wxTreeItemIcon which) const
445     {
446         int image = m_images[which];
447         if ( image == -1 )
448         {
449             switch ( which )
450             {
451                 case wxTreeItemIcon_SelectedExpanded:
452                     // We consider that expanded icon is more important than
453                     // selected so test for it first.
454                     image = m_images[wxTreeItemIcon_Expanded];
455                     if ( image == -1 )
456                         image = m_images[wxTreeItemIcon_Selected];
457                     if ( image != -1 )
458                         break;
459                     //else: fall through
460 
461                 case wxTreeItemIcon_Selected:
462                 case wxTreeItemIcon_Expanded:
463                     image = m_images[wxTreeItemIcon_Normal];
464                     break;
465 
466                 case wxTreeItemIcon_Normal:
467                     // no fallback
468                     break;
469 
470                 default:
471                     wxFAIL_MSG( wxT("unsupported wxTreeItemIcon value") );
472             }
473         }
474 
475         return image;
476     }
477         // change the given image
SetImage(int image,wxTreeItemIcon which)478     void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
479 
480         // get item
GetItem() const481     const wxTreeItemId& GetItem() const { return m_item; }
482         // set item
SetItem(const wxTreeItemId & item)483     void SetItem(const wxTreeItemId& item) { m_item = item; }
484 
485 protected:
486     // all the images associated with the item
487     int m_images[wxTreeItemIcon_Max];
488 
489     // item for sort callbacks
490     wxTreeItemId m_item;
491 
492     // the real client data
493     wxTreeItemData *m_data;
494 
495     wxDECLARE_NO_COPY_CLASS(wxTreeItemParam);
496 };
497 
498 // wxVirutalNode is used in place of a single root when 'hidden' root is
499 // specified.
500 class wxVirtualNode : public wxTreeViewItem
501 {
502 public:
wxVirtualNode(wxTreeItemParam * param)503     wxVirtualNode(wxTreeItemParam *param)
504         : wxTreeViewItem(TVI_ROOT, 0)
505     {
506         m_param = param;
507     }
508 
~wxVirtualNode()509     ~wxVirtualNode()
510     {
511         delete m_param;
512     }
513 
GetParam() const514     wxTreeItemParam *GetParam() const { return m_param; }
SetParam(wxTreeItemParam * param)515     void SetParam(wxTreeItemParam *param) { delete m_param; m_param = param; }
516 
517 private:
518     wxTreeItemParam *m_param;
519 
520     wxDECLARE_NO_COPY_CLASS(wxVirtualNode);
521 };
522 
523 #ifdef __VISUALC__
524 #pragma warning( default : 4097 )
525 #endif
526 
527 // a macro to get the virtual root, returns NULL if none
528 #define GET_VIRTUAL_ROOT() ((wxVirtualNode *)m_pVirtualRoot)
529 
530 // returns true if the item is the virtual root
531 #define IS_VIRTUAL_ROOT(item) (HITEM(item) == TVI_ROOT)
532 
533 // a class which encapsulates the tree traversal logic: it vists all (unless
534 // OnVisit() returns false) items under the given one
535 class wxTreeTraversal
536 {
537 public:
wxTreeTraversal(const wxTreeCtrl * tree)538     wxTreeTraversal(const wxTreeCtrl *tree)
539     {
540         m_tree = tree;
541     }
542 
543     // give it a virtual dtor: not really needed as the class is never used
544     // polymorphically and not even allocated on heap at all, but this is safer
545     // (in case it ever is) and silences the compiler warnings for now
~wxTreeTraversal()546     virtual ~wxTreeTraversal() { }
547 
548     // do traverse the tree: visit all items (recursively by default) under the
549     // given one; return true if all items were traversed or false if the
550     // traversal was aborted because OnVisit returned false
551     bool DoTraverse(const wxTreeItemId& root, bool recursively = true);
552 
553     // override this function to do whatever is needed for each item, return
554     // false to stop traversing
555     virtual bool OnVisit(const wxTreeItemId& item) = 0;
556 
557 protected:
GetTree() const558     const wxTreeCtrl *GetTree() const { return m_tree; }
559 
560 private:
561     bool Traverse(const wxTreeItemId& root, bool recursively);
562 
563     const wxTreeCtrl *m_tree;
564 
565     wxDECLARE_NO_COPY_CLASS(wxTreeTraversal);
566 };
567 
568 // internal class for getting the selected items
569 class TraverseSelections : public wxTreeTraversal
570 {
571 public:
TraverseSelections(const wxTreeCtrl * tree,wxArrayTreeItemIds & selections)572     TraverseSelections(const wxTreeCtrl *tree,
573                        wxArrayTreeItemIds& selections)
574         : wxTreeTraversal(tree), m_selections(selections)
575         {
576             m_selections.Empty();
577 
578             if (tree->GetCount() > 0)
579                 DoTraverse(tree->GetRootItem());
580         }
581 
OnVisit(const wxTreeItemId & item)582     virtual bool OnVisit(const wxTreeItemId& item)
583     {
584         const wxTreeCtrl * const tree = GetTree();
585 
586         // can't visit a virtual node.
587         if ( (tree->GetRootItem() == item) && tree->HasFlag(wxTR_HIDE_ROOT) )
588         {
589             return true;
590         }
591 
592         if ( ::IsItemSelected(GetHwndOf(tree), HITEM(item)) )
593         {
594             m_selections.Add(item);
595         }
596 
597         return true;
598     }
599 
GetCount() const600     size_t GetCount() const { return m_selections.GetCount(); }
601 
602 private:
603     wxArrayTreeItemIds& m_selections;
604 
605     wxDECLARE_NO_COPY_CLASS(TraverseSelections);
606 };
607 
608 // internal class for counting tree items
609 class TraverseCounter : public wxTreeTraversal
610 {
611 public:
TraverseCounter(const wxTreeCtrl * tree,const wxTreeItemId & root,bool recursively)612     TraverseCounter(const wxTreeCtrl *tree,
613                     const wxTreeItemId& root,
614                     bool recursively)
615         : wxTreeTraversal(tree)
616         {
617             m_count = 0;
618 
619             DoTraverse(root, recursively);
620         }
621 
OnVisit(const wxTreeItemId & WXUNUSED (item))622     virtual bool OnVisit(const wxTreeItemId& WXUNUSED(item))
623     {
624         m_count++;
625 
626         return true;
627     }
628 
GetCount() const629     size_t GetCount() const { return m_count; }
630 
631 private:
632     size_t m_count;
633 
634     wxDECLARE_NO_COPY_CLASS(TraverseCounter);
635 };
636 
637 // ----------------------------------------------------------------------------
638 // wxWin macros
639 // ----------------------------------------------------------------------------
640 
641 // ----------------------------------------------------------------------------
642 // constants
643 // ----------------------------------------------------------------------------
644 
645 // indices in gs_expandEvents table below
646 enum
647 {
648     IDX_COLLAPSE,
649     IDX_EXPAND,
650     IDX_WHAT_MAX
651 };
652 
653 enum
654 {
655     IDX_DONE,
656     IDX_DOING,
657     IDX_HOW_MAX
658 };
659 
660 // handy table for sending events - it has to be initialized during run-time
661 // now so can't be const any more
662 static /* const */ wxEventType gs_expandEvents[IDX_WHAT_MAX][IDX_HOW_MAX];
663 
664 /*
665    but logically it's a const table with the following entries:
666 =
667 {
668     { wxEVT_TREE_ITEM_COLLAPSED, wxEVT_TREE_ITEM_COLLAPSING },
669     { wxEVT_TREE_ITEM_EXPANDED,  wxEVT_TREE_ITEM_EXPANDING  }
670 };
671 */
672 
673 // ============================================================================
674 // implementation
675 // ============================================================================
676 
677 // ----------------------------------------------------------------------------
678 // tree traversal
679 // ----------------------------------------------------------------------------
680 
DoTraverse(const wxTreeItemId & root,bool recursively)681 bool wxTreeTraversal::DoTraverse(const wxTreeItemId& root, bool recursively)
682 {
683     if ( !OnVisit(root) )
684         return false;
685 
686     return Traverse(root, recursively);
687 }
688 
Traverse(const wxTreeItemId & root,bool recursively)689 bool wxTreeTraversal::Traverse(const wxTreeItemId& root, bool recursively)
690 {
691     wxTreeItemIdValue cookie;
692     wxTreeItemId child = m_tree->GetFirstChild(root, cookie);
693     while ( child.IsOk() )
694     {
695         // depth first traversal
696         if ( recursively && !Traverse(child, true) )
697             return false;
698 
699         if ( !OnVisit(child) )
700             return false;
701 
702         child = m_tree->GetNextChild(root, cookie);
703     }
704 
705     return true;
706 }
707 
708 // ----------------------------------------------------------------------------
709 // construction and destruction
710 // ----------------------------------------------------------------------------
711 
Init()712 void wxTreeCtrl::Init()
713 {
714     m_textCtrl = NULL;
715     m_hasAnyAttr = false;
716 #if wxUSE_DRAGIMAGE
717     m_dragImage = NULL;
718 #endif
719     m_pVirtualRoot = NULL;
720     m_dragStarted = false;
721     m_focusLost = true;
722     m_changingSelection = false;
723     m_triggerStateImageClick = false;
724     m_mouseUpDeselect = false;
725 
726     // initialize the global array of events now as it can't be done statically
727     // with the wxEVT_XXX values being allocated during run-time only
728     gs_expandEvents[IDX_COLLAPSE][IDX_DONE] = wxEVT_TREE_ITEM_COLLAPSED;
729     gs_expandEvents[IDX_COLLAPSE][IDX_DOING] = wxEVT_TREE_ITEM_COLLAPSING;
730     gs_expandEvents[IDX_EXPAND][IDX_DONE] = wxEVT_TREE_ITEM_EXPANDED;
731     gs_expandEvents[IDX_EXPAND][IDX_DOING] = wxEVT_TREE_ITEM_EXPANDING;
732 }
733 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)734 bool wxTreeCtrl::Create(wxWindow *parent,
735                         wxWindowID id,
736                         const wxPoint& pos,
737                         const wxSize& size,
738                         long style,
739                         const wxValidator& validator,
740                         const wxString& name)
741 {
742     Init();
743 
744     if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
745         style |= wxBORDER_SUNKEN;
746 
747     if ( !CreateControl(parent, id, pos, size, style, validator, name) )
748         return false;
749 
750     WXDWORD exStyle = 0;
751     DWORD wstyle = MSWGetStyle(m_windowStyle, & exStyle);
752     wstyle |= WS_TABSTOP | TVS_SHOWSELALWAYS;
753 
754     if ( !(m_windowStyle & wxTR_NO_LINES) )
755         wstyle |= TVS_HASLINES;
756     if ( m_windowStyle & wxTR_HAS_BUTTONS )
757         wstyle |= TVS_HASBUTTONS;
758 
759     if ( m_windowStyle & wxTR_EDIT_LABELS )
760         wstyle |= TVS_EDITLABELS;
761 
762     if ( m_windowStyle & wxTR_LINES_AT_ROOT )
763         wstyle |= TVS_LINESATROOT;
764 
765     if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT )
766     {
767         if ( wxApp::GetComCtl32Version() >= 471 )
768             wstyle |= TVS_FULLROWSELECT;
769     }
770 
771 #if !defined(__WXWINCE__) && defined(TVS_INFOTIP)
772     // Need so that TVN_GETINFOTIP messages will be sent
773     wstyle |= TVS_INFOTIP;
774 #endif
775 
776     // Create the tree control.
777     if ( !MSWCreateControl(WC_TREEVIEW, wstyle, pos, size) )
778         return false;
779 
780     SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
781     SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
782 
783     wxSetCCUnicodeFormat(GetHwnd());
784 
785     if ( m_windowStyle & wxTR_TWIST_BUTTONS )
786     {
787         // Under Vista and later Explorer uses rotating ("twist") buttons
788         // instead of the default "+/-" ones so apply its theme to the tree
789         // control to implement this style.
790         if ( wxGetWinVersion() >= wxWinVersion_Vista )
791         {
792             if ( wxUxThemeEngine *theme = wxUxThemeEngine::GetIfActive() )
793             {
794                 theme->SetWindowTheme(GetHwnd(), L"EXPLORER", NULL);
795             }
796         }
797     }
798 
799     return true;
800 }
801 
~wxTreeCtrl()802 wxTreeCtrl::~wxTreeCtrl()
803 {
804     m_isBeingDeleted = true;
805 
806     // delete any attributes
807     if ( m_hasAnyAttr )
808     {
809         WX_CLEAR_HASH_MAP(wxMapTreeAttr, m_attrs);
810 
811         // prevent TVN_DELETEITEM handler from deleting the attributes again!
812         m_hasAnyAttr = false;
813     }
814 
815     DeleteTextCtrl();
816 
817     // delete user data to prevent memory leaks
818     // also deletes hidden root node storage.
819     DeleteAllItems();
820 }
821 
822 // ----------------------------------------------------------------------------
823 // accessors
824 // ----------------------------------------------------------------------------
825 
826 /* static */ wxVisualAttributes
GetClassDefaultAttributes(wxWindowVariant variant)827 wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
828 {
829     wxVisualAttributes attrs = GetCompositeControlsDefaultAttributes(variant);
830 
831     // common controls have their own default font
832     attrs.font = wxGetCCDefaultFont();
833 
834     return attrs;
835 }
836 
837 
838 // simple wrappers which add error checking in debug mode
839 
DoGetItem(wxTreeViewItem * tvItem) const840 bool wxTreeCtrl::DoGetItem(wxTreeViewItem *tvItem) const
841 {
842     wxCHECK_MSG( tvItem->hItem != TVI_ROOT, false,
843                  wxT("can't retrieve virtual root item") );
844 
845     if ( !TreeView_GetItem(GetHwnd(), tvItem) )
846     {
847         wxLogLastError(wxT("TreeView_GetItem"));
848 
849         return false;
850     }
851 
852     return true;
853 }
854 
DoSetItem(wxTreeViewItem * tvItem)855 void wxTreeCtrl::DoSetItem(wxTreeViewItem *tvItem)
856 {
857     TreeItemUnlocker unlocker(tvItem->hItem);
858 
859     if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
860     {
861         wxLogLastError(wxT("TreeView_SetItem"));
862     }
863 }
864 
GetCount() const865 unsigned int wxTreeCtrl::GetCount() const
866 {
867     return (unsigned int)TreeView_GetCount(GetHwnd());
868 }
869 
GetIndent() const870 unsigned int wxTreeCtrl::GetIndent() const
871 {
872     return TreeView_GetIndent(GetHwnd());
873 }
874 
SetIndent(unsigned int indent)875 void wxTreeCtrl::SetIndent(unsigned int indent)
876 {
877     TreeView_SetIndent(GetHwnd(), indent);
878 }
879 
SetAnyImageList(wxImageList * imageList,int which)880 void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which)
881 {
882     // no error return
883     (void) TreeView_SetImageList(GetHwnd(),
884                                  imageList ? imageList->GetHIMAGELIST() : 0,
885                                  which);
886 }
887 
SetImageList(wxImageList * imageList)888 void wxTreeCtrl::SetImageList(wxImageList *imageList)
889 {
890     if (m_ownsImageListNormal)
891         delete m_imageListNormal;
892 
893     SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
894     m_ownsImageListNormal = false;
895 }
896 
SetStateImageList(wxImageList * imageList)897 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
898 {
899     if (m_ownsImageListState) delete m_imageListState;
900     SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
901     m_ownsImageListState = false;
902 }
903 
GetChildrenCount(const wxTreeItemId & item,bool recursively) const904 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
905                                     bool recursively) const
906 {
907     wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
908 
909     TraverseCounter counter(this, item, recursively);
910     return counter.GetCount() - 1;
911 }
912 
913 // ----------------------------------------------------------------------------
914 // control colours
915 // ----------------------------------------------------------------------------
916 
SetBackgroundColour(const wxColour & colour)917 bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour)
918 {
919     if ( !wxWindowBase::SetBackgroundColour(colour) )
920         return false;
921 
922     ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
923 
924     return true;
925 }
926 
SetForegroundColour(const wxColour & colour)927 bool wxTreeCtrl::SetForegroundColour(const wxColour &colour)
928 {
929     if ( !wxWindowBase::SetForegroundColour(colour) )
930         return false;
931 
932     ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
933 
934     return true;
935 }
936 
937 // ----------------------------------------------------------------------------
938 // Item access
939 // ----------------------------------------------------------------------------
940 
IsHiddenRoot(const wxTreeItemId & item) const941 bool wxTreeCtrl::IsHiddenRoot(const wxTreeItemId& item) const
942 {
943     return HITEM(item) == TVI_ROOT && HasFlag(wxTR_HIDE_ROOT);
944 }
945 
GetItemText(const wxTreeItemId & item) const946 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
947 {
948     wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
949 
950     wxChar buf[512];  // the size is arbitrary...
951 
952     wxTreeViewItem tvItem(item, TVIF_TEXT);
953     tvItem.pszText = buf;
954     tvItem.cchTextMax = WXSIZEOF(buf);
955     if ( !DoGetItem(&tvItem) )
956     {
957         // don't return some garbage which was on stack, but an empty string
958         buf[0] = wxT('\0');
959     }
960 
961     return wxString(buf);
962 }
963 
SetItemText(const wxTreeItemId & item,const wxString & text)964 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
965 {
966     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
967 
968     if ( IS_VIRTUAL_ROOT(item) )
969         return;
970 
971     wxTreeViewItem tvItem(item, TVIF_TEXT);
972     tvItem.pszText = wxMSW_CONV_LPTSTR(text);
973     DoSetItem(&tvItem);
974 
975     // when setting the text of the item being edited, the text control should
976     // be updated to reflect the new text as well, otherwise calling
977     // SetItemText() in the OnBeginLabelEdit() handler doesn't have any effect
978     //
979     // don't use GetEditControl() here because m_textCtrl is not set yet
980     HWND hwndEdit = TreeView_GetEditControl(GetHwnd());
981     if ( hwndEdit )
982     {
983         if ( item == m_idEdited )
984         {
985             ::SetWindowText(hwndEdit, text.t_str());
986         }
987     }
988 }
989 
GetItemImage(const wxTreeItemId & item,wxTreeItemIcon which) const990 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
991                              wxTreeItemIcon which) const
992 {
993     wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
994 
995     if ( IsHiddenRoot(item) )
996     {
997         // no images for hidden root item
998         return -1;
999     }
1000 
1001     wxTreeItemParam *param = GetItemParam(item);
1002 
1003     return param && param->HasImage(which) ? param->GetImage(which) : -1;
1004 }
1005 
SetItemImage(const wxTreeItemId & item,int image,wxTreeItemIcon which)1006 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image,
1007                               wxTreeItemIcon which)
1008 {
1009     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1010     wxCHECK_RET( which >= 0 &&
1011                  which < wxTreeItemIcon_Max,
1012                  wxT("invalid image index"));
1013 
1014 
1015     if ( IsHiddenRoot(item) )
1016     {
1017         // no images for hidden root item
1018         return;
1019     }
1020 
1021     wxTreeItemParam *data = GetItemParam(item);
1022     if ( !data )
1023         return;
1024 
1025     data->SetImage(image, which);
1026 
1027     RefreshItem(item);
1028 }
1029 
GetItemParam(const wxTreeItemId & item) const1030 wxTreeItemParam *wxTreeCtrl::GetItemParam(const wxTreeItemId& item) const
1031 {
1032     wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
1033 
1034     wxTreeViewItem tvItem(item, TVIF_PARAM);
1035 
1036     // hidden root may still have data.
1037     if ( IS_VIRTUAL_ROOT(item) )
1038     {
1039         return GET_VIRTUAL_ROOT()->GetParam();
1040     }
1041 
1042     // visible node.
1043     if ( !DoGetItem(&tvItem) )
1044     {
1045         return NULL;
1046     }
1047 
1048     return (wxTreeItemParam *)tvItem.lParam;
1049 }
1050 
HandleTreeEvent(wxTreeEvent & event) const1051 bool wxTreeCtrl::HandleTreeEvent(wxTreeEvent& event) const
1052 {
1053     if ( event.m_item.IsOk() )
1054     {
1055         event.SetClientObject(GetItemData(event.m_item));
1056     }
1057 
1058     return HandleWindowEvent(event);
1059 }
1060 
GetItemData(const wxTreeItemId & item) const1061 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
1062 {
1063     wxTreeItemParam *data = GetItemParam(item);
1064 
1065     return data ? data->GetData() : NULL;
1066 }
1067 
SetItemData(const wxTreeItemId & item,wxTreeItemData * data)1068 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
1069 {
1070     // first, associate this piece of data with this item
1071     if ( data )
1072     {
1073         data->SetId(item);
1074     }
1075 
1076     wxTreeItemParam *param = GetItemParam(item);
1077 
1078     wxCHECK_RET( param, wxT("failed to change tree items data") );
1079 
1080     param->SetData(data);
1081 }
1082 
SetItemHasChildren(const wxTreeItemId & item,bool has)1083 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
1084 {
1085     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1086 
1087     if ( IS_VIRTUAL_ROOT(item) )
1088         return;
1089 
1090     wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1091     tvItem.cChildren = (int)has;
1092     DoSetItem(&tvItem);
1093 }
1094 
SetItemBold(const wxTreeItemId & item,bool bold)1095 void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
1096 {
1097     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1098 
1099     if ( IS_VIRTUAL_ROOT(item) )
1100         return;
1101 
1102     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1103     tvItem.state = bold ? TVIS_BOLD : 0;
1104     DoSetItem(&tvItem);
1105 }
1106 
SetItemDropHighlight(const wxTreeItemId & item,bool highlight)1107 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight)
1108 {
1109     if ( IS_VIRTUAL_ROOT(item) )
1110         return;
1111 
1112     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
1113     tvItem.state = highlight ? TVIS_DROPHILITED : 0;
1114     DoSetItem(&tvItem);
1115 }
1116 
RefreshItem(const wxTreeItemId & item)1117 void wxTreeCtrl::RefreshItem(const wxTreeItemId& item)
1118 {
1119     if ( IS_VIRTUAL_ROOT(item) )
1120         return;
1121 
1122     wxRect rect;
1123     if ( GetBoundingRect(item, rect) )
1124     {
1125         RefreshRect(rect);
1126     }
1127 }
1128 
GetItemTextColour(const wxTreeItemId & item) const1129 wxColour wxTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
1130 {
1131     wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1132 
1133     wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
1134     return it == m_attrs.end() ? wxNullColour : it->second->GetTextColour();
1135 }
1136 
GetItemBackgroundColour(const wxTreeItemId & item) const1137 wxColour wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
1138 {
1139     wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
1140 
1141     wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
1142     return it == m_attrs.end() ? wxNullColour : it->second->GetBackgroundColour();
1143 }
1144 
GetItemFont(const wxTreeItemId & item) const1145 wxFont wxTreeCtrl::GetItemFont(const wxTreeItemId& item) const
1146 {
1147     wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
1148 
1149     wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
1150     return it == m_attrs.end() ? wxNullFont : it->second->GetFont();
1151 }
1152 
SetItemTextColour(const wxTreeItemId & item,const wxColour & col)1153 void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
1154                                    const wxColour& col)
1155 {
1156     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1157 
1158     wxTreeItemAttr *attr;
1159     wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
1160     if ( it == m_attrs.end() )
1161     {
1162         m_hasAnyAttr = true;
1163 
1164         m_attrs[item.m_pItem] =
1165         attr = new wxTreeItemAttr;
1166     }
1167     else
1168     {
1169         attr = it->second;
1170     }
1171 
1172     attr->SetTextColour(col);
1173 
1174     RefreshItem(item);
1175 }
1176 
SetItemBackgroundColour(const wxTreeItemId & item,const wxColour & col)1177 void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
1178                                          const wxColour& col)
1179 {
1180     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1181 
1182     wxTreeItemAttr *attr;
1183     wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
1184     if ( it == m_attrs.end() )
1185     {
1186         m_hasAnyAttr = true;
1187 
1188         m_attrs[item.m_pItem] =
1189         attr = new wxTreeItemAttr;
1190     }
1191     else // already in the hash
1192     {
1193         attr = it->second;
1194     }
1195 
1196     attr->SetBackgroundColour(col);
1197 
1198     RefreshItem(item);
1199 }
1200 
SetItemFont(const wxTreeItemId & item,const wxFont & font)1201 void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
1202 {
1203     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1204 
1205     wxTreeItemAttr *attr;
1206     wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
1207     if ( it == m_attrs.end() )
1208     {
1209         m_hasAnyAttr = true;
1210 
1211         m_attrs[item.m_pItem] =
1212         attr = new wxTreeItemAttr;
1213     }
1214     else // already in the hash
1215     {
1216         attr = it->second;
1217     }
1218 
1219     attr->SetFont(font);
1220 
1221     // Reset the item's text to ensure that the bounding rect will be adjusted
1222     // for the new font.
1223     SetItemText(item, GetItemText(item));
1224 
1225     RefreshItem(item);
1226 }
1227 
1228 // ----------------------------------------------------------------------------
1229 // Item status
1230 // ----------------------------------------------------------------------------
1231 
IsVisible(const wxTreeItemId & item) const1232 bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
1233 {
1234     wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1235 
1236     if ( item == wxTreeItemId(TVI_ROOT) )
1237     {
1238         // virtual (hidden) root is never visible
1239         return false;
1240     }
1241 
1242     // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
1243     TVGetItemRectParam param;
1244 
1245     // true means to get rect for just the text, not the whole line
1246     if ( !wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, TRUE) )
1247     {
1248         // if TVM_GETITEMRECT returned false, then the item is definitely not
1249         // visible (because its parent is not expanded)
1250         return false;
1251     }
1252 
1253     // however if it returned true, the item might still be outside the
1254     // currently visible part of the tree, test for it (notice that partly
1255     // visible means visible here)
1256     return param.rect.bottom > 0 && param.rect.top < GetClientSize().y;
1257 }
1258 
ItemHasChildren(const wxTreeItemId & item) const1259 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
1260 {
1261     wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1262 
1263     if ( IS_VIRTUAL_ROOT(item) )
1264     {
1265         wxTreeItemIdValue cookie;
1266         return GetFirstChild(item, cookie).IsOk();
1267     }
1268 
1269     wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1270     DoGetItem(&tvItem);
1271 
1272     return tvItem.cChildren != 0;
1273 }
1274 
IsExpanded(const wxTreeItemId & item) const1275 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
1276 {
1277     wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1278 
1279     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
1280     DoGetItem(&tvItem);
1281 
1282     return (tvItem.state & TVIS_EXPANDED) != 0;
1283 }
1284 
IsSelected(const wxTreeItemId & item) const1285 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
1286 {
1287     wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1288 
1289     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
1290     DoGetItem(&tvItem);
1291 
1292     return (tvItem.state & TVIS_SELECTED) != 0;
1293 }
1294 
IsBold(const wxTreeItemId & item) const1295 bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
1296 {
1297     wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
1298 
1299     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1300     DoGetItem(&tvItem);
1301 
1302     return (tvItem.state & TVIS_BOLD) != 0;
1303 }
1304 
1305 // ----------------------------------------------------------------------------
1306 // navigation
1307 // ----------------------------------------------------------------------------
1308 
GetRootItem() const1309 wxTreeItemId wxTreeCtrl::GetRootItem() const
1310 {
1311     // Root may be real (visible) or virtual (hidden).
1312     if ( GET_VIRTUAL_ROOT() )
1313         return TVI_ROOT;
1314 
1315     return wxTreeItemId(TreeView_GetRoot(GetHwnd()));
1316 }
1317 
GetSelection() const1318 wxTreeItemId wxTreeCtrl::GetSelection() const
1319 {
1320     wxCHECK_MSG( !HasFlag(wxTR_MULTIPLE), wxTreeItemId(),
1321                  wxT("this only works with single selection controls") );
1322 
1323     return GetFocusedItem();
1324 }
1325 
GetFocusedItem() const1326 wxTreeItemId wxTreeCtrl::GetFocusedItem() const
1327 {
1328     return wxTreeItemId(TreeView_GetSelection(GetHwnd()));
1329 }
1330 
GetItemParent(const wxTreeItemId & item) const1331 wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const
1332 {
1333     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1334 
1335     HTREEITEM hItem;
1336 
1337     if ( IS_VIRTUAL_ROOT(item) )
1338     {
1339         // no parent for the virtual root
1340         hItem = 0;
1341     }
1342     else // normal item
1343     {
1344         hItem = TreeView_GetParent(GetHwnd(), HITEM(item));
1345         if ( !hItem && HasFlag(wxTR_HIDE_ROOT) )
1346         {
1347             // the top level items should have the virtual root as their parent
1348             hItem = TVI_ROOT;
1349         }
1350     }
1351 
1352     return wxTreeItemId(hItem);
1353 }
1354 
GetFirstChild(const wxTreeItemId & item,wxTreeItemIdValue & cookie) const1355 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
1356                                        wxTreeItemIdValue& cookie) const
1357 {
1358     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1359 
1360     // remember the last child returned in 'cookie'
1361     cookie = TreeView_GetChild(GetHwnd(), HITEM(item));
1362 
1363     return wxTreeItemId(cookie);
1364 }
1365 
GetNextChild(const wxTreeItemId & WXUNUSED (item),wxTreeItemIdValue & cookie) const1366 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
1367                                       wxTreeItemIdValue& cookie) const
1368 {
1369     wxTreeItemId fromCookie(cookie);
1370 
1371     HTREEITEM hitem = HITEM(fromCookie);
1372 
1373     hitem = TreeView_GetNextSibling(GetHwnd(), hitem);
1374 
1375     wxTreeItemId item(hitem);
1376 
1377     cookie = item.m_pItem;
1378 
1379     return item;
1380 }
1381 
GetLastChild(const wxTreeItemId & item) const1382 wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
1383 {
1384     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1385 
1386     // can this be done more efficiently?
1387     wxTreeItemIdValue cookie;
1388 
1389     wxTreeItemId childLast,
1390     child = GetFirstChild(item, cookie);
1391     while ( child.IsOk() )
1392     {
1393         childLast = child;
1394         child = GetNextChild(item, cookie);
1395     }
1396 
1397     return childLast;
1398 }
1399 
GetNextSibling(const wxTreeItemId & item) const1400 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
1401 {
1402     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1403     return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item)));
1404 }
1405 
GetPrevSibling(const wxTreeItemId & item) const1406 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
1407 {
1408     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1409     return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item)));
1410 }
1411 
GetFirstVisibleItem() const1412 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
1413 {
1414     return wxTreeItemId(TreeView_GetFirstVisible(GetHwnd()));
1415 }
1416 
GetNextVisible(const wxTreeItemId & item) const1417 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
1418 {
1419     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1420     wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!"));
1421 
1422     wxTreeItemId next(TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
1423     if ( next.IsOk() && !IsVisible(next) )
1424     {
1425         // Win32 considers that any non-collapsed item is visible while we want
1426         // to return only really visible items
1427         next.Unset();
1428     }
1429 
1430     return next;
1431 }
1432 
GetPrevVisible(const wxTreeItemId & item) const1433 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1434 {
1435     wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1436     wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!"));
1437 
1438     wxTreeItemId prev(TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
1439     if ( prev.IsOk() && !IsVisible(prev) )
1440     {
1441         // just as above, Win32 function will happily return the previous item
1442         // in the tree for the first visible item too
1443         prev.Unset();
1444     }
1445 
1446     return prev;
1447 }
1448 
1449 // ----------------------------------------------------------------------------
1450 // multiple selections emulation
1451 // ----------------------------------------------------------------------------
1452 
GetSelections(wxArrayTreeItemIds & selections) const1453 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const
1454 {
1455     TraverseSelections selector(this, selections);
1456 
1457     return selector.GetCount();
1458 }
1459 
1460 // ----------------------------------------------------------------------------
1461 // Usual operations
1462 // ----------------------------------------------------------------------------
1463 
DoInsertAfter(const wxTreeItemId & parent,const wxTreeItemId & hInsertAfter,const wxString & text,int image,int selectedImage,wxTreeItemData * data)1464 wxTreeItemId wxTreeCtrl::DoInsertAfter(const wxTreeItemId& parent,
1465                                        const wxTreeItemId& hInsertAfter,
1466                                        const wxString& text,
1467                                        int image, int selectedImage,
1468                                        wxTreeItemData *data)
1469 {
1470     wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()),
1471                  wxTreeItemId(),
1472                  wxT("can't have more than one root in the tree") );
1473 
1474     TV_INSERTSTRUCT tvIns;
1475     tvIns.hParent = HITEM(parent);
1476     tvIns.hInsertAfter = HITEM(hInsertAfter);
1477 
1478     // this is how we insert the item as the first child: supply a NULL
1479     // hInsertAfter
1480     if ( !tvIns.hInsertAfter )
1481     {
1482         tvIns.hInsertAfter = TVI_FIRST;
1483     }
1484 
1485     UINT mask = 0;
1486     if ( !text.empty() )
1487     {
1488         mask |= TVIF_TEXT;
1489         tvIns.item.pszText = wxMSW_CONV_LPTSTR(text);
1490     }
1491     else
1492     {
1493         tvIns.item.pszText = NULL;
1494         tvIns.item.cchTextMax = 0;
1495     }
1496 
1497     // create the param which will store the other item parameters
1498     wxTreeItemParam *param = new wxTreeItemParam;
1499 
1500     // we return the images on demand as they depend on whether the item is
1501     // expanded or collapsed too in our case
1502     mask |= TVIF_IMAGE | TVIF_SELECTEDIMAGE;
1503     tvIns.item.iImage = I_IMAGECALLBACK;
1504     tvIns.item.iSelectedImage = I_IMAGECALLBACK;
1505 
1506     param->SetImage(image, wxTreeItemIcon_Normal);
1507     param->SetImage(selectedImage, wxTreeItemIcon_Selected);
1508 
1509     mask |= TVIF_PARAM;
1510     tvIns.item.lParam = (LPARAM)param;
1511     tvIns.item.mask = mask;
1512 
1513     // don't use the hack below for the children of hidden root: this results
1514     // in a crash inside comctl32.dll when we call TreeView_GetItemRect()
1515     const bool firstChild = !IsHiddenRoot(parent) &&
1516                                 !TreeView_GetChild(GetHwnd(), HITEM(parent));
1517 
1518     HTREEITEM id = TreeView_InsertItem(GetHwnd(), &tvIns);
1519     if ( id == 0 )
1520     {
1521         wxLogLastError(wxT("TreeView_InsertItem"));
1522     }
1523 
1524     // apparently some Windows versions (2000 and XP are reported to do this)
1525     // sometimes don't refresh the tree after adding the first child and so we
1526     // need this to make the "[+]" appear
1527     if ( firstChild )
1528     {
1529         TVGetItemRectParam param2;
1530 
1531         wxTreeView_GetItemRect(GetHwnd(), HITEM(parent), param2, FALSE);
1532         ::InvalidateRect(GetHwnd(), &param2.rect, FALSE);
1533     }
1534 
1535     // associate the application tree item with Win32 tree item handle
1536     param->SetItem(id);
1537 
1538     // setup wxTreeItemData
1539     if ( data != NULL )
1540     {
1541         param->SetData(data);
1542         data->SetId(id);
1543     }
1544 
1545     return wxTreeItemId(id);
1546 }
1547 
AddRoot(const wxString & text,int image,int selectedImage,wxTreeItemData * data)1548 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
1549                                  int image, int selectedImage,
1550                                  wxTreeItemData *data)
1551 {
1552     if ( HasFlag(wxTR_HIDE_ROOT) )
1553     {
1554         wxASSERT_MSG( !m_pVirtualRoot, wxT("tree can have only a single root") );
1555 
1556         // create a virtual root item, the parent for all the others
1557         wxTreeItemParam *param = new wxTreeItemParam;
1558         param->SetData(data);
1559 
1560         m_pVirtualRoot = new wxVirtualNode(param);
1561 
1562         return TVI_ROOT;
1563     }
1564 
1565     return DoInsertAfter(wxTreeItemId(), wxTreeItemId(),
1566                            text, image, selectedImage, data);
1567 }
1568 
DoInsertItem(const wxTreeItemId & parent,size_t index,const wxString & text,int image,int selectedImage,wxTreeItemData * data)1569 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
1570                                       size_t index,
1571                                       const wxString& text,
1572                                       int image, int selectedImage,
1573                                       wxTreeItemData *data)
1574 {
1575     wxTreeItemId idPrev;
1576     if ( index == (size_t)-1 )
1577     {
1578         // special value: append to the end
1579         idPrev = TVI_LAST;
1580     }
1581     else // find the item from index
1582     {
1583         wxTreeItemIdValue cookie;
1584         wxTreeItemId idCur = GetFirstChild(parent, cookie);
1585         while ( index != 0 && idCur.IsOk() )
1586         {
1587             index--;
1588 
1589             idPrev = idCur;
1590             idCur = GetNextChild(parent, cookie);
1591         }
1592 
1593         // assert, not check: if the index is invalid, we will append the item
1594         // to the end
1595         wxASSERT_MSG( index == 0, wxT("bad index in wxTreeCtrl::InsertItem") );
1596     }
1597 
1598     return DoInsertAfter(parent, idPrev, text, image, selectedImage, data);
1599 }
1600 
Delete(const wxTreeItemId & item)1601 void wxTreeCtrl::Delete(const wxTreeItemId& item)
1602 {
1603     // unlock tree selections on vista, without this the
1604     // tree ctrl will eventually crash after item deletion
1605     TreeItemUnlocker unlock_all;
1606 
1607     if ( HasFlag(wxTR_MULTIPLE) )
1608     {
1609         bool selected = IsSelected(item);
1610         wxTreeItemId next;
1611 
1612         if ( selected )
1613         {
1614             next = TreeView_GetNextVisible(GetHwnd(), HITEM(item));
1615 
1616             if ( !next.IsOk() )
1617             {
1618                 next = TreeView_GetPrevVisible(GetHwnd(), HITEM(item));
1619             }
1620         }
1621 
1622         {
1623             TempSetter set(m_changingSelection);
1624             if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
1625             {
1626                 wxLogLastError(wxT("TreeView_DeleteItem"));
1627                 return;
1628             }
1629         }
1630 
1631         if ( !selected )
1632         {
1633             return;
1634         }
1635 
1636         if ( item == m_htSelStart )
1637             m_htSelStart.Unset();
1638 
1639         if ( item == m_htClickedItem )
1640             m_htClickedItem.Unset();
1641 
1642         if ( next.IsOk() )
1643         {
1644             wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this, next);
1645 
1646             if ( IsTreeEventAllowed(changingEvent) )
1647             {
1648                 wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED, this, next);
1649                 (void)HandleTreeEvent(changedEvent);
1650             }
1651             else
1652             {
1653                 DoUnselectItem(next);
1654                 ClearFocusedItem();
1655             }
1656         }
1657     }
1658     else
1659     {
1660         TempSetter set(m_changingSelection);
1661         if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
1662         {
1663             wxLogLastError(wxT("TreeView_DeleteItem"));
1664         }
1665     }
1666 }
1667 
1668 // delete all children (but don't delete the item itself)
DeleteChildren(const wxTreeItemId & item)1669 void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
1670 {
1671     // unlock tree selections on vista for the duration of this call
1672     TreeItemUnlocker unlock_all;
1673 
1674     wxTreeItemIdValue cookie;
1675 
1676     wxArrayTreeItemIds children;
1677     wxTreeItemId child = GetFirstChild(item, cookie);
1678     while ( child.IsOk() )
1679     {
1680         children.Add(child);
1681 
1682         child = GetNextChild(item, cookie);
1683     }
1684 
1685     size_t nCount = children.Count();
1686     for ( size_t n = 0; n < nCount; n++ )
1687     {
1688         Delete(children[n]);
1689     }
1690 }
1691 
DeleteAllItems()1692 void wxTreeCtrl::DeleteAllItems()
1693 {
1694     // unlock tree selections on vista for the duration of this call
1695     TreeItemUnlocker unlock_all;
1696 
1697     // invalidate all the items we store as they're going to become invalid
1698     m_htSelStart =
1699     m_htClickedItem = wxTreeItemId();
1700 
1701     // delete the "virtual" root item.
1702     if ( GET_VIRTUAL_ROOT() )
1703     {
1704         delete GET_VIRTUAL_ROOT();
1705         m_pVirtualRoot = NULL;
1706     }
1707 
1708     // and all the real items
1709 
1710     if ( !TreeView_DeleteAllItems(GetHwnd()) )
1711     {
1712         wxLogLastError(wxT("TreeView_DeleteAllItems"));
1713     }
1714 }
1715 
DoExpand(const wxTreeItemId & item,int flag)1716 void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
1717 {
1718     wxASSERT_MSG( flag == TVE_COLLAPSE ||
1719                   flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
1720                   flag == TVE_EXPAND   ||
1721                   flag == TVE_TOGGLE,
1722                   wxT("Unknown flag in wxTreeCtrl::DoExpand") );
1723 
1724     // A hidden root can be neither expanded nor collapsed.
1725     wxCHECK_RET( !IsHiddenRoot(item),
1726                  wxT("Can't expand/collapse hidden root node!") );
1727 
1728     // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
1729     // emulate them. This behaviour has changed slightly with comctl32.dll
1730     // v 4.70 - now it does send them but only the first time. To maintain
1731     // compatible behaviour and also in order to not have surprises with the
1732     // future versions, don't rely on this and still do everything ourselves.
1733     // To avoid that the messages be sent twice when the item is expanded for
1734     // the first time we must clear TVIS_EXPANDEDONCE style manually.
1735 
1736     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
1737     tvItem.state = 0;
1738     DoSetItem(&tvItem);
1739 
1740     if ( IsExpanded(item) )
1741     {
1742         wxTreeEvent event(wxEVT_TREE_ITEM_COLLAPSING,
1743                           this, wxTreeItemId(item));
1744 
1745         if ( !IsTreeEventAllowed(event) )
1746             return;
1747     }
1748 
1749     if ( TreeView_Expand(GetHwnd(), HITEM(item), flag) )
1750     {
1751         if ( IsExpanded(item) )
1752             return;
1753 
1754         wxTreeEvent event(wxEVT_TREE_ITEM_COLLAPSED, this, item);
1755         (void)HandleTreeEvent(event);
1756     }
1757     //else: change didn't took place, so do nothing at all
1758 }
1759 
Expand(const wxTreeItemId & item)1760 void wxTreeCtrl::Expand(const wxTreeItemId& item)
1761 {
1762     DoExpand(item, TVE_EXPAND);
1763 }
1764 
Collapse(const wxTreeItemId & item)1765 void wxTreeCtrl::Collapse(const wxTreeItemId& item)
1766 {
1767     DoExpand(item, TVE_COLLAPSE);
1768 }
1769 
CollapseAndReset(const wxTreeItemId & item)1770 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
1771 {
1772     DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
1773 }
1774 
Toggle(const wxTreeItemId & item)1775 void wxTreeCtrl::Toggle(const wxTreeItemId& item)
1776 {
1777     DoExpand(item, TVE_TOGGLE);
1778 }
1779 
Unselect()1780 void wxTreeCtrl::Unselect()
1781 {
1782     wxASSERT_MSG( !HasFlag(wxTR_MULTIPLE),
1783                   wxT("doesn't make sense, may be you want UnselectAll()?") );
1784 
1785     // the current focus
1786     HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
1787 
1788     if ( !htFocus )
1789     {
1790         return;
1791     }
1792 
1793     if ( HasFlag(wxTR_MULTIPLE) )
1794     {
1795         wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
1796                                   this, wxTreeItemId());
1797         changingEvent.m_itemOld = htFocus;
1798 
1799         if ( IsTreeEventAllowed(changingEvent) )
1800         {
1801             ClearFocusedItem();
1802 
1803             wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
1804                                      this, wxTreeItemId());
1805             changedEvent.m_itemOld = htFocus;
1806             (void)HandleTreeEvent(changedEvent);
1807         }
1808     }
1809     else
1810     {
1811         ClearFocusedItem();
1812     }
1813 }
1814 
DoUnselectAll()1815 void wxTreeCtrl::DoUnselectAll()
1816 {
1817     wxArrayTreeItemIds selections;
1818     size_t count = GetSelections(selections);
1819 
1820     for ( size_t n = 0; n < count; n++ )
1821     {
1822         DoUnselectItem(selections[n]);
1823     }
1824 
1825     m_htSelStart.Unset();
1826 }
1827 
UnselectAll()1828 void wxTreeCtrl::UnselectAll()
1829 {
1830     if ( HasFlag(wxTR_MULTIPLE) )
1831     {
1832         HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
1833         if ( !htFocus ) return;
1834 
1835         wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this);
1836         changingEvent.m_itemOld = htFocus;
1837 
1838         if ( IsTreeEventAllowed(changingEvent) )
1839         {
1840             DoUnselectAll();
1841 
1842             wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED, this);
1843             changedEvent.m_itemOld = htFocus;
1844             (void)HandleTreeEvent(changedEvent);
1845         }
1846     }
1847     else
1848     {
1849         Unselect();
1850     }
1851 }
1852 
DoSelectChildren(const wxTreeItemId & parent)1853 void wxTreeCtrl::DoSelectChildren(const wxTreeItemId& parent)
1854 {
1855     DoUnselectAll();
1856 
1857     wxTreeItemIdValue cookie;
1858     wxTreeItemId child = GetFirstChild(parent, cookie);
1859     while ( child.IsOk() )
1860     {
1861         DoSelectItem(child, true);
1862         child = GetNextChild(child, cookie);
1863     }
1864 }
1865 
SelectChildren(const wxTreeItemId & parent)1866 void wxTreeCtrl::SelectChildren(const wxTreeItemId& parent)
1867 {
1868     wxCHECK_RET( HasFlag(wxTR_MULTIPLE),
1869                  "this only works with multiple selection controls" );
1870 
1871     HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
1872 
1873     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this);
1874     changingEvent.m_itemOld = htFocus;
1875 
1876     if ( IsTreeEventAllowed(changingEvent) )
1877     {
1878         DoSelectChildren(parent);
1879 
1880         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED, this);
1881         changedEvent.m_itemOld = htFocus;
1882         (void)HandleTreeEvent(changedEvent);
1883     }
1884 }
1885 
DoSelectItem(const wxTreeItemId & item,bool select)1886 void wxTreeCtrl::DoSelectItem(const wxTreeItemId& item, bool select)
1887 {
1888     TempSetter set(m_changingSelection);
1889 
1890     ::SelectItem(GetHwnd(), HITEM(item), select);
1891 }
1892 
SelectItem(const wxTreeItemId & item,bool select)1893 void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
1894 {
1895     wxCHECK_RET( !IsHiddenRoot(item), wxT("can't select hidden root item") );
1896 
1897     if ( select == IsSelected(item) )
1898     {
1899         // nothing to do, the item is already in the requested state
1900         return;
1901     }
1902 
1903     if ( HasFlag(wxTR_MULTIPLE) )
1904     {
1905         wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this, item);
1906 
1907         if ( IsTreeEventAllowed(changingEvent) )
1908         {
1909             HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(GetHwnd());
1910             DoSelectItem(item, select);
1911 
1912             if ( !htFocus )
1913             {
1914                 SetFocusedItem(item);
1915             }
1916 
1917             wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
1918                                      this, item);
1919             (void)HandleTreeEvent(changedEvent);
1920         }
1921     }
1922     else // single selection
1923     {
1924         wxTreeItemId itemOld, itemNew;
1925         if ( select )
1926         {
1927             itemOld = GetSelection();
1928             itemNew = item;
1929         }
1930         else // deselecting the currently selected item
1931         {
1932             itemOld = item;
1933             // leave itemNew invalid
1934         }
1935 
1936         // Recent versions of comctl32.dll send TVN_SELCHANG{ED,ING} events
1937         // when we call TreeView_SelectItem() but apparently some old ones did
1938         // not so send the events ourselves and ignore those generated by
1939         // TreeView_SelectItem() if m_changingSelection is set.
1940         wxTreeEvent
1941             changingEvent(wxEVT_TREE_SEL_CHANGING, this, itemNew);
1942         changingEvent.SetOldItem(itemOld);
1943 
1944         if ( IsTreeEventAllowed(changingEvent) )
1945         {
1946             TempSetter set(m_changingSelection);
1947 
1948             if ( !TreeView_SelectItem(GetHwnd(), HITEM(itemNew)) )
1949             {
1950                 wxLogLastError(wxT("TreeView_SelectItem"));
1951             }
1952             else // ok
1953             {
1954                 ::SetFocus(GetHwnd(), HITEM(item));
1955 
1956                 wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
1957                                          this, itemNew);
1958                 changedEvent.SetOldItem(itemOld);
1959                 (void)HandleTreeEvent(changedEvent);
1960             }
1961         }
1962         //else: program vetoed the change
1963     }
1964 }
1965 
EnsureVisible(const wxTreeItemId & item)1966 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
1967 {
1968     wxCHECK_RET( !IsHiddenRoot(item), wxT("can't show hidden root item") );
1969 
1970     // no error return
1971     TreeView_EnsureVisible(GetHwnd(), HITEM(item));
1972 }
1973 
ScrollTo(const wxTreeItemId & item)1974 void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
1975 {
1976     if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item)) )
1977     {
1978         wxLogLastError(wxT("TreeView_SelectSetFirstVisible"));
1979     }
1980 }
1981 
GetEditControl() const1982 wxTextCtrl *wxTreeCtrl::GetEditControl() const
1983 {
1984     return m_textCtrl;
1985 }
1986 
DeleteTextCtrl()1987 void wxTreeCtrl::DeleteTextCtrl()
1988 {
1989     if ( m_textCtrl )
1990     {
1991         // the HWND corresponding to this control is deleted by the tree
1992         // control itself and we don't know when exactly this happens, so check
1993         // if the window still exists before calling UnsubclassWin()
1994         if ( !::IsWindow(GetHwndOf(m_textCtrl)) )
1995         {
1996             m_textCtrl->SetHWND(0);
1997         }
1998 
1999         m_textCtrl->UnsubclassWin();
2000         m_textCtrl->SetHWND(0);
2001         wxDELETE(m_textCtrl);
2002 
2003         m_idEdited.Unset();
2004     }
2005 }
2006 
EditLabel(const wxTreeItemId & item,wxClassInfo * textControlClass)2007 wxTextCtrl *wxTreeCtrl::EditLabel(const wxTreeItemId& item,
2008                                   wxClassInfo *textControlClass)
2009 {
2010     wxASSERT( textControlClass->IsKindOf(wxCLASSINFO(wxTextCtrl)) );
2011 
2012     DeleteTextCtrl();
2013 
2014     m_idEdited = item;
2015     m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
2016     HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), HITEM(item));
2017 
2018     // this is not an error - the TVN_BEGINLABELEDIT handler might have
2019     // returned false
2020     if ( !hWnd )
2021     {
2022         wxDELETE(m_textCtrl);
2023         return NULL;
2024     }
2025 
2026     // textctrl is subclassed in MSWOnNotify
2027     return m_textCtrl;
2028 }
2029 
2030 // End label editing, optionally cancelling the edit
DoEndEditLabel(bool discardChanges)2031 void wxTreeCtrl::DoEndEditLabel(bool discardChanges)
2032 {
2033     TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
2034 
2035     DeleteTextCtrl();
2036 }
2037 
DoTreeHitTest(const wxPoint & point,int & flags) const2038 wxTreeItemId wxTreeCtrl::DoTreeHitTest(const wxPoint& point, int& flags) const
2039 {
2040     TV_HITTESTINFO hitTestInfo;
2041     hitTestInfo.pt.x = (int)point.x;
2042     hitTestInfo.pt.y = (int)point.y;
2043 
2044     (void) TreeView_HitTest(GetHwnd(), &hitTestInfo);
2045 
2046     flags = 0;
2047 
2048     // avoid repetition
2049     #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
2050                                     flags |= wxTREE_HITTEST_##flag
2051 
2052     TRANSLATE_FLAG(ABOVE);
2053     TRANSLATE_FLAG(BELOW);
2054     TRANSLATE_FLAG(NOWHERE);
2055     TRANSLATE_FLAG(ONITEMBUTTON);
2056     TRANSLATE_FLAG(ONITEMICON);
2057     TRANSLATE_FLAG(ONITEMINDENT);
2058     TRANSLATE_FLAG(ONITEMLABEL);
2059     TRANSLATE_FLAG(ONITEMRIGHT);
2060     TRANSLATE_FLAG(ONITEMSTATEICON);
2061     TRANSLATE_FLAG(TOLEFT);
2062     TRANSLATE_FLAG(TORIGHT);
2063 
2064     #undef TRANSLATE_FLAG
2065 
2066     return wxTreeItemId(hitTestInfo.hItem);
2067 }
2068 
GetBoundingRect(const wxTreeItemId & item,wxRect & rect,bool textOnly) const2069 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
2070                                  wxRect& rect,
2071                                  bool textOnly) const
2072 {
2073     // Virtual root items have no bounding rectangle
2074     if ( IS_VIRTUAL_ROOT(item) )
2075     {
2076         return false;
2077     }
2078 
2079     TVGetItemRectParam param;
2080 
2081     if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(item), param, textOnly) )
2082     {
2083         rect = wxRect(wxPoint(param.rect.left, param.rect.top),
2084                       wxPoint(param.rect.right, param.rect.bottom));
2085 
2086         return true;
2087     }
2088     else
2089     {
2090         // couldn't retrieve rect: for example, item isn't visible
2091         return false;
2092     }
2093 }
2094 
ClearFocusedItem()2095 void wxTreeCtrl::ClearFocusedItem()
2096 {
2097     TempSetter set(m_changingSelection);
2098 
2099     if ( !TreeView_SelectItem(GetHwnd(), 0) )
2100     {
2101         wxLogLastError(wxT("TreeView_SelectItem"));
2102     }
2103 }
2104 
SetFocusedItem(const wxTreeItemId & item)2105 void wxTreeCtrl::SetFocusedItem(const wxTreeItemId& item)
2106 {
2107     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2108 
2109     TempSetter set(m_changingSelection);
2110 
2111     ::SetFocus(GetHwnd(), HITEM(item));
2112 }
2113 
DoUnselectItem(const wxTreeItemId & item)2114 void wxTreeCtrl::DoUnselectItem(const wxTreeItemId& item)
2115 {
2116     TempSetter set(m_changingSelection);
2117 
2118     ::UnselectItem(GetHwnd(), HITEM(item));
2119 }
2120 
DoToggleItemSelection(const wxTreeItemId & item)2121 void wxTreeCtrl::DoToggleItemSelection(const wxTreeItemId& item)
2122 {
2123     TempSetter set(m_changingSelection);
2124 
2125     ::ToggleItemSelection(GetHwnd(), HITEM(item));
2126 }
2127 
2128 // ----------------------------------------------------------------------------
2129 // sorting stuff
2130 // ----------------------------------------------------------------------------
2131 
2132 // this is just a tiny namespace which is friend to wxTreeCtrl and so can use
2133 // functions such as IsDataIndirect()
2134 class wxTreeSortHelper
2135 {
2136 public:
2137     static int CALLBACK Compare(LPARAM data1, LPARAM data2, LPARAM tree);
2138 
2139 private:
GetIdFromData(LPARAM lParam)2140     static wxTreeItemId GetIdFromData(LPARAM lParam)
2141     {
2142         return ((wxTreeItemParam*)lParam)->GetItem();
2143         }
2144 };
2145 
Compare(LPARAM pItem1,LPARAM pItem2,LPARAM htree)2146 int CALLBACK wxTreeSortHelper::Compare(LPARAM pItem1,
2147                                        LPARAM pItem2,
2148                                        LPARAM htree)
2149 {
2150     wxCHECK_MSG( pItem1 && pItem2, 0,
2151                  wxT("sorting tree without data doesn't make sense") );
2152 
2153     wxTreeCtrl *tree = (wxTreeCtrl *)htree;
2154 
2155     return tree->OnCompareItems(GetIdFromData(pItem1),
2156                                 GetIdFromData(pItem2));
2157 }
2158 
SortChildren(const wxTreeItemId & item)2159 void wxTreeCtrl::SortChildren(const wxTreeItemId& item)
2160 {
2161     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2162 
2163     // rely on the fact that TreeView_SortChildren does the same thing as our
2164     // default behaviour, i.e. sorts items alphabetically and so call it
2165     // directly if we're not in derived class (much more efficient!)
2166     // RN: Note that if you find you're code doesn't sort as expected this
2167     //     may be why as if you don't use the DECLARE_CLASS/IMPLEMENT_CLASS
2168     //     combo for your derived wxTreeCtrl if will sort without
2169     //     OnCompareItems
2170     if ( GetClassInfo() == wxCLASSINFO(wxTreeCtrl) )
2171     {
2172         TreeView_SortChildren(GetHwnd(), HITEM(item), 0);
2173     }
2174     else
2175     {
2176         TV_SORTCB tvSort;
2177         tvSort.hParent = HITEM(item);
2178         tvSort.lpfnCompare = wxTreeSortHelper::Compare;
2179         tvSort.lParam = (LPARAM)this;
2180         TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */);
2181     }
2182 }
2183 
2184 // ----------------------------------------------------------------------------
2185 // implementation
2186 // ----------------------------------------------------------------------------
2187 
MSWShouldPreProcessMessage(WXMSG * msg)2188 bool wxTreeCtrl::MSWShouldPreProcessMessage(WXMSG* msg)
2189 {
2190     if ( msg->message == WM_KEYDOWN )
2191     {
2192         // Only eat VK_RETURN if not being used by the application in
2193         // conjunction with modifiers
2194         if ( (msg->wParam == VK_RETURN) && !wxIsAnyModifierDown() )
2195         {
2196             // we need VK_RETURN to generate wxEVT_TREE_ITEM_ACTIVATED
2197             return false;
2198         }
2199     }
2200 
2201     return wxTreeCtrlBase::MSWShouldPreProcessMessage(msg);
2202 }
2203 
MSWCommand(WXUINT cmd,WXWORD id_)2204 bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id_)
2205 {
2206     const int id = (signed short)id_;
2207 
2208     if ( cmd == EN_UPDATE )
2209     {
2210         wxCommandEvent event(wxEVT_TEXT, id);
2211         event.SetEventObject( this );
2212         ProcessCommand(event);
2213     }
2214     else if ( cmd == EN_KILLFOCUS )
2215     {
2216         wxCommandEvent event(wxEVT_KILL_FOCUS, id);
2217         event.SetEventObject( this );
2218         ProcessCommand(event);
2219     }
2220     else
2221     {
2222         // nothing done
2223         return false;
2224     }
2225 
2226     // command processed
2227     return true;
2228 }
2229 
MSWIsOnItem(unsigned flags) const2230 bool wxTreeCtrl::MSWIsOnItem(unsigned flags) const
2231 {
2232     unsigned mask = TVHT_ONITEM;
2233     if ( HasFlag(wxTR_FULL_ROW_HIGHLIGHT) )
2234         mask |= TVHT_ONITEMINDENT | TVHT_ONITEMRIGHT;
2235 
2236     return (flags & mask) != 0;
2237 }
2238 
MSWHandleSelectionKey(unsigned vkey)2239 bool wxTreeCtrl::MSWHandleSelectionKey(unsigned vkey)
2240 {
2241     const bool bCtrl = wxIsCtrlDown();
2242     const bool bShift = wxIsShiftDown();
2243     const HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
2244 
2245     switch ( vkey )
2246     {
2247         case VK_RETURN:
2248         case VK_SPACE:
2249             if ( !htSel )
2250                 break;
2251 
2252             if ( vkey != VK_RETURN && bCtrl )
2253             {
2254                 wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2255                                           this, htSel);
2256                 changingEvent.m_itemOld = htSel;
2257 
2258                 if ( IsTreeEventAllowed(changingEvent) )
2259                 {
2260                     DoToggleItemSelection(wxTreeItemId(htSel));
2261 
2262                     wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2263                                              this, htSel);
2264                     changedEvent.m_itemOld = htSel;
2265                     (void)HandleTreeEvent(changedEvent);
2266                 }
2267             }
2268             else
2269             {
2270                 wxArrayTreeItemIds selections;
2271                 size_t count = GetSelections(selections);
2272 
2273                 if ( count != 1 || HITEM(selections[0]) != htSel )
2274                 {
2275                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2276                                               this, htSel);
2277                     changingEvent.m_itemOld = htSel;
2278 
2279                     if ( IsTreeEventAllowed(changingEvent) )
2280                     {
2281                         DoUnselectAll();
2282                         DoSelectItem(wxTreeItemId(htSel));
2283 
2284                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2285                                                  this, htSel);
2286                         changedEvent.m_itemOld = htSel;
2287                         (void)HandleTreeEvent(changedEvent);
2288                     }
2289                 }
2290             }
2291             break;
2292 
2293         case VK_UP:
2294         case VK_DOWN:
2295             if ( !bCtrl && !bShift )
2296             {
2297                 wxArrayTreeItemIds selections;
2298                 wxTreeItemId next;
2299 
2300                 if ( htSel )
2301                 {
2302                     next = vkey == VK_UP
2303                             ? TreeView_GetPrevVisible(GetHwnd(), htSel)
2304                             : TreeView_GetNextVisible(GetHwnd(), htSel);
2305                 }
2306                 else
2307                 {
2308                     next = GetRootItem();
2309 
2310                     if ( IsHiddenRoot(next) )
2311                         next = TreeView_GetChild(GetHwnd(), HITEM(next));
2312                 }
2313 
2314                 if ( !next.IsOk() )
2315                 {
2316                     break;
2317                 }
2318 
2319                 wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2320                                           this, next);
2321                 changingEvent.m_itemOld = htSel;
2322 
2323                 if ( IsTreeEventAllowed(changingEvent) )
2324                 {
2325                     DoUnselectAll();
2326                     DoSelectItem(next);
2327                     SetFocusedItem(next);
2328 
2329                     wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2330                                              this, next);
2331                     changedEvent.m_itemOld = htSel;
2332                     (void)HandleTreeEvent(changedEvent);
2333                 }
2334             }
2335             else if ( htSel )
2336             {
2337                 wxTreeItemId next = vkey == VK_UP
2338                     ? TreeView_GetPrevVisible(GetHwnd(), htSel)
2339                     : TreeView_GetNextVisible(GetHwnd(), htSel);
2340 
2341                 if ( !next.IsOk() )
2342                 {
2343                     break;
2344                 }
2345 
2346                 if ( !m_htSelStart )
2347                 {
2348                     m_htSelStart = htSel;
2349                 }
2350 
2351                 if ( bShift && SelectRange(GetHwnd(), HITEM(m_htSelStart), HITEM(next),
2352                      SR_UNSELECT_OTHERS | SR_SIMULATE) )
2353                 {
2354                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this, next);
2355                     changingEvent.m_itemOld = htSel;
2356 
2357                     if ( IsTreeEventAllowed(changingEvent) )
2358                     {
2359                         SelectRange(GetHwnd(), HITEM(m_htSelStart), HITEM(next),
2360                                     SR_UNSELECT_OTHERS);
2361 
2362                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED, this, next);
2363                         changedEvent.m_itemOld = htSel;
2364                         (void)HandleTreeEvent(changedEvent);
2365                     }
2366                 }
2367 
2368                 SetFocusedItem(next);
2369             }
2370             break;
2371 
2372         case VK_LEFT:
2373             if ( HasChildren(htSel) && IsExpanded(htSel) )
2374             {
2375                 Collapse(htSel);
2376             }
2377             else
2378             {
2379                 wxTreeItemId next = GetItemParent(htSel);
2380 
2381                 if ( next.IsOk() && !IsHiddenRoot(next) )
2382                 {
2383                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2384                                               this, next);
2385                     changingEvent.m_itemOld = htSel;
2386 
2387                     if ( IsTreeEventAllowed(changingEvent) )
2388                     {
2389                         DoUnselectAll();
2390                         DoSelectItem(next);
2391                         SetFocusedItem(next);
2392 
2393                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2394                                                  this, next);
2395                         changedEvent.m_itemOld = htSel;
2396                         (void)HandleTreeEvent(changedEvent);
2397                     }
2398                 }
2399             }
2400             break;
2401 
2402         case VK_RIGHT:
2403             if ( !IsVisible(htSel) )
2404             {
2405                 EnsureVisible(htSel);
2406             }
2407 
2408             if ( !HasChildren(htSel) )
2409                 break;
2410 
2411             if ( !IsExpanded(htSel) )
2412             {
2413                 Expand(htSel);
2414             }
2415             else
2416             {
2417                 wxTreeItemId next = TreeView_GetChild(GetHwnd(), htSel);
2418 
2419                 wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING, this, next);
2420                 changingEvent.m_itemOld = htSel;
2421 
2422                 if ( IsTreeEventAllowed(changingEvent) )
2423                 {
2424                     DoUnselectAll();
2425                     DoSelectItem(next);
2426                     SetFocusedItem(next);
2427 
2428                     wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED, this, next);
2429                     changedEvent.m_itemOld = htSel;
2430                     (void)HandleTreeEvent(changedEvent);
2431                 }
2432             }
2433             break;
2434 
2435         case VK_HOME:
2436         case VK_END:
2437             {
2438                 wxTreeItemId next = GetRootItem();
2439 
2440                 if ( IsHiddenRoot(next) )
2441                 {
2442                     next = TreeView_GetChild(GetHwnd(), HITEM(next));
2443                 }
2444 
2445                 if ( !next.IsOk() )
2446                     break;
2447 
2448                 if ( vkey == VK_END )
2449                 {
2450                     for ( ;; )
2451                     {
2452                         wxTreeItemId nextTemp = TreeView_GetNextVisible(
2453                                                     GetHwnd(), HITEM(next));
2454 
2455                         if ( !nextTemp.IsOk() )
2456                             break;
2457 
2458                         next = nextTemp;
2459                     }
2460                 }
2461 
2462                 if ( htSel == HITEM(next) )
2463                     break;
2464 
2465                 if ( bShift )
2466                 {
2467                     if ( !m_htSelStart )
2468                     {
2469                         m_htSelStart = htSel;
2470                     }
2471 
2472                     if ( SelectRange(GetHwnd(),
2473                                      HITEM(m_htSelStart), HITEM(next),
2474                                      SR_UNSELECT_OTHERS | SR_SIMULATE) )
2475                     {
2476                         wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2477                                                   this, next);
2478                         changingEvent.m_itemOld = htSel;
2479 
2480                         if ( IsTreeEventAllowed(changingEvent) )
2481                         {
2482                             SelectRange(GetHwnd(),
2483                                         HITEM(m_htSelStart), HITEM(next),
2484                                         SR_UNSELECT_OTHERS);
2485                             SetFocusedItem(next);
2486 
2487                             wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2488                                                      this, next);
2489                             changedEvent.m_itemOld = htSel;
2490                             (void)HandleTreeEvent(changedEvent);
2491                         }
2492                     }
2493                 }
2494                 else // no Shift
2495                 {
2496                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2497                                               this, next);
2498                     changingEvent.m_itemOld = htSel;
2499 
2500                     if ( IsTreeEventAllowed(changingEvent) )
2501                     {
2502                         DoUnselectAll();
2503                         DoSelectItem(next);
2504                         SetFocusedItem(next);
2505 
2506                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2507                                                  this, next);
2508                         changedEvent.m_itemOld = htSel;
2509                         (void)HandleTreeEvent(changedEvent);
2510                     }
2511                 }
2512             }
2513             break;
2514 
2515         case VK_PRIOR:
2516         case VK_NEXT:
2517             if ( bCtrl )
2518             {
2519                 wxTreeItemId firstVisible = GetFirstVisibleItem();
2520                 size_t visibleCount = TreeView_GetVisibleCount(GetHwnd());
2521                 wxTreeItemId nextAdjacent = (vkey == VK_PRIOR) ?
2522                     TreeView_GetPrevVisible(GetHwnd(), HITEM(firstVisible)) :
2523                     TreeView_GetNextVisible(GetHwnd(), HITEM(firstVisible));
2524 
2525                 if ( !nextAdjacent )
2526                 {
2527                     break;
2528                 }
2529 
2530                 wxTreeItemId nextStart = firstVisible;
2531 
2532                 for ( size_t n = 1; n < visibleCount; n++ )
2533                 {
2534                     wxTreeItemId nextTemp = (vkey == VK_PRIOR) ?
2535                         TreeView_GetPrevVisible(GetHwnd(), HITEM(nextStart)) :
2536                         TreeView_GetNextVisible(GetHwnd(), HITEM(nextStart));
2537 
2538                     if ( nextTemp.IsOk() )
2539                     {
2540                         nextStart = nextTemp;
2541                     }
2542                     else
2543                     {
2544                         break;
2545                     }
2546                 }
2547 
2548                 EnsureVisible(nextStart);
2549 
2550                 if ( vkey == VK_NEXT )
2551                 {
2552                     wxTreeItemId nextEnd = nextStart;
2553 
2554                     for ( size_t n = 1; n < visibleCount; n++ )
2555                     {
2556                         wxTreeItemId nextTemp =
2557                             TreeView_GetNextVisible(GetHwnd(), HITEM(nextEnd));
2558 
2559                         if ( nextTemp.IsOk() )
2560                         {
2561                             nextEnd = nextTemp;
2562                         }
2563                         else
2564                         {
2565                             break;
2566                         }
2567                     }
2568 
2569                     EnsureVisible(nextEnd);
2570                 }
2571             }
2572             else // no Ctrl
2573             {
2574                 size_t visibleCount = TreeView_GetVisibleCount(GetHwnd());
2575                 wxTreeItemId nextAdjacent = (vkey == VK_PRIOR) ?
2576                     TreeView_GetPrevVisible(GetHwnd(), htSel) :
2577                     TreeView_GetNextVisible(GetHwnd(), htSel);
2578 
2579                 if ( !nextAdjacent )
2580                 {
2581                     break;
2582                 }
2583 
2584                 wxTreeItemId next(htSel);
2585 
2586                 for ( size_t n = 1; n < visibleCount; n++ )
2587                 {
2588                     wxTreeItemId nextTemp = vkey == VK_PRIOR ?
2589                         TreeView_GetPrevVisible(GetHwnd(), HITEM(next)) :
2590                         TreeView_GetNextVisible(GetHwnd(), HITEM(next));
2591 
2592                     if ( !nextTemp.IsOk() )
2593                         break;
2594 
2595                     next = nextTemp;
2596                 }
2597 
2598                 wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2599                                           this, next);
2600                 changingEvent.m_itemOld = htSel;
2601 
2602                 if ( IsTreeEventAllowed(changingEvent) )
2603                 {
2604                     DoUnselectAll();
2605                     m_htSelStart.Unset();
2606                     DoSelectItem(next);
2607                     SetFocusedItem(next);
2608 
2609                     wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2610                                              this, next);
2611                     changedEvent.m_itemOld = htSel;
2612                     (void)HandleTreeEvent(changedEvent);
2613                 }
2614             }
2615             break;
2616 
2617         default:
2618             return false;
2619     }
2620 
2621     return true;
2622 }
2623 
MSWHandleTreeKeyDownEvent(WXWPARAM wParam,WXLPARAM lParam)2624 bool wxTreeCtrl::MSWHandleTreeKeyDownEvent(WXWPARAM wParam, WXLPARAM lParam)
2625 {
2626     wxTreeEvent keyEvent(wxEVT_TREE_KEY_DOWN, this);
2627     keyEvent.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN, wParam, lParam);
2628 
2629     bool processed = HandleTreeEvent(keyEvent);
2630 
2631     // generate a separate event for Space/Return
2632     if ( !wxIsCtrlDown() && !wxIsShiftDown() && !wxIsAltDown() &&
2633          ((wParam == VK_SPACE) || (wParam == VK_RETURN)) )
2634     {
2635         const HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
2636         if ( htSel )
2637         {
2638             wxTreeEvent activatedEvent(wxEVT_TREE_ITEM_ACTIVATED,
2639                                        this, htSel);
2640             (void)HandleTreeEvent(activatedEvent);
2641         }
2642     }
2643 
2644     return processed;
2645 }
2646 
2647 // we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we
2648 // only do it during dragging, minimize wxWin overhead (this is important for
2649 // WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly
2650 // instead of passing by wxWin events
2651 WXLRESULT
MSWWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)2652 wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2653 {
2654     bool processed = false;
2655     WXLRESULT rc = 0;
2656     bool isMultiple = HasFlag(wxTR_MULTIPLE);
2657 
2658     if ( nMsg == WM_CONTEXTMENU )
2659     {
2660         int x = GET_X_LPARAM(lParam),
2661             y = GET_Y_LPARAM(lParam);
2662 
2663         // the item for which the menu should be shown
2664         wxTreeItemId item;
2665 
2666         // the position where the menu should be shown in client coordinates
2667         // (so that it can be passed directly to PopupMenu())
2668         wxPoint pt;
2669 
2670         if ( x == -1 || y == -1 )
2671         {
2672             // this means that the event was generated from keyboard (e.g. with
2673             // Shift-F10 or special Windows menu key)
2674             //
2675             // use the Explorer standard of putting the menu at the left edge
2676             // of the text, in the vertical middle of the text
2677             item = wxTreeItemId(TreeView_GetSelection(GetHwnd()));
2678             if ( item.IsOk() )
2679             {
2680                 // Use the bounding rectangle of only the text part
2681                 wxRect rect;
2682                 GetBoundingRect(item, rect, true);
2683                 pt = wxPoint(rect.GetX(), rect.GetY() + rect.GetHeight() / 2);
2684             }
2685         }
2686         else // event from mouse, use mouse position
2687         {
2688             pt = ScreenToClient(wxPoint(x, y));
2689 
2690             TV_HITTESTINFO tvhti;
2691             tvhti.pt.x = pt.x;
2692             tvhti.pt.y = pt.y;
2693 
2694             if ( TreeView_HitTest(GetHwnd(), &tvhti) )
2695                 item = wxTreeItemId(tvhti.hItem);
2696         }
2697 
2698         // create the event
2699         if ( item.IsOk() )
2700         {
2701             wxTreeEvent event(wxEVT_TREE_ITEM_MENU, this, item);
2702 
2703             event.m_pointDrag = pt;
2704 
2705             if ( HandleTreeEvent(event) )
2706                 processed = true;
2707             //else: continue with generating wxEVT_CONTEXT_MENU in base class code
2708         }
2709     }
2710     else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
2711     {
2712         // we only process mouse messages here and these parameters have the
2713         // same meaning for all of them
2714         int x = GET_X_LPARAM(lParam),
2715             y = GET_Y_LPARAM(lParam);
2716 
2717         TV_HITTESTINFO tvht;
2718         tvht.pt.x = x;
2719         tvht.pt.y = y;
2720 
2721         HTREEITEM htOldItem = TreeView_GetSelection(GetHwnd());
2722         HTREEITEM htItem = TreeView_HitTest(GetHwnd(), &tvht);
2723 
2724         switch ( nMsg )
2725         {
2726             case WM_LBUTTONDOWN:
2727                 if ( !isMultiple )
2728                     break;
2729 
2730                 m_htClickedItem.Unset();
2731 
2732                 if ( !MSWIsOnItem(tvht.flags) )
2733                 {
2734                     if ( tvht.flags & TVHT_ONITEMBUTTON )
2735                     {
2736                         // either it's going to be handled by user code or
2737                         // we're going to use it ourselves to toggle the
2738                         // branch, in either case don't pass it to the base
2739                         // class which would generate another mouse click event
2740                         // for it even though it's already handled here
2741                         processed = true;
2742                         SetFocus();
2743 
2744                         if ( !HandleMouseEvent(nMsg, x, y, wParam) )
2745                         {
2746                             if ( !IsExpanded(htItem) )
2747                             {
2748                                 Expand(htItem);
2749                             }
2750                             else
2751                             {
2752                                 Collapse(htItem);
2753                             }
2754                         }
2755                     }
2756 
2757                     m_focusLost = false;
2758                     break;
2759                 }
2760 
2761                 processed = true;
2762                 SetFocus();
2763                 m_htClickedItem = (WXHTREEITEM) htItem;
2764                 m_ptClick = wxPoint(x, y);
2765 
2766                 if ( wParam & MK_CONTROL )
2767                 {
2768                     if ( HandleMouseEvent(nMsg, x, y, wParam) )
2769                     {
2770                         m_htClickedItem.Unset();
2771                         break;
2772                     }
2773 
2774                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2775                                               this, htItem);
2776                     changingEvent.m_itemOld = htOldItem;
2777 
2778                     if ( IsTreeEventAllowed(changingEvent) )
2779                     {
2780                         // toggle selected state
2781                         DoToggleItemSelection(wxTreeItemId(htItem));
2782 
2783                         SetFocusedItem(wxTreeItemId(htItem));
2784 
2785                         // reset on any click without Shift
2786                         m_htSelStart.Unset();
2787 
2788                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2789                                                  this, htItem);
2790                         changedEvent.m_itemOld = htOldItem;
2791                         (void)HandleTreeEvent(changedEvent);
2792                     }
2793                 }
2794                 else if ( wParam & MK_SHIFT )
2795                 {
2796                     if ( HandleMouseEvent(nMsg, x, y, wParam) )
2797                     {
2798                         m_htClickedItem.Unset();
2799                         break;
2800                     }
2801 
2802                     int srFlags = 0;
2803                     bool willChange = true;
2804 
2805                     if ( !(wParam & MK_CONTROL) )
2806                     {
2807                         srFlags |= SR_UNSELECT_OTHERS;
2808                     }
2809 
2810                     if ( !m_htSelStart )
2811                     {
2812                         // take the focused item
2813                         m_htSelStart = htOldItem;
2814                     }
2815                     else
2816                     {
2817                         willChange = SelectRange(GetHwnd(), HITEM(m_htSelStart),
2818                                                  htItem, srFlags | SR_SIMULATE);
2819                     }
2820 
2821                     if ( willChange )
2822                     {
2823                         wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2824                                                   this, htItem);
2825                         changingEvent.m_itemOld = htOldItem;
2826 
2827                         if ( IsTreeEventAllowed(changingEvent) )
2828                         {
2829                             // this selects all items between the starting one
2830                             // and the current
2831                             if ( m_htSelStart )
2832                             {
2833                                 SelectRange(GetHwnd(), HITEM(m_htSelStart),
2834                                             htItem, srFlags);
2835                             }
2836                             else
2837                             {
2838                                 DoSelectItem(wxTreeItemId(htItem));
2839                             }
2840 
2841                             SetFocusedItem(wxTreeItemId(htItem));
2842 
2843                             wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2844                                                      this, htItem);
2845                             changedEvent.m_itemOld = htOldItem;
2846                             (void)HandleTreeEvent(changedEvent);
2847                         }
2848                     }
2849                 }
2850                 else // normal click
2851                 {
2852                     // avoid doing anything if we click on the only
2853                     // currently selected item
2854 
2855                     wxArrayTreeItemIds selections;
2856                     size_t count = GetSelections(selections);
2857 
2858                     if ( count == 0 ||
2859                          count > 1 ||
2860                          HITEM(selections[0]) != htItem )
2861                     {
2862                         if ( HandleMouseEvent(nMsg, x, y, wParam) )
2863                         {
2864                             m_htClickedItem.Unset();
2865                             break;
2866                         }
2867 
2868                         // clear the previously selected items, if the user
2869                         // clicked outside of the present selection, otherwise,
2870                         // perform the deselection on mouse-up, this allows
2871                         // multiple drag and drop to work.
2872                         if ( !IsItemSelected(GetHwnd(), htItem))
2873                         {
2874                             wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2875                                                       this, htItem);
2876                             changingEvent.m_itemOld = htOldItem;
2877 
2878                             if ( IsTreeEventAllowed(changingEvent) )
2879                             {
2880                                 DoUnselectAll();
2881                                 DoSelectItem(wxTreeItemId(htItem));
2882                                 SetFocusedItem(wxTreeItemId(htItem));
2883 
2884                                 wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2885                                                          this, htItem);
2886                                 changedEvent.m_itemOld = htOldItem;
2887                                 (void)HandleTreeEvent(changedEvent);
2888                             }
2889                         }
2890                         else
2891                         {
2892                             SetFocusedItem(wxTreeItemId(htItem));
2893                             m_mouseUpDeselect = true;
2894                         }
2895                     }
2896                     else // click on a single selected item
2897                     {
2898                         // don't interfere with the default processing in
2899                         // WM_MOUSEMOVE handler below as the default window
2900                         // proc will start the drag itself if we let have
2901                         // WM_LBUTTONDOWN
2902                         m_htClickedItem.Unset();
2903 
2904                         // prevent in-place editing from starting if focus lost
2905                         // since previous click
2906                         if ( m_focusLost )
2907                         {
2908                             ClearFocusedItem();
2909                             DoSelectItem(wxTreeItemId(htItem));
2910                             SetFocusedItem(wxTreeItemId(htItem));
2911                         }
2912                         else
2913                         {
2914                             processed = false;
2915                         }
2916                     }
2917 
2918                     // reset on any click without Shift
2919                     m_htSelStart.Unset();
2920                 }
2921 
2922                 m_focusLost = false;
2923 
2924                 // we consumed the event so we need to trigger state image
2925                 // click if needed
2926                 if ( processed )
2927                 {
2928                     if ( tvht.flags & TVHT_ONITEMSTATEICON )
2929                     {
2930                         m_triggerStateImageClick = true;
2931                     }
2932                 }
2933                 break;
2934 
2935             case WM_RBUTTONDOWN:
2936                 if ( !isMultiple )
2937                     break;
2938 
2939                 processed = true;
2940                 SetFocus();
2941 
2942                 if ( HandleMouseEvent(nMsg, x, y, wParam) || !htItem )
2943                 {
2944                     break;
2945                 }
2946 
2947                 // default handler removes the highlight from the currently
2948                 // focused item when right mouse button is pressed on another
2949                 // one but keeps the remaining items highlighted, which is
2950                 // confusing, so override this default behaviour
2951                 if ( !IsItemSelected(GetHwnd(), htItem) )
2952                 {
2953                     wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
2954                                               this, htItem);
2955                     changingEvent.m_itemOld = htOldItem;
2956 
2957                     if ( IsTreeEventAllowed(changingEvent) )
2958                     {
2959                         DoUnselectAll();
2960                         DoSelectItem(wxTreeItemId(htItem));
2961                         SetFocusedItem(wxTreeItemId(htItem));
2962 
2963                         wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
2964                                                  this, htItem);
2965                         changedEvent.m_itemOld = htOldItem;
2966                         (void)HandleTreeEvent(changedEvent);
2967                     }
2968                 }
2969 
2970                 break;
2971 
2972             case WM_MOUSEMOVE:
2973 #ifndef __WXWINCE__
2974                 if ( m_htClickedItem )
2975                 {
2976                     int cx = abs(m_ptClick.x - x);
2977                     int cy = abs(m_ptClick.y - y);
2978 
2979                     if ( cx > ::GetSystemMetrics(SM_CXDRAG) ||
2980                             cy > ::GetSystemMetrics(SM_CYDRAG) )
2981                     {
2982                         NM_TREEVIEW tv;
2983                         wxZeroMemory(tv);
2984 
2985                         tv.hdr.hwndFrom = GetHwnd();
2986                         tv.hdr.idFrom = ::GetWindowLong(GetHwnd(), GWL_ID);
2987                         tv.hdr.code = TVN_BEGINDRAG;
2988 
2989                         tv.itemNew.hItem = HITEM(m_htClickedItem);
2990 
2991 
2992                         TVITEM tviAux;
2993                         wxZeroMemory(tviAux);
2994 
2995                         tviAux.hItem = HITEM(m_htClickedItem);
2996                         tviAux.mask = TVIF_STATE | TVIF_PARAM;
2997                         tviAux.stateMask = 0xffffffff;
2998                         TreeView_GetItem(GetHwnd(), &tviAux);
2999 
3000                         tv.itemNew.state = tviAux.state;
3001                         tv.itemNew.lParam = tviAux.lParam;
3002 
3003                         tv.ptDrag.x = x;
3004                         tv.ptDrag.y = y;
3005 
3006                         // do it before SendMessage() call below to avoid
3007                         // reentrancies here if there is another WM_MOUSEMOVE
3008                         // in the queue already
3009                         m_htClickedItem.Unset();
3010 
3011                         ::SendMessage(GetHwndOf(GetParent()), WM_NOTIFY,
3012                                       tv.hdr.idFrom, (LPARAM)&tv );
3013 
3014                         // don't pass it to the default window proc, it would
3015                         // start dragging again
3016                         processed = true;
3017                     }
3018                 }
3019 #endif // __WXWINCE__
3020 
3021 #if wxUSE_DRAGIMAGE
3022                 if ( m_dragImage )
3023                 {
3024                     m_dragImage->Move(wxPoint(x, y));
3025                     if ( htItem )
3026                     {
3027                         // highlight the item as target (hiding drag image is
3028                         // necessary - otherwise the display will be corrupted)
3029                         m_dragImage->Hide();
3030                         TreeView_SelectDropTarget(GetHwnd(), htItem);
3031                         m_dragImage->Show();
3032                     }
3033                 }
3034 #endif // wxUSE_DRAGIMAGE
3035                 break;
3036 
3037             case WM_LBUTTONUP:
3038                 if ( isMultiple )
3039                 {
3040                     // deselect other items if needed
3041                     if ( htItem )
3042                     {
3043                         if ( m_mouseUpDeselect )
3044                         {
3045                             m_mouseUpDeselect = false;
3046 
3047                             wxTreeEvent changingEvent(wxEVT_TREE_SEL_CHANGING,
3048                                                       this, htItem);
3049                             changingEvent.m_itemOld = htOldItem;
3050 
3051                             if ( IsTreeEventAllowed(changingEvent) )
3052                             {
3053                                 DoUnselectAll();
3054                                 DoSelectItem(wxTreeItemId(htItem));
3055                                 SetFocusedItem(wxTreeItemId(htItem));
3056 
3057                                 wxTreeEvent changedEvent(wxEVT_TREE_SEL_CHANGED,
3058                                                          this, htItem);
3059                                 changedEvent.m_itemOld = htOldItem;
3060                                 (void)HandleTreeEvent(changedEvent);
3061                             }
3062                         }
3063                     }
3064 
3065                     m_htClickedItem.Unset();
3066 
3067                     if ( m_triggerStateImageClick )
3068                     {
3069                         if ( tvht.flags & TVHT_ONITEMSTATEICON )
3070                         {
3071                             wxTreeEvent event(wxEVT_TREE_STATE_IMAGE_CLICK,
3072                                               this, htItem);
3073                             (void)HandleTreeEvent(event);
3074 
3075                             m_triggerStateImageClick = false;
3076                             processed = true;
3077                         }
3078                     }
3079 
3080                     if ( !m_dragStarted && MSWIsOnItem(tvht.flags) )
3081                     {
3082                         processed = true;
3083                     }
3084                 }
3085 
3086                 // fall through
3087 
3088             case WM_RBUTTONUP:
3089 #if wxUSE_DRAGIMAGE
3090                 if ( m_dragImage )
3091                 {
3092                     m_dragImage->EndDrag();
3093                     wxDELETE(m_dragImage);
3094 
3095                     // generate the drag end event
3096                     wxTreeEvent event(wxEVT_TREE_END_DRAG,
3097                                       this, htItem);
3098                     event.m_pointDrag = wxPoint(x, y);
3099                     (void)HandleTreeEvent(event);
3100 
3101                     // if we don't do it, the tree seems to think that 2 items
3102                     // are selected simultaneously which is quite weird
3103                     TreeView_SelectDropTarget(GetHwnd(), 0);
3104                 }
3105 #endif // wxUSE_DRAGIMAGE
3106 
3107                 if ( isMultiple && nMsg == WM_RBUTTONUP )
3108                 {
3109                     // send NM_RCLICK
3110                     NMHDR nmhdr;
3111                     nmhdr.hwndFrom = GetHwnd();
3112                     nmhdr.idFrom = ::GetWindowLong(GetHwnd(), GWL_ID);
3113                     nmhdr.code = NM_RCLICK;
3114                     ::SendMessage(::GetParent(GetHwnd()), WM_NOTIFY,
3115                                   nmhdr.idFrom, (LPARAM)&nmhdr);
3116                     processed = true;
3117                 }
3118 
3119                 m_dragStarted = false;
3120 
3121                 break;
3122         }
3123     }
3124     else if ( (nMsg == WM_SETFOCUS || nMsg == WM_KILLFOCUS) )
3125     {
3126         if ( isMultiple )
3127         {
3128             // the tree control greys out the selected item when it loses focus
3129             // and paints it as selected again when it regains it, but it won't
3130             // do it for the other items itself - help it
3131             wxArrayTreeItemIds selections;
3132             size_t count = GetSelections(selections);
3133             TVGetItemRectParam param;
3134 
3135             for ( size_t n = 0; n < count; n++ )
3136             {
3137                 // TreeView_GetItemRect() will return false if item is not
3138                 // visible, which may happen perfectly well
3139                 if ( wxTreeView_GetItemRect(GetHwnd(), HITEM(selections[n]),
3140                                             param, TRUE) )
3141                 {
3142                     ::InvalidateRect(GetHwnd(), &param.rect, FALSE);
3143                 }
3144             }
3145         }
3146 
3147         if ( nMsg == WM_KILLFOCUS )
3148         {
3149             m_focusLost = true;
3150         }
3151     }
3152     else if ( (nMsg == WM_KEYDOWN || nMsg == WM_SYSKEYDOWN) && isMultiple )
3153     {
3154         // normally we want to generate wxEVT_KEY_DOWN events from TVN_KEYDOWN
3155         // notification but for the keys which can be used to change selection
3156         // we need to do it from here so as to not apply the default behaviour
3157         // if the events are handled by the user code
3158         switch ( wParam )
3159         {
3160             case VK_RETURN:
3161             case VK_SPACE:
3162             case VK_UP:
3163             case VK_DOWN:
3164             case VK_LEFT:
3165             case VK_RIGHT:
3166             case VK_HOME:
3167             case VK_END:
3168             case VK_PRIOR:
3169             case VK_NEXT:
3170                 if ( !HandleKeyDown(wParam, lParam) &&
3171                         !MSWHandleTreeKeyDownEvent(wParam, lParam) )
3172                 {
3173                     // use the key to update the selection if it was left
3174                     // unprocessed
3175                     MSWHandleSelectionKey(wParam);
3176                 }
3177 
3178                 // pretend that we did process it in any case as we already
3179                 // generated an event for it
3180                 processed = true;
3181 
3182             //default: for all the other keys leave processed as false so that
3183             //         the tree control generates a TVN_KEYDOWN for us
3184         }
3185 
3186     }
3187     else if ( nMsg == WM_COMMAND )
3188     {
3189         // if we receive a EN_KILLFOCUS command from the in-place edit control
3190         // used for label editing, make sure to end editing
3191         WORD id, cmd;
3192         WXHWND hwnd;
3193         UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
3194 
3195         if ( cmd == EN_KILLFOCUS )
3196         {
3197             if ( m_textCtrl && m_textCtrl->GetHandle() == hwnd )
3198             {
3199                 DoEndEditLabel();
3200 
3201                 processed = true;
3202             }
3203         }
3204     }
3205 
3206     if ( !processed )
3207         rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
3208 
3209     return rc;
3210 }
3211 
3212 WXLRESULT
MSWDefWindowProc(WXUINT nMsg,WXWPARAM wParam,WXLPARAM lParam)3213 wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
3214 {
3215     if ( nMsg == WM_CHAR )
3216     {
3217         // don't let the control process Space and Return keys because it
3218         // doesn't do anything useful with them anyhow but always beeps
3219         // annoyingly when it receives them and there is no way to turn it off
3220         // simply if you just process TREEITEM_ACTIVATED event to which Space
3221         // and Enter presses are mapped in your code
3222         if ( wParam == VK_SPACE || wParam == VK_RETURN )
3223             return 0;
3224     }
3225 #if wxUSE_DRAGIMAGE
3226     else if ( nMsg == WM_KEYDOWN )
3227     {
3228         if ( wParam == VK_ESCAPE )
3229         {
3230             if ( m_dragImage )
3231             {
3232                 m_dragImage->EndDrag();
3233                 wxDELETE(m_dragImage);
3234 
3235                 // if we don't do it, the tree seems to think that 2 items
3236                 // are selected simultaneously which is quite weird
3237                 TreeView_SelectDropTarget(GetHwnd(), 0);
3238             }
3239         }
3240     }
3241 #endif // wxUSE_DRAGIMAGE
3242 
3243     return wxControl::MSWDefWindowProc(nMsg, wParam, lParam);
3244 }
3245 
3246 // process WM_NOTIFY Windows message
MSWOnNotify(int idCtrl,WXLPARAM lParam,WXLPARAM * result)3247 bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
3248 {
3249     wxTreeEvent event(wxEVT_NULL, this);
3250     wxEventType eventType = wxEVT_NULL;
3251     NMHDR *hdr = (NMHDR *)lParam;
3252 
3253     switch ( hdr->code )
3254     {
3255         case TVN_BEGINDRAG:
3256             eventType = wxEVT_TREE_BEGIN_DRAG;
3257             // fall through
3258 
3259         case TVN_BEGINRDRAG:
3260             {
3261                 if ( eventType == wxEVT_NULL )
3262                     eventType = wxEVT_TREE_BEGIN_RDRAG;
3263                 //else: left drag, already set above
3264 
3265                 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
3266 
3267                 event.m_item = tv->itemNew.hItem;
3268                 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
3269 
3270                 // don't allow dragging by default: the user code must
3271                 // explicitly say that it wants to allow it to avoid breaking
3272                 // the old apps
3273                 event.Veto();
3274             }
3275             break;
3276 
3277         case TVN_BEGINLABELEDIT:
3278             {
3279                 eventType = wxEVT_TREE_BEGIN_LABEL_EDIT;
3280                 NMTVDISPINFO *info = (NMTVDISPINFO *)lParam;
3281 
3282                 // although the user event handler may still veto it, it is
3283                 // important to set it now so that calls to SetItemText() from
3284                 // the event handler would change the text controls contents
3285                 m_idEdited =
3286                 event.m_item = info->item.hItem;
3287                 event.m_label = info->item.pszText;
3288                 event.m_editCancelled = false;
3289             }
3290             break;
3291 
3292         case TVN_DELETEITEM:
3293             {
3294                 eventType = wxEVT_TREE_DELETE_ITEM;
3295                 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
3296 
3297                 event.m_item = tv->itemOld.hItem;
3298 
3299                 if ( m_hasAnyAttr )
3300                 {
3301                     wxMapTreeAttr::iterator it = m_attrs.find(tv->itemOld.hItem);
3302                     if ( it != m_attrs.end() )
3303                     {
3304                         delete it->second;
3305                         m_attrs.erase(it);
3306                     }
3307                 }
3308             }
3309             break;
3310 
3311         case TVN_ENDLABELEDIT:
3312             {
3313                 eventType = wxEVT_TREE_END_LABEL_EDIT;
3314                 NMTVDISPINFO *info = (NMTVDISPINFO *)lParam;
3315 
3316                 event.m_item = info->item.hItem;
3317                 event.m_label = info->item.pszText;
3318                 event.m_editCancelled = info->item.pszText == NULL;
3319                 break;
3320             }
3321 
3322 #ifndef __WXWINCE__
3323         // These *must* not be removed or TVN_GETINFOTIP will
3324         // not be processed each time the mouse is moved
3325         // and the tooltip will only ever update once.
3326         case TTN_NEEDTEXTA:
3327         case TTN_NEEDTEXTW:
3328             {
3329                 *result = 0;
3330 
3331                 break;
3332             }
3333 
3334 #ifdef TVN_GETINFOTIP
3335         case TVN_GETINFOTIP:
3336             {
3337                 eventType = wxEVT_TREE_ITEM_GETTOOLTIP;
3338                 NMTVGETINFOTIP *info = (NMTVGETINFOTIP*)lParam;
3339 
3340                 // Which item are we trying to get a tooltip for?
3341                 event.m_item = info->hItem;
3342 
3343                 break;
3344             }
3345 #endif // TVN_GETINFOTIP
3346 #endif // !__WXWINCE__
3347 
3348         case TVN_GETDISPINFO:
3349             eventType = wxEVT_TREE_GET_INFO;
3350             // fall through
3351 
3352         case TVN_SETDISPINFO:
3353             {
3354                 if ( eventType == wxEVT_NULL )
3355                     eventType = wxEVT_TREE_SET_INFO;
3356                 //else: get, already set above
3357 
3358                 NMTVDISPINFO *info = (NMTVDISPINFO *)lParam;
3359 
3360                 event.m_item = info->item.hItem;
3361                 break;
3362             }
3363 
3364         case TVN_ITEMEXPANDING:
3365         case TVN_ITEMEXPANDED:
3366             {
3367                 NM_TREEVIEW *tv = (NM_TREEVIEW*)lParam;
3368 
3369                 int what;
3370                 switch ( tv->action )
3371                 {
3372                     default:
3373                         wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv->action);
3374                         // fall through
3375 
3376                     case TVE_EXPAND:
3377                         what = IDX_EXPAND;
3378                         break;
3379 
3380                     case TVE_COLLAPSE:
3381                         what = IDX_COLLAPSE;
3382                         break;
3383                 }
3384 
3385                 int how = hdr->code == TVN_ITEMEXPANDING ? IDX_DOING
3386                                                          : IDX_DONE;
3387 
3388                 eventType = gs_expandEvents[what][how];
3389 
3390                 event.m_item = tv->itemNew.hItem;
3391             }
3392             break;
3393 
3394         case TVN_KEYDOWN:
3395             {
3396                 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
3397 
3398                 // fabricate the lParam and wParam parameters sufficiently
3399                 // similar to the ones from a "real" WM_KEYDOWN so that
3400                 // CreateKeyEvent() works correctly
3401                 return MSWHandleTreeKeyDownEvent(
3402                         info->wVKey, (wxIsAltDown() ? KF_ALTDOWN : 0) << 16);
3403             }
3404 
3405 
3406         // Vista's tree control has introduced some problems with our
3407         // multi-selection tree.  When TreeView_SelectItem() is called,
3408         // the wrong items are deselected.
3409 
3410         // Fortunately, Vista provides a new notification, TVN_ITEMCHANGING
3411         // that can be used to regulate this incorrect behaviour.  The
3412         // following messages will allow only the unlocked item's selection
3413         // state to change
3414 
3415         case TVN_ITEMCHANGINGA:
3416         case TVN_ITEMCHANGINGW:
3417             {
3418                 // we only need to handles these in multi-select trees
3419                 if ( HasFlag(wxTR_MULTIPLE) )
3420                 {
3421                     // get info about the item about to be changed
3422                     NMTVITEMCHANGE* info = (NMTVITEMCHANGE*)lParam;
3423                     if (TreeItemUnlocker::IsLocked(info->hItem))
3424                     {
3425                         // item's state is locked, don't allow the change
3426                         // returning 1 will disallow the change
3427                         *result = 1;
3428                         return true;
3429                     }
3430                 }
3431 
3432                 // allow the state change
3433             }
3434             return false;
3435 
3436         // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of
3437         //     TVN_SELCHANGEDW in Unicode mode under Win98. Therefore
3438         //     we have to handle both messages:
3439         case TVN_SELCHANGEDA:
3440         case TVN_SELCHANGEDW:
3441             if ( !m_changingSelection )
3442             {
3443                 eventType = wxEVT_TREE_SEL_CHANGED;
3444             }
3445             // fall through
3446 
3447         case TVN_SELCHANGINGA:
3448         case TVN_SELCHANGINGW:
3449             if ( !m_changingSelection )
3450             {
3451                 if ( eventType == wxEVT_NULL )
3452                     eventType = wxEVT_TREE_SEL_CHANGING;
3453                 //else: already set above
3454 
3455                 if (hdr->code == TVN_SELCHANGINGW ||
3456                     hdr->code == TVN_SELCHANGEDW)
3457                 {
3458                     NM_TREEVIEWW *tv = (NM_TREEVIEWW *)lParam;
3459                     event.m_item = tv->itemNew.hItem;
3460                     event.m_itemOld = tv->itemOld.hItem;
3461                 }
3462                 else
3463                 {
3464                     NM_TREEVIEWA *tv = (NM_TREEVIEWA *)lParam;
3465                     event.m_item = tv->itemNew.hItem;
3466                     event.m_itemOld = tv->itemOld.hItem;
3467                 }
3468             }
3469 
3470             // we receive this message from WM_LBUTTONDOWN handler inside
3471             // comctl32.dll and so before the click is passed to
3472             // DefWindowProc() which sets the focus to the window which was
3473             // clicked and this can lead to unexpected event sequences: for
3474             // example, we may get a "selection change" event from the tree
3475             // before getting a "kill focus" event for the text control which
3476             // had the focus previously, thus breaking user code doing input
3477             // validation
3478             //
3479             // to avoid such surprises, we force the generation of focus events
3480             // now, before we generate the selection change ones
3481             if ( !m_changingSelection && !m_isBeingDeleted )
3482                 SetFocus();
3483             break;
3484 
3485         // instead of explicitly checking for _WIN32_IE, check if the
3486         // required symbols are available in the headers
3487 #if defined(CDDS_PREPAINT)
3488         case NM_CUSTOMDRAW:
3489             {
3490                 LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam;
3491                 NMCUSTOMDRAW& nmcd = lptvcd->nmcd;
3492                 switch ( nmcd.dwDrawStage )
3493                 {
3494                     case CDDS_PREPAINT:
3495                         // if we've got any items with non standard attributes,
3496                         // notify us before painting each item
3497                         *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
3498                                                : CDRF_DODEFAULT;
3499 
3500                         // windows in TreeCtrl use one-based index for item state images,
3501                         // 0 indexed image is not being used, we're using zero-based index,
3502                         // so we have to add temp image (of zero index) to state image list
3503                         // before we draw any item, then after items are drawn we have to
3504                         // delete it (in POSTPAINT notify)
3505                         if (m_imageListState && m_imageListState->GetImageCount() > 0)
3506                         {
3507                             typedef BOOL (wxSTDCALL *ImageList_Copy_t)
3508                                 (HIMAGELIST, int, HIMAGELIST, int, UINT);
3509                             static ImageList_Copy_t s_pfnImageList_Copy = NULL;
3510                             static bool loaded = false;
3511 
3512                             if ( !loaded )
3513                             {
3514                                 wxLoadedDLL dllComCtl32(wxT("comctl32.dll"));
3515                                 if ( dllComCtl32.IsLoaded() )
3516                                 {
3517                                     wxDL_INIT_FUNC(s_pfn, ImageList_Copy, dllComCtl32);
3518                                     loaded = true;
3519                                 }
3520                             }
3521 
3522                             if ( !s_pfnImageList_Copy )
3523                             {
3524                                 // this code is broken with ImageList_Copy()
3525                                 // but I don't care enough about Win95 support
3526                                 // to write it now -- if anybody does, please
3527                                 // do it
3528                                 wxFAIL_MSG("TODO: implement this for Win95");
3529                                 break;
3530                             }
3531 
3532                             const HIMAGELIST
3533                                 hImageList = GetHimagelistOf(m_imageListState);
3534 
3535                             // add temporary image
3536                             int width, height;
3537                             m_imageListState->GetSize(0, width, height);
3538 
3539                             HBITMAP hbmpTemp = ::CreateBitmap(width, height, 1, 1, NULL);
3540                             int index = ::ImageList_Add(hImageList, hbmpTemp, hbmpTemp);
3541                             ::DeleteObject(hbmpTemp);
3542 
3543                             if ( index != -1 )
3544                             {
3545                                 // move images to right
3546                                 for ( int i = index; i > 0; i-- )
3547                                 {
3548                                     (*s_pfnImageList_Copy)(hImageList, i,
3549                                                            hImageList, i-1,
3550                                                            ILCF_MOVE);
3551                                 }
3552 
3553                                 // we must remove the image in POSTPAINT notify
3554                                 *result |= CDRF_NOTIFYPOSTPAINT;
3555                             }
3556                         }
3557                         break;
3558 
3559                     case CDDS_POSTPAINT:
3560                         // we are deleting temp image of 0 index, which was
3561                         // added before items were drawn (in PREPAINT notify)
3562                         if (m_imageListState && m_imageListState->GetImageCount() > 0)
3563                             m_imageListState->Remove(0);
3564                         break;
3565 
3566                     case CDDS_ITEMPREPAINT:
3567                         {
3568                             wxMapTreeAttr::iterator
3569                                 it = m_attrs.find((void *)nmcd.dwItemSpec);
3570 
3571                             if ( it == m_attrs.end() )
3572                             {
3573                                 // nothing to do for this item
3574                                 *result = CDRF_DODEFAULT;
3575                                 break;
3576                             }
3577 
3578                             wxTreeItemAttr * const attr = it->second;
3579 
3580                             wxTreeViewItem tvItem((void *)nmcd.dwItemSpec,
3581                                                   TVIF_STATE, TVIS_DROPHILITED);
3582                             DoGetItem(&tvItem);
3583                             const UINT tvItemState = tvItem.state;
3584 
3585                             // selection colours should override ours,
3586                             // otherwise it is too confusing to the user
3587                             if ( !(nmcd.uItemState & CDIS_SELECTED) &&
3588                                  !(tvItemState & TVIS_DROPHILITED) )
3589                             {
3590                                 wxColour colBack;
3591                                 if ( attr->HasBackgroundColour() )
3592                                 {
3593                                     colBack = attr->GetBackgroundColour();
3594                                     lptvcd->clrTextBk = wxColourToRGB(colBack);
3595                                 }
3596                             }
3597 
3598                             // but we still want to keep the special foreground
3599                             // colour when we don't have focus (we can't keep
3600                             // it when we do, it would usually be unreadable on
3601                             // the almost inverted bg colour...)
3602                             if ( ( !(nmcd.uItemState & CDIS_SELECTED) ||
3603                                     FindFocus() != this ) &&
3604                                  !(tvItemState & TVIS_DROPHILITED) )
3605                             {
3606                                 wxColour colText;
3607                                 if ( attr->HasTextColour() )
3608                                 {
3609                                     colText = attr->GetTextColour();
3610                                     lptvcd->clrText = wxColourToRGB(colText);
3611                                 }
3612                             }
3613 
3614                             if ( attr->HasFont() )
3615                             {
3616                                 HFONT hFont = GetHfontOf(attr->GetFont());
3617 
3618                                 ::SelectObject(nmcd.hdc, hFont);
3619 
3620                                 *result = CDRF_NEWFONT;
3621                             }
3622                             else // no specific font
3623                             {
3624                                 *result = CDRF_DODEFAULT;
3625                             }
3626                         }
3627                         break;
3628 
3629                     default:
3630                         *result = CDRF_DODEFAULT;
3631                 }
3632             }
3633 
3634             // we always process it
3635             return true;
3636 #endif // have owner drawn support in headers
3637 
3638         case NM_CLICK:
3639             {
3640                 DWORD pos = GetMessagePos();
3641                 POINT point;
3642                 point.x = GET_X_LPARAM(pos);
3643                 point.y = GET_Y_LPARAM(pos);
3644                 ::MapWindowPoints(HWND_DESKTOP, GetHwnd(), &point, 1);
3645                 int htFlags = 0;
3646                 wxTreeItemId item = HitTest(wxPoint(point.x, point.y), htFlags);
3647 
3648                 if ( htFlags & wxTREE_HITTEST_ONITEMSTATEICON )
3649                 {
3650                     event.m_item = item;
3651                     eventType = wxEVT_TREE_STATE_IMAGE_CLICK;
3652                 }
3653 
3654                 break;
3655             }
3656 
3657         case NM_DBLCLK:
3658         case NM_RCLICK:
3659             {
3660                 TV_HITTESTINFO tvhti;
3661                 wxGetCursorPosMSW(&tvhti.pt);
3662                 ::ScreenToClient(GetHwnd(), &tvhti.pt);
3663                 if ( TreeView_HitTest(GetHwnd(), &tvhti) )
3664                 {
3665                     if ( MSWIsOnItem(tvhti.flags) )
3666                     {
3667                         event.m_item = tvhti.hItem;
3668                         eventType = hdr->code == NM_DBLCLK
3669                                     ? wxEVT_TREE_ITEM_ACTIVATED
3670                                     : wxEVT_TREE_ITEM_RIGHT_CLICK;
3671 
3672                         event.m_pointDrag.x = tvhti.pt.x;
3673                         event.m_pointDrag.y = tvhti.pt.y;
3674                     }
3675 
3676                     break;
3677                 }
3678             }
3679             // fall through
3680 
3681         default:
3682             return wxControl::MSWOnNotify(idCtrl, lParam, result);
3683     }
3684 
3685     event.SetEventType(eventType);
3686 
3687     bool processed = HandleTreeEvent(event);
3688 
3689     // post processing
3690     switch ( hdr->code )
3691     {
3692         case NM_DBLCLK:
3693             // we translate NM_DBLCLK into ACTIVATED event and if the user
3694             // handled the activation of the item we shouldn't proceed with
3695             // also using the same double click for toggling the item expanded
3696             // state -- but OTOH do let the user to expand/collapse the item by
3697             // double clicking on it if the activation is not handled specially
3698             *result = processed;
3699             break;
3700 
3701         case NM_RCLICK:
3702             // prevent tree control from sending WM_CONTEXTMENU to our parent
3703             // (which it does if NM_RCLICK is not handled) because we want to
3704             // send it to the control itself
3705             *result =
3706             processed = true;
3707 
3708             ::SendMessage(GetHwnd(), WM_CONTEXTMENU,
3709                           (WPARAM)GetHwnd(), ::GetMessagePos());
3710             break;
3711 
3712         case TVN_BEGINDRAG:
3713         case TVN_BEGINRDRAG:
3714 #if wxUSE_DRAGIMAGE
3715             if ( event.IsAllowed() )
3716             {
3717                 // normally this is impossible because the m_dragImage is
3718                 // deleted once the drag operation is over
3719                 wxASSERT_MSG( !m_dragImage, wxT("starting to drag once again?") );
3720 
3721                 m_dragImage = new wxDragImage(*this, event.m_item);
3722                 m_dragImage->BeginDrag(wxPoint(0,0), this);
3723                 m_dragImage->Show();
3724 
3725                 m_dragStarted = true;
3726             }
3727 #endif // wxUSE_DRAGIMAGE
3728             break;
3729 
3730         case TVN_DELETEITEM:
3731             {
3732                 // NB: we might process this message using wxWidgets event
3733                 //     tables, but due to overhead of wxWin event system we
3734                 //     prefer to do it here ourself (otherwise deleting a tree
3735                 //     with many items is just too slow)
3736                 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
3737 
3738                 wxTreeItemParam *param =
3739                         (wxTreeItemParam *)tv->itemOld.lParam;
3740                 delete param;
3741 
3742                 processed = true; // Make sure we don't get called twice
3743             }
3744             break;
3745 
3746         case TVN_BEGINLABELEDIT:
3747             // return true to cancel label editing
3748             *result = !event.IsAllowed();
3749 
3750             // set ES_WANTRETURN ( like we do in BeginLabelEdit )
3751             if ( event.IsAllowed() )
3752             {
3753                 HWND hText = TreeView_GetEditControl(GetHwnd());
3754                 if ( hText )
3755                 {
3756                     // MBN: if m_textCtrl already has an HWND, it is a stale
3757                     // pointer from a previous edit (because the user
3758                     // didn't modify the label before dismissing the control,
3759                     // and TVN_ENDLABELEDIT was not sent), so delete it
3760                     if ( m_textCtrl && m_textCtrl->GetHWND() )
3761                         DeleteTextCtrl();
3762                     if ( !m_textCtrl )
3763                         m_textCtrl = new wxTextCtrl();
3764                     m_textCtrl->SetParent(this);
3765                     m_textCtrl->SetHWND((WXHWND)hText);
3766                     m_textCtrl->SubclassWin((WXHWND)hText);
3767 
3768                     // set wxTE_PROCESS_ENTER style for the text control to
3769                     // force it to process the Enter presses itself, otherwise
3770                     // they could be stolen from it by the dialog
3771                     // navigation code
3772                     m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle()
3773                                                | wxTE_PROCESS_ENTER);
3774                 }
3775             }
3776             else // we had set m_idEdited before
3777             {
3778                 m_idEdited.Unset();
3779             }
3780             break;
3781 
3782         case TVN_ENDLABELEDIT:
3783             // return true to set the label to the new string: note that we
3784             // also must pretend that we did process the message or it is going
3785             // to be passed to DefWindowProc() which will happily return false
3786             // cancelling the label change
3787             *result = event.IsAllowed();
3788             processed = true;
3789 
3790             // ensure that we don't have the text ctrl which is going to be
3791             // deleted any more
3792             DeleteTextCtrl();
3793             break;
3794 
3795 #ifndef __WXWINCE__
3796 #ifdef TVN_GETINFOTIP
3797          case TVN_GETINFOTIP:
3798             {
3799                 // If the user permitted a tooltip change, change it
3800                 if (event.IsAllowed())
3801                 {
3802                     SetToolTip(event.m_label);
3803                 }
3804             }
3805             break;
3806 #endif
3807 #endif
3808 
3809         case TVN_SELCHANGING:
3810         case TVN_ITEMEXPANDING:
3811             // return true to prevent the action from happening
3812             *result = !event.IsAllowed();
3813             break;
3814 
3815         case TVN_ITEMEXPANDED:
3816             {
3817                 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
3818                 const wxTreeItemId id(tv->itemNew.hItem);
3819 
3820                 if ( tv->action == TVE_COLLAPSE )
3821                 {
3822                     if ( wxApp::GetComCtl32Version() >= 600 )
3823                     {
3824                         // for some reason the item selection rectangle depends
3825                         // on whether it is expanded or collapsed (at least
3826                         // with comctl32.dll v6): it is wider (by 3 pixels) in
3827                         // the expanded state, so when the item collapses and
3828                         // then is deselected the rightmost 3 pixels of the
3829                         // previously drawn selection are left on the screen
3830                         //
3831                         // it's not clear if it's a bug in comctl32.dll or in
3832                         // our code (because it does not happen in Explorer but
3833                         // OTOH we don't do anything which could result in this
3834                         // AFAICS) but we do need to work around it to avoid
3835                         // ugly artifacts
3836                         RefreshItem(id);
3837                     }
3838                 }
3839                 else // expand
3840                 {
3841                     // the item is also not refreshed properly after expansion when
3842                     // it has an image depending on the expanded/collapsed state:
3843                     // again, it's not clear if the bug is in comctl32.dll or our
3844                     // code...
3845                     int image = GetItemImage(id, wxTreeItemIcon_Expanded);
3846                     if ( image != -1 )
3847                     {
3848                         RefreshItem(id);
3849                     }
3850                 }
3851             }
3852             break;
3853 
3854         case TVN_GETDISPINFO:
3855             // NB: so far the user can't set the image himself anyhow, so do it
3856             //     anyway - but this may change later
3857             //if ( /* !processed && */ )
3858             {
3859                 wxTreeItemId item = event.m_item;
3860                 NMTVDISPINFO *info = (NMTVDISPINFO *)lParam;
3861 
3862                 const wxTreeItemParam * const param = GetItemParam(item);
3863                 if ( !param )
3864                     break;
3865 
3866                 if ( info->item.mask & TVIF_IMAGE )
3867                 {
3868                     info->item.iImage =
3869                         param->GetImage
3870                         (
3871                          IsExpanded(item) ? wxTreeItemIcon_Expanded
3872                                           : wxTreeItemIcon_Normal
3873                         );
3874                 }
3875                 if ( info->item.mask & TVIF_SELECTEDIMAGE )
3876                 {
3877                     info->item.iSelectedImage =
3878                         param->GetImage
3879                         (
3880                          IsExpanded(item) ? wxTreeItemIcon_SelectedExpanded
3881                                           : wxTreeItemIcon_Selected
3882                         );
3883                 }
3884             }
3885             break;
3886 
3887         //default:
3888             // for the other messages the return value is ignored and there is
3889             // nothing special to do
3890     }
3891     return processed;
3892 }
3893 
3894 // ----------------------------------------------------------------------------
3895 // State control.
3896 // ----------------------------------------------------------------------------
3897 
3898 // why do they define INDEXTOSTATEIMAGEMASK but not the inverse?
3899 #define STATEIMAGEMASKTOINDEX(state) (((state) & TVIS_STATEIMAGEMASK) >> 12)
3900 
DoGetItemState(const wxTreeItemId & item) const3901 int wxTreeCtrl::DoGetItemState(const wxTreeItemId& item) const
3902 {
3903     wxCHECK_MSG( item.IsOk(), wxTREE_ITEMSTATE_NONE, wxT("invalid tree item") );
3904 
3905     // receive the desired information
3906     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
3907     DoGetItem(&tvItem);
3908 
3909     // state images are one-based
3910     return STATEIMAGEMASKTOINDEX(tvItem.state) - 1;
3911 }
3912 
DoSetItemState(const wxTreeItemId & item,int state)3913 void wxTreeCtrl::DoSetItemState(const wxTreeItemId& item, int state)
3914 {
3915     wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
3916 
3917     wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
3918 
3919     // state images are one-based
3920     // 0 if no state image display (wxTREE_ITEMSTATE_NONE = -1)
3921     tvItem.state = INDEXTOSTATEIMAGEMASK(state + 1);
3922 
3923     DoSetItem(&tvItem);
3924 }
3925 
3926 // ----------------------------------------------------------------------------
3927 // Update locking.
3928 // ----------------------------------------------------------------------------
3929 
3930 // Using WM_SETREDRAW with the native control is a bad idea as it's broken in
3931 // some Windows versions (see http://support.microsoft.com/kb/130611) and
3932 // doesn't seem to do anything in other ones (e.g. under Windows 7 the tree
3933 // control keeps updating its scrollbars while the items are added to it,
3934 // resulting in horrible flicker when adding even a couple of dozen items).
3935 // So we resize it to the smallest possible size instead of freezing -- this
3936 // still flickers, but actually not as badly as it would if we didn't do it.
3937 
DoFreeze()3938 void wxTreeCtrl::DoFreeze()
3939 {
3940     if ( IsShown() )
3941     {
3942         RECT rc;
3943         ::GetWindowRect(GetHwnd(), &rc);
3944         m_thawnSize = wxRectFromRECT(rc).GetSize();
3945 
3946         ::SetWindowPos(GetHwnd(), 0, 0, 0, 1, 1,
3947                        SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE);
3948     }
3949 }
3950 
DoThaw()3951 void wxTreeCtrl::DoThaw()
3952 {
3953     if ( IsShown() )
3954     {
3955         if ( m_thawnSize != wxDefaultSize )
3956         {
3957             ::SetWindowPos(GetHwnd(), 0, 0, 0, m_thawnSize.x, m_thawnSize.y,
3958                            SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
3959         }
3960     }
3961 }
3962 
3963 // We also need to override DoSetSize() to ensure that m_thawnSize is reset if
3964 // the window is resized while being frozen -- in this case, we need to avoid
3965 // resizing it back to its original, pre-freeze, size when it's thawed.
DoSetSize(int x,int y,int width,int height,int sizeFlags)3966 void wxTreeCtrl::DoSetSize(int x, int y, int width, int height, int sizeFlags)
3967 {
3968     m_thawnSize = wxDefaultSize;
3969 
3970     wxTreeCtrlBase::DoSetSize(x, y, width, height, sizeFlags);
3971 }
3972 
3973 #endif // wxUSE_TREECTRL
3974