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