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