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