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