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