]> git.saurik.com Git - wxWidgets.git/blame - src/msw/treectrl.cpp
Smooth Aqua buttons in wxTreeCtrl.
[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
08b7c251 9// Licence: wxWindows licence
b823f5a1 10/////////////////////////////////////////////////////////////////////////////
2bda0e17 11
08b7c251
VZ
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
1e6feb95 19
2bda0e17 20#ifdef __GNUG__
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
BM
33#include "wx/msw/private.h"
34
1e6feb95
VZ
35// Set this to 1 to be _absolutely_ sure that repainting will work for all
36// comctl32.dll versions
f6bcfd97
BP
37#define wxUSE_COMCTL32_SAFELY 0
38
0c589ad0
BM
39// Mingw32 is a bit mental even though this is done in winundef
40#ifdef GetFirstChild
d220ae32 41 #undef GetFirstChild
0c589ad0 42#endif
d220ae32 43
0c589ad0 44#ifdef GetNextSibling
d220ae32 45 #undef GetNextSibling
2bda0e17
KB
46#endif
47
2bda0e17
KB
48#if defined(__WIN95__)
49
c6f4913a 50#include "wx/app.h"
08b7c251 51#include "wx/log.h"
ce3ed50d 52#include "wx/dynarray.h"
08b7c251 53#include "wx/imaglist.h"
77cff606 54#include "wx/settings.h"
484523cf 55#include "wx/msw/treectrl.h"
23f681ec
VZ
56#include "wx/msw/dragimag.h"
57
c42404a5
VZ
58#ifdef __GNUWIN32_OLD__
59 #include "wx/msw/gnuwin32/extra.h"
65fd5cb0 60#endif
9a05fd8d 61
ae090fdb 62#if defined(__WIN95__) && !((defined(__GNUWIN32_OLD__) || defined(__TWIN32__)) && !defined(__CYGWIN10__))
08b7c251 63 #include <commctrl.h>
2bda0e17
KB
64#endif
65
66// Bug in headers, sometimes
67#ifndef TVIS_FOCUSED
08b7c251 68 #define TVIS_FOCUSED 0x0001
2bda0e17
KB
69#endif
70
bb448552
VZ
71#ifndef TV_FIRST
72 #define TV_FIRST 0x1100
73#endif
74
b94ae1ea
VZ
75#ifndef TVS_CHECKBOXES
76 #define TVS_CHECKBOXES 0x0100
77#endif
78
bb448552
VZ
79// old headers might miss these messages (comctl32.dll 4.71+ only)
80#ifndef TVM_SETBKCOLOR
81 #define TVM_SETBKCOLOR (TV_FIRST + 29)
82 #define TVM_SETTEXTCOLOR (TV_FIRST + 30)
83#endif
84
3f7bc32b
VZ
85// a macro to hide the ugliness of nested casts
86#define HITEM(item) (HTREEITEM)(WXHTREEITEM)(item)
87
88// the native control doesn't support multiple selections under MSW and we
89// have 2 ways to emulate them: either using TVS_CHECKBOXES style and let
90// checkboxes be the selection status (checked == selected) or by really
91// emulating everything, i.e. intercepting mouse and key events &c. The first
92// approach is much easier but doesn't work with comctl32.dll < 4.71 and also
93// looks quite ugly.
94#define wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE 0
95
2e4df4bf 96
3f7bc32b
VZ
97// ----------------------------------------------------------------------------
98// private functions
99// ----------------------------------------------------------------------------
100
101// wrapper for TreeView_HitTest
102static HTREEITEM GetItemFromPoint(HWND hwndTV, int x, int y)
103{
104 TV_HITTESTINFO tvht;
105 tvht.pt.x = x;
106 tvht.pt.y = y;
107
3f7bc32b
VZ
108 return (HTREEITEM)TreeView_HitTest(hwndTV, &tvht);
109}
110
111#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
112
113// wrappers for TreeView_GetItem/TreeView_SetItem
114static bool IsItemSelected(HWND hwndTV, HTREEITEM hItem)
115{
116 TV_ITEM tvi;
117 tvi.mask = TVIF_STATE | TVIF_HANDLE;
118 tvi.stateMask = TVIS_SELECTED;
119 tvi.hItem = hItem;
120
121 if ( !TreeView_GetItem(hwndTV, &tvi) )
122 {
f6bcfd97 123 wxLogLastError(wxT("TreeView_GetItem"));
3f7bc32b
VZ
124 }
125
126 return (tvi.state & TVIS_SELECTED) != 0;
127}
128
129static void SelectItem(HWND hwndTV, HTREEITEM hItem, bool select = TRUE)
130{
131 TV_ITEM tvi;
132 tvi.mask = TVIF_STATE | TVIF_HANDLE;
133 tvi.stateMask = TVIS_SELECTED;
134 tvi.state = select ? TVIS_SELECTED : 0;
135 tvi.hItem = hItem;
136
137 if ( TreeView_SetItem(hwndTV, &tvi) == -1 )
138 {
f6bcfd97 139 wxLogLastError(wxT("TreeView_SetItem"));
3f7bc32b
VZ
140 }
141}
142
143static inline void UnselectItem(HWND hwndTV, HTREEITEM htItem)
144{
145 SelectItem(hwndTV, htItem, FALSE);
146}
147
148static inline void ToggleItemSelection(HWND hwndTV, HTREEITEM htItem)
149{
150 SelectItem(hwndTV, htItem, !IsItemSelected(hwndTV, htItem));
151}
152
153// helper function which selects all items in a range and, optionally,
154// unselects all others
155static void SelectRange(HWND hwndTV,
156 HTREEITEM htFirst,
157 HTREEITEM htLast,
158 bool unselectOthers = TRUE)
159{
160 // find the first (or last) item and select it
161 bool cont = TRUE;
2c8e4738 162 HTREEITEM htItem = (HTREEITEM)TreeView_GetRoot(hwndTV);
3f7bc32b
VZ
163 while ( htItem && cont )
164 {
165 if ( (htItem == htFirst) || (htItem == htLast) )
166 {
167 if ( !IsItemSelected(hwndTV, htItem) )
168 {
169 SelectItem(hwndTV, htItem);
170 }
171
172 cont = FALSE;
173 }
174 else
175 {
176 if ( unselectOthers && IsItemSelected(hwndTV, htItem) )
177 {
178 UnselectItem(hwndTV, htItem);
179 }
180 }
181
2c8e4738 182 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
183 }
184
185 // select the items in range
186 cont = htFirst != htLast;
187 while ( htItem && cont )
188 {
189 if ( !IsItemSelected(hwndTV, htItem) )
190 {
191 SelectItem(hwndTV, htItem);
192 }
193
194 cont = (htItem != htFirst) && (htItem != htLast);
195
2c8e4738 196 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
197 }
198
199 // unselect the rest
200 if ( unselectOthers )
201 {
202 while ( htItem )
203 {
204 if ( IsItemSelected(hwndTV, htItem) )
205 {
206 UnselectItem(hwndTV, htItem);
207 }
208
2c8e4738 209 htItem = (HTREEITEM)TreeView_GetNextVisible(hwndTV, htItem);
3f7bc32b
VZ
210 }
211 }
212
213 // seems to be necessary - otherwise the just selected items don't always
214 // appear as selected
215 UpdateWindow(hwndTV);
216}
217
218// helper function which tricks the standard control into changing the focused
219// item without changing anything else (if someone knows why Microsoft doesn't
220// allow to do it by just setting TVIS_FOCUSED flag, please tell me!)
221static void SetFocus(HWND hwndTV, HTREEITEM htItem)
222{
223 // the current focus
2c8e4738 224 HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV);
3f7bc32b
VZ
225
226 if ( htItem )
227 {
228 // set the focus
229 if ( htItem != htFocus )
230 {
231 // remember the selection state of the item
232 bool wasSelected = IsItemSelected(hwndTV, htItem);
233
234 if ( htFocus && IsItemSelected(hwndTV, htFocus) )
235 {
236 // prevent the tree from unselecting the old focus which it
237 // would do by default (TreeView_SelectItem unselects the
238 // focused item)
239 TreeView_SelectItem(hwndTV, 0);
240 SelectItem(hwndTV, htFocus);
241 }
242
243 TreeView_SelectItem(hwndTV, htItem);
244
245 if ( !wasSelected )
246 {
247 // need to clear the selection which TreeView_SelectItem() gave
248 // us
249 UnselectItem(hwndTV, htItem);
250 }
251 //else: was selected, still selected - ok
252 }
253 //else: nothing to do, focus already there
254 }
255 else
256 {
257 if ( htFocus )
258 {
259 bool wasFocusSelected = IsItemSelected(hwndTV, htFocus);
260
261 // just clear the focus
262 TreeView_SelectItem(hwndTV, 0);
263
264 if ( wasFocusSelected )
265 {
266 // restore the selection state
267 SelectItem(hwndTV, htFocus);
268 }
269 }
270 //else: nothing to do, no focus already
271 }
272}
273
274#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
275
08b7c251
VZ
276// ----------------------------------------------------------------------------
277// private classes
278// ----------------------------------------------------------------------------
2bda0e17 279
08b7c251 280// a convenient wrapper around TV_ITEM struct which adds a ctor
f3ef286f 281#ifdef __VISUALC__
3f7bc32b 282#pragma warning( disable : 4097 ) // inheriting from typedef
f3ef286f
JS
283#endif
284
08b7c251
VZ
285struct wxTreeViewItem : public TV_ITEM
286{
9dfbf520
VZ
287 wxTreeViewItem(const wxTreeItemId& item, // the item handle
288 UINT mask_, // fields which are valid
289 UINT stateMask_ = 0) // for TVIF_STATE only
08b7c251 290 {
9dfbf520
VZ
291 // hItem member is always valid
292 mask = mask_ | TVIF_HANDLE;
08b7c251 293 stateMask = stateMask_;
3f7bc32b 294 hItem = HITEM(item);
08b7c251
VZ
295 }
296};
f3ef286f
JS
297
298#ifdef __VISUALC__
197dd9af 299#pragma warning( default : 4097 )
f3ef286f 300#endif
2bda0e17 301
9dfbf520
VZ
302// a class which encapsulates the tree traversal logic: it vists all (unless
303// OnVisit() returns FALSE) items under the given one
304class wxTreeTraversal
305{
306public:
307 wxTreeTraversal(const wxTreeCtrl *tree)
308 {
309 m_tree = tree;
310 }
311
312 // do traverse the tree: visit all items (recursively by default) under the
313 // given one; return TRUE if all items were traversed or FALSE if the
314 // traversal was aborted because OnVisit returned FALSE
315 bool DoTraverse(const wxTreeItemId& root, bool recursively = TRUE);
316
317 // override this function to do whatever is needed for each item, return
318 // FALSE to stop traversing
319 virtual bool OnVisit(const wxTreeItemId& item) = 0;
320
321protected:
322 const wxTreeCtrl *GetTree() const { return m_tree; }
323
324private:
325 bool Traverse(const wxTreeItemId& root, bool recursively);
326
327 const wxTreeCtrl *m_tree;
328};
329
74b31181
VZ
330// internal class for getting the selected items
331class TraverseSelections : public wxTreeTraversal
332{
333public:
334 TraverseSelections(const wxTreeCtrl *tree,
335 wxArrayTreeItemIds& selections)
336 : wxTreeTraversal(tree), m_selections(selections)
337 {
338 m_selections.Empty();
339
340 DoTraverse(tree->GetRootItem());
341 }
342
343 virtual bool OnVisit(const wxTreeItemId& item)
344 {
3f7bc32b 345#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
74b31181 346 if ( GetTree()->IsItemChecked(item) )
3f7bc32b
VZ
347#else
348 if ( ::IsItemSelected(GetHwndOf(GetTree()), HITEM(item)) )
349#endif
74b31181
VZ
350 {
351 m_selections.Add(item);
352 }
353
354 return TRUE;
355 }
356
e47c4d48
VZ
357 size_t GetCount() const { return m_selections.GetCount(); }
358
74b31181
VZ
359private:
360 wxArrayTreeItemIds& m_selections;
361};
362
363// internal class for counting tree items
364class TraverseCounter : public wxTreeTraversal
365{
366public:
367 TraverseCounter(const wxTreeCtrl *tree,
368 const wxTreeItemId& root,
369 bool recursively)
370 : wxTreeTraversal(tree)
371 {
372 m_count = 0;
373
374 DoTraverse(root, recursively);
375 }
376
33ac7e6f 377 virtual bool OnVisit(const wxTreeItemId& WXUNUSED(item))
74b31181
VZ
378 {
379 m_count++;
380
381 return TRUE;
382 }
383
384 size_t GetCount() const { return m_count; }
385
386private:
387 size_t m_count;
388};
389
390// ----------------------------------------------------------------------------
391// This class is needed for support of different images: the Win32 common
392// control natively supports only 2 images (the normal one and another for the
393// selected state). We wish to provide support for 2 more of them for folder
394// items (i.e. those which have children): for expanded state and for expanded
395// selected state. For this we use this structure to store the additional items
396// images.
397//
398// There is only one problem with this: when we retrieve the item's data, we
399// don't know whether we get a pointer to wxTreeItemData or
400// wxTreeItemIndirectData. So we have to maintain a list of all items which
401// have indirect data inside the listctrl itself.
402// ----------------------------------------------------------------------------
696e1ea0 403
74b31181
VZ
404class wxTreeItemIndirectData
405{
406public:
407 // ctor associates this data with the item and the real item data becomes
408 // available through our GetData() method
409 wxTreeItemIndirectData(wxTreeCtrl *tree, const wxTreeItemId& item)
410 {
411 for ( size_t n = 0; n < WXSIZEOF(m_images); n++ )
412 {
413 m_images[n] = -1;
414 }
415
416 // save the old data
417 m_data = tree->GetItemData(item);
418
419 // and set ourselves as the new one
420 tree->SetIndirectItemData(item, this);
421 }
422
423 // dtor deletes the associated data as well
424 ~wxTreeItemIndirectData() { delete m_data; }
425
426 // accessors
427 // get the real data associated with the item
428 wxTreeItemData *GetData() const { return m_data; }
429 // change it
430 void SetData(wxTreeItemData *data) { m_data = data; }
431
432 // do we have such image?
433 bool HasImage(wxTreeItemIcon which) const { return m_images[which] != -1; }
434 // get image
435 int GetImage(wxTreeItemIcon which) const { return m_images[which]; }
436 // change it
437 void SetImage(int image, wxTreeItemIcon which) { m_images[which] = image; }
438
439private:
440 // all the images associated with the item
441 int m_images[wxTreeItemIcon_Max];
442
443 wxTreeItemData *m_data;
444};
445
23f681ec 446// ----------------------------------------------------------------------------
3f7bc32b 447// wxWin macros
08b7c251
VZ
448// ----------------------------------------------------------------------------
449
23f681ec 450IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl)
2bda0e17 451
08b7c251
VZ
452// ----------------------------------------------------------------------------
453// variables
454// ----------------------------------------------------------------------------
455
456// handy table for sending events
9ab5ef13 457static wxEventType g_events[2][2] =
2bda0e17 458{
08b7c251
VZ
459 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING },
460 { wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxEVT_COMMAND_TREE_ITEM_EXPANDING }
461};
462
463// ============================================================================
464// implementation
465// ============================================================================
466
9dfbf520
VZ
467// ----------------------------------------------------------------------------
468// tree traversal
469// ----------------------------------------------------------------------------
470
471bool wxTreeTraversal::DoTraverse(const wxTreeItemId& root, bool recursively)
472{
473 if ( !OnVisit(root) )
474 return FALSE;
475
476 return Traverse(root, recursively);
477}
478
479bool wxTreeTraversal::Traverse(const wxTreeItemId& root, bool recursively)
480{
481 long cookie;
482 wxTreeItemId child = m_tree->GetFirstChild(root, cookie);
483 while ( child.IsOk() )
484 {
485 // depth first traversal
486 if ( recursively && !Traverse(child, TRUE) )
487 return FALSE;
488
489 if ( !OnVisit(child) )
490 return FALSE;
491
492 child = m_tree->GetNextChild(root, cookie);
493 }
494
495 return TRUE;
496}
497
08b7c251
VZ
498// ----------------------------------------------------------------------------
499// construction and destruction
500// ----------------------------------------------------------------------------
501
502void wxTreeCtrl::Init()
503{
504 m_imageListNormal = NULL;
505 m_imageListState = NULL;
0377efa2 506 m_ownsImageListNormal = m_ownsImageListState = FALSE;
08b7c251 507 m_textCtrl = NULL;
696e1ea0 508 m_hasAnyAttr = FALSE;
23f681ec 509 m_dragImage = NULL;
3f7bc32b 510 m_htSelStart = 0;
9ab5ef13
JS
511
512 // Initialize static array of events, because with the new event system,
513 // they may not be initialized yet.
514
515 g_events[0][0] = wxEVT_COMMAND_TREE_ITEM_COLLAPSED;
516 g_events[0][1] = wxEVT_COMMAND_TREE_ITEM_COLLAPSING;
517 g_events[1][0] = wxEVT_COMMAND_TREE_ITEM_EXPANDED;
518 g_events[1][1] = wxEVT_COMMAND_TREE_ITEM_EXPANDING;
2bda0e17
KB
519}
520
9dfbf520
VZ
521bool wxTreeCtrl::Create(wxWindow *parent,
522 wxWindowID id,
523 const wxPoint& pos,
524 const wxSize& size,
525 long style,
526 const wxValidator& validator,
08b7c251 527 const wxString& name)
2bda0e17 528{
08b7c251 529 Init();
2bda0e17 530
9dfbf520
VZ
531 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
532 return FALSE;
2bda0e17 533
5ea47806 534 DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
b0766406
JS
535 TVS_SHOWSELALWAYS;
536
537 if ( m_windowStyle & wxCLIP_SIBLINGS )
538 wstyle |= WS_CLIPSIBLINGS;
2bda0e17 539
63da7df7
JS
540 if ((m_windowStyle & wxTR_NO_LINES) == 0)
541 wstyle |= TVS_HASLINES;
08b7c251
VZ
542 if ( m_windowStyle & wxTR_HAS_BUTTONS )
543 wstyle |= TVS_HASBUTTONS;
2bda0e17 544
08b7c251
VZ
545 if ( m_windowStyle & wxTR_EDIT_LABELS )
546 wstyle |= TVS_EDITLABELS;
2bda0e17 547
08b7c251
VZ
548 if ( m_windowStyle & wxTR_LINES_AT_ROOT )
549 wstyle |= TVS_LINESATROOT;
c6f4913a
VS
550
551 if ( m_windowStyle & wxTR_FULL_ROW_HIGHLIGHT )
552 {
553 if ( wxTheApp->GetComCtl32Version() >= 471 )
554 wstyle |= TVS_FULLROWSELECT;
555 }
556
2bda0e17 557
3f7bc32b
VZ
558 // using TVS_CHECKBOXES for emulation of a multiselection tree control
559 // doesn't work without the new enough headers
560#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE && \
561 !defined( __GNUWIN32_OLD__ ) && \
c42404a5
VZ
562 !defined( __BORLANDC__ ) && \
563 !defined( __WATCOMC__ ) && \
564 (!defined(__VISUALC__) || (__VISUALC__ > 1010))
a20a10fe 565
9dfbf520
VZ
566 // we emulate the multiple selection tree controls by using checkboxes: set
567 // up the image list we need for this if we do have multiple selections
568 if ( m_windowStyle & wxTR_MULTIPLE )
10fcf31a 569 wstyle |= TVS_CHECKBOXES;
3f7bc32b 570#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520 571
08b7c251 572 // Create the tree control.
9dfbf520
VZ
573 if ( !MSWCreateControl(WC_TREEVIEW, wstyle) )
574 return FALSE;
575
f6bcfd97
BP
576#if wxUSE_COMCTL32_SAFELY
577 wxWindow::SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
578 wxWindow::SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
579#elif 1
5aeeab14 580 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
f6bcfd97 581 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
fbdcff4a
JS
582#else
583 // This works around a bug in the Windows tree control whereby for some versions
584 // of comctrl32, setting any colour actually draws the background in black.
585 // This will initialise the background to the system colour.
f6bcfd97
BP
586 // THIS FIX NOW REVERTED since it caused problems on _other_ systems.
587 // Assume the user has an updated comctl32.dll.
fbdcff4a
JS
588 ::SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0,-1);
589 wxWindow::SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
f6bcfd97 590 SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
fbdcff4a
JS
591#endif
592
5aeeab14 593
9dfbf520
VZ
594 // VZ: this is some experimental code which may be used to get the
595 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
596 // AFAIK, the standard DLL does about the same thing anyhow.
597#if 0
598 if ( m_windowStyle & wxTR_MULTIPLE )
599 {
600 wxBitmap bmp;
601
602 // create the DC compatible with the current screen
603 HDC hdcMem = CreateCompatibleDC(NULL);
604
605 // create a mono bitmap of the standard size
606 int x = GetSystemMetrics(SM_CXMENUCHECK);
607 int y = GetSystemMetrics(SM_CYMENUCHECK);
608 wxImageList imagelistCheckboxes(x, y, FALSE, 2);
609 HBITMAP hbmpCheck = CreateBitmap(x, y, // bitmap size
610 1, // # of color planes
611 1, // # bits needed for one pixel
612 0); // array containing colour data
613 SelectObject(hdcMem, hbmpCheck);
614
615 // then draw a check mark into it
616 RECT rect = { 0, 0, x, y };
617 if ( !::DrawFrameControl(hdcMem, &rect,
618 DFC_BUTTON,
619 DFCS_BUTTONCHECK | DFCS_CHECKED) )
620 {
223d09f6 621 wxLogLastError(wxT("DrawFrameControl(check)"));
9dfbf520
VZ
622 }
623
624 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
625 imagelistCheckboxes.Add(bmp);
626
627 if ( !::DrawFrameControl(hdcMem, &rect,
628 DFC_BUTTON,
629 DFCS_BUTTONCHECK) )
630 {
223d09f6 631 wxLogLastError(wxT("DrawFrameControl(uncheck)"));
9dfbf520
VZ
632 }
633
634 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
635 imagelistCheckboxes.Add(bmp);
636
637 // clean up
638 ::DeleteDC(hdcMem);
639
640 // set the imagelist
641 SetStateImageList(&imagelistCheckboxes);
642 }
643#endif // 0
644
645 SetSize(pos.x, pos.y, size.x, size.y);
2bda0e17 646
08b7c251 647 return TRUE;
2bda0e17
KB
648}
649
08b7c251 650wxTreeCtrl::~wxTreeCtrl()
2bda0e17 651{
696e1ea0
VZ
652 // delete any attributes
653 if ( m_hasAnyAttr )
654 {
655 for ( wxNode *node = m_attrs.Next(); node; node = m_attrs.Next() )
656 {
657 delete (wxTreeItemAttr *)node->Data();
658 }
659
660 // prevent TVN_DELETEITEM handler from deleting the attributes again!
661 m_hasAnyAttr = FALSE;
662 }
663
08b7c251 664 DeleteTextCtrl();
2bda0e17 665
08b7c251
VZ
666 // delete user data to prevent memory leaks
667 DeleteAllItems();
33ac7e6f 668
0377efa2
VS
669 if (m_ownsImageListNormal) delete m_imageListNormal;
670 if (m_ownsImageListState) delete m_imageListState;
2bda0e17
KB
671}
672
08b7c251
VZ
673// ----------------------------------------------------------------------------
674// accessors
675// ----------------------------------------------------------------------------
2bda0e17 676
08b7c251 677// simple wrappers which add error checking in debug mode
2bda0e17 678
08b7c251 679bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const
2bda0e17 680{
d220ae32 681 if ( !TreeView_GetItem(GetHwnd(), tvItem) )
2bda0e17 682 {
f6bcfd97 683 wxLogLastError(wxT("TreeView_GetItem"));
08b7c251
VZ
684
685 return FALSE;
686 }
687
688 return TRUE;
2bda0e17
KB
689}
690
08b7c251 691void wxTreeCtrl::DoSetItem(wxTreeViewItem* tvItem)
2bda0e17 692{
d220ae32 693 if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
2bda0e17 694 {
f6bcfd97 695 wxLogLastError(wxT("TreeView_SetItem"));
08b7c251 696 }
2bda0e17
KB
697}
698
08b7c251 699size_t wxTreeCtrl::GetCount() const
2bda0e17 700{
d220ae32 701 return (size_t)TreeView_GetCount(GetHwnd());
2bda0e17
KB
702}
703
08b7c251 704unsigned int wxTreeCtrl::GetIndent() const
2bda0e17 705{
d220ae32 706 return TreeView_GetIndent(GetHwnd());
2bda0e17
KB
707}
708
08b7c251 709void wxTreeCtrl::SetIndent(unsigned int indent)
2bda0e17 710{
d220ae32 711 TreeView_SetIndent(GetHwnd(), indent);
2bda0e17
KB
712}
713
08b7c251 714wxImageList *wxTreeCtrl::GetImageList() const
2bda0e17 715{
08b7c251 716 return m_imageListNormal;
2bda0e17
KB
717}
718
08b7c251 719wxImageList *wxTreeCtrl::GetStateImageList() const
2bda0e17 720{
08b7c251 721 return m_imageListNormal;
2bda0e17
KB
722}
723
08b7c251 724void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which)
2bda0e17 725{
08b7c251 726 // no error return
d220ae32 727 TreeView_SetImageList(GetHwnd(),
08b7c251
VZ
728 imageList ? imageList->GetHIMAGELIST() : 0,
729 which);
2bda0e17
KB
730}
731
08b7c251 732void wxTreeCtrl::SetImageList(wxImageList *imageList)
2bda0e17 733{
0377efa2 734 if (m_ownsImageListNormal) delete m_imageListNormal;
08b7c251 735 SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
0377efa2 736 m_ownsImageListNormal = FALSE;
2bda0e17
KB
737}
738
08b7c251 739void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
2bda0e17 740{
0377efa2 741 if (m_ownsImageListState) delete m_imageListState;
08b7c251 742 SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
0377efa2
VS
743 m_ownsImageListState = FALSE;
744}
745
746void wxTreeCtrl::AssignImageList(wxImageList *imageList)
747{
748 SetImageList(imageList);
749 m_ownsImageListNormal = TRUE;
750}
751
752void wxTreeCtrl::AssignStateImageList(wxImageList *imageList)
753{
754 SetStateImageList(imageList);
755 m_ownsImageListState = TRUE;
2bda0e17
KB
756}
757
33961d59
RR
758size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
759 bool recursively) const
760{
761 TraverseCounter counter(this, item, recursively);
23fd5130 762
73974df1 763 return counter.GetCount() - 1;
23fd5130
VZ
764}
765
bb448552
VZ
766// ----------------------------------------------------------------------------
767// control colours
768// ----------------------------------------------------------------------------
769
770bool wxTreeCtrl::SetBackgroundColour(const wxColour &colour)
771{
f6bcfd97 772#if !wxUSE_COMCTL32_SAFELY
bb448552
VZ
773 if ( !wxWindowBase::SetBackgroundColour(colour) )
774 return FALSE;
775
776 SendMessage(GetHwnd(), TVM_SETBKCOLOR, 0, colour.GetPixel());
f6bcfd97 777#endif
bb448552
VZ
778
779 return TRUE;
780}
781
782bool wxTreeCtrl::SetForegroundColour(const wxColour &colour)
783{
f6bcfd97 784#if !wxUSE_COMCTL32_SAFELY
bb448552
VZ
785 if ( !wxWindowBase::SetForegroundColour(colour) )
786 return FALSE;
787
788 SendMessage(GetHwnd(), TVM_SETTEXTCOLOR, 0, colour.GetPixel());
f6bcfd97 789#endif
bb448552
VZ
790
791 return TRUE;
792}
793
08b7c251
VZ
794// ----------------------------------------------------------------------------
795// Item access
796// ----------------------------------------------------------------------------
797
798wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
2bda0e17 799{
837e5743 800 wxChar buf[512]; // the size is arbitrary...
02ce7b72 801
08b7c251
VZ
802 wxTreeViewItem tvItem(item, TVIF_TEXT);
803 tvItem.pszText = buf;
804 tvItem.cchTextMax = WXSIZEOF(buf);
805 if ( !DoGetItem(&tvItem) )
806 {
807 // don't return some garbage which was on stack, but an empty string
223d09f6 808 buf[0] = wxT('\0');
08b7c251 809 }
2bda0e17 810
08b7c251
VZ
811 return wxString(buf);
812}
2bda0e17 813
08b7c251
VZ
814void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
815{
816 wxTreeViewItem tvItem(item, TVIF_TEXT);
837e5743 817 tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok
08b7c251
VZ
818 DoSetItem(&tvItem);
819}
2bda0e17 820
74b31181
VZ
821int wxTreeCtrl::DoGetItemImageFromData(const wxTreeItemId& item,
822 wxTreeItemIcon which) const
823{
824 wxTreeViewItem tvItem(item, TVIF_PARAM);
825 if ( !DoGetItem(&tvItem) )
826 {
827 return -1;
828 }
829
830 return ((wxTreeItemIndirectData *)tvItem.lParam)->GetImage(which);
831}
832
833void wxTreeCtrl::DoSetItemImageFromData(const wxTreeItemId& item,
834 int image,
835 wxTreeItemIcon which) const
836{
837 wxTreeViewItem tvItem(item, TVIF_PARAM);
838 if ( !DoGetItem(&tvItem) )
839 {
840 return;
841 }
842
843 wxTreeItemIndirectData *data = ((wxTreeItemIndirectData *)tvItem.lParam);
844
845 data->SetImage(image, which);
846
847 // make sure that we have selected images as well
848 if ( which == wxTreeItemIcon_Normal &&
849 !data->HasImage(wxTreeItemIcon_Selected) )
850 {
851 data->SetImage(image, wxTreeItemIcon_Selected);
852 }
853
854 if ( which == wxTreeItemIcon_Expanded &&
855 !data->HasImage(wxTreeItemIcon_SelectedExpanded) )
856 {
857 data->SetImage(image, wxTreeItemIcon_SelectedExpanded);
858 }
859}
860
9dfbf520
VZ
861void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item,
862 int image,
863 int imageSel)
864{
865 wxTreeViewItem tvItem(item, TVIF_IMAGE | TVIF_SELECTEDIMAGE);
866 tvItem.iSelectedImage = imageSel;
867 tvItem.iImage = image;
868 DoSetItem(&tvItem);
869}
870
74b31181
VZ
871int wxTreeCtrl::GetItemImage(const wxTreeItemId& item,
872 wxTreeItemIcon which) const
08b7c251 873{
74b31181
VZ
874 if ( HasIndirectData(item) )
875 {
876 return DoGetItemImageFromData(item, which);
877 }
2bda0e17 878
74b31181
VZ
879 UINT mask;
880 switch ( which )
881 {
882 default:
223d09f6 883 wxFAIL_MSG( wxT("unknown tree item image type") );
2bda0e17 884
74b31181
VZ
885 case wxTreeItemIcon_Normal:
886 mask = TVIF_IMAGE;
887 break;
2bda0e17 888
74b31181
VZ
889 case wxTreeItemIcon_Selected:
890 mask = TVIF_SELECTEDIMAGE;
891 break;
892
893 case wxTreeItemIcon_Expanded:
894 case wxTreeItemIcon_SelectedExpanded:
895 return -1;
896 }
897
898 wxTreeViewItem tvItem(item, mask);
08b7c251 899 DoGetItem(&tvItem);
2bda0e17 900
74b31181 901 return mask == TVIF_IMAGE ? tvItem.iImage : tvItem.iSelectedImage;
2bda0e17
KB
902}
903
74b31181
VZ
904void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image,
905 wxTreeItemIcon which)
2bda0e17 906{
74b31181
VZ
907 int imageNormal, imageSel;
908 switch ( which )
909 {
910 default:
223d09f6 911 wxFAIL_MSG( wxT("unknown tree item image type") );
74b31181
VZ
912
913 case wxTreeItemIcon_Normal:
914 imageNormal = image;
915 imageSel = GetItemSelectedImage(item);
916 break;
917
918 case wxTreeItemIcon_Selected:
919 imageNormal = GetItemImage(item);
920 imageSel = image;
921 break;
922
923 case wxTreeItemIcon_Expanded:
924 case wxTreeItemIcon_SelectedExpanded:
925 if ( !HasIndirectData(item) )
926 {
927 // we need to get the old images first, because after we create
928 // the wxTreeItemIndirectData GetItemXXXImage() will use it to
929 // get the images
930 imageNormal = GetItemImage(item);
931 imageSel = GetItemSelectedImage(item);
932
933 // if it doesn't have it yet, add it
934 wxTreeItemIndirectData *data = new
935 wxTreeItemIndirectData(this, item);
936
937 // copy the data to the new location
938 data->SetImage(imageNormal, wxTreeItemIcon_Normal);
939 data->SetImage(imageSel, wxTreeItemIcon_Selected);
940 }
941
942 DoSetItemImageFromData(item, image, which);
943
944 // reset the normal/selected images because we won't use them any
945 // more - now they're stored inside the indirect data
946 imageNormal =
947 imageSel = I_IMAGECALLBACK;
948 break;
949 }
950
9dfbf520
VZ
951 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
952 // change both normal and selected image - otherwise the change simply
953 // doesn't take place!
74b31181 954 DoSetItemImages(item, imageNormal, imageSel);
2bda0e17
KB
955}
956
08b7c251 957wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
2bda0e17 958{
08b7c251
VZ
959 wxTreeViewItem tvItem(item, TVIF_PARAM);
960 if ( !DoGetItem(&tvItem) )
961 {
962 return NULL;
963 }
2bda0e17 964
74b31181
VZ
965 if ( HasIndirectData(item) )
966 {
967 return ((wxTreeItemIndirectData *)tvItem.lParam)->GetData();
968 }
969 else
970 {
971 return (wxTreeItemData *)tvItem.lParam;
972 }
2bda0e17
KB
973}
974
08b7c251 975void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
2bda0e17 976{
188781db
VZ
977 // first, associate this piece of data with this item
978 if ( data )
979 {
980 data->SetId(item);
981 }
982
08b7c251 983 wxTreeViewItem tvItem(item, TVIF_PARAM);
74b31181
VZ
984
985 if ( HasIndirectData(item) )
986 {
987 if ( DoGetItem(&tvItem) )
988 {
989 ((wxTreeItemIndirectData *)tvItem.lParam)->SetData(data);
990 }
991 else
992 {
223d09f6 993 wxFAIL_MSG( wxT("failed to change tree items data") );
74b31181
VZ
994 }
995 }
996 else
997 {
998 tvItem.lParam = (LPARAM)data;
999 DoSetItem(&tvItem);
1000 }
1001}
1002
1003void wxTreeCtrl::SetIndirectItemData(const wxTreeItemId& item,
1004 wxTreeItemIndirectData *data)
1005{
1006 // this should never happen because it's unnecessary and will probably lead
1007 // to crash too because the code elsewhere supposes that the pointer the
1008 // wxTreeItemIndirectData has is a real wxItemData and not
1009 // wxTreeItemIndirectData as well
223d09f6 1010 wxASSERT_MSG( !HasIndirectData(item), wxT("setting indirect data twice?") );
74b31181
VZ
1011
1012 SetItemData(item, (wxTreeItemData *)data);
1013
1014 m_itemsWithIndirectData.Add(item);
1015}
1016
1017bool wxTreeCtrl::HasIndirectData(const wxTreeItemId& item) const
1018{
1019 return m_itemsWithIndirectData.Index(item) != wxNOT_FOUND;
08b7c251 1020}
2bda0e17 1021
3a5a2f56
VZ
1022void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
1023{
1024 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1025 tvItem.cChildren = (int)has;
1026 DoSetItem(&tvItem);
1027}
1028
add28c55
VZ
1029void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
1030{
1031 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1032 tvItem.state = bold ? TVIS_BOLD : 0;
1033 DoSetItem(&tvItem);
1034}
1035
58a8ab88
JS
1036void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight)
1037{
1038 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
1039 tvItem.state = highlight ? TVIS_DROPHILITED : 0;
1040 DoSetItem(&tvItem);
1041}
1042
d00407b2
VZ
1043void wxTreeCtrl::RefreshItem(const wxTreeItemId& item)
1044{
1045 wxRect rect;
1046 if ( GetBoundingRect(item, rect) )
1047 {
1048 RefreshRect(rect);
1049 }
1050}
1051
696e1ea0
VZ
1052void wxTreeCtrl::SetItemTextColour(const wxTreeItemId& item,
1053 const wxColour& col)
1054{
1055 m_hasAnyAttr = TRUE;
1056
1057 long id = (long)(WXHTREEITEM)item;
1058 wxTreeItemAttr *attr = (wxTreeItemAttr *)m_attrs.Get(id);
1059 if ( !attr )
1060 {
1061 attr = new wxTreeItemAttr;
1062 m_attrs.Put(id, (wxObject *)attr);
1063 }
1064
1065 attr->SetTextColour(col);
d00407b2
VZ
1066
1067 RefreshItem(item);
696e1ea0
VZ
1068}
1069
1070void wxTreeCtrl::SetItemBackgroundColour(const wxTreeItemId& item,
1071 const wxColour& col)
1072{
1073 m_hasAnyAttr = TRUE;
1074
1075 long id = (long)(WXHTREEITEM)item;
1076 wxTreeItemAttr *attr = (wxTreeItemAttr *)m_attrs.Get(id);
1077 if ( !attr )
1078 {
1079 attr = new wxTreeItemAttr;
1080 m_attrs.Put(id, (wxObject *)attr);
1081 }
1082
1083 attr->SetBackgroundColour(col);
d00407b2
VZ
1084
1085 RefreshItem(item);
696e1ea0
VZ
1086}
1087
1088void wxTreeCtrl::SetItemFont(const wxTreeItemId& item, const wxFont& font)
1089{
1090 m_hasAnyAttr = TRUE;
1091
1092 long id = (long)(WXHTREEITEM)item;
1093 wxTreeItemAttr *attr = (wxTreeItemAttr *)m_attrs.Get(id);
1094 if ( !attr )
1095 {
1096 attr = new wxTreeItemAttr;
1097 m_attrs.Put(id, (wxObject *)attr);
1098 }
1099
1100 attr->SetFont(font);
d00407b2
VZ
1101
1102 RefreshItem(item);
696e1ea0
VZ
1103}
1104
08b7c251
VZ
1105// ----------------------------------------------------------------------------
1106// Item status
1107// ----------------------------------------------------------------------------
2bda0e17 1108
08b7c251
VZ
1109bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
1110{
add28c55 1111 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
08b7c251 1112 RECT rect;
955be36c
VZ
1113
1114 // this ugliness comes directly from MSDN - it *is* the correct way to pass
1115 // the HTREEITEM with TVM_GETITEMRECT
1116 *(WXHTREEITEM *)&rect = (WXHTREEITEM)item;
1117
1118 // FALSE means get item rect for the whole item, not only text
1c74a900 1119 return SendMessage(GetHwnd(), TVM_GETITEMRECT, FALSE, (LPARAM)&rect) != 0;
2bda0e17
KB
1120}
1121
08b7c251 1122bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
2bda0e17 1123{
08b7c251
VZ
1124 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
1125 DoGetItem(&tvItem);
2bda0e17 1126
08b7c251 1127 return tvItem.cChildren != 0;
2bda0e17
KB
1128}
1129
08b7c251 1130bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
2bda0e17 1131{
08b7c251
VZ
1132 // probably not a good idea to put it here
1133 //wxASSERT( ItemHasChildren(item) );
2bda0e17 1134
08b7c251
VZ
1135 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
1136 DoGetItem(&tvItem);
2bda0e17 1137
08b7c251 1138 return (tvItem.state & TVIS_EXPANDED) != 0;
2bda0e17
KB
1139}
1140
08b7c251 1141bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
2bda0e17 1142{
08b7c251
VZ
1143 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
1144 DoGetItem(&tvItem);
2bda0e17 1145
08b7c251 1146 return (tvItem.state & TVIS_SELECTED) != 0;
2bda0e17
KB
1147}
1148
add28c55
VZ
1149bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
1150{
1151 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
1152 DoGetItem(&tvItem);
1153
1154 return (tvItem.state & TVIS_BOLD) != 0;
1155}
1156
08b7c251
VZ
1157// ----------------------------------------------------------------------------
1158// navigation
1159// ----------------------------------------------------------------------------
2bda0e17 1160
08b7c251
VZ
1161wxTreeItemId wxTreeCtrl::GetRootItem() const
1162{
d220ae32 1163 return wxTreeItemId((WXHTREEITEM) TreeView_GetRoot(GetHwnd()));
08b7c251 1164}
2bda0e17 1165
08b7c251
VZ
1166wxTreeItemId wxTreeCtrl::GetSelection() const
1167{
ad90972f 1168 wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (long)(WXHTREEITEM)0,
223d09f6 1169 wxT("this only works with single selection controls") );
9dfbf520 1170
d220ae32 1171 return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd()));
2bda0e17
KB
1172}
1173
08b7c251 1174wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
2bda0e17 1175{
3f7bc32b 1176 return wxTreeItemId((WXHTREEITEM) TreeView_GetParent(GetHwnd(), HITEM(item)));
08b7c251 1177}
2bda0e17 1178
08b7c251 1179wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
06e38c8e 1180 long& _cookie) const
08b7c251
VZ
1181{
1182 // remember the last child returned in 'cookie'
3f7bc32b 1183 _cookie = (long)TreeView_GetChild(GetHwnd(), HITEM(item));
2bda0e17 1184
06e38c8e 1185 return wxTreeItemId((WXHTREEITEM)_cookie);
2bda0e17
KB
1186}
1187
08b7c251 1188wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
06e38c8e 1189 long& _cookie) const
2bda0e17 1190{
d220ae32 1191 wxTreeItemId l = wxTreeItemId((WXHTREEITEM)TreeView_GetNextSibling(GetHwnd(),
3f7bc32b 1192 HITEM(_cookie)));
23fd5130
VZ
1193 _cookie = (long)l;
1194
2e5dddb0 1195 return l;
08b7c251 1196}
2bda0e17 1197
978f38c2
VZ
1198wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
1199{
1200 // can this be done more efficiently?
1201 long cookie;
1202
1203 wxTreeItemId childLast,
2165ad93 1204 child = GetFirstChild(item, cookie);
978f38c2
VZ
1205 while ( child.IsOk() )
1206 {
1207 childLast = child;
2165ad93 1208 child = GetNextChild(item, cookie);
978f38c2
VZ
1209 }
1210
1211 return childLast;
1212}
1213
08b7c251
VZ
1214wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
1215{
3f7bc32b 1216 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextSibling(GetHwnd(), HITEM(item)));
2bda0e17
KB
1217}
1218
08b7c251 1219wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
2bda0e17 1220{
3f7bc32b 1221 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevSibling(GetHwnd(), HITEM(item)));
2bda0e17
KB
1222}
1223
08b7c251 1224wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
2bda0e17 1225{
d220ae32 1226 return wxTreeItemId((WXHTREEITEM) TreeView_GetFirstVisible(GetHwnd()));
2bda0e17
KB
1227}
1228
08b7c251 1229wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
2bda0e17 1230{
f6bcfd97 1231 wxASSERT_MSG( IsVisible(item), wxT("The item you call GetNextVisible() for must be visible itself!"));
02ce7b72 1232
3f7bc32b 1233 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), HITEM(item)));
08b7c251 1234}
02ce7b72 1235
08b7c251
VZ
1236wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
1237{
f6bcfd97 1238 wxASSERT_MSG( IsVisible(item), wxT("The item you call GetPrevVisible() for must be visible itself!"));
02ce7b72 1239
3f7bc32b 1240 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), HITEM(item)));
08b7c251 1241}
02ce7b72 1242
9dfbf520
VZ
1243// ----------------------------------------------------------------------------
1244// multiple selections emulation
1245// ----------------------------------------------------------------------------
1246
1247bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const
1248{
1249 // receive the desired information.
1250 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
1251 DoGetItem(&tvItem);
1252
1253 // state image indices are 1 based
1254 return ((tvItem.state >> 12) - 1) == 1;
1255}
1256
1257void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check)
1258{
1259 // receive the desired information.
1260 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
1261
1262 // state images are one-based
1263 tvItem.state = (check ? 2 : 1) << 12;
1264
1265 DoSetItem(&tvItem);
1266}
1267
33961d59
RR
1268size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const
1269{
1270 TraverseSelections selector(this, selections);
9dfbf520 1271
e47c4d48 1272 return selector.GetCount();
9dfbf520
VZ
1273}
1274
08b7c251
VZ
1275// ----------------------------------------------------------------------------
1276// Usual operations
1277// ----------------------------------------------------------------------------
02ce7b72 1278
08b7c251
VZ
1279wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
1280 wxTreeItemId hInsertAfter,
1281 const wxString& text,
1282 int image, int selectedImage,
1283 wxTreeItemData *data)
1284{
0e935169
VZ
1285 wxCHECK_MSG( parent.IsOk() || !TreeView_GetRoot(GetHwnd()),
1286 wxTreeItemId(),
1287 _T("can't have more than one root in the tree") );
1288
08b7c251 1289 TV_INSERTSTRUCT tvIns;
3f7bc32b
VZ
1290 tvIns.hParent = HITEM(parent);
1291 tvIns.hInsertAfter = HITEM(hInsertAfter);
58a8ab88 1292
74b31181
VZ
1293 // this is how we insert the item as the first child: supply a NULL
1294 // hInsertAfter
1295 if ( !tvIns.hInsertAfter )
58a8ab88
JS
1296 {
1297 tvIns.hInsertAfter = TVI_FIRST;
1298 }
1299
08b7c251
VZ
1300 UINT mask = 0;
1301 if ( !text.IsEmpty() )
1302 {
1303 mask |= TVIF_TEXT;
837e5743 1304 tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
08b7c251 1305 }
f6bcfd97
BP
1306 else
1307 {
1308 tvIns.item.pszText = NULL;
1309 tvIns.item.cchTextMax = 0;
1310 }
02ce7b72 1311
08b7c251
VZ
1312 if ( image != -1 )
1313 {
1314 mask |= TVIF_IMAGE;
1315 tvIns.item.iImage = image;
3a5a2f56 1316
6b037754 1317 if ( selectedImage == -1 )
3a5a2f56
VZ
1318 {
1319 // take the same image for selected icon if not specified
1320 selectedImage = image;
1321 }
08b7c251 1322 }
02ce7b72 1323
08b7c251
VZ
1324 if ( selectedImage != -1 )
1325 {
1326 mask |= TVIF_SELECTEDIMAGE;
1327 tvIns.item.iSelectedImage = selectedImage;
1328 }
02ce7b72 1329
08b7c251
VZ
1330 if ( data != NULL )
1331 {
1332 mask |= TVIF_PARAM;
1333 tvIns.item.lParam = (LPARAM)data;
1334 }
02ce7b72 1335
08b7c251 1336 tvIns.item.mask = mask;
02ce7b72 1337
d220ae32 1338 HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns);
08b7c251
VZ
1339 if ( id == 0 )
1340 {
f6bcfd97 1341 wxLogLastError(wxT("TreeView_InsertItem"));
08b7c251 1342 }
02ce7b72 1343
fd3f686c
VZ
1344 if ( data != NULL )
1345 {
1346 // associate the application tree item with Win32 tree item handle
1347 data->SetId((WXHTREEITEM)id);
1348 }
1349
06e38c8e 1350 return wxTreeItemId((WXHTREEITEM)id);
2bda0e17
KB
1351}
1352
08b7c251
VZ
1353// for compatibility only
1354wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1355 const wxString& text,
1356 int image, int selImage,
1357 long insertAfter)
2bda0e17 1358{
06e38c8e 1359 return DoInsertItem(parent, (WXHTREEITEM)insertAfter, text,
08b7c251 1360 image, selImage, NULL);
2bda0e17
KB
1361}
1362
08b7c251
VZ
1363wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
1364 int image, int selectedImage,
1365 wxTreeItemData *data)
2bda0e17 1366{
ad90972f 1367 return DoInsertItem(wxTreeItemId((long) (WXHTREEITEM) 0), (long)(WXHTREEITEM) 0,
08b7c251 1368 text, image, selectedImage, data);
2bda0e17
KB
1369}
1370
08b7c251
VZ
1371wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
1372 const wxString& text,
1373 int image, int selectedImage,
1374 wxTreeItemData *data)
2bda0e17 1375{
06e38c8e 1376 return DoInsertItem(parent, (WXHTREEITEM) TVI_FIRST,
08b7c251 1377 text, image, selectedImage, data);
2bda0e17
KB
1378}
1379
08b7c251
VZ
1380wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1381 const wxTreeItemId& idPrevious,
1382 const wxString& text,
1383 int image, int selectedImage,
1384 wxTreeItemData *data)
2bda0e17 1385{
08b7c251 1386 return DoInsertItem(parent, idPrevious, text, image, selectedImage, data);
2bda0e17
KB
1387}
1388
2ef31e80
VZ
1389wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
1390 size_t index,
1391 const wxString& text,
1392 int image, int selectedImage,
1393 wxTreeItemData *data)
1394{
1395 // find the item from index
1396 long cookie;
1397 wxTreeItemId idPrev, idCur = GetFirstChild(parent, cookie);
1398 while ( index != 0 && idCur.IsOk() )
1399 {
1400 index--;
1401
1402 idPrev = idCur;
1403 idCur = GetNextChild(parent, cookie);
1404 }
1405
1406 // assert, not check: if the index is invalid, we will append the item
1407 // to the end
1408 wxASSERT_MSG( index == 0, _T("bad index in wxTreeCtrl::InsertItem") );
1409
1410 return DoInsertItem(parent, idPrev, text, image, selectedImage, data);
1411}
1412
08b7c251
VZ
1413wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parent,
1414 const wxString& text,
1415 int image, int selectedImage,
1416 wxTreeItemData *data)
2bda0e17 1417{
06e38c8e 1418 return DoInsertItem(parent, (WXHTREEITEM) TVI_LAST,
08b7c251 1419 text, image, selectedImage, data);
2bda0e17
KB
1420}
1421
08b7c251 1422void wxTreeCtrl::Delete(const wxTreeItemId& item)
2bda0e17 1423{
3f7bc32b 1424 if ( !TreeView_DeleteItem(GetHwnd(), HITEM(item)) )
bbcdf8bc 1425 {
f6bcfd97 1426 wxLogLastError(wxT("TreeView_DeleteItem"));
bbcdf8bc 1427 }
bbcdf8bc
JS
1428}
1429
23fd5130
VZ
1430// delete all children (but don't delete the item itself)
1431void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
1432{
1433 long cookie;
1434
1435 wxArrayLong children;
1436 wxTreeItemId child = GetFirstChild(item, cookie);
1437 while ( child.IsOk() )
1438 {
1439 children.Add((long)(WXHTREEITEM)child);
1440
1441 child = GetNextChild(item, cookie);
1442 }
1443
1444 size_t nCount = children.Count();
1445 for ( size_t n = 0; n < nCount; n++ )
1446 {
d220ae32 1447 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)children[n]) )
23fd5130 1448 {
f6bcfd97 1449 wxLogLastError(wxT("TreeView_DeleteItem"));
23fd5130
VZ
1450 }
1451 }
1452}
1453
08b7c251 1454void wxTreeCtrl::DeleteAllItems()
bbcdf8bc 1455{
d220ae32 1456 if ( !TreeView_DeleteAllItems(GetHwnd()) )
bbcdf8bc 1457 {
f6bcfd97 1458 wxLogLastError(wxT("TreeView_DeleteAllItems"));
bbcdf8bc 1459 }
2bda0e17
KB
1460}
1461
08b7c251 1462void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
2bda0e17 1463{
dd3646fd
VZ
1464 wxASSERT_MSG( flag == TVE_COLLAPSE ||
1465 flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
1466 flag == TVE_EXPAND ||
1467 flag == TVE_TOGGLE,
223d09f6 1468 wxT("Unknown flag in wxTreeCtrl::DoExpand") );
08b7c251
VZ
1469
1470 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
d220ae32
VZ
1471 // emulate them. This behaviour has changed slightly with comctl32.dll
1472 // v 4.70 - now it does send them but only the first time. To maintain
1473 // compatible behaviour and also in order to not have surprises with the
1474 // future versions, don't rely on this and still do everything ourselves.
1475 // To avoid that the messages be sent twice when the item is expanded for
1476 // the first time we must clear TVIS_EXPANDEDONCE style manually.
1477
1478 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
1479 tvItem.state = 0;
1480 DoSetItem(&tvItem);
1481
3f7bc32b 1482 if ( TreeView_Expand(GetHwnd(), HITEM(item), flag) != 0 )
08b7c251
VZ
1483 {
1484 wxTreeEvent event(wxEVT_NULL, m_windowId);
1485 event.m_item = item;
1486
1487 bool isExpanded = IsExpanded(item);
2bda0e17 1488
08b7c251 1489 event.SetEventObject(this);
2bda0e17 1490
d220ae32 1491 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
08b7c251
VZ
1492 event.SetEventType(g_events[isExpanded][TRUE]);
1493 GetEventHandler()->ProcessEvent(event);
2bda0e17 1494
08b7c251
VZ
1495 event.SetEventType(g_events[isExpanded][FALSE]);
1496 GetEventHandler()->ProcessEvent(event);
1497 }
d220ae32 1498 //else: change didn't took place, so do nothing at all
2bda0e17
KB
1499}
1500
08b7c251 1501void wxTreeCtrl::Expand(const wxTreeItemId& item)
2bda0e17 1502{
08b7c251 1503 DoExpand(item, TVE_EXPAND);
2bda0e17 1504}
2bda0e17 1505
08b7c251 1506void wxTreeCtrl::Collapse(const wxTreeItemId& item)
2bda0e17 1507{
08b7c251 1508 DoExpand(item, TVE_COLLAPSE);
2bda0e17
KB
1509}
1510
08b7c251 1511void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
2bda0e17 1512{
dd3646fd 1513 DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
2bda0e17
KB
1514}
1515
08b7c251 1516void wxTreeCtrl::Toggle(const wxTreeItemId& item)
2bda0e17 1517{
08b7c251 1518 DoExpand(item, TVE_TOGGLE);
2bda0e17
KB
1519}
1520
42c5812d
UU
1521void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action)
1522{
9dfbf520 1523 DoExpand(item, action);
42c5812d
UU
1524}
1525
08b7c251 1526void wxTreeCtrl::Unselect()
2bda0e17 1527{
3f7bc32b
VZ
1528 wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE),
1529 wxT("doesn't make sense, may be you want UnselectAll()?") );
9dfbf520
VZ
1530
1531 // just remove the selection
ad90972f 1532 SelectItem(wxTreeItemId((long) (WXHTREEITEM) 0));
08b7c251 1533}
02ce7b72 1534
9dfbf520 1535void wxTreeCtrl::UnselectAll()
08b7c251 1536{
9dfbf520 1537 if ( m_windowStyle & wxTR_MULTIPLE )
2bda0e17 1538 {
9dfbf520
VZ
1539 wxArrayTreeItemIds selections;
1540 size_t count = GetSelections(selections);
1541 for ( size_t n = 0; n < count; n++ )
d220ae32 1542 {
3f7bc32b 1543#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520 1544 SetItemCheck(selections[n], FALSE);
3f7bc32b
VZ
1545#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
1546 ::UnselectItem(GetHwnd(), HITEM(selections[n]));
1547#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
d220ae32 1548 }
9dfbf520
VZ
1549 }
1550 else
1551 {
1552 // just remove the selection
1553 Unselect();
1554 }
1555}
1556
1557void wxTreeCtrl::SelectItem(const wxTreeItemId& item)
1558{
1559 if ( m_windowStyle & wxTR_MULTIPLE )
1560 {
3f7bc32b 1561#if wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520
VZ
1562 // selecting the item means checking it
1563 SetItemCheck(item);
3f7bc32b
VZ
1564#else // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
1565 ::SelectItem(GetHwnd(), HITEM(item));
1566#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE/!wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
9dfbf520
VZ
1567 }
1568 else
1569 {
1570 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
1571 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
1572 // send them ourselves
1573
1574 wxTreeEvent event(wxEVT_NULL, m_windowId);
1575 event.m_item = item;
1576 event.SetEventObject(this);
1577
1578 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING);
1579 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
d220ae32 1580 {
3f7bc32b 1581 if ( !TreeView_SelectItem(GetHwnd(), HITEM(item)) )
9dfbf520 1582 {
f6bcfd97 1583 wxLogLastError(wxT("TreeView_SelectItem"));
9dfbf520
VZ
1584 }
1585 else
1586 {
1587 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
1588 (void)GetEventHandler()->ProcessEvent(event);
1589 }
d220ae32 1590 }
9dfbf520 1591 //else: program vetoed the change
2bda0e17 1592 }
08b7c251 1593}
2bda0e17 1594
08b7c251
VZ
1595void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
1596{
1597 // no error return
3f7bc32b 1598 TreeView_EnsureVisible(GetHwnd(), HITEM(item));
08b7c251
VZ
1599}
1600
1601void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
1602{
3f7bc32b 1603 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), HITEM(item)) )
2bda0e17 1604 {
f6bcfd97 1605 wxLogLastError(wxT("TreeView_SelectSetFirstVisible"));
2bda0e17 1606 }
08b7c251
VZ
1607}
1608
1609wxTextCtrl* wxTreeCtrl::GetEditControl() const
1610{
b94ae1ea
VZ
1611 // normally, we could try to do something like this to return something
1612 // even when the editing was started by the user and not by calling
1613 // EditLabel() - but as nobody has asked for this so far and there might be
1614 // problems in the code below, I leave it disabled for now (VZ)
1615#if 0
1616 if ( !m_textCtrl )
1617 {
1618 HWND hwndText = TreeView_GetEditControl(GetHwnd());
1619 if ( hwndText )
1620 {
1621 m_textCtrl = new wxTextCtrl(this, -1);
1622 m_textCtrl->Hide();
1623 m_textCtrl->SetHWND((WXHWND)hwndText);
1624 }
1625 //else: not editing label right now
1626 }
1627#endif // 0
1628
08b7c251
VZ
1629 return m_textCtrl;
1630}
1631
1632void wxTreeCtrl::DeleteTextCtrl()
1633{
1634 if ( m_textCtrl )
2bda0e17 1635 {
b6a3d6ad
VZ
1636 // the HWND corresponding to this control is deleted by the tree
1637 // control itself and we don't know when exactly this happens, so check
1638 // if the window still exists before calling UnsubclassWin()
1639 if ( !::IsWindow(GetHwndOf(m_textCtrl)) )
1640 {
1641 m_textCtrl->SetHWND(0);
1642 }
1643
08b7c251
VZ
1644 m_textCtrl->UnsubclassWin();
1645 m_textCtrl->SetHWND(0);
1646 delete m_textCtrl;
1647 m_textCtrl = NULL;
2bda0e17 1648 }
08b7c251 1649}
2bda0e17 1650
08b7c251
VZ
1651wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item,
1652 wxClassInfo* textControlClass)
1653{
1654 wxASSERT( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)) );
1655
b94ae1ea
VZ
1656 DeleteTextCtrl();
1657
3f7bc32b 1658 HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), HITEM(item));
2bda0e17 1659
5ea47806
VZ
1660 // this is not an error - the TVN_BEGINLABELEDIT handler might have
1661 // returned FALSE
1662 if ( !hWnd )
1663 {
1664 return NULL;
1665 }
2bda0e17 1666
08b7c251 1667 m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
c8f14476 1668 m_textCtrl->SetParent(this);
08b7c251
VZ
1669 m_textCtrl->SetHWND((WXHWND)hWnd);
1670 m_textCtrl->SubclassWin((WXHWND)hWnd);
2bda0e17 1671
08b7c251 1672 return m_textCtrl;
2bda0e17
KB
1673}
1674
08b7c251 1675// End label editing, optionally cancelling the edit
33ac7e6f 1676void wxTreeCtrl::EndEditLabel(const wxTreeItemId& WXUNUSED(item), bool discardChanges)
2bda0e17 1677{
d220ae32 1678 TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
08b7c251
VZ
1679
1680 DeleteTextCtrl();
2bda0e17
KB
1681}
1682
08b7c251 1683wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
2bda0e17 1684{
08b7c251
VZ
1685 TV_HITTESTINFO hitTestInfo;
1686 hitTestInfo.pt.x = (int)point.x;
1687 hitTestInfo.pt.y = (int)point.y;
2bda0e17 1688
d220ae32 1689 TreeView_HitTest(GetHwnd(), &hitTestInfo);
2bda0e17 1690
08b7c251
VZ
1691 flags = 0;
1692
1693 // avoid repetition
1694 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
1695 flags |= wxTREE_HITTEST_##flag
1696
1697 TRANSLATE_FLAG(ABOVE);
1698 TRANSLATE_FLAG(BELOW);
1699 TRANSLATE_FLAG(NOWHERE);
1700 TRANSLATE_FLAG(ONITEMBUTTON);
1701 TRANSLATE_FLAG(ONITEMICON);
1702 TRANSLATE_FLAG(ONITEMINDENT);
1703 TRANSLATE_FLAG(ONITEMLABEL);
1704 TRANSLATE_FLAG(ONITEMRIGHT);
1705 TRANSLATE_FLAG(ONITEMSTATEICON);
1706 TRANSLATE_FLAG(TOLEFT);
1707 TRANSLATE_FLAG(TORIGHT);
2bda0e17 1708
08b7c251
VZ
1709 #undef TRANSLATE_FLAG
1710
06e38c8e 1711 return wxTreeItemId((WXHTREEITEM) hitTestInfo.hItem);
08b7c251
VZ
1712}
1713
f7c832a7
VZ
1714bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
1715 wxRect& rect,
1716 bool textOnly) const
1717{
1718 RECT rc;
3f7bc32b 1719 if ( TreeView_GetItemRect(GetHwnd(), HITEM(item),
f7c832a7
VZ
1720 &rc, textOnly) )
1721 {
1722 rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
1723
1724 return TRUE;
1725 }
1726 else
1727 {
1728 // couldn't retrieve rect: for example, item isn't visible
1729 return FALSE;
1730 }
1731}
1732
23fd5130
VZ
1733// ----------------------------------------------------------------------------
1734// sorting stuff
1735// ----------------------------------------------------------------------------
f7c832a7 1736
23fd5130
VZ
1737static int CALLBACK TreeView_CompareCallback(wxTreeItemData *pItem1,
1738 wxTreeItemData *pItem2,
1739 wxTreeCtrl *tree)
1740{
096c9f9b 1741 wxCHECK_MSG( pItem1 && pItem2, 0,
223d09f6 1742 wxT("sorting tree without data doesn't make sense") );
096c9f9b 1743
23fd5130
VZ
1744 return tree->OnCompareItems(pItem1->GetId(), pItem2->GetId());
1745}
1746
95aabccc
VZ
1747int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1748 const wxTreeItemId& item2)
08b7c251 1749{
837e5743 1750 return wxStrcmp(GetItemText(item1), GetItemText(item2));
95aabccc
VZ
1751}
1752
1753void wxTreeCtrl::SortChildren(const wxTreeItemId& item)
1754{
1755 // rely on the fact that TreeView_SortChildren does the same thing as our
23fd5130
VZ
1756 // default behaviour, i.e. sorts items alphabetically and so call it
1757 // directly if we're not in derived class (much more efficient!)
1758 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
2bda0e17 1759 {
3f7bc32b 1760 TreeView_SortChildren(GetHwnd(), HITEM(item), 0);
2bda0e17 1761 }
08b7c251 1762 else
2bda0e17 1763 {
62448488 1764 TV_SORTCB tvSort;
3f7bc32b 1765 tvSort.hParent = HITEM(item);
23fd5130
VZ
1766 tvSort.lpfnCompare = (PFNTVCOMPARE)TreeView_CompareCallback;
1767 tvSort.lParam = (LPARAM)this;
d220ae32 1768 TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */);
2bda0e17 1769 }
08b7c251 1770}
2bda0e17 1771
08b7c251
VZ
1772// ----------------------------------------------------------------------------
1773// implementation
1774// ----------------------------------------------------------------------------
2bda0e17 1775
08b7c251
VZ
1776bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id)
1777{
1778 if ( cmd == EN_UPDATE )
2bda0e17 1779 {
08b7c251
VZ
1780 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
1781 event.SetEventObject( this );
1782 ProcessCommand(event);
2bda0e17 1783 }
08b7c251 1784 else if ( cmd == EN_KILLFOCUS )
2bda0e17 1785 {
08b7c251
VZ
1786 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
1787 event.SetEventObject( this );
1788 ProcessCommand(event);
2bda0e17 1789 }
08b7c251 1790 else
2bda0e17 1791 {
08b7c251
VZ
1792 // nothing done
1793 return FALSE;
2bda0e17 1794 }
08b7c251
VZ
1795
1796 // command processed
1797 return TRUE;
1798}
1799
23f681ec
VZ
1800// we hook into WndProc to process WM_MOUSEMOVE/WM_BUTTONUP messages - as we
1801// only do it during dragging, minimize wxWin overhead (this is important for
1802// WM_MOUSEMOVE as they're a lot of them) by catching Windows messages directly
1803// instead of passing by wxWin events
1804long wxTreeCtrl::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
1805{
3f7bc32b
VZ
1806 bool processed = FALSE;
1807 long rc = 0;
3f7bc32b
VZ
1808 bool isMultiple = (GetWindowStyle() & wxTR_MULTIPLE) != 0;
1809
1810 if ( (nMsg >= WM_MOUSEFIRST) && (nMsg <= WM_MOUSELAST) )
23f681ec 1811 {
3f7bc32b
VZ
1812 // we only process mouse messages here and these parameters have the same
1813 // meaning for all of them
1814 int x = GET_X_LPARAM(lParam),
1815 y = GET_Y_LPARAM(lParam);
1816 HTREEITEM htItem = GetItemFromPoint(GetHwnd(), x, y);
1817
23f681ec
VZ
1818 switch ( nMsg )
1819 {
3f7bc32b
VZ
1820#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
1821 case WM_LBUTTONDOWN:
1822 if ( htItem && isMultiple )
23f681ec 1823 {
3f7bc32b
VZ
1824 if ( wParam & MK_CONTROL )
1825 {
1826 SetFocus();
23f681ec 1827
3f7bc32b
VZ
1828 // toggle selected state
1829 ToggleItemSelection(GetHwnd(), htItem);
1830
1831 ::SetFocus(GetHwnd(), htItem);
1832
1833 // reset on any click without Shift
1834 m_htSelStart = 0;
1835
1836 processed = TRUE;
1837 }
1838 else if ( wParam & MK_SHIFT )
1839 {
1840 // this selects all items between the starting one and
1841 // the current
1842
1843 if ( !m_htSelStart )
1844 {
1845 // take the focused item
1846 m_htSelStart = (WXHTREEITEM)
1847 TreeView_GetSelection(GetHwnd());
1848 }
1849
1850 SelectRange(GetHwnd(), HITEM(m_htSelStart), htItem,
1851 !(wParam & MK_CONTROL));
1852
1853 ::SetFocus(GetHwnd(), htItem);
23f681ec 1854
3f7bc32b
VZ
1855 processed = TRUE;
1856 }
1857 else // normal click
1858 {
1859 // clear the selection and then let the default handler
1860 // do the job
1861 UnselectAll();
1862
1863 // prevent the click from starting in-place editing
1864 // when there was no selection in the control
1865 TreeView_SelectItem(GetHwnd(), 0);
1866
1867 // reset on any click without Shift
1868 m_htSelStart = 0;
1869 }
1870 }
1871 break;
1872#endif // wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
1873
1874 case WM_MOUSEMOVE:
1875 if ( m_dragImage )
1876 {
afff720b 1877 m_dragImage->Move(wxPoint(x, y));
3f7bc32b 1878 if ( htItem )
23f681ec
VZ
1879 {
1880 // highlight the item as target (hiding drag image is
1881 // necessary - otherwise the display will be corrupted)
68be9f09 1882 m_dragImage->Hide();
3f7bc32b 1883 TreeView_SelectDropTarget(GetHwnd(), htItem);
68be9f09 1884 m_dragImage->Show();
23f681ec
VZ
1885 }
1886 }
1887 break;
1888
1889 case WM_LBUTTONUP:
1890 case WM_RBUTTONUP:
3f7bc32b 1891 if ( m_dragImage )
23f681ec 1892 {
68be9f09 1893 m_dragImage->EndDrag();
23f681ec
VZ
1894 delete m_dragImage;
1895 m_dragImage = NULL;
1896
1897 // generate the drag end event
1898 wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, m_windowId);
1899
3f7bc32b 1900 event.m_item = (WXHTREEITEM)htItem;
23f681ec
VZ
1901 event.m_pointDrag = wxPoint(x, y);
1902 event.SetEventObject(this);
1903
1904 (void)GetEventHandler()->ProcessEvent(event);
225fe9d6
VZ
1905
1906 // if we don't do it, the tree seems to think that 2 items
1907 // are selected simultaneously which is quite weird
1908 TreeView_SelectDropTarget(GetHwnd(), 0);
23f681ec
VZ
1909 }
1910 break;
1911 }
1912 }
3f7bc32b
VZ
1913#if !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
1914 else if ( (nMsg == WM_SETFOCUS || nMsg == WM_KILLFOCUS) && isMultiple )
1915 {
1916 // the tree control greys out the selected item when it loses focus and
1917 // paints it as selected again when it regains it, but it won't do it
1918 // for the other items itself - help it
1919 wxArrayTreeItemIds selections;
1920 size_t count = GetSelections(selections);
1921 RECT rect;
1922 for ( size_t n = 0; n < count; n++ )
1923 {
1924 // TreeView_GetItemRect() will return FALSE if item is not visible,
1925 // which may happen perfectly well
1926 if ( TreeView_GetItemRect(GetHwnd(), HITEM(selections[n]),
1927 &rect, TRUE) )
1928 {
1929 ::InvalidateRect(GetHwnd(), &rect, FALSE);
1930 }
1931 }
1932 }
1933 else if ( nMsg == WM_KEYDOWN && isMultiple )
1934 {
1935 bool bCtrl = wxIsCtrlDown(),
1936 bShift = wxIsShiftDown();
1937
1938 // we handle.arrows and space, but not page up/down and home/end: the
1939 // latter should be easy, but not the former
1940
2c8e4738 1941 HTREEITEM htSel = (HTREEITEM)TreeView_GetSelection(GetHwnd());
3f7bc32b
VZ
1942 if ( !m_htSelStart )
1943 {
1944 m_htSelStart = (WXHTREEITEM)htSel;
1945 }
1946
1947 if ( wParam == VK_SPACE )
1948 {
1949 if ( bCtrl )
1950 {
1951 ToggleItemSelection(GetHwnd(), htSel);
1952 }
1953 else
1954 {
1955 UnselectAll();
1956
1957 ::SelectItem(GetHwnd(), htSel);
1958 }
23f681ec 1959
3f7bc32b
VZ
1960 processed = TRUE;
1961 }
1962 else if ( wParam == VK_UP || wParam == VK_DOWN )
1963 {
1964 if ( !bCtrl && !bShift )
1965 {
1966 // no modifiers, just clear selection and then let the default
1967 // processing to take place
1968 UnselectAll();
1969 }
1970 else if ( htSel )
1971 {
1972 (void)wxControl::MSWWindowProc(nMsg, wParam, lParam);
1973
2c8e4738
VZ
1974 HTREEITEM htNext = (HTREEITEM)(wParam == VK_UP
1975 ? TreeView_GetPrevVisible(GetHwnd(), htSel)
1976 : TreeView_GetNextVisible(GetHwnd(), htSel));
3f7bc32b
VZ
1977
1978 if ( !htNext )
1979 {
1980 // at the top/bottom
1981 htNext = htSel;
1982 }
1983
1984 if ( bShift )
1985 {
1986 SelectRange(GetHwnd(), HITEM(m_htSelStart), htNext);
1987 }
1988 else // bCtrl
1989 {
1990 // without changing selection
1991 ::SetFocus(GetHwnd(), htNext);
1992 }
1993
1994 processed = TRUE;
1995 }
1996 }
1997 }
1998#endif // !wxUSE_CHECKBOXES_IN_MULTI_SEL_TREE
3f7bc32b
VZ
1999 if ( !processed )
2000 rc = wxControl::MSWWindowProc(nMsg, wParam, lParam);
2001
2002 return rc;
23f681ec
VZ
2003}
2004
08b7c251 2005// process WM_NOTIFY Windows message
a23fd0e1 2006bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
08b7c251
VZ
2007{
2008 wxTreeEvent event(wxEVT_NULL, m_windowId);
2009 wxEventType eventType = wxEVT_NULL;
2010 NMHDR *hdr = (NMHDR *)lParam;
2011
2012 switch ( hdr->code )
2bda0e17 2013 {
08b7c251
VZ
2014 case TVN_BEGINDRAG:
2015 eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
2016 // fall through
2017
2018 case TVN_BEGINRDRAG:
2019 {
2020 if ( eventType == wxEVT_NULL )
2021 eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
2022 //else: left drag, already set above
2023
2024 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
2025
06e38c8e 2026 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
08b7c251 2027 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
23f681ec
VZ
2028
2029 // don't allow dragging by default: the user code must
2030 // explicitly say that it wants to allow it to avoid breaking
2031 // the old apps
2032 event.Veto();
08b7c251 2033 }
696e1ea0 2034 break;
08b7c251
VZ
2035
2036 case TVN_BEGINLABELEDIT:
2037 {
2038 eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
2039 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2040
06e38c8e 2041 event.m_item = (WXHTREEITEM) info->item.hItem;
5ea47806 2042 event.m_label = info->item.pszText;
08b7c251 2043 }
696e1ea0 2044 break;
08b7c251
VZ
2045
2046 case TVN_DELETEITEM:
2047 {
2048 eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
2049 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
2050
696e1ea0
VZ
2051 event.m_item = (WXHTREEITEM)tv->itemOld.hItem;
2052
2053 if ( m_hasAnyAttr )
2054 {
2055 delete (wxTreeItemAttr *)m_attrs.
2056 Delete((long)tv->itemOld.hItem);
2057 }
08b7c251 2058 }
696e1ea0 2059 break;
08b7c251
VZ
2060
2061 case TVN_ENDLABELEDIT:
2062 {
2063 eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
2064 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2065
5ea47806
VZ
2066 event.m_item = (WXHTREEITEM)info->item.hItem;
2067 event.m_label = info->item.pszText;
1ee4ead5
VZ
2068 if (info->item.pszText == NULL)
2069 return FALSE;
08b7c251
VZ
2070 break;
2071 }
2072
2073 case TVN_GETDISPINFO:
2074 eventType = wxEVT_COMMAND_TREE_GET_INFO;
2075 // fall through
2076
2077 case TVN_SETDISPINFO:
2078 {
2079 if ( eventType == wxEVT_NULL )
2080 eventType = wxEVT_COMMAND_TREE_SET_INFO;
2081 //else: get, already set above
2082
2083 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2084
06e38c8e 2085 event.m_item = (WXHTREEITEM) info->item.hItem;
08b7c251
VZ
2086 break;
2087 }
2088
2089 case TVN_ITEMEXPANDING:
08b7c251
VZ
2090 case TVN_ITEMEXPANDED:
2091 {
2092 NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
2093
2094 bool expand = FALSE;
2095 switch ( tv->action )
2096 {
2097 case TVE_EXPAND:
2098 expand = TRUE;
2099 break;
2100
2101 case TVE_COLLAPSE:
2102 expand = FALSE;
2103 break;
2104
2105 default:
f6bcfd97 2106 wxLogDebug(wxT("unexpected code %d in TVN_ITEMEXPAND message"), tv->action);
08b7c251
VZ
2107 }
2108
a17e237f 2109 bool ing = ((int)hdr->code == TVN_ITEMEXPANDING);
08b7c251
VZ
2110 eventType = g_events[expand][ing];
2111
06e38c8e 2112 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
08b7c251 2113 }
696e1ea0 2114 break;
08b7c251
VZ
2115
2116 case TVN_KEYDOWN:
2117 {
2118 eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
2119 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
2120
b09bda68
VZ
2121 // we pass 0 as last CreateKeyEvent() parameter because we
2122 // don't have access to the real key press flags here - but as
2123 // it is only used to determin wxKeyEvent::m_altDown flag it's
2124 // not too bad
8191741f
VZ
2125 event.m_evtKey = CreateKeyEvent(wxEVT_KEY_DOWN,
2126 wxCharCodeMSWToWX(info->wVKey),
2127 0);
23fd5130 2128
3f7bc32b
VZ
2129 // a separate event for Space/Return
2130 if ( !wxIsCtrlDown() && !wxIsShiftDown() &&
2131 ((info->wVKey == VK_SPACE) || (info->wVKey == VK_RETURN)) )
23fd5130
VZ
2132 {
2133 wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
2134 m_windowId);
2135 event2.SetEventObject(this);
3f7bc32b
VZ
2136 if ( !(GetWindowStyle() & wxTR_MULTIPLE) )
2137 {
2138 event2.m_item = GetSelection();
2139 }
2140 //else: don't know how to get it
23fd5130 2141
3f7bc32b 2142 (void)GetEventHandler()->ProcessEvent(event2);
23fd5130 2143 }
08b7c251 2144 }
696e1ea0 2145 break;
08b7c251
VZ
2146
2147 case TVN_SELCHANGED:
2148 eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
2149 // fall through
2150
2151 case TVN_SELCHANGING:
2152 {
2153 if ( eventType == wxEVT_NULL )
2154 eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
2155 //else: already set above
2156
2157 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
2158
06e38c8e
JS
2159 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
2160 event.m_itemOld = (WXHTREEITEM) tv->itemOld.hItem;
08b7c251 2161 }
696e1ea0
VZ
2162 break;
2163
bf9b6266 2164#if defined(_WIN32_IE) && _WIN32_IE >= 0x300 && !wxUSE_COMCTL32_SAFELY && !( defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 1, 0 ) )
696e1ea0
VZ
2165 case NM_CUSTOMDRAW:
2166 {
2167 LPNMTVCUSTOMDRAW lptvcd = (LPNMTVCUSTOMDRAW)lParam;
2168 NMCUSTOMDRAW& nmcd = lptvcd->nmcd;
d7926e0b 2169 switch ( nmcd.dwDrawStage )
696e1ea0
VZ
2170 {
2171 case CDDS_PREPAINT:
2172 // if we've got any items with non standard attributes,
2173 // notify us before painting each item
2174 *result = m_hasAnyAttr ? CDRF_NOTIFYITEMDRAW
2175 : CDRF_DODEFAULT;
d7926e0b 2176 break;
696e1ea0
VZ
2177
2178 case CDDS_ITEMPREPAINT:
2179 {
2180 wxTreeItemAttr *attr =
2181 (wxTreeItemAttr *)m_attrs.Get(nmcd.dwItemSpec);
2182
2183 if ( !attr )
2184 {
2185 // nothing to do for this item
d7926e0b
VZ
2186 *result = CDRF_DODEFAULT;
2187 break;
696e1ea0
VZ
2188 }
2189
2190 HFONT hFont;
2191 wxColour colText, colBack;
2192 if ( attr->HasFont() )
2193 {
2194 wxFont font = attr->GetFont();
2195 hFont = (HFONT)font.GetResourceHandle();
2196 }
2197 else
2198 {
2199 hFont = 0;
2200 }
2201
2202 if ( attr->HasTextColour() )
2203 {
2204 colText = attr->GetTextColour();
2205 }
2206 else
2207 {
2208 colText = GetForegroundColour();
2209 }
2210
2211 // selection colours should override ours
2212 if ( nmcd.uItemState & CDIS_SELECTED )
2213 {
2214 DWORD clrBk = ::GetSysColor(COLOR_HIGHLIGHT);
2215 lptvcd->clrTextBk = clrBk;
2216
2217 // try to make the text visible
2218 lptvcd->clrText = wxColourToRGB(colText);
2219 lptvcd->clrText |= ~clrBk;
2220 lptvcd->clrText &= 0x00ffffff;
2221 }
2222 else
2223 {
2224 if ( attr->HasBackgroundColour() )
2225 {
2226 colBack = attr->GetBackgroundColour();
2227 }
2228 else
2229 {
2230 colBack = GetBackgroundColour();
2231 }
2232
2233 lptvcd->clrText = wxColourToRGB(colText);
2234 lptvcd->clrTextBk = wxColourToRGB(colBack);
2235 }
2236
2237 // note that if we wanted to set colours for
2238 // individual columns (subitems), we would have
2239 // returned CDRF_NOTIFYSUBITEMREDRAW from here
2240 if ( hFont )
2241 {
2242 ::SelectObject(nmcd.hdc, hFont);
2243
2244 *result = CDRF_NEWFONT;
2245 }
2246 else
2247 {
2248 *result = CDRF_DODEFAULT;
2249 }
696e1ea0 2250 }
d7926e0b 2251 break;
696e1ea0
VZ
2252
2253 default:
2254 *result = CDRF_DODEFAULT;
696e1ea0
VZ
2255 }
2256 }
d7926e0b
VZ
2257
2258 // we always process it
2259 return TRUE;
6ecfe2ac 2260#endif // _WIN32_IE >= 0x300
08b7c251 2261
f6bcfd97
BP
2262 case NM_DBLCLK:
2263 case NM_RCLICK:
2264 {
2265 TV_HITTESTINFO tvhti;
2266 ::GetCursorPos(&tvhti.pt);
2267 ::ScreenToClient(GetHwnd(), &tvhti.pt);
2268 if ( TreeView_HitTest(GetHwnd(), &tvhti) )
2269 {
2270 if ( tvhti.flags & TVHT_ONITEM )
2271 {
2272 event.m_item = (WXHTREEITEM) tvhti.hItem;
2273 eventType = (int)hdr->code == NM_DBLCLK
2274 ? wxEVT_COMMAND_TREE_ITEM_ACTIVATED
2275 : wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK;
8591268f
VZ
2276
2277 event.m_pointDrag.x = tvhti.pt.x;
2278 event.m_pointDrag.y = tvhti.pt.y;
f6bcfd97
BP
2279 }
2280
2281 break;
2282 }
2283 }
2284 // fall through
2285
08b7c251 2286 default:
a23fd0e1 2287 return wxControl::MSWOnNotify(idCtrl, lParam, result);
2bda0e17 2288 }
08b7c251
VZ
2289
2290 event.SetEventObject(this);
2291 event.SetEventType(eventType);
2292
fd3f686c 2293 bool processed = GetEventHandler()->ProcessEvent(event);
08b7c251
VZ
2294
2295 // post processing
5ea47806 2296 switch ( hdr->code )
2bda0e17 2297 {
f6bcfd97
BP
2298 case NM_DBLCLK:
2299 // we translate NM_DBLCLK into ACTIVATED event, so don't interpret
2300 // the return code of this event handler as the return value for
2301 // NM_DBLCLK - otherwise, double clicking the item to toggle its
2302 // expanded status would never work
2303 *result = FALSE;
2304 break;
2305
23f681ec
VZ
2306 case TVN_BEGINDRAG:
2307 case TVN_BEGINRDRAG:
2308 if ( event.IsAllowed() )
2309 {
2310 // normally this is impossible because the m_dragImage is
2311 // deleted once the drag operation is over
2312 wxASSERT_MSG( !m_dragImage, _T("starting to drag once again?") );
2313
2314 m_dragImage = new wxDragImage(*this, event.m_item);
2315 m_dragImage->BeginDrag(wxPoint(0, 0), this);
68be9f09 2316 m_dragImage->Show();
23f681ec
VZ
2317 }
2318 break;
2319
5ea47806
VZ
2320 case TVN_DELETEITEM:
2321 {
2322 // NB: we might process this message using wxWindows event
2323 // tables, but due to overhead of wxWin event system we
2324 // prefer to do it here ourself (otherwise deleting a tree
2325 // with many items is just too slow)
2326 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
74b31181
VZ
2327
2328 wxTreeItemId item = event.m_item;
2329 if ( HasIndirectData(item) )
2330 {
2331 wxTreeItemIndirectData *data = (wxTreeItemIndirectData *)
2332 tv->itemOld.lParam;
2333 delete data; // can't be NULL here
2334
2335 m_itemsWithIndirectData.Remove(item);
484523cf
JS
2336#if 0
2337 int iIndex = m_itemsWithIndirectData.Index(item);
2338 wxASSERT( iIndex != wxNOT_FOUND) ;
2339 m_itemsWithIndirectData.wxBaseArray::RemoveAt((size_t)iIndex);
2340#endif
74b31181
VZ
2341 }
2342 else
2343 {
2344 wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam;
2345 delete data; // may be NULL, ok
2346 }
08b7c251 2347
5ea47806
VZ
2348 processed = TRUE; // Make sure we don't get called twice
2349 }
2350 break;
2351
2352 case TVN_BEGINLABELEDIT:
2353 // return TRUE to cancel label editing
2354 *result = !event.IsAllowed();
2355 break;
2356
2357 case TVN_ENDLABELEDIT:
188781db
VZ
2358 // return TRUE to set the label to the new string: note that we
2359 // also must pretend that we did process the message or it is going
2360 // to be passed to DefWindowProc() which will happily return FALSE
2361 // cancelling the label change
5ea47806 2362 *result = event.IsAllowed();
188781db 2363 processed = TRUE;
5ea47806
VZ
2364
2365 // ensure that we don't have the text ctrl which is going to be
2366 // deleted any more
2367 DeleteTextCtrl();
2368 break;
2369
2370 case TVN_SELCHANGING:
2371 case TVN_ITEMEXPANDING:
2372 // return TRUE to prevent the action from happening
2373 *result = !event.IsAllowed();
2374 break;
2375
74b31181
VZ
2376 case TVN_GETDISPINFO:
2377 // NB: so far the user can't set the image himself anyhow, so do it
2378 // anyway - but this may change later
33ac7e6f 2379// if ( /* !processed && */ 1 )
74b31181
VZ
2380 {
2381 wxTreeItemId item = event.m_item;
2382 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
2383 if ( info->item.mask & TVIF_IMAGE )
2384 {
2385 info->item.iImage =
2386 DoGetItemImageFromData
2387 (
2388 item,
2389 IsExpanded(item) ? wxTreeItemIcon_Expanded
2390 : wxTreeItemIcon_Normal
2391 );
2392 }
2393 if ( info->item.mask & TVIF_SELECTEDIMAGE )
2394 {
2395 info->item.iSelectedImage =
2396 DoGetItemImageFromData
2397 (
2398 item,
2399 IsExpanded(item) ? wxTreeItemIcon_SelectedExpanded
2400 : wxTreeItemIcon_Selected
2401 );
2402 }
33ac7e6f 2403 }
74b31181
VZ
2404 break;
2405
5ea47806
VZ
2406 //default:
2407 // for the other messages the return value is ignored and there is
2408 // nothing special to do
2409 }
fd3f686c 2410 return processed;
2bda0e17
KB
2411}
2412
08b7c251 2413#endif // __WIN95__
2bda0e17 2414
1e6feb95 2415#endif // wxUSE_TREECTRL