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