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