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