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