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