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