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