]> git.saurik.com Git - wxWidgets.git/blame - src/msw/treectrl.cpp
Removed unnecessary bitmaps
[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
14f355c2 20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
08b7c251 21 #pragma implementation "treectrl.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
08b7c251 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
1e6feb95
VZ
31#if wxUSE_TREECTRL
32
0c589ad0 33#include "wx/msw/private.h"
f3f71703 34#include "wx/msw/missing.h"
0c589ad0 35
1e6feb95
VZ
36// Set this to 1 to be _absolutely_ sure that repainting will work for all
37// comctl32.dll versions
f6bcfd97
BP
38#define wxUSE_COMCTL32_SAFELY 0
39
c6f4913a 40#include "wx/app.h"
08b7c251 41#include "wx/log.h"
ce3ed50d 42#include "wx/dynarray.h"
08b7c251 43#include "wx/imaglist.h"
77cff606 44#include "wx/settings.h"
484523cf 45#include "wx/msw/treectrl.h"
23f681ec
VZ
46#include "wx/msw/dragimag.h"
47
0d236fd0
VZ
48// include <commctrl.h> "properly"
49#include "wx/msw/wrapcctl.h"
bb448552 50
f888d614
VZ
51// macros to hide the cast ugliness
52// --------------------------------
53
54// ptr is the real item id, i.e. wxTreeItemId::m_pItem
55#define HITEM_PTR(ptr) (HTREEITEM)(ptr)
56
57// item here is a wxTreeItemId
58#define HITEM(item) HITEM_PTR((item).m_pItem)
3f7bc32b
VZ
59
60// the native control doesn't support multiple selections under MSW and we
61// have 2 ways to emulate them: either using TVS_CHECKBOXES style and let
62// checkboxes be the selection status (checked == selected) or by really
63// emulating everything, i.e. intercepting mouse and key events &c. The first
64// approach is much easier but doesn't work with comctl32.dll < 4.71 and also
65// looks quite ugly.
66#define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0
67
3f7bc32b
VZ
68// ----------------------------------------------------------------------------
69// private functions
70// ----------------------------------------------------------------------------
71
72// wrapper for TreeView_HitTest
73static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y)
74{
75 TV_HITTESTINFO tvht;
76 tvht.pt.x = x;
77 tvht.pt.y = y;
78
3f7bc32b
VZ
79 return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht);
80}
81
82#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
83
84// wrappers for TreeView_GetItem/TreeView_SetItem
85static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
86{
a9c1265f 87
3f7bc32b
VZ
88 TV_ITEM tvi;
89 tvi.mask = TVIF_STATE | TVIF_HANDLE;
90 tvi.stateMask = TVIS_SELECTED;
91 tvi.hItem = hItem;
92
93 if ( !TreeView_GetItem(hwndTV, &tvi) )
94 {
f6bcfd97 95 wxLogLastError(wxT("TreeView_GetItem"));
3f7bc32b
VZ
96 }
97
98 return (tvi.state & TVIS_SELECTED) != 0;
99}
100
3e9af289 101static bool SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = true)
3f7bc32b
VZ
102{
103 TV_ITEM tvi;
104 tvi.mask = TVIF_STATE | TVIF_HANDLE;
105 tvi.stateMask = TVIS_SELECTED;
106 tvi.state = select ? TVIS_SELECTED : 0;
107 tvi.hItem = hItem;
108
109 if ( TreeView_SetItem(hwndTV, &tvi) == -1 )
110 {
f6bcfd97 111 wxLogLastError(wxT("TreeView_SetItem"));
3e9af289 112 return false;
3f7bc32b 113 }
3e9af289
VZ
114
115 return true;
3f7bc32b
VZ
116}
117
118static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem)
119{
04cd30de 120 SelectItem(hwndTV, htItem, false);
3f7bc32b
VZ
121}
122
123static inline void ToggleItemSelection(HWND hwndTV, HTREEITEM htItem)
124{
125 SelectItem(hwndTV, htItem, !IsItemSelected(hwndTV, htItem));
126}
127
128// helper function which selects all items in a range and, optionally,
129// unselects all others
130static void SelectRange(HWND hwndTV,
131 HTREEITEM htFirst,
132 HTREEITEM htLast,
04cd30de 133 bool unselectOthers = true)
3f7bc32b
VZ
134{
135 // find the first (or last) item and select it
04cd30de 136 bool cont = true;
2c8e4738 137 HTREEITEM htItem = (HTREEITEM)TreeView_GetRoot(hwndTV);
3f7bc32b
VZ
138 while ( htItem && cont )
139 {
140 if ( (htItem == htFirst) || (htItem == htLast) )
141 {
142 if ( !IsItemSelected(hwndTV, htItem) )
143 {
144 SelectItem(hwndTV, htItem);
145 }
146
04cd30de 147 cont = false;
3f7bc32b
VZ
148 }
149 else
150 {
151 if ( unselectOthers && IsItemSelected(hwndTV, htItem) )
152 {
153 UnselectItem(hwndTV, htItem);
154 }
155 }
156
2c8e4738 157 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
158 }
159
160 // select the items in range
161 cont = htFirst != htLast;
162 while ( htItem && cont )
163 {
164 if ( !IsItemSelected(hwndTV, htItem) )
165 {
166 SelectItem(hwndTV, htItem);
167 }
168
169 cont = (htItem != htFirst) && (htItem != htLast);
170
2c8e4738 171 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
172 }
173
174 // unselect the rest
175 if ( unselectOthers )
176 {
177 while ( htItem )
178 {
179 if ( IsItemSelected(hwndTV, htItem) )
180 {
181 UnselectItem(hwndTV, htItem);
182 }
183
2c8e4738 184 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
185 }
186 }
187
188 // seems to be necessary - otherwise the just selected items don't always
189 // appear as selected
190 UpdateWindow(hwndTV);
191}
192
193// helper function which tricks the standard control into changing the focused
194// item without changing anything else (if someone knows why Microsoft doesn't
195// allow to do it by just setting TVIS_FOCUSED flag, please tell me!)
196static void SetFocus(HWND hwndTV, HTREEITEM htItem)
197{
198 // the current focus
2c8e4738 199 HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV);
3f7bc32b
VZ
200
201 if ( htItem )
202 {
203 // set the focus
204 if ( htItem != htFocus )
205 {
206 // remember the selection state of the item
207 bool wasSelected = IsItemSelected(hwndTV, htItem);
208
209 if ( htFocus && IsItemSelected(hwndTV, htFocus) )
210 {
211 // prevent the tree from unselecting the old focus which it
212 // would do by default (TreeView_SelectItem unselects the
213 // focused item)
214 TreeView_SelectItem(hwndTV, 0);
215 SelectItem(hwndTV, htFocus);
216 }
217
218 TreeView_SelectItem(hwndTV, htItem);
219
220 if ( !wasSelected )
221 {
222 // need to clear the selection which TreeView_SelectItem() gave
223 // us
224 UnselectItem(hwndTV, htItem);
225 }
226 //else: was selected, still selected - ok
227 }
228 //else: nothing to do, focus already there
229 }
230 else
231 {
232 if ( htFocus )
233 {
234 bool wasFocusSelected = IsItemSelected(hwndTV, htFocus);
235
236 // just clear the focus
237 TreeView_SelectItem(hwndTV, 0);
238
239 if ( wasFocusSelected )
240 {
241 // restore the selection state
242 SelectItem(hwndTV, htFocus);
243 }
244 }
245 //else: nothing to do, no focus already
246 }
247}
248
249#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
250
08b7c251
VZ
251// ----------------------------------------------------------------------------
252// private classes
253// ----------------------------------------------------------------------------
2bda0e17 254
08b7c251 255// a convenient wrapper around TV_ITEM struct which adds a ctor
f3ef286f 256#ifdef __VISUALC__
3f7bc32b 257#pragma warning( disable : 4097 ) // inheriting from typedef
f3ef286f
JS
258#endif
259
08b7c251
VZ
260struct wxTreeViewItem : public TV_ITEM
261{
9dfbf520
VZ
262 wxTreeViewItem(const wxTreeItemId& item, // the item handle
263 UINT mask_, // fields which are valid
264 UINT stateMask_ = 0) // for TVIF_STATE only
08b7c251 265 {
a9c1265f
VZ
266 wxZeroMemory(*this);
267
9dfbf520
VZ
268 // hItem member is always valid
269 mask = mask_ | TVIF_HANDLE;
08b7c251 270 stateMask = stateMask_;
3f7bc32b 271 hItem = HITEM(item);
08b7c251
VZ
272 }
273};
f3ef286f 274
a9c1265f
VZ
275// wxVirutalNode is used in place of a single root when 'hidden' root is
276// specified.
efa38350 277class wxVirtualNode : public wxTreeViewItem
a9c1265f
VZ
278{
279public:
280 wxVirtualNode(wxTreeItemData *data)
efa38350 281 : wxTreeViewItem(TVI_ROOT, 0)
a9c1265f
VZ
282 {
283 m_data = data;
a9c1265f
VZ
284 }
285
286 ~wxVirtualNode()
287 {
288 delete m_data;
a9c1265f
VZ
289 }
290
291 wxTreeItemData *GetData() const { return m_data; }
292 void SetData(wxTreeItemData *data) { delete m_data; m_data = data; }
293
294private:
a9c1265f 295 wxTreeItemData *m_data;
22f3361e
VZ
296
297 DECLARE_NO_COPY_CLASS(wxVirtualNode)
a9c1265f
VZ
298};
299
f3ef286f 300#ifdef __VISUALC__
197dd9af 301#pragma warning( default : 4097 )
f3ef286f 302#endif
2bda0e17 303
a9c1265f
VZ
304// a macro to get the virtual root, returns NULL if none
305#define GET_VIRTUAL_ROOT() ((wxVirtualNode *)m_pVirtualRoot)
306
04cd30de 307// returns true if the item is the virtual root
a9c1265f
VZ
308#define IS_VIRTUAL_ROOT(item) (HITEM(item) == TVI_ROOT)
309
9dfbf520 310// a class which encapsulates the tree traversal logic: it vists all (unless
04cd30de 311// OnVisit() returns false) items under the given one
9dfbf520
VZ
312class wxTreeTraversal
313{
314public:
315 wxTreeTraversal(const wxTreeCtrl *tree)
316 {
317 m_tree = tree;
318 }
319
320 // do traverse the tree: visit all items (recursively by default) under the
04cd30de
RL
321 // given one; return true if all items were traversed or false if the
322 // traversal was aborted because OnVisit returned false
323 bool DoTraverse(const wxTreeItemId& root, bool recursively = true);
9dfbf520
VZ
324
325 // override this function to do whatever is needed for each item, return
04cd30de 326 // false to stop traversing
9dfbf520
VZ
327 virtual bool OnVisit(const wxTreeItemId& item) = 0;
328
329protected:
330 const wxTreeCtrl *GetTree() const { return m_tree; }
331
332private:
333 bool Traverse(const wxTreeItemId& root, bool recursively);
334
335 const wxTreeCtrl *m_tree;
22f3361e
VZ
336
337 DECLARE_NO_COPY_CLASS(wxTreeTraversal)
9dfbf520
VZ
338};
339
74b31181
VZ
340// internal class for getting the selected items
341class TraverseSelections : public wxTreeTraversal
342{
343public:
344 TraverseSelections(const wxTreeCtrl *tree,
345 wxArrayTreeItemIds& selections)
346 : wxTreeTraversal(tree), m_selections(selections)
347 {
348 m_selections.Empty();
349
350 DoTraverse(tree->GetRootItem());
351 }
352
353 virtual bool OnVisit(const wxTreeItemId& item)
354 {
a9c1265f
VZ
355 // can't visit a virtual node.
356 if ( (GetTree()->GetRootItem() == item) && (GetTree()->GetWindowStyle() & wxTR_HIDE_ROOT))
357 {
04cd30de 358 return true;
a9c1265f
VZ
359 }
360
3f7bc32b 361#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
74b31181 362 if ( GetTree()->IsItemChecked(item) )
3f7bc32b
VZ
363#else
364 if ( ::IsItemSelected(GetHwndOf(GetTree()), HITEM(item)) )
365#endif
74b31181
VZ
366 {
367 m_selections.Add(item);
368 }
369
04cd30de 370 return true;
74b31181
VZ
371 }
372
e47c4d48
VZ
373 size_t GetCount() const { return m_selections.GetCount(); }
374
74b31181
VZ
375private:
376 wxArrayTreeItemIds& m_selections;
fc7a2a60
VZ
377
378 DECLARE_NO_COPY_CLASS(TraverseSelections)
74b31181
VZ
379};
380
381// internal class for counting tree items
382class TraverseCounter : public wxTreeTraversal
383{
384public:
385 TraverseCounter(const wxTreeCtrl *tree,
386 const wxTreeItemId& root,
387 bool recursively)
388 : wxTreeTraversal(tree)
389 {
390 m_count = 0;
391
392 DoTraverse(root, recursively);
393 }
394
33ac7e6f 395 virtual bool OnVisit(const wxTreeItemId& WXUNUSED(item))
74b31181
VZ
396 {
397 m_count++;
398
04cd30de 399 return true;
74b31181
VZ
400 }
401
402 size_t GetCount() const { return m_count; }
403
404private:
405 size_t m_count;
fc7a2a60
VZ
406
407 DECLARE_NO_COPY_CLASS(TraverseCounter)
74b31181
VZ
408};
409
410// ----------------------------------------------------------------------------
411// This class is needed for support of different images: the Win32 common
412// control natively supports only 2 images (the normal one and another for the
413// selected state). We wish to provide support for 2 more of them for folder
414// items (i.e. those which have children): for expanded state and for expanded
415// selected state. For this we use this structure to store the additional items
416// images.
417//
418// There is only one problem with this: when we retrieve the item's data, we
419// don't know whether we get a pointer to wxTreeItemData or
502a2b18
VZ
420// wxTreeItemIndirectData. So we always set the item id to an invalid value
421// in this class and the code using the client data checks for it and retrieves
422// the real client data in this case.
74b31181 423// ----------------------------------------------------------------------------
696e1ea0 424
502a2b18 425class wxTreeItemIndirectData : public wxTreeItemData
74b31181
VZ
426{
427public:
428 // ctor associates this data with the item and the real item data becomes
429 // available through our GetData() method
430 wxTreeItemIndirectData(wxTreeCtrl *tree, const wxTreeItemId& item)
431 {
432 for ( size_t n = 0; n < WXSIZEOF(m_images); n++ )
433 {
434 m_images[n] = -1;
435 }
436
437 // save the old data
438 m_data = tree->GetItemData(item);
439
440 // and set ourselves as the new one
441 tree->SetIndirectItemData(item, this);
cf48ccc0
VZ
442
443 // we must have the invalid value for the item
444 m_pItem = 0l;
74b31181
VZ
445 }
446
447 // dtor deletes the associated data as well
502a2b18 448 virtual ~wxTreeItemIndirectData() { delete m_data; }
74b31181
VZ
449
450 // accessors
451 // get the real data associated with the item
452 wxTreeItemData *GetData() const { return m_data; }
453 // change it
454 void SetData(wxTreeItemData *data) { m_data = data; }
455
456 // do we have such image?
457 bool HasImage(wxTreeItemIcon which) const { return m_images[which] != -1; }
458 // get image
459 int GetImage(wxTreeItemIcon which) const { return m_images[which]; }
460 // change it
461 void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
462
463private:
464 // all the images associated with the item
465 int m_images[wxTreeItemIcon_Max];
466
502a2b18 467 // the real client data
74b31181 468 wxTreeItemData *m_data;
22f3361e
VZ
469
470 DECLARE_NO_COPY_CLASS(wxTreeItemIndirectData)
74b31181
VZ
471};
472
23f681ec 473// ----------------------------------------------------------------------------
3f7bc32b 474// wxWin macros
08b7c251
VZ
475// ----------------------------------------------------------------------------
476
786a2425 477#if wxUSE_EXTENDED_RTTI
bc9fb572
JS
478WX_DEFINE_FLAGS( wxTreeCtrlStyle )
479
3ff066a4 480wxBEGIN_FLAGS( wxTreeCtrlStyle )
bc9fb572
JS
481 // new style border flags, we put them first to
482 // use them for streaming out
3ff066a4
SC
483 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
484 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
485 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
486 wxFLAGS_MEMBER(wxBORDER_RAISED)
487 wxFLAGS_MEMBER(wxBORDER_STATIC)
488 wxFLAGS_MEMBER(wxBORDER_NONE)
5e7718a2 489
bc9fb572 490 // old style border flags
3ff066a4
SC
491 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
492 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
493 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
494 wxFLAGS_MEMBER(wxRAISED_BORDER)
495 wxFLAGS_MEMBER(wxSTATIC_BORDER)
cb0afb26 496 wxFLAGS_MEMBER(wxBORDER)
bc9fb572
JS
497
498 // standard window styles
3ff066a4
SC
499 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
500 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
501 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
502 wxFLAGS_MEMBER(wxWANTS_CHARS)
cb0afb26 503 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
3ff066a4
SC
504 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
505 wxFLAGS_MEMBER(wxVSCROLL)
506 wxFLAGS_MEMBER(wxHSCROLL)
507
508 wxFLAGS_MEMBER(wxTR_EDIT_LABELS)
509 wxFLAGS_MEMBER(wxTR_NO_BUTTONS)
510 wxFLAGS_MEMBER(wxTR_HAS_BUTTONS)
511 wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS)
512 wxFLAGS_MEMBER(wxTR_NO_LINES)
513 wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT)
514 wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT)
515 wxFLAGS_MEMBER(wxTR_HIDE_ROOT)
516 wxFLAGS_MEMBER(wxTR_ROW_LINES)
517 wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT)
518 wxFLAGS_MEMBER(wxTR_SINGLE)
519 wxFLAGS_MEMBER(wxTR_MULTIPLE)
520 wxFLAGS_MEMBER(wxTR_EXTENDED)
521 wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE)
522
523wxEND_FLAGS( wxTreeCtrlStyle )
bc9fb572 524
786a2425
SC
525IMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl,"wx/treectrl.h")
526
3ff066a4 527wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl)
5e7718a2 528 wxEVENT_PROPERTY( TextUpdated , wxEVT_COMMAND_TEXT_UPDATED , wxCommandEvent )
3ff066a4 529 wxEVENT_RANGE_PROPERTY( TreeEvent , wxEVT_COMMAND_TREE_BEGIN_DRAG , wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK , wxTreeEvent )
af498247 530 wxPROPERTY_FLAGS( WindowStyle , wxTreeCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
3ff066a4 531wxEND_PROPERTIES_TABLE()
786a2425 532
3ff066a4
SC
533wxBEGIN_HANDLERS_TABLE(wxTreeCtrl)
534wxEND_HANDLERS_TABLE()
786a2425 535
5e7718a2 536wxCONSTRUCTOR_5( wxTreeCtrl , wxWindow* , Parent , wxWindowID , Id , wxPoint , Position , wxSize , Size , long , WindowStyle )
786a2425 537#else
23f681ec 538IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl)
786a2425 539#endif
2bda0e17 540
08b7c251 541// ----------------------------------------------------------------------------
deb1de30 542// constants
08b7c251
VZ
543// ----------------------------------------------------------------------------
544
deb1de30
VZ
545// indices in gs_expandEvents table below
546enum
547{
548 IDX_COLLAPSE,
549 IDX_EXPAND,
550 IDX_WHAT_MAX
551};
552
553enum
554{
555 IDX_DONE,
556 IDX_DOING,
557 IDX_HOW_MAX
558};
559
560// handy table for sending events - it has to be initialized during run-time
561// now so can't be const any more
562static /* const */ wxEventType gs_expandEvents[IDX_WHAT_MAX][IDX_HOW_MAX];
563
564/*
565 but logically it's a const table with the following entries:
566=
2bda0e17 567{
08b7c251
VZ
568 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING },
569 { wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxEVT_COMMAND_TREE_ITEM_EXPANDING }
570};
deb1de30 571*/
08b7c251
VZ
572
573// ============================================================================
574// implementation
575// ============================================================================
576
9dfbf520
VZ
577// ----------------------------------------------------------------------------
578// tree traversal
579// ----------------------------------------------------------------------------
580
581bool wxTreeTraversal::DoTraverse(const wxTreeItemId& root, bool recursively)
582{
583 if ( !OnVisit(root) )
04cd30de 584 return false;
9dfbf520
VZ
585
586 return Traverse(root, recursively);
587}
588
589bool wxTreeTraversal::Traverse(const wxTreeItemId& root, bool recursively)
590{
ee4b2721 591 wxTreeItemIdValue cookie;
9dfbf520
VZ
592 wxTreeItemId child = m_tree->GetFirstChild(root, cookie);
593 while ( child.IsOk() )
594 {
595 // depth first traversal
04cd30de
RL
596 if ( recursively && !Traverse(child, true) )
597 return false;
9dfbf520
VZ
598
599 if ( !OnVisit(child) )
04cd30de 600 return false;
9dfbf520
VZ
601
602 child = m_tree->GetNextChild(root, cookie);
603 }
604
04cd30de 605 return true;
9dfbf520
VZ
606}
607
08b7c251
VZ
608// ----------------------------------------------------------------------------
609// construction and destruction
610// ----------------------------------------------------------------------------
611
612void wxTreeCtrl::Init()
613{
614 m_imageListNormal = NULL;
615 m_imageListState = NULL;
04cd30de 616 m_ownsImageListNormal = m_ownsImageListState = false;
08b7c251 617 m_textCtrl = NULL;
04cd30de 618 m_hasAnyAttr = false;
23f681ec 619 m_dragImage = NULL;
a9c1265f 620 m_pVirtualRoot = NULL;
9ab5ef13 621
deb1de30
VZ
622 // initialize the global array of events now as it can't be done statically
623 // with the wxEVT_XXX values being allocated during run-time only
624 gs_expandEvents[IDX_COLLAPSE][IDX_DONE] = wxEVT_COMMAND_TREE_ITEM_COLLAPSED;
625 gs_expandEvents[IDX_COLLAPSE][IDX_DOING] = wxEVT_COMMAND_TREE_ITEM_COLLAPSING;
626 gs_expandEvents[IDX_EXPAND][IDX_DONE] = wxEVT_COMMAND_TREE_ITEM_EXPANDED;
627 gs_expandEvents[IDX_EXPAND][IDX_DOING] = wxEVT_COMMAND_TREE_ITEM_EXPANDING;
2bda0e17
KB
628}
629
9dfbf520
VZ
630bool wxTreeCtrl::Create(wxWindow *parent,
631 wxWindowID id,
632 const wxPoint& pos,
633 const wxSize& size,
634 long style,
635 const wxValidator& validator,
08b7c251 636 const wxString& name)
2bda0e17 637{
08b7c251 638 Init();
2bda0e17 639
7699361c
JS
640 if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
641 style |= wxBORDER_SUNKEN;
642
9dfbf520 643 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
04cd30de 644 return false;
2bda0e17 645
7699361c
JS
646 DWORD exStyle = 0;
647 DWORD wstyle = MSWGetStyle(m_windowStyle, & exStyle);
648 wstyle |= WS_TABSTOP | TVS_SHOWSELALWAYS;
2bda0e17 649
63da7df7
JS
650 if ((m_windowStyle & wxTR_NO_LINES) == 0)
651 wstyle |= TVS_HASLINES;
08b7c251
VZ
652 if ( m_windowStyle & wxTR_HAS_BUTTONS )
653 wstyle |= TVS_HASBUTTONS;
2bda0e17 654
08b7c251
VZ
655 if ( m_windowStyle & wxTR_EDIT_LABELS )
656 wstyle |= TVS_EDITLABELS;
2bda0e17 657
08b7c251
VZ
658 if ( m_windowStyle & wxTR_LINES_AT_ROOT )
659 wstyle |= TVS_LINESATROOT;
deb1de30 660
c6f4913a 661 if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT )
deb1de30 662 {
2a1f999f 663 if ( wxApp::GetComCtl32Version() >= 471 )
c6f4913a
VS
664 wstyle |= TVS_FULLROWSELECT;
665 }
666
3f7bc32b
VZ
667 // using TVS_CHECKBOXES for emulation of a multiselection tree control
668 // doesn't work without the new enough headers
669#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE && \
670 !defined( __GNUWIN32_OLD__ ) && \
c42404a5
VZ
671 !defined( __BORLANDC__ ) && \
672 !defined( __WATCOMC__ ) && \
673 (!defined(__VISUALC__) || (__VISUALC__ > 1010))
a20a10fe 674
9dfbf520
VZ
675 // we emulate the multiple selection tree controls by using checkboxes: set
676 // up the image list we need for this if we do have multiple selections
677 if ( m_windowStyle & wxTR_MULTIPLE )
10fcf31a 678 wstyle |= TVS_CHECKBOXES;
3f7bc32b 679#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520 680
f3f71703 681#if !defined(__WXWINCE__) && defined(TVS_INFOTIP)
156194e1
JS
682 // Need so that TVN_GETINFOTIP messages will be sent
683 wstyle |= TVS_INFOTIP;
676d6550 684#endif
5e7718a2 685
08b7c251 686 // Create the tree control.
8779cca3 687 if ( !MSWCreateControl(WC_TREEVIEW, wstyle, pos, size) )
04cd30de 688 return false;
9dfbf520 689
f6bcfd97 690#if wxUSE_COMCTL32_SAFELY
a756f210 691 wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
f6bcfd97
BP
692 wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
693#elif 1
a756f210 694 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
f6bcfd97 695 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
fbdcff4a
JS
696#else
697 // This works around a bug in the Windows tree control whereby for some versions
698 // of comctrl32, setting any colour actually draws the background in black.
699 // This will initialise the background to the system colour.
f6bcfd97
BP
700 // THIS FIX NOW REVERTED since it caused problems on _other_ systems.
701 // Assume the user has an updated comctl32.dll.
fbdcff4a 702 ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0,-1);
a756f210 703 wxWindow::SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
f6bcfd97 704 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
fbdcff4a
JS
705#endif
706
5aeeab14 707
9dfbf520
VZ
708 // VZ: this is some experimental code which may be used to get the
709 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
710 // AFAIK, the standard DLL does about the same thing anyhow.
711#if 0
712 if ( m_windowStyle & wxTR_MULTIPLE )
713 {
714 wxBitmap bmp;
715
716 // create the DC compatible with the current screen
717 HDC hdcMem = CreateCompatibleDC(NULL);
718
719 // create a mono bitmap of the standard size
46697f31
WS
720 int x = ::GetSystemMetrics(SM_CXMENUCHECK);
721 int y = ::GetSystemMetrics(SM_CYMENUCHECK);
04cd30de 722 wxImageList imagelistCheckboxes(x, y, false, 2);
9dfbf520
VZ
723 HBITMAP hbmpCheck = CreateBitmap(x, y, // bitmap size
724 1, // # of color planes
725 1, // # bits needed for one pixel
726 0); // array containing colour data
727 SelectObject(hdcMem, hbmpCheck);
728
729 // then draw a check mark into it
730 RECT rect = { 0, 0, x, y };
731 if ( !::DrawFrameControl(hdcMem, &rect,
732 DFC_BUTTON,
733 DFCS_BUTTONCHECK | DFCS_CHECKED) )
734 {
223d09f6 735 wxLogLastError(wxT("DrawFrameControl(check)"));
9dfbf520
VZ
736 }
737
738 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
739 imagelistCheckboxes.Add(bmp);
740
741 if ( !::DrawFrameControl(hdcMem, &rect,
742 DFC_BUTTON,
743 DFCS_BUTTONCHECK) )
744 {
223d09f6 745 wxLogLastError(wxT("DrawFrameControl(uncheck)"));
9dfbf520
VZ
746 }
747
748 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
749 imagelistCheckboxes.Add(bmp);
750
751 // clean up
752 ::DeleteDC(hdcMem);
753
754 // set the imagelist
755 SetStateImageList(&imagelistCheckboxes);
756 }
757#endif // 0
758
9a63feff
VZ
759 wxSetCCUnicodeFormat(GetHwnd());
760
04cd30de 761 return true;
2bda0e17
KB
762}
763
08b7c251 764wxTreeCtrl::~wxTreeCtrl()
2bda0e17 765{
696e1ea0
VZ
766 // delete any attributes
767 if ( m_hasAnyAttr )
768 {
ee4b2721 769 WX_CLEAR_HASH_MAP(wxMapTreeAttr, m_attrs);
696e1ea0
VZ
770
771 // prevent TVN_DELETEITEM handler from deleting the attributes again!
04cd30de 772 m_hasAnyAttr = false;
696e1ea0
VZ
773 }
774
08b7c251 775 DeleteTextCtrl();
2bda0e17 776
08b7c251 777 // delete user data to prevent memory leaks
a9c1265f 778 // also deletes hidden root node storage.
08b7c251 779 DeleteAllItems();
33ac7e6f 780
0377efa2
VS
781 if (m_ownsImageListNormal) delete m_imageListNormal;
782 if (m_ownsImageListState) delete m_imageListState;
2bda0e17
KB
783}
784
08b7c251
VZ
785// ----------------------------------------------------------------------------
786// accessors
787// ----------------------------------------------------------------------------
2bda0e17 788
39c7a53c
VZ
789/* static */ wxVisualAttributes
790wxTreeCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
791{
792 wxVisualAttributes attrs = GetCompositeControlsDefaultAttributes(variant);
793
794 // common controls have their own default font
795 attrs.font = wxGetCCDefaultFont();
796
797 return attrs;
798}
799
800
08b7c251 801// simple wrappers which add error checking in debug mode
2bda0e17 802
08b7c251 803bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const
2bda0e17 804{
04cd30de 805 wxCHECK_MSG( tvItem->hItem != TVI_ROOT, false,
a9c1265f
VZ
806 _T("can't retrieve virtual root item") );
807
d220ae32 808 if ( !TreeView_GetItem(GetHwnd(), tvItem) )
2bda0e17 809 {
f6bcfd97 810 wxLogLastError(wxT("TreeView_GetItem"));
08b7c251 811
04cd30de 812 return false;
08b7c251
VZ
813 }
814
04cd30de 815 return true;
2bda0e17
KB
816}
817
08b7c251 818void wxTreeCtrl::DoSetItem(wxTreeViewItem* tvItem)
2bda0e17 819{
d220ae32 820 if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
2bda0e17 821 {
f6bcfd97 822 wxLogLastError(wxT("TreeView_SetItem"));
08b7c251 823 }
2bda0e17
KB
824}
825
08b7c251 826size_t wxTreeCtrl::GetCount() const
2bda0e17 827{
d220ae32 828 return (size_t)TreeView_GetCount(GetHwnd());
2bda0e17
KB
829}
830
08b7c251 831unsigned int wxTreeCtrl::GetIndent() const
2bda0e17 832{
d220ae32 833 return TreeView_GetIndent(GetHwnd());
2bda0e17
KB
834}
835
08b7c251 836void wxTreeCtrl::SetIndent(unsigned int indent)
2bda0e17 837{
d220ae32 838 TreeView_SetIndent(GetHwnd(), indent);
2bda0e17
KB
839}
840
08b7c251 841wxImageList *wxTreeCtrl::GetImageList() const
2bda0e17 842{
08b7c251 843 return m_imageListNormal;
2bda0e17
KB
844}
845
08b7c251 846wxImageList *wxTreeCtrl::GetStateImageList() const
2bda0e17 847{
8a000b6b 848 return m_imageListState;
2bda0e17
KB
849}
850
08b7c251 851void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which)
2bda0e17 852{
08b7c251 853 // no error return
d220ae32 854 TreeView_SetImageList(GetHwnd(),
08b7c251
VZ
855 imageList ? imageList->GetHIMAGELIST() : 0,
856 which);
2bda0e17
KB
857}
858
08b7c251 859void wxTreeCtrl::SetImageList(wxImageList *imageList)
2bda0e17 860{
a9c1265f
VZ
861 if (m_ownsImageListNormal)
862 delete m_imageListNormal;
863
08b7c251 864 SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
04cd30de 865 m_ownsImageListNormal = false;
2bda0e17
KB
866}
867
08b7c251 868void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
2bda0e17 869{
0377efa2 870 if (m_ownsImageListState) delete m_imageListState;
08b7c251 871 SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
04cd30de 872 m_ownsImageListState = false;
0377efa2
VS
873}
874
875void wxTreeCtrl::AssignImageList(wxImageList *imageList)
876{
877 SetImageList(imageList);
04cd30de 878 m_ownsImageListNormal = true;
0377efa2
VS
879}
880
881void wxTreeCtrl::AssignStateImageList(wxImageList *imageList)
882{
883 SetStateImageList(imageList);
04cd30de 884 m_ownsImageListState = true;
2bda0e17
KB
885}
886
33961d59
RR
887size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
888 bool recursively) const
889{
05b2a432 890 wxCHECK_MSG( item.IsOk(), 0u, wxT("invalid tree item") );
23fd5130 891
05b2a432 892 TraverseCounter counter(this, item, recursively);
73974df1 893 return counter.GetCount() - 1;
23fd5130
VZ
894}
895
bb448552
VZ
896// ----------------------------------------------------------------------------
897// control colours
898// ----------------------------------------------------------------------------
899
900bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour)
901{
f6bcfd97 902#if !wxUSE_COMCTL32_SAFELY
bb448552 903 if ( !wxWindowBase::SetBackgroundColour(colour) )
04cd30de 904 return false;
bb448552 905
bfbb0b4c 906 ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
f6bcfd97 907#endif
bb448552 908
04cd30de 909 return true;
bb448552
VZ
910}
911
912bool wxTreeCtrl::SetForegroundColour(const wxColour &colour)
913{
f6bcfd97 914#if !wxUSE_COMCTL32_SAFELY
bb448552 915 if ( !wxWindowBase::SetForegroundColour(colour) )
04cd30de 916 return false;
bb448552 917
bfbb0b4c 918 ::SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
f6bcfd97 919#endif
bb448552 920
04cd30de 921 return true;
bb448552
VZ
922}
923
08b7c251
VZ
924// ----------------------------------------------------------------------------
925// Item access
926// ----------------------------------------------------------------------------
927
928wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
2bda0e17 929{
f31a4098 930 wxCHECK_MSG( item.IsOk(), wxEmptyString, wxT("invalid tree item") );
05b2a432 931
837e5743 932 wxChar buf[512]; // the size is arbitrary...
02ce7b72 933
08b7c251
VZ
934 wxTreeViewItem tvItem(item, TVIF_TEXT);
935 tvItem.pszText = buf;
936 tvItem.cchTextMax = WXSIZEOF(buf);
937 if ( !DoGetItem(&tvItem) )
938 {
939 // don't return some garbage which was on stack, but an empty string
223d09f6 940 buf[0] = wxT('\0');
08b7c251 941 }
2bda0e17 942
08b7c251
VZ
943 return wxString(buf);
944}
2bda0e17 945
08b7c251
VZ
946void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
947{
05b2a432
RD
948 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
949
a9c1265f
VZ
950 if ( IS_VIRTUAL_ROOT(item) )
951 return;
952
08b7c251 953 wxTreeViewItem tvItem(item, TVIF_TEXT);
837e5743 954 tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok
08b7c251 955 DoSetItem(&tvItem);
44fbc477
VZ
956
957 // when setting the text of the item being edited, the text control should
958 // be updated to reflect the new text as well, otherwise calling
959 // SetItemText() in the OnBeginLabelEdit() handler doesn't have any effect
960 //
961 // don't use GetEditControl() here because m_textCtrl is not set yet
962 HWND hwndEdit = TreeView_GetEditControl(GetHwnd());
963 if ( hwndEdit )
964 {
1a4088e1 965 if ( item == m_idEdited )
44fbc477
VZ
966 {
967 ::SetWindowText(hwndEdit, text);
968 }
969 }
08b7c251 970}
2bda0e17 971
74b31181
VZ
972int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId& item,
973 wxTreeItemIcon which) const
974{
975 wxTreeViewItem tvItem(item, TVIF_PARAM);
976 if ( !DoGetItem(&tvItem) )
977 {
978 return -1;
979 }
980
981 return ((wxTreeItemIndirectData *)tvItem.lParam)->GetImage(which);
982}
983
984void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId& item,
985 int image,
986 wxTreeItemIcon which) const
987{
988 wxTreeViewItem tvItem(item, TVIF_PARAM);
989 if ( !DoGetItem(&tvItem) )
990 {
991 return;
992 }
993
994 wxTreeItemIndirectData *data = ((wxTreeItemIndirectData *)tvItem.lParam);
995
996 data->SetImage(image, which);
997
998 // make sure that we have selected images as well
999 if ( which == wxTreeItemIcon_Normal &&
1000 !data->HasImage(wxTreeItemIcon_Selected) )
1001 {
1002 data->SetImage(image, wxTreeItemIcon_Selected);
1003 }
1004
1005 if ( which == wxTreeItemIcon_Expanded &&
1006 !data->HasImage(wxTreeItemIcon_SelectedExpanded) )
1007 {
1008 data->SetImage(image, wxTreeItemIcon_SelectedExpanded);
1009 }
1010}
1011
9dfbf520
VZ
1012void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item,
1013 int image,
1014 int imageSel)
1015{
1016 wxTreeViewItem tvItem(item, TVIF_IMAGE | TVIF_SELECTEDIMAGE);
1017 tvItem.iSelectedImage = imageSel;
1018 tvItem.iImage = image;
1019 DoSetItem(&tvItem);
1020}
1021
74b31181
VZ
1022int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
1023 wxTreeItemIcon which) const
08b7c251 1024{
05b2a432
RD
1025 wxCHECK_MSG( item.IsOk(), -1, wxT("invalid tree item") );
1026
a9c1265f
VZ
1027 if ( (HITEM(item) == TVI_ROOT) && (m_windowStyle & wxTR_HIDE_ROOT) )
1028 {
1029 // TODO: Maybe a hidden root can still provide images?
1030 return -1;
1031 }
1032
74b31181
VZ
1033 if ( HasIndirectData(item) )
1034 {
1035 return DoGetItemImageFromData(item, which);
1036 }
2bda0e17 1037
74b31181
VZ
1038 UINT mask;
1039 switch ( which )
1040 {
1041 default:
223d09f6 1042 wxFAIL_MSG( wxT("unknown tree item image type") );
2bda0e17 1043
74b31181
VZ
1044 case wxTreeItemIcon_Normal:
1045 mask = TVIF_IMAGE;
1046 break;
2bda0e17 1047
74b31181
VZ
1048 case wxTreeItemIcon_Selected:
1049 mask = TVIF_SELECTEDIMAGE;
1050 break;
1051
1052 case wxTreeItemIcon_Expanded:
1053 case wxTreeItemIcon_SelectedExpanded:
1054 return -1;
1055 }
1056
1057 wxTreeViewItem tvItem(item, mask);
08b7c251 1058 DoGetItem(&tvItem);
2bda0e17 1059
74b31181 1060 return mask == TVIF_IMAGE ? tvItem.iImage : tvItem.iSelectedImage;
2bda0e17
KB
1061}
1062
74b31181
VZ
1063void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image,
1064 wxTreeItemIcon which)
2bda0e17 1065{
05b2a432
RD
1066 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1067
a9c1265f
VZ
1068 if ( IS_VIRTUAL_ROOT(item) )
1069 {
1070 // TODO: Maybe a hidden root can still store images?
1071 return;
1072 }
1073
7ab0c3ad
VZ
1074 int imageNormal,
1075 imageSel;
1076
74b31181
VZ
1077 switch ( which )
1078 {
1079 default:
223d09f6 1080 wxFAIL_MSG( wxT("unknown tree item image type") );
7ab0c3ad 1081 // fall through
74b31181
VZ
1082
1083 case wxTreeItemIcon_Normal:
7ab0c3ad
VZ
1084 {
1085 const int imageNormalOld = GetItemImage(item);
b5f6b52a
MB
1086 const int imageSelOld =
1087 GetItemImage(item, wxTreeItemIcon_Selected);
7ab0c3ad
VZ
1088
1089 // always set the normal image
1090 imageNormal = image;
1091
1092 // if the selected and normal images were the same, they should
1093 // be the same after the update, otherwise leave the selected
1094 // image as it was
1095 imageSel = imageNormalOld == imageSelOld ? image : imageSelOld;
1096 }
74b31181
VZ
1097 break;
1098
1099 case wxTreeItemIcon_Selected:
1100 imageNormal = GetItemImage(item);
1101 imageSel = image;
1102 break;
1103
1104 case wxTreeItemIcon_Expanded:
1105 case wxTreeItemIcon_SelectedExpanded:
1106 if ( !HasIndirectData(item) )
1107 {
1108 // we need to get the old images first, because after we create
1109 // the wxTreeItemIndirectData GetItemXXXImage() will use it to
1110 // get the images
1111 imageNormal = GetItemImage(item);
b5f6b52a 1112 imageSel = GetItemImage(item, wxTreeItemIcon_Selected);
74b31181
VZ
1113
1114 // if it doesn't have it yet, add it
1115 wxTreeItemIndirectData *data = new
1116 wxTreeItemIndirectData(this, item);
1117
1118 // copy the data to the new location
1119 data->SetImage(imageNormal, wxTreeItemIcon_Normal);
1120 data->SetImage(imageSel, wxTreeItemIcon_Selected);
1121 }
1122
1123 DoSetItemImageFromData(item, image, which);
1124
1125 // reset the normal/selected images because we won't use them any
1126 // more - now they're stored inside the indirect data
1127 imageNormal =
1128 imageSel = I_IMAGECALLBACK;
1129 break;
1130 }
1131
9dfbf520
VZ
1132 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
1133 // change both normal and selected image - otherwise the change simply
1134 // doesn't take place!
74b31181 1135 DoSetItemImages(item, imageNormal, imageSel);
2bda0e17
KB
1136}
1137
08b7c251 1138wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
2bda0e17 1139{
05b2a432
RD
1140 wxCHECK_MSG( item.IsOk(), NULL, wxT("invalid tree item") );
1141
08b7c251 1142 wxTreeViewItem tvItem(item, TVIF_PARAM);
a9c1265f
VZ
1143
1144 // Hidden root may have data.
1145 if ( IS_VIRTUAL_ROOT(item) )
1146 {
1147 return GET_VIRTUAL_ROOT()->GetData();
1148 }
1149
1150 // Visible node.
08b7c251
VZ
1151 if ( !DoGetItem(&tvItem) )
1152 {
1153 return NULL;
1154 }
2bda0e17 1155
502a2b18
VZ
1156 wxTreeItemData *data = (wxTreeItemData *)tvItem.lParam;
1157 if ( IsDataIndirect(data) )
74b31181 1158 {
502a2b18 1159 data = ((wxTreeItemIndirectData *)data)->GetData();
74b31181 1160 }
502a2b18
VZ
1161
1162 return data;
2bda0e17
KB
1163}
1164
08b7c251 1165void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
2bda0e17 1166{
05b2a432
RD
1167 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1168
a9c1265f
VZ
1169 if ( IS_VIRTUAL_ROOT(item) )
1170 {
1171 GET_VIRTUAL_ROOT()->SetData(data);
1172 }
1173
188781db
VZ
1174 // first, associate this piece of data with this item
1175 if ( data )
1176 {
1177 data->SetId(item);
1178 }
1179
08b7c251 1180 wxTreeViewItem tvItem(item, TVIF_PARAM);
74b31181
VZ
1181
1182 if ( HasIndirectData(item) )
1183 {
1184 if ( DoGetItem(&tvItem) )
1185 {
1186 ((wxTreeItemIndirectData *)tvItem.lParam)->SetData(data);
1187 }
1188 else
1189 {
223d09f6 1190 wxFAIL_MSG( wxT("failed to change tree items data") );
74b31181
VZ
1191 }
1192 }
1193 else
1194 {
1195 tvItem.lParam = (LPARAM)data;
1196 DoSetItem(&tvItem);
1197 }
1198}
1199
1200void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId& item,
1201 wxTreeItemIndirectData *data)
1202{
1203 // this should never happen because it's unnecessary and will probably lead
1204 // to crash too because the code elsewhere supposes that the pointer the
1205 // wxTreeItemIndirectData has is a real wxItemData and not
1206 // wxTreeItemIndirectData as well
223d09f6 1207 wxASSERT_MSG( !HasIndirectData(item), wxT("setting indirect data twice?") );
74b31181 1208
502a2b18 1209 SetItemData(item, data);
74b31181
VZ
1210}
1211
1212bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const
1213{
502a2b18
VZ
1214 // query the item itself
1215 wxTreeViewItem tvItem(item, TVIF_PARAM);
1216 if ( !DoGetItem(&tvItem) )
1217 {
04cd30de 1218 return false;
502a2b18
VZ
1219 }
1220
1221 wxTreeItemData *data = (wxTreeItemData *)tvItem.lParam;
1222
1223 return data && IsDataIndirect(data);
08b7c251 1224}
2bda0e17 1225
3a5a2f56
VZ
1226void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
1227{
05b2a432
RD
1228 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1229
a9c1265f
VZ
1230 if ( IS_VIRTUAL_ROOT(item) )
1231 return;
1232
3a5a2f56
VZ
1233 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1234 tvItem.cChildren = (int)has;
1235 DoSetItem(&tvItem);
1236}
1237
add28c55
VZ
1238void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
1239{
05b2a432
RD
1240 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1241
a9c1265f
VZ
1242 if ( IS_VIRTUAL_ROOT(item) )
1243 return;
1244
add28c55
VZ
1245 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1246 tvItem.state = bold ? TVIS_BOLD : 0;
1247 DoSetItem(&tvItem);
1248}
1249
58a8ab88
JS
1250void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight)
1251{
a9c1265f
VZ
1252 if ( IS_VIRTUAL_ROOT(item) )
1253 return;
1254
58a8ab88
JS
1255 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
1256 tvItem.state = highlight ? TVIS_DROPHILITED : 0;
1257 DoSetItem(&tvItem);
1258}
1259
d00407b2
VZ
1260void wxTreeCtrl::RefreshItem(const wxTreeItemId& item)
1261{
a9c1265f
VZ
1262 if ( IS_VIRTUAL_ROOT(item) )
1263 return;
1264
d00407b2
VZ
1265 wxRect rect;
1266 if ( GetBoundingRect(item, rect) )
1267 {
1268 RefreshRect(rect);
1269 }
1270}
1271
2b5f62a0
VZ
1272wxColour wxTreeCtrl::GetItemTextColour(const wxTreeItemId& item) const
1273{
05b2a432 1274 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
2b5f62a0 1275
05b2a432 1276 wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1277 return it == m_attrs.end() ? wxNullColour : it->second->GetTextColour();
2b5f62a0
VZ
1278}
1279
1280wxColour wxTreeCtrl::GetItemBackgroundColour(const wxTreeItemId& item) const
1281{
05b2a432 1282 wxCHECK_MSG( item.IsOk(), wxNullColour, wxT("invalid tree item") );
2b5f62a0 1283
05b2a432 1284 wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1285 return it == m_attrs.end() ? wxNullColour : it->second->GetBackgroundColour();
2b5f62a0
VZ
1286}
1287
1288wxFont wxTreeCtrl::GetItemFont(const wxTreeItemId& item) const
1289{
05b2a432 1290 wxCHECK_MSG( item.IsOk(), wxNullFont, wxT("invalid tree item") );
2b5f62a0 1291
05b2a432 1292 wxMapTreeAttr::const_iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1293 return it == m_attrs.end() ? wxNullFont : it->second->GetFont();
2b5f62a0
VZ
1294}
1295
696e1ea0
VZ
1296void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
1297 const wxColour& col)
1298{
05b2a432
RD
1299 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1300
ee4b2721 1301 wxTreeItemAttr *attr;
f888d614 1302 wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1303 if ( it == m_attrs.end() )
696e1ea0 1304 {
ee4b2721
VZ
1305 m_hasAnyAttr = true;
1306
f888d614 1307 m_attrs[item.m_pItem] =
696e1ea0 1308 attr = new wxTreeItemAttr;
ee4b2721
VZ
1309 }
1310 else
1311 {
1312 attr = it->second;
696e1ea0
VZ
1313 }
1314
1315 attr->SetTextColour(col);
d00407b2
VZ
1316
1317 RefreshItem(item);
696e1ea0
VZ
1318}
1319
1320void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
1321 const wxColour& col)
1322{
05b2a432
RD
1323 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1324
ee4b2721 1325 wxTreeItemAttr *attr;
f888d614 1326 wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1327 if ( it == m_attrs.end() )
696e1ea0 1328 {
ee4b2721
VZ
1329 m_hasAnyAttr = true;
1330
f888d614 1331 m_attrs[item.m_pItem] =
696e1ea0 1332 attr = new wxTreeItemAttr;
ee4b2721
VZ
1333 }
1334 else // already in the hash
1335 {
1336 attr = it->second;
696e1ea0
VZ
1337 }
1338
1339 attr->SetBackgroundColour(col);
d00407b2
VZ
1340
1341 RefreshItem(item);
696e1ea0
VZ
1342}
1343
1344void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
1345{
05b2a432
RD
1346 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1347
ee4b2721 1348 wxTreeItemAttr *attr;
f888d614 1349 wxMapTreeAttr::iterator it = m_attrs.find(item.m_pItem);
ee4b2721 1350 if ( it == m_attrs.end() )
696e1ea0 1351 {
ee4b2721
VZ
1352 m_hasAnyAttr = true;
1353
f888d614 1354 m_attrs[item.m_pItem] =
696e1ea0 1355 attr = new wxTreeItemAttr;
ee4b2721
VZ
1356 }
1357 else // already in the hash
1358 {
1359 attr = it->second;
696e1ea0
VZ
1360 }
1361
1362 attr->SetFont(font);
d00407b2
VZ
1363
1364 RefreshItem(item);
696e1ea0
VZ
1365}
1366
08b7c251
VZ
1367// ----------------------------------------------------------------------------
1368// Item status
1369// ----------------------------------------------------------------------------
2bda0e17 1370
08b7c251
VZ
1371bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
1372{
bfbb0b4c 1373 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
05b2a432 1374
2b5f62a0
VZ
1375 if ( item == wxTreeItemId(TVI_ROOT) )
1376 {
1377 // virtual (hidden) root is never visible
04cd30de 1378 return false;
2b5f62a0
VZ
1379 }
1380
add28c55 1381 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
08b7c251 1382 RECT rect;
955be36c
VZ
1383
1384 // this ugliness comes directly from MSDN - it *is* the correct way to pass
1385 // the HTREEITEM with TVM_GETITEMRECT
ee4b2721 1386 *(HTREEITEM *)&rect = HITEM(item);
955be36c 1387
caea927d
VZ
1388 // true means to get rect for just the text, not the whole line
1389 if ( !::SendMessage(GetHwnd(), TVM_GETITEMRECT, true, (LPARAM)&rect) )
1390 {
1391 // if TVM_GETITEMRECT returned false, then the item is definitely not
1392 // visible (because its parent is not expanded)
1393 return false;
1394 }
1395
1396 // however if it returned true, the item might still be outside the
1397 // currently visible part of the tree, test for it (notice that partly
1398 // visible means visible here)
1399 return rect.bottom > 0 && rect.top < GetClientSize().y;
2bda0e17
KB
1400}
1401
08b7c251 1402bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
2bda0e17 1403{
bfbb0b4c 1404 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
05b2a432 1405
08b7c251
VZ
1406 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1407 DoGetItem(&tvItem);
2bda0e17 1408
08b7c251 1409 return tvItem.cChildren != 0;
2bda0e17
KB
1410}
1411
08b7c251 1412bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
2bda0e17 1413{
bfbb0b4c 1414 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
2bda0e17 1415
08b7c251
VZ
1416 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
1417 DoGetItem(&tvItem);
2bda0e17 1418
08b7c251 1419 return (tvItem.state & TVIS_EXPANDED) != 0;
2bda0e17
KB
1420}
1421
08b7c251 1422bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
2bda0e17 1423{
bfbb0b4c 1424 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
05b2a432 1425
08b7c251
VZ
1426 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
1427 DoGetItem(&tvItem);
2bda0e17 1428
08b7c251 1429 return (tvItem.state & TVIS_SELECTED) != 0;
2bda0e17
KB
1430}
1431
add28c55
VZ
1432bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
1433{
bfbb0b4c 1434 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
05b2a432 1435
add28c55
VZ
1436 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1437 DoGetItem(&tvItem);
1438
1439 return (tvItem.state & TVIS_BOLD) != 0;
1440}
1441
08b7c251
VZ
1442// ----------------------------------------------------------------------------
1443// navigation
1444// ----------------------------------------------------------------------------
2bda0e17 1445
08b7c251
VZ
1446wxTreeItemId wxTreeCtrl::GetRootItem() const
1447{
a9c1265f
VZ
1448 // Root may be real (visible) or virtual (hidden).
1449 if ( GET_VIRTUAL_ROOT() )
1450 return TVI_ROOT;
1451
ee4b2721 1452 return wxTreeItemId(TreeView_GetRoot(GetHwnd()));
08b7c251 1453}
2bda0e17 1454
08b7c251
VZ
1455wxTreeItemId wxTreeCtrl::GetSelection() const
1456{
f888d614 1457 wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), wxTreeItemId(),
223d09f6 1458 wxT("this only works with single selection controls") );
9dfbf520 1459
ee4b2721 1460 return wxTreeItemId(TreeView_GetSelection(GetHwnd()));
2bda0e17
KB
1461}
1462
99006e44 1463wxTreeItemId wxTreeCtrl::GetItemParent(const wxTreeItemId& item) const
2bda0e17 1464{
05b2a432
RD
1465 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1466
c9b142c9
VZ
1467 HTREEITEM hItem;
1468
1469 if ( IS_VIRTUAL_ROOT(item) )
1470 {
1471 // no parent for the virtual root
1472 hItem = 0;
1473 }
1474 else // normal item
a9c1265f 1475 {
c9b142c9
VZ
1476 hItem = TreeView_GetParent(GetHwnd(), HITEM(item));
1477 if ( !hItem && HasFlag(wxTR_HIDE_ROOT) )
1478 {
1479 // the top level items should have the virtual root as their parent
1480 hItem = TVI_ROOT;
1481 }
a9c1265f
VZ
1482 }
1483
ee4b2721 1484 return wxTreeItemId(hItem);
08b7c251 1485}
2bda0e17 1486
08b7c251 1487wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
ee4b2721 1488 wxTreeItemIdValue& cookie) const
08b7c251 1489{
05b2a432
RD
1490 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1491
08b7c251 1492 // remember the last child returned in 'cookie'
ee4b2721
VZ
1493 cookie = TreeView_GetChild(GetHwnd(), HITEM(item));
1494
1495 return wxTreeItemId(cookie);
1496}
1497
1498wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
1499 wxTreeItemIdValue& cookie) const
1500{
44d60c0b
WS
1501 wxTreeItemId fromCookie(cookie);
1502
1503 HTREEITEM hitem = HITEM(fromCookie);
1504
1505 hitem = TreeView_GetNextSibling(GetHwnd(), hitem);
1506
1507 wxTreeItemId item(hitem);
1508
ee4b2721
VZ
1509 cookie = item.m_pItem;
1510
1511 return item;
1512}
2bda0e17 1513
ee4b2721
VZ
1514#if WXWIN_COMPATIBILITY_2_4
1515
1516wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
1517 long& cookie) const
1518{
05b2a432
RD
1519 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1520
ee4b2721
VZ
1521 cookie = (long)TreeView_GetChild(GetHwnd(), HITEM(item));
1522
1523 return wxTreeItemId((void *)cookie);
2bda0e17
KB
1524}
1525
08b7c251 1526wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
ee4b2721 1527 long& cookie) const
2bda0e17 1528{
44d60c0b
WS
1529 wxTreeItemId fromCookie((void *)cookie);
1530
1531 HTREEITEM hitem = HITEM(fromCookie);
1532
1533 hitem = TreeView_GetNextSibling(GetHwnd(), hitem);
1534
1535 wxTreeItemId item(hitem);
1536
ee4b2721 1537 cookie = (long)item.m_pItem;
23fd5130 1538
ee4b2721 1539 return item;
08b7c251 1540}
2bda0e17 1541
ee4b2721
VZ
1542#endif // WXWIN_COMPATIBILITY_2_4
1543
978f38c2
VZ
1544wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
1545{
05b2a432
RD
1546 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
1547
978f38c2 1548 // can this be done more efficiently?
ee4b2721 1549 wxTreeItemIdValue cookie;
978f38c2
VZ
1550
1551 wxTreeItemId childLast,
2165ad93 1552 child = GetFirstChild(item, cookie);
978f38c2
VZ
1553 while ( child.IsOk() )
1554 {
1555 childLast = child;
2165ad93 1556 child = GetNextChild(item, cookie);
978f38c2
VZ
1557 }
1558
1559 return childLast;
1560}
1561
08b7c251
VZ
1562wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
1563{
05b2a432 1564 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
ee4b2721 1565 return wxTreeItemId(TreeView_GetNextSibling(GetHwnd(), HITEM(item)));
2bda0e17
KB
1566}
1567
08b7c251 1568wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
2bda0e17 1569{
05b2a432 1570 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
ee4b2721 1571 return wxTreeItemId(TreeView_GetPrevSibling(GetHwnd(), HITEM(item)));
2bda0e17
KB
1572}
1573
08b7c251 1574wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
2bda0e17 1575{
ee4b2721 1576 return wxTreeItemId(TreeView_GetFirstVisible(GetHwnd()));
2bda0e17
KB
1577}
1578
08b7c251 1579wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
2bda0e17 1580{
05b2a432 1581 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f6bcfd97 1582 wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!"));
02ce7b72 1583
ee4b2721 1584 return wxTreeItemId(TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
08b7c251 1585}
02ce7b72 1586
08b7c251
VZ
1587wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1588{
05b2a432 1589 wxCHECK_MSG( item.IsOk(), wxTreeItemId(), wxT("invalid tree item") );
f6bcfd97 1590 wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!"));
02ce7b72 1591
ee4b2721 1592 return wxTreeItemId(TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
08b7c251 1593}
02ce7b72 1594
9dfbf520
VZ
1595// ----------------------------------------------------------------------------
1596// multiple selections emulation
1597// ----------------------------------------------------------------------------
1598
1599bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const
1600{
bfbb0b4c 1601 wxCHECK_MSG( item.IsOk(), false, wxT("invalid tree item") );
05b2a432 1602
9dfbf520
VZ
1603 // receive the desired information.
1604 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
1605 DoGetItem(&tvItem);
1606
1607 // state image indices are 1 based
1608 return ((tvItem.state >> 12) - 1) == 1;
1609}
1610
1611void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check)
1612{
05b2a432
RD
1613 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
1614
9dfbf520
VZ
1615 // receive the desired information.
1616 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
1617
a9c1265f
VZ
1618 DoGetItem(&tvItem);
1619
9dfbf520
VZ
1620 // state images are one-based
1621 tvItem.state = (check ? 2 : 1) << 12;
1622
1623 DoSetItem(&tvItem);
1624}
1625
33961d59
RR
1626size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const
1627{
1628 TraverseSelections selector(this, selections);
9dfbf520 1629
e47c4d48 1630 return selector.GetCount();
9dfbf520
VZ
1631}
1632
08b7c251
VZ
1633// ----------------------------------------------------------------------------
1634// Usual operations
1635// ----------------------------------------------------------------------------
02ce7b72 1636
08b7c251
VZ
1637wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
1638 wxTreeItemId hInsertAfter,
1639 const wxString& text,
1640 int image, int selectedImage,
1641 wxTreeItemData *data)
1642{
0e935169
VZ
1643 wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()),
1644 wxTreeItemId(),
1645 _T("can't have more than one root in the tree") );
1646
08b7c251 1647 TV_INSERTSTRUCT tvIns;
3f7bc32b
VZ
1648 tvIns.hParent = HITEM(parent);
1649 tvIns.hInsertAfter = HITEM(hInsertAfter);
58a8ab88 1650
74b31181
VZ
1651 // this is how we insert the item as the first child: supply a NULL
1652 // hInsertAfter
1653 if ( !tvIns.hInsertAfter )
58a8ab88
JS
1654 {
1655 tvIns.hInsertAfter = TVI_FIRST;
1656 }
1657
08b7c251 1658 UINT mask = 0;
44d60c0b 1659 if ( !text.empty() )
08b7c251
VZ
1660 {
1661 mask |= TVIF_TEXT;
837e5743 1662 tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
08b7c251 1663 }
f6bcfd97
BP
1664 else
1665 {
1666 tvIns.item.pszText = NULL;
1667 tvIns.item.cchTextMax = 0;
1668 }
02ce7b72 1669
08b7c251
VZ
1670 if ( image != -1 )
1671 {
1672 mask |= TVIF_IMAGE;
1673 tvIns.item.iImage = image;
3a5a2f56 1674
6b037754 1675 if ( selectedImage == -1 )
3a5a2f56
VZ
1676 {
1677 // take the same image for selected icon if not specified
1678 selectedImage = image;
1679 }
08b7c251 1680 }
02ce7b72 1681
08b7c251
VZ
1682 if ( selectedImage != -1 )
1683 {
1684 mask |= TVIF_SELECTEDIMAGE;
1685 tvIns.item.iSelectedImage = selectedImage;
1686 }
02ce7b72 1687
08b7c251
VZ
1688 if ( data != NULL )
1689 {
1690 mask |= TVIF_PARAM;
1691 tvIns.item.lParam = (LPARAM)data;
1692 }
02ce7b72 1693
08b7c251 1694 tvIns.item.mask = mask;
02ce7b72 1695
d220ae32 1696 HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns);
08b7c251
VZ
1697 if ( id == 0 )
1698 {
f6bcfd97 1699 wxLogLastError(wxT("TreeView_InsertItem"));
08b7c251 1700 }
02ce7b72 1701
fd3f686c
VZ
1702 if ( data != NULL )
1703 {
1704 // associate the application tree item with Win32 tree item handle
ee4b2721 1705 data->SetId(id);
fd3f686c
VZ
1706 }
1707
ee4b2721 1708 return wxTreeItemId(id);
2bda0e17
KB
1709}
1710
08b7c251 1711// for compatibility only
ee4b2721
VZ
1712#if WXWIN_COMPATIBILITY_2_4
1713
08b7c251
VZ
1714wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1715 const wxString& text,
1716 int image, int selImage,
1717 long insertAfter)
2bda0e17 1718{
ee4b2721 1719 return DoInsertItem(parent, wxTreeItemId((void *)insertAfter), text,
08b7c251 1720 image, selImage, NULL);
2bda0e17
KB
1721}
1722
a6fb8636
WS
1723wxImageList *wxTreeCtrl::GetImageList(int) const
1724{
1725 return GetImageList();
1726}
1727
1728void wxTreeCtrl::SetImageList(wxImageList *imageList, int)
1729{
1730 SetImageList(imageList);
1731}
1732
1733int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
1734{
1735 return GetItemImage(item, wxTreeItemIcon_Selected);
1736}
1737
1738void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
1739{
1740 SetItemImage(item, image, wxTreeItemIcon_Selected);
1741}
1742
ee4b2721
VZ
1743#endif // WXWIN_COMPATIBILITY_2_4
1744
08b7c251
VZ
1745wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
1746 int image, int selectedImage,
1747 wxTreeItemData *data)
2bda0e17 1748{
a9c1265f
VZ
1749
1750 if ( m_windowStyle & wxTR_HIDE_ROOT )
1751 {
1752 // create a virtual root item, the parent for all the others
1753 m_pVirtualRoot = new wxVirtualNode(data);
1754
1755 return TVI_ROOT;
1756 }
1757
f888d614 1758 return DoInsertItem(wxTreeItemId(), wxTreeItemId(),
08b7c251 1759 text, image, selectedImage, data);
2bda0e17
KB
1760}
1761
08b7c251
VZ
1762wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
1763 const wxString& text,
1764 int image, int selectedImage,
1765 wxTreeItemData *data)
2bda0e17 1766{
ee4b2721 1767 return DoInsertItem(parent, TVI_FIRST,
08b7c251 1768 text, image, selectedImage, data);
2bda0e17
KB
1769}
1770
08b7c251
VZ
1771wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1772 const wxTreeItemId& idPrevious,
1773 const wxString& text,
1774 int image, int selectedImage,
1775 wxTreeItemData *data)
2bda0e17 1776{
08b7c251 1777 return DoInsertItem(parent, idPrevious, text, image, selectedImage, data);
2bda0e17
KB
1778}
1779
2ef31e80
VZ
1780wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1781 size_t index,
1782 const wxString& text,
1783 int image, int selectedImage,
1784 wxTreeItemData *data)
1785{
1786 // find the item from index
ee4b2721 1787 wxTreeItemIdValue cookie;
2ef31e80
VZ
1788 wxTreeItemId idPrev, idCur = GetFirstChild(parent, cookie);
1789 while ( index != 0 && idCur.IsOk() )
1790 {
1791 index--;
1792
1793 idPrev = idCur;
1794 idCur = GetNextChild(parent, cookie);
1795 }
1796
1797 // assert, not check: if the index is invalid, we will append the item
1798 // to the end
1799 wxASSERT_MSG( index == 0, _T("bad index in wxTreeCtrl::InsertItem") );
1800
1801 return DoInsertItem(parent, idPrev, text, image, selectedImage, data);
1802}
1803
08b7c251
VZ
1804wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parent,
1805 const wxString& text,
1806 int image, int selectedImage,
1807 wxTreeItemData *data)
2bda0e17 1808{
ee4b2721 1809 return DoInsertItem(parent, TVI_LAST,
08b7c251 1810 text, image, selectedImage, data);
2bda0e17
KB
1811}
1812
08b7c251 1813void wxTreeCtrl::Delete(const wxTreeItemId& item)
2bda0e17 1814{
3f7bc32b 1815 if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
bbcdf8bc 1816 {
f6bcfd97 1817 wxLogLastError(wxT("TreeView_DeleteItem"));
bbcdf8bc 1818 }
bbcdf8bc
JS
1819}
1820
23fd5130
VZ
1821// delete all children (but don't delete the item itself)
1822void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
1823{
ee4b2721 1824 wxTreeItemIdValue cookie;
23fd5130 1825
ee4b2721 1826 wxArrayTreeItemIds children;
23fd5130
VZ
1827 wxTreeItemId child = GetFirstChild(item, cookie);
1828 while ( child.IsOk() )
1829 {
ee4b2721 1830 children.Add(child);
23fd5130
VZ
1831
1832 child = GetNextChild(item, cookie);
1833 }
1834
1835 size_t nCount = children.Count();
1836 for ( size_t n = 0; n < nCount; n++ )
1837 {
f888d614 1838 if ( !TreeView_DeleteItem(GetHwnd(), HITEM_PTR(children[n])) )
23fd5130 1839 {
f6bcfd97 1840 wxLogLastError(wxT("TreeView_DeleteItem"));
23fd5130
VZ
1841 }
1842 }
1843}
1844
08b7c251 1845void wxTreeCtrl::DeleteAllItems()
bbcdf8bc 1846{
bfedf621
VZ
1847 // delete the "virtual" root item.
1848 if ( GET_VIRTUAL_ROOT() )
1849 {
1850 delete GET_VIRTUAL_ROOT();
1851 m_pVirtualRoot = NULL;
1852 }
1853
1854 // and all the real items
a9c1265f 1855
d220ae32 1856 if ( !TreeView_DeleteAllItems(GetHwnd()) )
bbcdf8bc 1857 {
f6bcfd97 1858 wxLogLastError(wxT("TreeView_DeleteAllItems"));
bbcdf8bc 1859 }
2bda0e17
KB
1860}
1861
08b7c251 1862void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
2bda0e17 1863{
dd3646fd
VZ
1864 wxASSERT_MSG( flag == TVE_COLLAPSE ||
1865 flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
1866 flag == TVE_EXPAND ||
1867 flag == TVE_TOGGLE,
223d09f6 1868 wxT("Unknown flag in wxTreeCtrl::DoExpand") );
08b7c251 1869
a9c1265f 1870 // A hidden root can be neither expanded nor collapsed.
e9616381 1871 wxCHECK_RET( !(m_windowStyle & wxTR_HIDE_ROOT) || (HITEM(item) != TVI_ROOT),
0d6a0101 1872 wxT("Can't expand/collapse hidden root node!") )
a9c1265f 1873
08b7c251 1874 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
d220ae32
VZ
1875 // emulate them. This behaviour has changed slightly with comctl32.dll
1876 // v 4.70 - now it does send them but only the first time. To maintain
1877 // compatible behaviour and also in order to not have surprises with the
1878 // future versions, don't rely on this and still do everything ourselves.
1879 // To avoid that the messages be sent twice when the item is expanded for
1880 // the first time we must clear TVIS_EXPANDEDONCE style manually.
1881
1882 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
1883 tvItem.state = 0;
1884 DoSetItem(&tvItem);
1885
3f7bc32b 1886 if ( TreeView_Expand(GetHwnd(), HITEM(item), flag) != 0 )
08b7c251
VZ
1887 {
1888 wxTreeEvent event(wxEVT_NULL, m_windowId);
1889 event.m_item = item;
08b7c251 1890 event.SetEventObject(this);
2bda0e17 1891
deb1de30
VZ
1892 // note that the {EXPAND|COLLAPS}ING event is sent by TreeView_Expand()
1893 // itself
1894 event.SetEventType(gs_expandEvents[IsExpanded(item) ? IDX_EXPAND
1895 : IDX_COLLAPSE]
1896 [IDX_DONE]);
2bda0e17 1897
deb1de30 1898 (void)GetEventHandler()->ProcessEvent(event);
08b7c251 1899 }
d220ae32 1900 //else: change didn't took place, so do nothing at all
2bda0e17
KB
1901}
1902
08b7c251 1903void wxTreeCtrl::Expand(const wxTreeItemId& item)
2bda0e17 1904{
08b7c251 1905 DoExpand(item, TVE_EXPAND);
2bda0e17 1906}
2bda0e17 1907
08b7c251 1908void wxTreeCtrl::Collapse(const wxTreeItemId& item)
2bda0e17 1909{
08b7c251 1910 DoExpand(item, TVE_COLLAPSE);
2bda0e17
KB
1911}
1912
08b7c251 1913void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
2bda0e17 1914{
dd3646fd 1915 DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
2bda0e17
KB
1916}
1917
08b7c251 1918void wxTreeCtrl::Toggle(const wxTreeItemId& item)
2bda0e17 1919{
08b7c251 1920 DoExpand(item, TVE_TOGGLE);
2bda0e17
KB
1921}
1922
b5f6b52a 1923#if WXWIN_COMPATIBILITY_2_4
a6fb8636 1924
42c5812d
UU
1925void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action)
1926{
9dfbf520 1927 DoExpand(item, action);
42c5812d 1928}
a6fb8636 1929
b5f6b52a 1930#endif
42c5812d 1931
08b7c251 1932void wxTreeCtrl::Unselect()
2bda0e17 1933{
3f7bc32b
VZ
1934 wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE),
1935 wxT("doesn't make sense, may be you want UnselectAll()?") );
9dfbf520
VZ
1936
1937 // just remove the selection
ee4b2721 1938 SelectItem(wxTreeItemId());
08b7c251 1939}
02ce7b72 1940
9dfbf520 1941void wxTreeCtrl::UnselectAll()
08b7c251 1942{
9dfbf520 1943 if ( m_windowStyle & wxTR_MULTIPLE )
2bda0e17 1944 {
9dfbf520
VZ
1945 wxArrayTreeItemIds selections;
1946 size_t count = GetSelections(selections);
1947 for ( size_t n = 0; n < count; n++ )
d220ae32 1948 {
3f7bc32b 1949#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
f888d614 1950 SetItemCheck(HITEM_PTR(selections[n]), false);
3f7bc32b 1951#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
f888d614 1952 ::UnselectItem(GetHwnd(), HITEM_PTR(selections[n]));
3f7bc32b 1953#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
d220ae32 1954 }
ba8a1961
VZ
1955
1956 m_htSelStart.Unset();
9dfbf520
VZ
1957 }
1958 else
1959 {
1960 // just remove the selection
1961 Unselect();
1962 }
1963}
1964
3e9af289 1965void wxTreeCtrl::SelectItem(const wxTreeItemId& item, bool select)
9dfbf520
VZ
1966{
1967 if ( m_windowStyle & wxTR_MULTIPLE )
1968 {
3f7bc32b 1969#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520 1970 // selecting the item means checking it
3e9af289 1971 SetItemCheck(item, select);
3f7bc32b 1972#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
3e9af289 1973 ::SelectItem(GetHwnd(), HITEM(item), select);
3f7bc32b 1974#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520
VZ
1975 }
1976 else
1977 {
3e9af289
VZ
1978 wxASSERT_MSG( select,
1979 _T("SelectItem(false) works only for multiselect") );
1980
9dfbf520
VZ
1981 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
1982 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
1983 // send them ourselves
1984
1985 wxTreeEvent event(wxEVT_NULL, m_windowId);
1986 event.m_item = item;
1987 event.SetEventObject(this);
1988
1989 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING);
1990 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
d220ae32 1991 {
5e7718a2
VZ
1992 if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) )
1993 {
1994 wxLogLastError(wxT("TreeView_SelectItem"));
1995 }
1996 else // ok
9dfbf520
VZ
1997 {
1998 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
1999 (void)GetEventHandler()->ProcessEvent(event);
2000 }
d220ae32 2001 }
9dfbf520 2002 //else: program vetoed the change
2bda0e17 2003 }
08b7c251 2004}
2bda0e17 2005
3e9af289
VZ
2006void wxTreeCtrl::UnselectItem(const wxTreeItemId& item)
2007{
2008 SelectItem(item, false);
2009}
2010
2011void wxTreeCtrl::ToggleItemSelection(const wxTreeItemId& item)
2012{
2013 SelectItem(item, !IsSelected(item));
2014}
2015
08b7c251
VZ
2016void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
2017{
2018 // no error return
3f7bc32b 2019 TreeView_EnsureVisible(GetHwnd(), HITEM(item));
08b7c251
VZ
2020}
2021
2022void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
2023{
3f7bc32b 2024 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item)) )
2bda0e17 2025 {
f6bcfd97 2026 wxLogLastError(wxT("TreeView_SelectSetFirstVisible"));
2bda0e17 2027 }
08b7c251
VZ
2028}
2029
44fbc477 2030wxTextCtrl *wxTreeCtrl::GetEditControl() const
08b7c251
VZ
2031{
2032 return m_textCtrl;
2033}
2034
2035void wxTreeCtrl::DeleteTextCtrl()
2036{
2037 if ( m_textCtrl )
2bda0e17 2038 {
b6a3d6ad
VZ
2039 // the HWND corresponding to this control is deleted by the tree
2040 // control itself and we don't know when exactly this happens, so check
2041 // if the window still exists before calling UnsubclassWin()
2042 if ( !::IsWindow(GetHwndOf(m_textCtrl)) )
2043 {
2044 m_textCtrl->SetHWND(0);
2045 }
2046
08b7c251
VZ
2047 m_textCtrl->UnsubclassWin();
2048 m_textCtrl->SetHWND(0);
2049 delete m_textCtrl;
2050 m_textCtrl = NULL;
1a4088e1
VZ
2051
2052 m_idEdited.Unset();
2bda0e17 2053 }
08b7c251 2054}
2bda0e17 2055
08b7c251
VZ
2056wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item,
2057 wxClassInfo* textControlClass)
2058{
2059 wxASSERT( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)) );
2060
b94ae1ea
VZ
2061 DeleteTextCtrl();
2062
1a4088e1 2063 m_idEdited = item;
cd6bd270 2064 m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
3f7bc32b 2065 HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), HITEM(item));
2bda0e17 2066
5ea47806 2067 // this is not an error - the TVN_BEGINLABELEDIT handler might have
04cd30de 2068 // returned false
5ea47806
VZ
2069 if ( !hWnd )
2070 {
cd6bd270
MB
2071 delete m_textCtrl;
2072 m_textCtrl = NULL;
5ea47806
VZ
2073 return NULL;
2074 }
2bda0e17 2075
cd6bd270 2076 // textctrl is subclassed in MSWOnNotify
08b7c251 2077 return m_textCtrl;
2bda0e17
KB
2078}
2079
08b7c251 2080// End label editing, optionally cancelling the edit
d35dce3a 2081void wxTreeCtrl::DoEndEditLabel(bool discardChanges)
2bda0e17 2082{
d220ae32 2083 TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
08b7c251
VZ
2084
2085 DeleteTextCtrl();
2bda0e17
KB
2086}
2087
08b7c251 2088wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
2bda0e17 2089{
08b7c251
VZ
2090 TV_HITTESTINFO hitTestInfo;
2091 hitTestInfo.pt.x = (int)point.x;
2092 hitTestInfo.pt.y = (int)point.y;
2bda0e17 2093
d220ae32 2094 TreeView_HitTest(GetHwnd(), &hitTestInfo);
2bda0e17 2095
08b7c251
VZ
2096 flags = 0;
2097
2098 // avoid repetition
2099 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
2100 flags |= wxTREE_HITTEST_##flag
2101
2102 TRANSLATE_FLAG(ABOVE);
2103 TRANSLATE_FLAG(BELOW);
2104 TRANSLATE_FLAG(NOWHERE);
2105 TRANSLATE_FLAG(ONITEMBUTTON);
2106 TRANSLATE_FLAG(ONITEMICON);
2107 TRANSLATE_FLAG(ONITEMINDENT);
2108 TRANSLATE_FLAG(ONITEMLABEL);
2109 TRANSLATE_FLAG(ONITEMRIGHT);
2110 TRANSLATE_FLAG(ONITEMSTATEICON);
2111 TRANSLATE_FLAG(TOLEFT);
2112 TRANSLATE_FLAG(TORIGHT);
2bda0e17 2113
08b7c251
VZ
2114 #undef TRANSLATE_FLAG
2115
ee4b2721 2116 return wxTreeItemId(hitTestInfo.hItem);
08b7c251
VZ
2117}
2118
f7c832a7
VZ
2119bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
2120 wxRect& rect,
2121 bool textOnly) const
2122{
2123 RECT rc;
f2c3db9d
RD
2124
2125 // Virtual root items have no bounding rectangle
2126 if ( IS_VIRTUAL_ROOT(item) )
2127 {
2128 return false;
2129 }
2130
3f7bc32b 2131 if ( TreeView_GetItemRect(GetHwnd(), HITEM(item),
f7c832a7
VZ
2132 &rc, textOnly) )
2133 {
2134 rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
2135
04cd30de 2136 return true;
f7c832a7
VZ
2137 }
2138 else
2139 {
2140 // couldn't retrieve rect: for example, item isn't visible
04cd30de 2141 return false;
f7c832a7
VZ
2142 }
2143}
2144
23fd5130
VZ
2145// ----------------------------------------------------------------------------
2146// sorting stuff
2147// ----------------------------------------------------------------------------
f7c832a7 2148
502a2b18
VZ
2149// this is just a tiny namespace which is friend to wxTreeCtrl and so can use
2150// functions such as IsDataIndirect()
2151class wxTreeSortHelper
2152{
2153public:
2154 static int CALLBACK Compare(LPARAM data1, LPARAM data2, LPARAM tree);
2155
2156private:
2157 static wxTreeItemId GetIdFromData(wxTreeCtrl *tree, LPARAM item)
2158 {
2159 wxTreeItemData *data = (wxTreeItemData *)item;
2160 if ( tree->IsDataIndirect(data) )
2161 {
2162 data = ((wxTreeItemIndirectData *)data)->GetData();
2163 }
2164
2165 return data->GetId();
2166 }
2167};
2168
2169int CALLBACK wxTreeSortHelper::Compare(LPARAM pItem1,
2170 LPARAM pItem2,
2171 LPARAM htree)
23fd5130 2172{
096c9f9b 2173 wxCHECK_MSG( pItem1 && pItem2, 0,
223d09f6 2174 wxT("sorting tree without data doesn't make sense") );
096c9f9b 2175
502a2b18
VZ
2176 wxTreeCtrl *tree = (wxTreeCtrl *)htree;
2177
2178 return tree->OnCompareItems(GetIdFromData(tree, pItem1),
2179 GetIdFromData(tree, pItem2));
23fd5130
VZ
2180}
2181
95aabccc
VZ
2182int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
2183 const wxTreeItemId& item2)
08b7c251 2184{
837e5743 2185 return wxStrcmp(GetItemText(item1), GetItemText(item2));
95aabccc
VZ
2186}
2187
2188void wxTreeCtrl::SortChildren(const wxTreeItemId& item)
2189{
05b2a432
RD
2190 wxCHECK_RET( item.IsOk(), wxT("invalid tree item") );
2191
95aabccc 2192 // rely on the fact that TreeView_SortChildren does the same thing as our
23fd5130
VZ
2193 // default behaviour, i.e. sorts items alphabetically and so call it
2194 // directly if we're not in derived class (much more efficient!)
2195 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
2bda0e17 2196 {
3f7bc32b 2197 TreeView_SortChildren(GetHwnd(), HITEM(item), 0);
2bda0e17 2198 }
08b7c251 2199 else
2bda0e17 2200 {
62448488 2201 TV_SORTCB tvSort;
3f7bc32b 2202 tvSort.hParent = HITEM(item);
502a2b18 2203 tvSort.lpfnCompare = wxTreeSortHelper::Compare;
23fd5130 2204 tvSort.lParam = (LPARAM)this;
d220ae32 2205 TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */);
2bda0e17 2206 }
08b7c251 2207}
2bda0e17 2208
08b7c251
VZ
2209// ----------------------------------------------------------------------------
2210// implementation
2211// ----------------------------------------------------------------------------
2bda0e17 2212
08b7c251
VZ
2213bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id)
2214{
2215 if ( cmd == EN_UPDATE )
2bda0e17 2216 {
08b7c251
VZ
2217 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
2218 event.SetEventObject( this );
2219 ProcessCommand(event);
2bda0e17 2220 }
08b7c251 2221 else if ( cmd == EN_KILLFOCUS )
2bda0e17 2222 {
08b7c251
VZ
2223 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
2224 event.SetEventObject( this );
2225 ProcessCommand(event);
2bda0e17 2226 }
08b7c251 2227 else
2bda0e17 2228 {
08b7c251 2229 // nothing done
04cd30de 2230 return false;
2bda0e17 2231 }
08b7c251
VZ
2232
2233 // command processed
04cd30de 2234 return true;
08b7c251
VZ
2235}
2236
23f681ec
VZ
2237// we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we
2238// only do it during dragging, minimize wxWin overhead (this is important for
2239// WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly
2240// instead of passing by wxWin events
c140b7e7 2241WXLRESULT wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
23f681ec 2242{
04cd30de 2243 bool processed = false;
c140b7e7 2244 WXLRESULT rc = 0;
2b9a7d4c 2245 bool isMultiple = HasFlag(wxTR_MULTIPLE);
3f7bc32b 2246
499bbaeb 2247 if ( nMsg == WM_CONTEXTMENU )
30f5e34f
RR
2248 {
2249 wxTreeEvent event( wxEVT_COMMAND_TREE_ITEM_MENU, GetId() );
9ea0f2c5
VZ
2250
2251 // can't use GetSelection() here as it would assert in multiselect mode
2252 event.m_item = wxTreeItemId(TreeView_GetSelection(GetHwnd()));
30f5e34f 2253 event.SetEventObject( this );
9ea0f2c5 2254
9994e2da 2255 if ( GetEventHandler()->ProcessEvent(event) )
2b9a7d4c 2256 processed = true;
9994e2da 2257 //else: continue with generating wxEVT_CONTEXT_MENU in base class code
30f5e34f 2258 }
2b9a7d4c 2259 else if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
23f681ec 2260 {
7bb8798c
VZ
2261 // we only process mouse messages here and these parameters have the
2262 // same meaning for all of them
3f7bc32b
VZ
2263 int x = GET_X_LPARAM(lParam),
2264 y = GET_Y_LPARAM(lParam);
2265 HTREEITEM htItem = GetItemFromPoint(GetHwnd(), x, y);
2266
23f681ec
VZ
2267 switch ( nMsg )
2268 {
de6ac339 2269 case WM_RBUTTONDOWN:
92679f9f
VZ
2270 // if the item we are about to right click on is not already
2271 // selected or if we click outside of any item, remove the
2272 // entire previous selection
2273 if ( !htItem || !::IsItemSelected(GetHwnd(), htItem) )
de6ac339
JS
2274 {
2275 UnselectAll();
2276 }
2277
2278 // select item and set the focus to the
2279 // newly selected item
2280 ::SelectItem(GetHwnd(), htItem);
2281 ::SetFocus(GetHwnd(), htItem);
2282 break;
2283
3f7bc32b
VZ
2284#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
2285 case WM_LBUTTONDOWN:
2286 if ( htItem && isMultiple )
23f681ec 2287 {
3f7bc32b
VZ
2288 if ( wParam & MK_CONTROL )
2289 {
2290 SetFocus();
23f681ec 2291
3f7bc32b 2292 // toggle selected state
3e9af289 2293 ::ToggleItemSelection(GetHwnd(), htItem);
3f7bc32b
VZ
2294
2295 ::SetFocus(GetHwnd(), htItem);
2296
2297 // reset on any click without Shift
f888d614 2298 m_htSelStart.Unset();
3f7bc32b 2299
04cd30de 2300 processed = true;
3f7bc32b
VZ
2301 }
2302 else if ( wParam & MK_SHIFT )
2303 {
2304 // this selects all items between the starting one and
2305 // the current
2306
2307 if ( !m_htSelStart )
2308 {
2309 // take the focused item
ee4b2721 2310 m_htSelStart = TreeView_GetSelection(GetHwnd());
3f7bc32b
VZ
2311 }
2312
2313 SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
2314 !(wParam & MK_CONTROL));
2315
2316 ::SetFocus(GetHwnd(), htItem);
23f681ec 2317
04cd30de 2318 processed = true;
3f7bc32b
VZ
2319 }
2320 else // normal click
2321 {
df4ac4c7
VZ
2322 // avoid doing anything if we click on the only
2323 // currently selected item
5e7718a2 2324
df4ac4c7
VZ
2325 wxArrayTreeItemIds selections;
2326 size_t count = GetSelections(selections);
2327 if ( count == 0 ||
35cf1ec6 2328 count > 1 ||
f888d614 2329 HITEM_PTR(selections[0]) != htItem )
df4ac4c7 2330 {
35cf1ec6
VZ
2331 // clear the previously selected items, if the
2332 // user clicked outside of the present selection.
2333 // otherwise, perform the deselection on mouse-up.
2334 // this allows multiple drag and drop to work.
5e7718a2 2335
35cf1ec6
VZ
2336 if (IsItemSelected(GetHwnd(), htItem))
2337 {
2338 ::SetFocus(GetHwnd(), htItem);
2339 }
2340 else
2341 {
2342 UnselectAll();
2343
2344 // prevent the click from starting in-place editing
2345 // which should only happen if we click on the
2346 // already selected item (and nothing else is
2347 // selected)
2348
2349 TreeView_SelectItem(GetHwnd(), 0);
2350 ::SelectItem(GetHwnd(), htItem);
2351 }
df4ac4c7 2352 }
3f7bc32b
VZ
2353
2354 // reset on any click without Shift
f888d614 2355 m_htSelStart.Unset();
3f7bc32b
VZ
2356 }
2357 }
2358 break;
2359#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
2360
2361 case WM_MOUSEMOVE:
2362 if ( m_dragImage )
2363 {
afff720b 2364 m_dragImage->Move(wxPoint(x, y));
3f7bc32b 2365 if ( htItem )
23f681ec
VZ
2366 {
2367 // highlight the item as target (hiding drag image is
2368 // necessary - otherwise the display will be corrupted)
68be9f09 2369 m_dragImage->Hide();
3f7bc32b 2370 TreeView_SelectDropTarget(GetHwnd(), htItem);
68be9f09 2371 m_dragImage->Show();
23f681ec
VZ
2372 }
2373 }
2374 break;
2375
2376 case WM_LBUTTONUP:
35cf1ec6
VZ
2377
2378 // facilitates multiple drag-and-drop
2379 if (htItem && isMultiple)
2380 {
2381 wxArrayTreeItemIds selections;
2382 size_t count = GetSelections(selections);
2383
2384 if (count > 1 &&
2385 !(wParam & MK_CONTROL) &&
2386 !(wParam & MK_SHIFT))
2387 {
2388 UnselectAll();
2389 TreeView_SelectItem(GetHwnd(), htItem);
2390 }
2391 }
2392
2393 // fall through
2394
23f681ec 2395 case WM_RBUTTONUP:
3f7bc32b 2396 if ( m_dragImage )
23f681ec 2397 {
68be9f09 2398 m_dragImage->EndDrag();
23f681ec
VZ
2399 delete m_dragImage;
2400 m_dragImage = NULL;
2401
2402 // generate the drag end event
2403 wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, m_windowId);
2404
ee4b2721 2405 event.m_item = htItem;
23f681ec
VZ
2406 event.m_pointDrag = wxPoint(x, y);
2407 event.SetEventObject(this);
2408
2409 (void)GetEventHandler()->ProcessEvent(event);
225fe9d6
VZ
2410
2411 // if we don't do it, the tree seems to think that 2 items
2412 // are selected simultaneously which is quite weird
2413 TreeView_SelectDropTarget(GetHwnd(), 0);
23f681ec
VZ
2414 }
2415 break;
2416 }
2417 }
3f7bc32b
VZ
2418#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
2419 else if ( (nMsg == WM_SETFOCUS || nMsg == WM_KILLFOCUS) && isMultiple )
2420 {
2421 // the tree control greys out the selected item when it loses focus and
2422 // paints it as selected again when it regains it, but it won't do it
2423 // for the other items itself - help it
2424 wxArrayTreeItemIds selections;
2425 size_t count = GetSelections(selections);
2426 RECT rect;
2427 for ( size_t n = 0; n < count; n++ )
2428 {
04cd30de 2429 // TreeView_GetItemRect() will return false if item is not visible,
3f7bc32b 2430 // which may happen perfectly well
f888d614 2431 if ( TreeView_GetItemRect(GetHwnd(), HITEM_PTR(selections[n]),
2b9a7d4c 2432 &rect, TRUE) )
3f7bc32b 2433 {
2b9a7d4c 2434 ::InvalidateRect(GetHwnd(), &rect, FALSE);
3f7bc32b
VZ
2435 }
2436 }
2437 }
2438 else if ( nMsg == WM_KEYDOWN && isMultiple )
2439 {
2440 bool bCtrl = wxIsCtrlDown(),
2441 bShift = wxIsShiftDown();
2442
2c8e4738 2443 HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
ba8a1961 2444 switch ( wParam )
3f7bc32b 2445 {
ba8a1961
VZ
2446 case VK_SPACE:
2447 if ( bCtrl )
2448 {
2449 ::ToggleItemSelection(GetHwnd(), htSel);
2450 }
2451 else
2452 {
2453 UnselectAll();
23f681ec 2454
ba8a1961
VZ
2455 ::SelectItem(GetHwnd(), htSel);
2456 }
3f7bc32b 2457
ba8a1961
VZ
2458 processed = true;
2459 break;
3f7bc32b 2460
ba8a1961
VZ
2461 case VK_UP:
2462 case VK_DOWN:
2463 if ( !bCtrl && !bShift )
3f7bc32b 2464 {
ba8a1961
VZ
2465 // no modifiers, just clear selection and then let the default
2466 // processing to take place
2467 UnselectAll();
3f7bc32b 2468 }
ba8a1961 2469 else if ( htSel )
3f7bc32b 2470 {
ba8a1961
VZ
2471 (void)wxControl::MSWWindowProc(nMsg, wParam, lParam);
2472
2473 HTREEITEM htNext = (HTREEITEM)
2474 TreeView_GetNextItem
2475 (
2476 GetHwnd(),
2477 htSel,
2478 wParam == VK_UP ? TVGN_PREVIOUSVISIBLE
2479 : TVGN_NEXTVISIBLE
2480 );
2481
2482 if ( !htNext )
2483 {
2484 // at the top/bottom
2485 htNext = htSel;
2486 }
2487
2488 if ( bShift )
2489 {
2490 if ( !m_htSelStart )
2491 m_htSelStart = htSel;
2492
2493 SelectRange(GetHwnd(), HITEM(m_htSelStart), htNext);
2494 }
2495 else // bCtrl
2496 {
2497 // without changing selection
2498 ::SetFocus(GetHwnd(), htNext);
2499 }
2500
2501 processed = true;
3f7bc32b 2502 }
ba8a1961
VZ
2503 break;
2504
2505 case VK_HOME:
2506 case VK_END:
2507 case VK_PRIOR:
2508 case VK_NEXT:
2509 // TODO: handle Shift/Ctrl with these keys
2510 if ( !bCtrl && !bShift )
3f7bc32b 2511 {
ba8a1961 2512 UnselectAll();
3f7bc32b 2513
ba8a1961
VZ
2514 m_htSelStart.Unset();
2515 }
3f7bc32b
VZ
2516 }
2517 }
2518#endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
d35dce3a
VZ
2519 else if ( nMsg == WM_COMMAND )
2520 {
2521 // if we receive a EN_KILLFOCUS command from the in-place edit control
2522 // used for label editing, make sure to end editing
2523 WORD id, cmd;
2524 WXHWND hwnd;
2525 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
2526
2527 if ( cmd == EN_KILLFOCUS )
2528 {
2529 if ( m_textCtrl && m_textCtrl->GetHandle() == hwnd )
2530 {
2531 DoEndEditLabel();
2532
2533 processed = true;
2534 }
2535 }
2536 }
7bb8798c 2537
3f7bc32b
VZ
2538 if ( !processed )
2539 rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
2540
2541 return rc;
23f681ec
VZ
2542}
2543
fbd8ac52
VZ
2544WXLRESULT
2545wxTreeCtrl::MSWDefWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
2546{
2547 // default WM_RBUTTONDOWN handler enters modal loop inside DefWindowProc()
2548 // waiting for WM_RBUTTONUP and then sends the resulting WM_CONTEXTMENU to
2549 // the parent window, not us, which completely breaks everything so simply
2550 // don't let it see this message at all
2551 if ( nMsg == WM_RBUTTONDOWN )
2552 return 0;
2553
286694ba
VZ
2554 // but because of the above we don't get NM_RCLICK which is normally
2555 // generated by tree window proc when the modal loop mentioned above ends
2556 // because the mouse is released -- synthesize it ourselves instead
2557 if ( nMsg == WM_RBUTTONUP )
2558 {
2559 NMHDR hdr;
2560 hdr.hwndFrom = GetHwnd();
2561 hdr.idFrom = GetId();
2562 hdr.code = NM_RCLICK;
2563
2564 WXLPARAM rc;
2565 MSWOnNotify(GetId(), (LPARAM)&hdr, &rc);
2566
2567 // continue as usual
2568 }
2569
068b764a
VZ
2570 if ( nMsg == WM_CHAR )
2571 {
2572 // also don't let the control process Space and Return keys because it
2573 // doesn't do anything useful with them anyhow but always beeps
2574 // annoyingly when it receives them and there is no way to turn it off
2575 // simply if you just process TREEITEM_ACTIVATED event to which Space
2576 // and Enter presses are mapped in your code
2577 if ( wParam == VK_SPACE || wParam == VK_RETURN )
2578 return 0;
2579 }
2580
fbd8ac52
VZ
2581 return wxControl::MSWDefWindowProc(nMsg, wParam, lParam);
2582}
2583
08b7c251 2584// process WM_NOTIFY Windows message
a23fd0e1 2585bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
08b7c251
VZ
2586{
2587 wxTreeEvent event(wxEVT_NULL, m_windowId);
2588 wxEventType eventType = wxEVT_NULL;
2589 NMHDR *hdr = (NMHDR *)lParam;
2590
2591 switch ( hdr->code )
2bda0e17 2592 {
08b7c251
VZ
2593 case TVN_BEGINDRAG:
2594 eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
2595 // fall through
2596
2597 case TVN_BEGINRDRAG:
2598 {
2599 if ( eventType == wxEVT_NULL )
2600 eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
2601 //else: left drag, already set above
2602
2603 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
2604
ee4b2721 2605 event.m_item = tv->itemNew.hItem;
08b7c251 2606 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
23f681ec
VZ
2607
2608 // don't allow dragging by default: the user code must
2609 // explicitly say that it wants to allow it to avoid breaking
2610 // the old apps
2611 event.Veto();
08b7c251 2612 }
696e1ea0 2613 break;
08b7c251
VZ
2614
2615 case TVN_BEGINLABELEDIT:
2616 {
2617 eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
2618 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2619
646a8a4d
VZ
2620 // although the user event handler may still veto it, it is
2621 // important to set it now so that calls to SetItemText() from
2622 // the event handler would change the text controls contents
2623 m_idEdited =
ee4b2721 2624 event.m_item = info->item.hItem;
5ea47806 2625 event.m_label = info->item.pszText;
04cd30de 2626 event.m_editCancelled = false;
08b7c251 2627 }
696e1ea0 2628 break;
08b7c251
VZ
2629
2630 case TVN_DELETEITEM:
2631 {
2632 eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
2633 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
2634
ee4b2721 2635 event.m_item = tv->itemOld.hItem;
696e1ea0
VZ
2636
2637 if ( m_hasAnyAttr )
2638 {
ee4b2721
VZ
2639 wxMapTreeAttr::iterator it = m_attrs.find(tv->itemOld.hItem);
2640 if ( it != m_attrs.end() )
2641 {
2642 delete it->second;
2643 m_attrs.erase(it);
2644 }
696e1ea0 2645 }
08b7c251 2646 }
696e1ea0 2647 break;
08b7c251
VZ
2648
2649 case TVN_ENDLABELEDIT:
2650 {
2651 eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
2652 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2653
ee4b2721 2654 event.m_item = info->item.hItem;
5ea47806 2655 event.m_label = info->item.pszText;
06110b00 2656 event.m_editCancelled = info->item.pszText == NULL;
08b7c251
VZ
2657 break;
2658 }
2659
676d6550 2660#ifndef __WXWINCE__
156194e1
JS
2661 // These *must* not be removed or TVN_GETINFOTIP will
2662 // not be processed each time the mouse is moved
2663 // and the tooltip will only ever update once.
2664 case TTN_NEEDTEXTA:
2665 case TTN_NEEDTEXTW:
2666 {
2667 *result = 0;
2668
2669 break;
2670 }
2671
f3f71703 2672#ifdef TVN_GETINFOTIP
156194e1
JS
2673 case TVN_GETINFOTIP:
2674 {
2675 eventType = wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP;
2676 NMTVGETINFOTIP *info = (NMTVGETINFOTIP*)lParam;
2677
2678 // Which item are we trying to get a tooltip for?
bc0aebab 2679 event.m_item = info->hItem;
156194e1
JS
2680
2681 break;
2682 }
f3f71703 2683#endif
676d6550 2684#endif
5e7718a2 2685
08b7c251
VZ
2686 case TVN_GETDISPINFO:
2687 eventType = wxEVT_COMMAND_TREE_GET_INFO;
2688 // fall through
2689
2690 case TVN_SETDISPINFO:
2691 {
2692 if ( eventType == wxEVT_NULL )
2693 eventType = wxEVT_COMMAND_TREE_SET_INFO;
2694 //else: get, already set above
2695
2696 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2697
ee4b2721 2698 event.m_item = info->item.hItem;
08b7c251
VZ
2699 break;
2700 }
2701
2702 case TVN_ITEMEXPANDING:
08b7c251
VZ
2703 case TVN_ITEMEXPANDED:
2704 {
2705 NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
2706
deb1de30 2707 int what;
08b7c251
VZ
2708 switch ( tv->action )
2709 {
deb1de30
VZ
2710 default:
2711 wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv->action);
2712 // fall through
2713
08b7c251 2714 case TVE_EXPAND:
deb1de30 2715 what = IDX_EXPAND;
08b7c251
VZ
2716 break;
2717
2718 case TVE_COLLAPSE:
deb1de30 2719 what = IDX_COLLAPSE;
08b7c251 2720 break;
08b7c251
VZ
2721 }
2722
2b5f62a0
VZ
2723 int how = hdr->code == TVN_ITEMEXPANDING ? IDX_DOING
2724 : IDX_DONE;
deb1de30
VZ
2725
2726 eventType = gs_expandEvents[what][how];
08b7c251 2727
ee4b2721 2728 event.m_item = tv->itemNew.hItem;
08b7c251 2729 }
696e1ea0 2730 break;
08b7c251
VZ
2731
2732 case TVN_KEYDOWN:
2733 {
2734 eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
2735 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
2736
1944ad76
VZ
2737 // fabricate the lParam and wParam parameters sufficiently
2738 // similar to the ones from a "real" WM_KEYDOWN so that
2739 // CreateKeyEvent() works correctly
2740 WXLPARAM lParam =
1fdf858b 2741 (::GetKeyState(VK_MENU) < 0 ? KF_ALTDOWN : 0) << 16;
1944ad76
VZ
2742
2743 WXWPARAM wParam = info->wVKey;
2744
2745 int keyCode = wxCharCodeMSWToWX(info->wVKey);
2746 if ( !keyCode )
2747 {
2748 // wxCharCodeMSWToWX() returns 0 to indicate that this is a
2749 // simple ASCII key
2750 keyCode = wParam;
2751 }
2752
8191741f 2753 event.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN,
1944ad76
VZ
2754 keyCode,
2755 lParam,
2756 wParam);
23fd5130 2757
3f7bc32b
VZ
2758 // a separate event for Space/Return
2759 if ( !wxIsCtrlDown() && !wxIsShiftDown() &&
2760 ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) )
23fd5130
VZ
2761 {
2762 wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
2763 m_windowId);
2764 event2.SetEventObject(this);
3f7bc32b
VZ
2765 if ( !(GetWindowStyle() & wxTR_MULTIPLE) )
2766 {
2767 event2.m_item = GetSelection();
2768 }
2769 //else: don't know how to get it
23fd5130 2770
3f7bc32b 2771 (void)GetEventHandler()->ProcessEvent(event2);
23fd5130 2772 }
08b7c251 2773 }
696e1ea0 2774 break;
08b7c251 2775
5e7718a2 2776 // NB: MSLU is broken and sends TVN_SELCHANGEDA instead of
69a8b5b4
VS
2777 // TVN_SELCHANGEDW in Unicode mode under Win98. Therefore
2778 // we have to handle both messages:
2779 case TVN_SELCHANGEDA:
2780 case TVN_SELCHANGEDW:
08b7c251
VZ
2781 eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
2782 // fall through
2783
69a8b5b4
VS
2784 case TVN_SELCHANGINGA:
2785 case TVN_SELCHANGINGW:
08b7c251
VZ
2786 {
2787 if ( eventType == wxEVT_NULL )
2788 eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
2789 //else: already set above
2790
5e7718a2 2791 if (hdr->code == TVN_SELCHANGINGW ||
69a8b5b4
VS
2792 hdr->code == TVN_SELCHANGEDW)
2793 {
2794 NM_TREEVIEWW* tv = (NM_TREEVIEWW *)lParam;
ee4b2721
VZ
2795 event.m_item = tv->itemNew.hItem;
2796 event.m_itemOld = tv->itemOld.hItem;
69a8b5b4
VS
2797 }
2798 else
2799 {
2800 NM_TREEVIEWA* tv = (NM_TREEVIEWA *)lParam;
ee4b2721
VZ
2801 event.m_item = tv->itemNew.hItem;
2802 event.m_itemOld = tv->itemOld.hItem;
69a8b5b4 2803 }
08b7c251 2804 }
696e1ea0
VZ
2805 break;
2806
5b59df83
VZ
2807 // instead of explicitly checking for _WIN32_IE, check if the
2808 // required symbols are available in the headers
2809#if defined(CDDS_PREPAINT) && !wxUSE_COMCTL32_SAFELY
696e1ea0
VZ
2810 case NM_CUSTOMDRAW:
2811 {
2812 LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam;
2813 NMCUSTOMDRAW& nmcd = lptvcd->nmcd;
d7926e0b 2814 switch ( nmcd.dwDrawStage )
696e1ea0
VZ
2815 {
2816 case CDDS_PREPAINT:
2817 // if we've got any items with non standard attributes,
2818 // notify us before painting each item
2819 *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
2820 : CDRF_DODEFAULT;
d7926e0b 2821 break;
696e1ea0
VZ
2822
2823 case CDDS_ITEMPREPAINT:
2824 {
ee4b2721
VZ
2825 wxMapTreeAttr::iterator
2826 it = m_attrs.find((void *)nmcd.dwItemSpec);
696e1ea0 2827
ee4b2721 2828 if ( it == m_attrs.end() )
696e1ea0
VZ
2829 {
2830 // nothing to do for this item
d7926e0b
VZ
2831 *result = CDRF_DODEFAULT;
2832 break;
696e1ea0
VZ
2833 }
2834
ee4b2721
VZ
2835 wxTreeItemAttr * const attr = it->second;
2836
19da49fc
VZ
2837 // selection colours should override ours,
2838 // otherwise it is too confusing ot the user
2839 if ( !(nmcd.uItemState & CDIS_SELECTED) )
696e1ea0 2840 {
2076893b 2841 wxColour colBack;
696e1ea0
VZ
2842 if ( attr->HasBackgroundColour() )
2843 {
2844 colBack = attr->GetBackgroundColour();
19da49fc 2845 lptvcd->clrTextBk = wxColourToRGB(colBack);
696e1ea0 2846 }
19da49fc
VZ
2847 }
2848
2849 // but we still want to keep the special foreground
2850 // colour when we don't have focus (we can't keep
2851 // it when we do, it would usually be unreadable on
2852 // the almost inverted bg colour...)
2853 if ( !(nmcd.uItemState & CDIS_SELECTED) ||
2854 FindFocus() != this )
2855 {
2856 wxColour colText;
2857 if ( attr->HasTextColour() )
696e1ea0 2858 {
19da49fc
VZ
2859 colText = attr->GetTextColour();
2860 lptvcd->clrText = wxColourToRGB(colText);
696e1ea0 2861 }
696e1ea0
VZ
2862 }
2863
19da49fc 2864 if ( attr->HasFont() )
696e1ea0 2865 {
19da49fc
VZ
2866 HFONT hFont = GetHfontOf(attr->GetFont());
2867
696e1ea0
VZ
2868 ::SelectObject(nmcd.hdc, hFont);
2869
2870 *result = CDRF_NEWFONT;
2871 }
19da49fc 2872 else // no specific font
696e1ea0
VZ
2873 {
2874 *result = CDRF_DODEFAULT;
2875 }
696e1ea0 2876 }
d7926e0b 2877 break;
696e1ea0
VZ
2878
2879 default:
2880 *result = CDRF_DODEFAULT;
696e1ea0
VZ
2881 }
2882 }
d7926e0b
VZ
2883
2884 // we always process it
04cd30de 2885 return true;
5b59df83 2886#endif // have owner drawn support in headers
08b7c251 2887
8a000b6b
VZ
2888 case NM_CLICK:
2889 {
2890 DWORD pos = GetMessagePos();
2891 POINT point;
2892 point.x = LOWORD(pos);
2893 point.y = HIWORD(pos);
2894 ::MapWindowPoints(HWND_DESKTOP, GetHwnd(), &point, 1);
2895 int flags = 0;
2896 wxTreeItemId item = HitTest(wxPoint(point.x, point.y), flags);
2897 if (flags & wxTREE_HITTEST_ONITEMSTATEICON)
2898 {
2899 event.m_item = item;
2900 eventType = wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK;
2901 }
2902 break;
2903 }
2904
f6bcfd97
BP
2905 case NM_DBLCLK:
2906 case NM_RCLICK:
2907 {
2908 TV_HITTESTINFO tvhti;
2909 ::GetCursorPos(&tvhti.pt);
2910 ::ScreenToClient(GetHwnd(), &tvhti.pt);
2911 if ( TreeView_HitTest(GetHwnd(), &tvhti) )
2912 {
2913 if ( tvhti.flags & TVHT_ONITEM )
2914 {
ee4b2721 2915 event.m_item = tvhti.hItem;
f6bcfd97
BP
2916 eventType = (int)hdr->code == NM_DBLCLK
2917 ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED
2918 : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK;
8591268f
VZ
2919
2920 event.m_pointDrag.x = tvhti.pt.x;
2921 event.m_pointDrag.y = tvhti.pt.y;
f6bcfd97
BP
2922 }
2923
2924 break;
2925 }
2926 }
2927 // fall through
2928
08b7c251 2929 default:
a23fd0e1 2930 return wxControl::MSWOnNotify(idCtrl, lParam, result);
2bda0e17 2931 }
08b7c251
VZ
2932
2933 event.SetEventObject(this);
2934 event.SetEventType(eventType);
2935
fd3f686c 2936 bool processed = GetEventHandler()->ProcessEvent(event);
08b7c251
VZ
2937
2938 // post processing
5ea47806 2939 switch ( hdr->code )
2bda0e17 2940 {
f6bcfd97
BP
2941 case NM_DBLCLK:
2942 // we translate NM_DBLCLK into ACTIVATED event, so don't interpret
2943 // the return code of this event handler as the return value for
2944 // NM_DBLCLK - otherwise, double clicking the item to toggle its
2945 // expanded status would never work
04cd30de 2946 *result = false;
f6bcfd97
BP
2947 break;
2948
23f681ec
VZ
2949 case TVN_BEGINDRAG:
2950 case TVN_BEGINRDRAG:
2951 if ( event.IsAllowed() )
2952 {
2953 // normally this is impossible because the m_dragImage is
2954 // deleted once the drag operation is over
2955 wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") );
2956
2957 m_dragImage = new wxDragImage(*this, event.m_item);
c47addef 2958 m_dragImage->BeginDrag(wxPoint(0,0), this);
68be9f09 2959 m_dragImage->Show();
23f681ec
VZ
2960 }
2961 break;
2962
5ea47806
VZ
2963 case TVN_DELETEITEM:
2964 {
77ffb593 2965 // NB: we might process this message using wxWidgets event
5ea47806
VZ
2966 // tables, but due to overhead of wxWin event system we
2967 // prefer to do it here ourself (otherwise deleting a tree
2968 // with many items is just too slow)
2969 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
74b31181
VZ
2970
2971 wxTreeItemId item = event.m_item;
2972 if ( HasIndirectData(item) )
2973 {
2974 wxTreeItemIndirectData *data = (wxTreeItemIndirectData *)
2975 tv->itemOld.lParam;
2976 delete data; // can't be NULL here
74b31181
VZ
2977 }
2978 else
2979 {
2980 wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam;
2981 delete data; // may be NULL, ok
2982 }
08b7c251 2983
04cd30de 2984 processed = true; // Make sure we don't get called twice
5ea47806
VZ
2985 }
2986 break;
2987
2988 case TVN_BEGINLABELEDIT:
04cd30de 2989 // return true to cancel label editing
5ea47806 2990 *result = !event.IsAllowed();
646a8a4d 2991
cd6bd270 2992 // set ES_WANTRETURN ( like we do in BeginLabelEdit )
646a8a4d 2993 if ( event.IsAllowed() )
cd6bd270
MB
2994 {
2995 HWND hText = TreeView_GetEditControl(GetHwnd());
2996 if(hText != NULL)
2997 {
5e7718a2 2998 // MBN: if m_textCtrl already has an HWND, it is a stale
cd6bd270
MB
2999 // pointer from a previous edit (because the user
3000 // didn't modify the label before dismissing the control,
3001 // and TVN_ENDLABELEDIT was not sent), so delete it
3002 if(m_textCtrl && m_textCtrl->GetHWND() != 0)
3003 DeleteTextCtrl();
3004 if(!m_textCtrl)
3005 m_textCtrl = new wxTextCtrl();
3006 m_textCtrl->SetParent(this);
3007 m_textCtrl->SetHWND((WXHWND)hText);
3008 m_textCtrl->SubclassWin((WXHWND)hText);
3009
3010 // set wxTE_PROCESS_ENTER style for the text control to
3011 // force it to process the Enter presses itself, otherwise
3012 // they could be stolen from it by the dialog
3013 // navigation code
3014 m_textCtrl->SetWindowStyle(m_textCtrl->GetWindowStyle()
3015 | wxTE_PROCESS_ENTER);
3016 }
3017 }
646a8a4d
VZ
3018 else // we had set m_idEdited before
3019 {
3020 m_idEdited.Unset();
3021 }
5ea47806
VZ
3022 break;
3023
3024 case TVN_ENDLABELEDIT:
04cd30de 3025 // return true to set the label to the new string: note that we
188781db 3026 // also must pretend that we did process the message or it is going
04cd30de 3027 // to be passed to DefWindowProc() which will happily return false
188781db 3028 // cancelling the label change
5ea47806 3029 *result = event.IsAllowed();
04cd30de 3030 processed = true;
5ea47806
VZ
3031
3032 // ensure that we don't have the text ctrl which is going to be
3033 // deleted any more
3034 DeleteTextCtrl();
3035 break;
3036
676d6550 3037#ifndef __WXWINCE__
f3f71703 3038#ifdef TVN_GETINFOTIP
156194e1
JS
3039 case TVN_GETINFOTIP:
3040 {
3041 // If the user permitted a tooltip change, change it
3042 if (event.IsAllowed())
3043 {
3044 SetToolTip(event.m_label);
3045 }
3046 }
3047 break;
f3f71703 3048#endif
676d6550 3049#endif
5e7718a2 3050
5ea47806
VZ
3051 case TVN_SELCHANGING:
3052 case TVN_ITEMEXPANDING:
04cd30de 3053 // return true to prevent the action from happening
5ea47806
VZ
3054 *result = !event.IsAllowed();
3055 break;
3056
deb1de30
VZ
3057 case TVN_ITEMEXPANDED:
3058 // the item is not refreshed properly after expansion when it has
3059 // an image depending on the expanded/collapsed state - bug in
3060 // comctl32.dll or our code?
3061 {
bf43d750 3062 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
ee4b2721 3063 wxTreeItemId id(tv->itemNew.hItem);
deb1de30 3064
bf43d750
VZ
3065 int image = GetItemImage(id, wxTreeItemIcon_Expanded);
3066 if ( image != -1 )
3067 {
3068 RefreshItem(id);
deb1de30
VZ
3069 }
3070 }
3071 break;
3072
74b31181
VZ
3073 case TVN_GETDISPINFO:
3074 // NB: so far the user can't set the image himself anyhow, so do it
3075 // anyway - but this may change later
bf43d750 3076 //if ( /* !processed && */ 1 )
74b31181
VZ
3077 {
3078 wxTreeItemId item = event.m_item;
3079 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
3080 if ( info->item.mask & TVIF_IMAGE )
3081 {
3082 info->item.iImage =
3083 DoGetItemImageFromData
3084 (
3085 item,
3086 IsExpanded(item) ? wxTreeItemIcon_Expanded
3087 : wxTreeItemIcon_Normal
3088 );
3089 }
3090 if ( info->item.mask & TVIF_SELECTEDIMAGE )
3091 {
3092 info->item.iSelectedImage =
3093 DoGetItemImageFromData
3094 (
3095 item,
3096 IsExpanded(item) ? wxTreeItemIcon_SelectedExpanded
3097 : wxTreeItemIcon_Selected
3098 );
3099 }
deb1de30 3100 }
74b31181
VZ
3101 break;
3102
5ea47806
VZ
3103 //default:
3104 // for the other messages the return value is ignored and there is
3105 // nothing special to do
3106 }
fd3f686c 3107 return processed;
2bda0e17
KB
3108}
3109
8a000b6b
VZ
3110// ----------------------------------------------------------------------------
3111// State control.
3112// ----------------------------------------------------------------------------
3113
3114// why do they define INDEXTOSTATEIMAGEMASK but not the inverse?
3115#define STATEIMAGEMASKTOINDEX(state) (((state) & TVIS_STATEIMAGEMASK) >> 12)
3116
3117void wxTreeCtrl::SetState(const wxTreeItemId& node, int state)
3118{
3119 TV_ITEM tvi;
3120 tvi.hItem = (HTREEITEM)node.m_pItem;
3121 tvi.mask = TVIF_STATE;
3122 tvi.stateMask = TVIS_STATEIMAGEMASK;
3123
3124 // Select the specified state, or -1 == cycle to the next one.
3125 if ( state == -1 )
3126 {
5e7718a2 3127 TreeView_GetItem(GetHwnd(), &tvi);
8a000b6b
VZ
3128
3129 state = STATEIMAGEMASKTOINDEX(tvi.state) + 1;
3130 if ( state == m_imageListState->GetImageCount() )
3131 state = 1;
3132 }
3133
3134 wxCHECK_RET( state < m_imageListState->GetImageCount(),
3135 _T("wxTreeCtrl::SetState(): item index out of bounds") );
3136
5e7718a2 3137 tvi.state = INDEXTOSTATEIMAGEMASK(state);
8a000b6b
VZ
3138
3139 TreeView_SetItem(GetHwnd(), &tvi);
3140}
3141
3142int wxTreeCtrl::GetState(const wxTreeItemId& node)
3143{
3144 TV_ITEM tvi;
3145 tvi.hItem = (HTREEITEM)node.m_pItem;
3146 tvi.mask = TVIF_STATE;
3147 tvi.stateMask = TVIS_STATEIMAGEMASK;
5e7718a2 3148 TreeView_GetItem(GetHwnd(), &tvi);
8a000b6b
VZ
3149
3150 return STATEIMAGEMASKTOINDEX(tvi.state);
3151}
3152
1729813a
WS
3153#if WXWIN_COMPATIBILITY_2_2
3154
3155wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
3156{
3157 return GetItemParent( item );
3158}
3159
3160#endif // WXWIN_COMPATIBILITY_2_2
3161
1e6feb95 3162#endif // wxUSE_TREECTRL
8a000b6b 3163