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