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