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