]> git.saurik.com Git - wxWidgets.git/blob - src/msw/treectrl.cpp
Made changes to allow build with new mingw32/gcc-2.95
[wxWidgets.git] / src / msw / treectrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: treectrl.cpp
3 // Purpose: wxTreeCtrl
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin to be less MSW-specific on 10.10.98
6 // Created: 1997
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19 #ifdef __GNUG__
20 #pragma implementation "treectrl.h"
21 #endif
22
23 // For compilers that support precompilation, includes "wx.h".
24 #include "wx/wxprec.h"
25
26 #ifdef __BORLANDC__
27 #pragma hdrstop
28 #endif
29
30 #include "wx/window.h"
31 #include "wx/msw/private.h"
32
33 // Mingw32 is a bit mental even though this is done in winundef
34 #ifdef GetFirstChild
35 #undef GetFirstChild
36 #endif
37
38 #ifdef GetNextSibling
39 #undef GetNextSibling
40 #endif
41
42 #if defined(__WIN95__)
43
44 #include "wx/log.h"
45 #include "wx/dynarray.h"
46 #include "wx/imaglist.h"
47 #include "wx/treectrl.h"
48
49 #ifdef __GNUWIN32__
50 #ifndef wxUSE_NORLANDER_HEADERS
51 #include "wx/msw/gnuwin32/extra.h"
52 #endif
53 #endif
54
55 #if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__) || defined(wxUSE_NORLANDER_HEADERS)
56 #include <commctrl.h>
57 #endif
58
59 // Bug in headers, sometimes
60 #ifndef TVIS_FOCUSED
61 #define TVIS_FOCUSED 0x0001
62 #endif
63
64 // ----------------------------------------------------------------------------
65 // private classes
66 // ----------------------------------------------------------------------------
67
68 // a convenient wrapper around TV_ITEM struct which adds a ctor
69 struct wxTreeViewItem : public TV_ITEM
70 {
71 wxTreeViewItem(const wxTreeItemId& item, // the item handle
72 UINT mask_, // fields which are valid
73 UINT stateMask_ = 0) // for TVIF_STATE only
74 {
75 // hItem member is always valid
76 mask = mask_ | TVIF_HANDLE;
77 stateMask = stateMask_;
78 hItem = (HTREEITEM) (WXHTREEITEM) item;
79 }
80 };
81
82 // a class which encapsulates the tree traversal logic: it vists all (unless
83 // OnVisit() returns FALSE) items under the given one
84 class wxTreeTraversal
85 {
86 public:
87 wxTreeTraversal(const wxTreeCtrl *tree)
88 {
89 m_tree = tree;
90 }
91
92 // do traverse the tree: visit all items (recursively by default) under the
93 // given one; return TRUE if all items were traversed or FALSE if the
94 // traversal was aborted because OnVisit returned FALSE
95 bool DoTraverse(const wxTreeItemId& root, bool recursively = TRUE);
96
97 // override this function to do whatever is needed for each item, return
98 // FALSE to stop traversing
99 virtual bool OnVisit(const wxTreeItemId& item) = 0;
100
101 protected:
102 const wxTreeCtrl *GetTree() const { return m_tree; }
103
104 private:
105 bool Traverse(const wxTreeItemId& root, bool recursively);
106
107 const wxTreeCtrl *m_tree;
108 };
109
110 // ----------------------------------------------------------------------------
111 // macros
112 // ----------------------------------------------------------------------------
113
114 #if !USE_SHARED_LIBRARY
115 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl)
116 #endif
117
118 // ----------------------------------------------------------------------------
119 // variables
120 // ----------------------------------------------------------------------------
121
122 // handy table for sending events
123 static const wxEventType g_events[2][2] =
124 {
125 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING },
126 { wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxEVT_COMMAND_TREE_ITEM_EXPANDING }
127 };
128
129 // ============================================================================
130 // implementation
131 // ============================================================================
132
133 // ----------------------------------------------------------------------------
134 // tree traversal
135 // ----------------------------------------------------------------------------
136
137 bool wxTreeTraversal::DoTraverse(const wxTreeItemId& root, bool recursively)
138 {
139 if ( !OnVisit(root) )
140 return FALSE;
141
142 return Traverse(root, recursively);
143 }
144
145 bool wxTreeTraversal::Traverse(const wxTreeItemId& root, bool recursively)
146 {
147 long cookie;
148 wxTreeItemId child = m_tree->GetFirstChild(root, cookie);
149 while ( child.IsOk() )
150 {
151 // depth first traversal
152 if ( recursively && !Traverse(child, TRUE) )
153 return FALSE;
154
155 if ( !OnVisit(child) )
156 return FALSE;
157
158 child = m_tree->GetNextChild(root, cookie);
159 }
160
161 return TRUE;
162 }
163
164 // ----------------------------------------------------------------------------
165 // construction and destruction
166 // ----------------------------------------------------------------------------
167
168 void wxTreeCtrl::Init()
169 {
170 m_imageListNormal = NULL;
171 m_imageListState = NULL;
172 m_textCtrl = NULL;
173 }
174
175 bool wxTreeCtrl::Create(wxWindow *parent,
176 wxWindowID id,
177 const wxPoint& pos,
178 const wxSize& size,
179 long style,
180 const wxValidator& validator,
181 const wxString& name)
182 {
183 Init();
184
185 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
186 return FALSE;
187
188 DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP |
189 TVS_HASLINES | TVS_SHOWSELALWAYS;
190
191 if ( m_windowStyle & wxTR_HAS_BUTTONS )
192 wstyle |= TVS_HASBUTTONS;
193
194 if ( m_windowStyle & wxTR_EDIT_LABELS )
195 wstyle |= TVS_EDITLABELS;
196
197 if ( m_windowStyle & wxTR_LINES_AT_ROOT )
198 wstyle |= TVS_LINESATROOT;
199
200 #if !defined( __GNUWIN32__ ) && !defined(wxUSE_NORLANDER_HEADERS)
201 // we emulate the multiple selection tree controls by using checkboxes: set
202 // up the image list we need for this if we do have multiple selections
203 if ( m_windowStyle & wxTR_MULTIPLE )
204 wstyle |= TVS_CHECKBOXES;
205 #endif
206
207 // Create the tree control.
208 if ( !MSWCreateControl(WC_TREEVIEW, wstyle) )
209 return FALSE;
210
211 // the treectrl with any other background looks ugly because the items
212 // background is white anyhow
213 SetBackgroundColour(*wxWHITE);
214
215 // VZ: this is some experimental code which may be used to get the
216 // TVS_CHECKBOXES style functionality for comctl32.dll < 4.71.
217 // AFAIK, the standard DLL does about the same thing anyhow.
218 #if 0
219 if ( m_windowStyle & wxTR_MULTIPLE )
220 {
221 wxBitmap bmp;
222
223 // create the DC compatible with the current screen
224 HDC hdcMem = CreateCompatibleDC(NULL);
225
226 // create a mono bitmap of the standard size
227 int x = GetSystemMetrics(SM_CXMENUCHECK);
228 int y = GetSystemMetrics(SM_CYMENUCHECK);
229 wxImageList imagelistCheckboxes(x, y, FALSE, 2);
230 HBITMAP hbmpCheck = CreateBitmap(x, y, // bitmap size
231 1, // # of color planes
232 1, // # bits needed for one pixel
233 0); // array containing colour data
234 SelectObject(hdcMem, hbmpCheck);
235
236 // then draw a check mark into it
237 RECT rect = { 0, 0, x, y };
238 if ( !::DrawFrameControl(hdcMem, &rect,
239 DFC_BUTTON,
240 DFCS_BUTTONCHECK | DFCS_CHECKED) )
241 {
242 wxLogLastError(_T("DrawFrameControl(check)"));
243 }
244
245 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
246 imagelistCheckboxes.Add(bmp);
247
248 if ( !::DrawFrameControl(hdcMem, &rect,
249 DFC_BUTTON,
250 DFCS_BUTTONCHECK) )
251 {
252 wxLogLastError(_T("DrawFrameControl(uncheck)"));
253 }
254
255 bmp.SetHBITMAP((WXHBITMAP)hbmpCheck);
256 imagelistCheckboxes.Add(bmp);
257
258 // clean up
259 ::DeleteDC(hdcMem);
260
261 // set the imagelist
262 SetStateImageList(&imagelistCheckboxes);
263 }
264 #endif // 0
265
266 SetSize(pos.x, pos.y, size.x, size.y);
267
268 return TRUE;
269 }
270
271 wxTreeCtrl::~wxTreeCtrl()
272 {
273 DeleteTextCtrl();
274
275 // delete user data to prevent memory leaks
276 DeleteAllItems();
277 }
278
279 // ----------------------------------------------------------------------------
280 // accessors
281 // ----------------------------------------------------------------------------
282
283 // simple wrappers which add error checking in debug mode
284
285 bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const
286 {
287 if ( !TreeView_GetItem(GetHwnd(), tvItem) )
288 {
289 wxLogLastError("TreeView_GetItem");
290
291 return FALSE;
292 }
293
294 return TRUE;
295 }
296
297 void wxTreeCtrl::DoSetItem(wxTreeViewItem* tvItem)
298 {
299 if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
300 {
301 wxLogLastError("TreeView_SetItem");
302 }
303 }
304
305 size_t wxTreeCtrl::GetCount() const
306 {
307 return (size_t)TreeView_GetCount(GetHwnd());
308 }
309
310 unsigned int wxTreeCtrl::GetIndent() const
311 {
312 return TreeView_GetIndent(GetHwnd());
313 }
314
315 void wxTreeCtrl::SetIndent(unsigned int indent)
316 {
317 TreeView_SetIndent(GetHwnd(), indent);
318 }
319
320 wxImageList *wxTreeCtrl::GetImageList() const
321 {
322 return m_imageListNormal;
323 }
324
325 wxImageList *wxTreeCtrl::GetStateImageList() const
326 {
327 return m_imageListNormal;
328 }
329
330 void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which)
331 {
332 // no error return
333 TreeView_SetImageList(GetHwnd(),
334 imageList ? imageList->GetHIMAGELIST() : 0,
335 which);
336 }
337
338 void wxTreeCtrl::SetImageList(wxImageList *imageList)
339 {
340 SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
341 }
342
343 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
344 {
345 SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
346 }
347
348 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item,
349 bool recursively) const
350 {
351 class TraverseCounter : public wxTreeTraversal
352 {
353 public:
354 TraverseCounter(const wxTreeCtrl *tree,
355 const wxTreeItemId& root,
356 bool recursively)
357 : wxTreeTraversal(tree)
358 {
359 m_count = 0;
360
361 DoTraverse(root, recursively);
362 }
363
364 virtual bool OnVisit(const wxTreeItemId& item)
365 {
366 m_count++;
367
368 return TRUE;
369 }
370
371 size_t GetCount() const { return m_count; }
372
373 private:
374 size_t m_count;
375 } counter(this, item, recursively);
376
377 return counter.GetCount();
378 }
379
380 // ----------------------------------------------------------------------------
381 // Item access
382 // ----------------------------------------------------------------------------
383
384 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
385 {
386 wxChar buf[512]; // the size is arbitrary...
387
388 wxTreeViewItem tvItem(item, TVIF_TEXT);
389 tvItem.pszText = buf;
390 tvItem.cchTextMax = WXSIZEOF(buf);
391 if ( !DoGetItem(&tvItem) )
392 {
393 // don't return some garbage which was on stack, but an empty string
394 buf[0] = _T('\0');
395 }
396
397 return wxString(buf);
398 }
399
400 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
401 {
402 wxTreeViewItem tvItem(item, TVIF_TEXT);
403 tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok
404 DoSetItem(&tvItem);
405 }
406
407 void wxTreeCtrl::DoSetItemImages(const wxTreeItemId& item,
408 int image,
409 int imageSel)
410 {
411 wxTreeViewItem tvItem(item, TVIF_IMAGE | TVIF_SELECTEDIMAGE);
412 tvItem.iSelectedImage = imageSel;
413 tvItem.iImage = image;
414 DoSetItem(&tvItem);
415 }
416
417 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
418 {
419 wxTreeViewItem tvItem(item, TVIF_IMAGE);
420 DoGetItem(&tvItem);
421
422 return tvItem.iImage;
423 }
424
425 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
426 {
427 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
428 // change both normal and selected image - otherwise the change simply
429 // doesn't take place!
430 DoSetItemImages(item, image, GetItemSelectedImage(item));
431 }
432
433 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
434 {
435 wxTreeViewItem tvItem(item, TVIF_SELECTEDIMAGE);
436 DoGetItem(&tvItem);
437
438 return tvItem.iSelectedImage;
439 }
440
441 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
442 {
443 // NB: at least in version 5.00.0518.9 of comctl32.dll we need to always
444 // change both normal and selected image - otherwise the change simply
445 // doesn't take place!
446 DoSetItemImages(item, GetItemImage(item), image);
447 }
448
449 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
450 {
451 wxTreeViewItem tvItem(item, TVIF_PARAM);
452 if ( !DoGetItem(&tvItem) )
453 {
454 return NULL;
455 }
456
457 return (wxTreeItemData *)tvItem.lParam;
458 }
459
460 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
461 {
462 wxTreeViewItem tvItem(item, TVIF_PARAM);
463 tvItem.lParam = (LPARAM)data;
464 DoSetItem(&tvItem);
465 }
466
467 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
468 {
469 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
470 tvItem.cChildren = (int)has;
471 DoSetItem(&tvItem);
472 }
473
474 void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
475 {
476 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
477 tvItem.state = bold ? TVIS_BOLD : 0;
478 DoSetItem(&tvItem);
479 }
480
481 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight)
482 {
483 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
484 tvItem.state = highlight ? TVIS_DROPHILITED : 0;
485 DoSetItem(&tvItem);
486 }
487
488 // ----------------------------------------------------------------------------
489 // Item status
490 // ----------------------------------------------------------------------------
491
492 bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
493 {
494 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
495 RECT rect;
496 return SendMessage(GetHwnd(), TVM_GETITEMRECT, FALSE, (LPARAM)&rect) != 0;
497
498 }
499
500 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
501 {
502 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
503 DoGetItem(&tvItem);
504
505 return tvItem.cChildren != 0;
506 }
507
508 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
509 {
510 // probably not a good idea to put it here
511 //wxASSERT( ItemHasChildren(item) );
512
513 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
514 DoGetItem(&tvItem);
515
516 return (tvItem.state & TVIS_EXPANDED) != 0;
517 }
518
519 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
520 {
521 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
522 DoGetItem(&tvItem);
523
524 return (tvItem.state & TVIS_SELECTED) != 0;
525 }
526
527 bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
528 {
529 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
530 DoGetItem(&tvItem);
531
532 return (tvItem.state & TVIS_BOLD) != 0;
533 }
534
535 // ----------------------------------------------------------------------------
536 // navigation
537 // ----------------------------------------------------------------------------
538
539 wxTreeItemId wxTreeCtrl::GetRootItem() const
540 {
541 return wxTreeItemId((WXHTREEITEM) TreeView_GetRoot(GetHwnd()));
542 }
543
544 wxTreeItemId wxTreeCtrl::GetSelection() const
545 {
546 wxCHECK_MSG( !(m_windowStyle & wxTR_MULTIPLE), (WXHTREEITEM)0,
547 _T("this only works with single selection controls") );
548
549 return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd()));
550 }
551
552 wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
553 {
554 return wxTreeItemId((WXHTREEITEM) TreeView_GetParent(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
555 }
556
557 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
558 long& _cookie) const
559 {
560 // remember the last child returned in 'cookie'
561 _cookie = (long)TreeView_GetChild(GetHwnd(), (HTREEITEM) (WXHTREEITEM)item);
562
563 return wxTreeItemId((WXHTREEITEM)_cookie);
564 }
565
566 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
567 long& _cookie) const
568 {
569 wxTreeItemId l = wxTreeItemId((WXHTREEITEM)TreeView_GetNextSibling(GetHwnd(),
570 (HTREEITEM)(WXHTREEITEM)_cookie));
571 _cookie = (long)l;
572
573 return l;
574 }
575
576 wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
577 {
578 // can this be done more efficiently?
579 long cookie;
580
581 wxTreeItemId childLast,
582 child = GetFirstChild(item, cookie);
583 while ( child.IsOk() )
584 {
585 childLast = child;
586 child = GetNextChild(item, cookie);
587 }
588
589 return childLast;
590 }
591
592 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
593 {
594 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
595 }
596
597 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
598 {
599 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
600 }
601
602 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
603 {
604 return wxTreeItemId((WXHTREEITEM) TreeView_GetFirstVisible(GetHwnd()));
605 }
606
607 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
608 {
609 wxASSERT_MSG( IsVisible(item), _T("The item you call GetNextVisible() "
610 "for must be visible itself!"));
611
612 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
613 }
614
615 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
616 {
617 wxASSERT_MSG( IsVisible(item), _T("The item you call GetPrevVisible() "
618 "for must be visible itself!"));
619
620 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
621 }
622
623 // ----------------------------------------------------------------------------
624 // multiple selections emulation
625 // ----------------------------------------------------------------------------
626
627 bool wxTreeCtrl::IsItemChecked(const wxTreeItemId& item) const
628 {
629 // receive the desired information.
630 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
631 DoGetItem(&tvItem);
632
633 // state image indices are 1 based
634 return ((tvItem.state >> 12) - 1) == 1;
635 }
636
637 void wxTreeCtrl::SetItemCheck(const wxTreeItemId& item, bool check)
638 {
639 // receive the desired information.
640 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_STATEIMAGEMASK);
641
642 // state images are one-based
643 tvItem.state = (check ? 2 : 1) << 12;
644
645 DoSetItem(&tvItem);
646 }
647
648 size_t wxTreeCtrl::GetSelections(wxArrayTreeItemIds& selections) const
649 {
650 class TraverseSelections : public wxTreeTraversal
651 {
652 public:
653 TraverseSelections(const wxTreeCtrl *tree,
654 wxArrayTreeItemIds& selections)
655 : wxTreeTraversal(tree), m_selections(selections)
656 {
657 m_selections.Empty();
658
659 DoTraverse(tree->GetRootItem());
660 }
661
662 virtual bool OnVisit(const wxTreeItemId& item)
663 {
664 if ( GetTree()->IsItemChecked(item) )
665 {
666 m_selections.Add(item);
667 }
668
669 return TRUE;
670 }
671
672 private:
673 wxArrayTreeItemIds& m_selections;
674 } selector(this, selections);
675
676 return selections.GetCount();
677 }
678
679 // ----------------------------------------------------------------------------
680 // Usual operations
681 // ----------------------------------------------------------------------------
682
683 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
684 wxTreeItemId hInsertAfter,
685 const wxString& text,
686 int image, int selectedImage,
687 wxTreeItemData *data)
688 {
689 TV_INSERTSTRUCT tvIns;
690 tvIns.hParent = (HTREEITEM) (WXHTREEITEM)parent;
691 tvIns.hInsertAfter = (HTREEITEM) (WXHTREEITEM) hInsertAfter;
692
693 // This is how we insert the item as the first child: supply a NULL hInsertAfter
694 if (tvIns.hInsertAfter == (HTREEITEM) 0)
695 {
696 tvIns.hInsertAfter = TVI_FIRST;
697 }
698
699 UINT mask = 0;
700 if ( !text.IsEmpty() )
701 {
702 mask |= TVIF_TEXT;
703 tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
704 }
705
706 if ( image != -1 )
707 {
708 mask |= TVIF_IMAGE;
709 tvIns.item.iImage = image;
710
711 if ( selectedImage == -1 )
712 {
713 // take the same image for selected icon if not specified
714 selectedImage = image;
715 }
716 }
717
718 if ( selectedImage != -1 )
719 {
720 mask |= TVIF_SELECTEDIMAGE;
721 tvIns.item.iSelectedImage = selectedImage;
722 }
723
724 if ( data != NULL )
725 {
726 mask |= TVIF_PARAM;
727 tvIns.item.lParam = (LPARAM)data;
728 }
729
730 tvIns.item.mask = mask;
731
732 HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns);
733 if ( id == 0 )
734 {
735 wxLogLastError("TreeView_InsertItem");
736 }
737
738 if ( data != NULL )
739 {
740 // associate the application tree item with Win32 tree item handle
741 data->SetId((WXHTREEITEM)id);
742 }
743
744 return wxTreeItemId((WXHTREEITEM)id);
745 }
746
747 // for compatibility only
748 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
749 const wxString& text,
750 int image, int selImage,
751 long insertAfter)
752 {
753 return DoInsertItem(parent, (WXHTREEITEM)insertAfter, text,
754 image, selImage, NULL);
755 }
756
757 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
758 int image, int selectedImage,
759 wxTreeItemData *data)
760 {
761 return DoInsertItem(wxTreeItemId((WXHTREEITEM) 0), (WXHTREEITEM) 0,
762 text, image, selectedImage, data);
763 }
764
765 wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
766 const wxString& text,
767 int image, int selectedImage,
768 wxTreeItemData *data)
769 {
770 return DoInsertItem(parent, (WXHTREEITEM) TVI_FIRST,
771 text, image, selectedImage, data);
772 }
773
774 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
775 const wxTreeItemId& idPrevious,
776 const wxString& text,
777 int image, int selectedImage,
778 wxTreeItemData *data)
779 {
780 return DoInsertItem(parent, idPrevious, text, image, selectedImage, data);
781 }
782
783 wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parent,
784 const wxString& text,
785 int image, int selectedImage,
786 wxTreeItemData *data)
787 {
788 return DoInsertItem(parent, (WXHTREEITEM) TVI_LAST,
789 text, image, selectedImage, data);
790 }
791
792 void wxTreeCtrl::Delete(const wxTreeItemId& item)
793 {
794 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item) )
795 {
796 wxLogLastError("TreeView_DeleteItem");
797 }
798 }
799
800 // delete all children (but don't delete the item itself)
801 void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
802 {
803 long cookie;
804
805 wxArrayLong children;
806 wxTreeItemId child = GetFirstChild(item, cookie);
807 while ( child.IsOk() )
808 {
809 children.Add((long)(WXHTREEITEM)child);
810
811 child = GetNextChild(item, cookie);
812 }
813
814 size_t nCount = children.Count();
815 for ( size_t n = 0; n < nCount; n++ )
816 {
817 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)children[n]) )
818 {
819 wxLogLastError("TreeView_DeleteItem");
820 }
821 }
822 }
823
824 void wxTreeCtrl::DeleteAllItems()
825 {
826 if ( !TreeView_DeleteAllItems(GetHwnd()) )
827 {
828 wxLogLastError("TreeView_DeleteAllItems");
829 }
830 }
831
832 void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
833 {
834 wxASSERT_MSG( flag == TVE_COLLAPSE ||
835 flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
836 flag == TVE_EXPAND ||
837 flag == TVE_TOGGLE,
838 _T("Unknown flag in wxTreeCtrl::DoExpand") );
839
840 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
841 // emulate them. This behaviour has changed slightly with comctl32.dll
842 // v 4.70 - now it does send them but only the first time. To maintain
843 // compatible behaviour and also in order to not have surprises with the
844 // future versions, don't rely on this and still do everything ourselves.
845 // To avoid that the messages be sent twice when the item is expanded for
846 // the first time we must clear TVIS_EXPANDEDONCE style manually.
847
848 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
849 tvItem.state = 0;
850 DoSetItem(&tvItem);
851
852 if ( TreeView_Expand(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item, flag) != 0 )
853 {
854 wxTreeEvent event(wxEVT_NULL, m_windowId);
855 event.m_item = item;
856
857 bool isExpanded = IsExpanded(item);
858
859 event.SetEventObject(this);
860
861 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
862 event.SetEventType(g_events[isExpanded][TRUE]);
863 GetEventHandler()->ProcessEvent(event);
864
865 event.SetEventType(g_events[isExpanded][FALSE]);
866 GetEventHandler()->ProcessEvent(event);
867 }
868 //else: change didn't took place, so do nothing at all
869 }
870
871 void wxTreeCtrl::Expand(const wxTreeItemId& item)
872 {
873 DoExpand(item, TVE_EXPAND);
874 }
875
876 void wxTreeCtrl::Collapse(const wxTreeItemId& item)
877 {
878 DoExpand(item, TVE_COLLAPSE);
879 }
880
881 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
882 {
883 DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
884 }
885
886 void wxTreeCtrl::Toggle(const wxTreeItemId& item)
887 {
888 DoExpand(item, TVE_TOGGLE);
889 }
890
891 void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action)
892 {
893 DoExpand(item, action);
894 }
895
896 void wxTreeCtrl::Unselect()
897 {
898 wxASSERT_MSG( !(m_windowStyle & wxTR_MULTIPLE), _T("doesn't make sense") );
899
900 // just remove the selection
901 SelectItem(wxTreeItemId((WXHTREEITEM) 0));
902 }
903
904 void wxTreeCtrl::UnselectAll()
905 {
906 if ( m_windowStyle & wxTR_MULTIPLE )
907 {
908 wxArrayTreeItemIds selections;
909 size_t count = GetSelections(selections);
910 for ( size_t n = 0; n < count; n++ )
911 {
912 SetItemCheck(selections[n], FALSE);
913 }
914 }
915 else
916 {
917 // just remove the selection
918 Unselect();
919 }
920 }
921
922 void wxTreeCtrl::SelectItem(const wxTreeItemId& item)
923 {
924 if ( m_windowStyle & wxTR_MULTIPLE )
925 {
926 // selecting the item means checking it
927 SetItemCheck(item);
928 }
929 else
930 {
931 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
932 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
933 // send them ourselves
934
935 wxTreeEvent event(wxEVT_NULL, m_windowId);
936 event.m_item = item;
937 event.SetEventObject(this);
938
939 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING);
940 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
941 {
942 if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
943 {
944 wxLogLastError("TreeView_SelectItem");
945 }
946 else
947 {
948 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
949 (void)GetEventHandler()->ProcessEvent(event);
950 }
951 }
952 //else: program vetoed the change
953 }
954 }
955
956 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
957 {
958 // no error return
959 TreeView_EnsureVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
960 }
961
962 void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
963 {
964 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
965 {
966 wxLogLastError("TreeView_SelectSetFirstVisible");
967 }
968 }
969
970 wxTextCtrl* wxTreeCtrl::GetEditControl() const
971 {
972 return m_textCtrl;
973 }
974
975 void wxTreeCtrl::DeleteTextCtrl()
976 {
977 if ( m_textCtrl )
978 {
979 m_textCtrl->UnsubclassWin();
980 m_textCtrl->SetHWND(0);
981 delete m_textCtrl;
982 m_textCtrl = NULL;
983 }
984 }
985
986 wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item,
987 wxClassInfo* textControlClass)
988 {
989 wxASSERT( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)) );
990
991 HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
992
993 // this is not an error - the TVN_BEGINLABELEDIT handler might have
994 // returned FALSE
995 if ( !hWnd )
996 {
997 return NULL;
998 }
999
1000 DeleteTextCtrl();
1001
1002 m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
1003 m_textCtrl->SetHWND((WXHWND)hWnd);
1004 m_textCtrl->SubclassWin((WXHWND)hWnd);
1005
1006 return m_textCtrl;
1007 }
1008
1009 // End label editing, optionally cancelling the edit
1010 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& item, bool discardChanges)
1011 {
1012 TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
1013
1014 DeleteTextCtrl();
1015 }
1016
1017 wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
1018 {
1019 TV_HITTESTINFO hitTestInfo;
1020 hitTestInfo.pt.x = (int)point.x;
1021 hitTestInfo.pt.y = (int)point.y;
1022
1023 TreeView_HitTest(GetHwnd(), &hitTestInfo);
1024
1025 flags = 0;
1026
1027 // avoid repetition
1028 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
1029 flags |= wxTREE_HITTEST_##flag
1030
1031 TRANSLATE_FLAG(ABOVE);
1032 TRANSLATE_FLAG(BELOW);
1033 TRANSLATE_FLAG(NOWHERE);
1034 TRANSLATE_FLAG(ONITEMBUTTON);
1035 TRANSLATE_FLAG(ONITEMICON);
1036 TRANSLATE_FLAG(ONITEMINDENT);
1037 TRANSLATE_FLAG(ONITEMLABEL);
1038 TRANSLATE_FLAG(ONITEMRIGHT);
1039 TRANSLATE_FLAG(ONITEMSTATEICON);
1040 TRANSLATE_FLAG(TOLEFT);
1041 TRANSLATE_FLAG(TORIGHT);
1042
1043 #undef TRANSLATE_FLAG
1044
1045 return wxTreeItemId((WXHTREEITEM) hitTestInfo.hItem);
1046 }
1047
1048 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
1049 wxRect& rect,
1050 bool textOnly) const
1051 {
1052 RECT rc;
1053 if ( TreeView_GetItemRect(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item,
1054 &rc, textOnly) )
1055 {
1056 rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
1057
1058 return TRUE;
1059 }
1060 else
1061 {
1062 // couldn't retrieve rect: for example, item isn't visible
1063 return FALSE;
1064 }
1065 }
1066
1067 // ----------------------------------------------------------------------------
1068 // sorting stuff
1069 // ----------------------------------------------------------------------------
1070
1071 static int CALLBACK TreeView_CompareCallback(wxTreeItemData *pItem1,
1072 wxTreeItemData *pItem2,
1073 wxTreeCtrl *tree)
1074 {
1075 wxCHECK_MSG( pItem1 && pItem2, 0,
1076 _T("sorting tree without data doesn't make sense") );
1077
1078 return tree->OnCompareItems(pItem1->GetId(), pItem2->GetId());
1079 }
1080
1081 int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
1082 const wxTreeItemId& item2)
1083 {
1084 return wxStrcmp(GetItemText(item1), GetItemText(item2));
1085 }
1086
1087 void wxTreeCtrl::SortChildren(const wxTreeItemId& item)
1088 {
1089 // rely on the fact that TreeView_SortChildren does the same thing as our
1090 // default behaviour, i.e. sorts items alphabetically and so call it
1091 // directly if we're not in derived class (much more efficient!)
1092 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
1093 {
1094 TreeView_SortChildren(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item, 0);
1095 }
1096 else
1097 {
1098 TV_SORTCB tvSort;
1099 tvSort.hParent = (HTREEITEM)(WXHTREEITEM)item;
1100 tvSort.lpfnCompare = (PFNTVCOMPARE)TreeView_CompareCallback;
1101 tvSort.lParam = (LPARAM)this;
1102 TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */);
1103 }
1104 }
1105
1106 // ----------------------------------------------------------------------------
1107 // implementation
1108 // ----------------------------------------------------------------------------
1109
1110 bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id)
1111 {
1112 if ( cmd == EN_UPDATE )
1113 {
1114 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
1115 event.SetEventObject( this );
1116 ProcessCommand(event);
1117 }
1118 else if ( cmd == EN_KILLFOCUS )
1119 {
1120 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
1121 event.SetEventObject( this );
1122 ProcessCommand(event);
1123 }
1124 else
1125 {
1126 // nothing done
1127 return FALSE;
1128 }
1129
1130 // command processed
1131 return TRUE;
1132 }
1133
1134 // process WM_NOTIFY Windows message
1135 bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
1136 {
1137 wxTreeEvent event(wxEVT_NULL, m_windowId);
1138 wxEventType eventType = wxEVT_NULL;
1139 NMHDR *hdr = (NMHDR *)lParam;
1140
1141 switch ( hdr->code )
1142 {
1143 case TVN_BEGINDRAG:
1144 eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
1145 // fall through
1146
1147 case TVN_BEGINRDRAG:
1148 {
1149 if ( eventType == wxEVT_NULL )
1150 eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
1151 //else: left drag, already set above
1152
1153 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
1154
1155 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1156 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
1157 break;
1158 }
1159
1160 case TVN_BEGINLABELEDIT:
1161 {
1162 eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
1163 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1164
1165 event.m_item = (WXHTREEITEM) info->item.hItem;
1166 event.m_label = info->item.pszText;
1167 break;
1168 }
1169
1170 case TVN_DELETEITEM:
1171 {
1172 eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
1173 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
1174
1175 event.m_item = (WXHTREEITEM) tv->itemOld.hItem;
1176 break;
1177 }
1178
1179 case TVN_ENDLABELEDIT:
1180 {
1181 eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
1182 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1183
1184 event.m_item = (WXHTREEITEM)info->item.hItem;
1185 event.m_label = info->item.pszText;
1186 break;
1187 }
1188
1189 case TVN_GETDISPINFO:
1190 eventType = wxEVT_COMMAND_TREE_GET_INFO;
1191 // fall through
1192
1193 case TVN_SETDISPINFO:
1194 {
1195 if ( eventType == wxEVT_NULL )
1196 eventType = wxEVT_COMMAND_TREE_SET_INFO;
1197 //else: get, already set above
1198
1199 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
1200
1201 event.m_item = (WXHTREEITEM) info->item.hItem;
1202 break;
1203 }
1204
1205 case TVN_ITEMEXPANDING:
1206 event.m_code = FALSE;
1207 // fall through
1208
1209 case TVN_ITEMEXPANDED:
1210 {
1211 NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
1212
1213 bool expand = FALSE;
1214 switch ( tv->action )
1215 {
1216 case TVE_EXPAND:
1217 expand = TRUE;
1218 break;
1219
1220 case TVE_COLLAPSE:
1221 expand = FALSE;
1222 break;
1223
1224 default:
1225 wxLogDebug(_T("unexpected code %d in TVN_ITEMEXPAND "
1226 "message"), tv->action);
1227 }
1228
1229 bool ing = (hdr->code == TVN_ITEMEXPANDING);
1230 eventType = g_events[expand][ing];
1231
1232 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1233 break;
1234 }
1235
1236 case TVN_KEYDOWN:
1237 {
1238 eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
1239 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
1240
1241 event.m_code = wxCharCodeMSWToWX(info->wVKey);
1242
1243 // a separate event for this case
1244 if ( info->wVKey == VK_SPACE || info->wVKey == VK_RETURN )
1245 {
1246 wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
1247 m_windowId);
1248 event2.SetEventObject(this);
1249
1250 GetEventHandler()->ProcessEvent(event2);
1251 }
1252 break;
1253 }
1254
1255 case TVN_SELCHANGED:
1256 eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
1257 // fall through
1258
1259 case TVN_SELCHANGING:
1260 {
1261 if ( eventType == wxEVT_NULL )
1262 eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
1263 //else: already set above
1264
1265 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1266
1267 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1268 event.m_itemOld = (WXHTREEITEM) tv->itemOld.hItem;
1269 break;
1270 }
1271
1272 default:
1273 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1274 }
1275
1276 event.SetEventObject(this);
1277 event.SetEventType(eventType);
1278
1279 bool processed = GetEventHandler()->ProcessEvent(event);
1280
1281 // post processing
1282 switch ( hdr->code )
1283 {
1284 case TVN_DELETEITEM:
1285 {
1286 // NB: we might process this message using wxWindows event
1287 // tables, but due to overhead of wxWin event system we
1288 // prefer to do it here ourself (otherwise deleting a tree
1289 // with many items is just too slow)
1290 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1291 wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam;
1292 delete data; // may be NULL, ok
1293
1294 processed = TRUE; // Make sure we don't get called twice
1295 }
1296 break;
1297
1298 case TVN_BEGINLABELEDIT:
1299 // return TRUE to cancel label editing
1300 *result = !event.IsAllowed();
1301 break;
1302
1303 case TVN_ENDLABELEDIT:
1304 // return TRUE to set the label to the new string
1305 *result = event.IsAllowed();
1306
1307 // ensure that we don't have the text ctrl which is going to be
1308 // deleted any more
1309 DeleteTextCtrl();
1310 break;
1311
1312 case TVN_SELCHANGING:
1313 case TVN_ITEMEXPANDING:
1314 // return TRUE to prevent the action from happening
1315 *result = !event.IsAllowed();
1316 break;
1317
1318 //default:
1319 // for the other messages the return value is ignored and there is
1320 // nothing special to do
1321 }
1322
1323 return processed;
1324 }
1325
1326 // ----------------------------------------------------------------------------
1327 // Tree event
1328 // ----------------------------------------------------------------------------
1329
1330 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
1331
1332 wxTreeEvent::wxTreeEvent(wxEventType commandType, int id)
1333 : wxNotifyEvent(commandType, id)
1334 {
1335 m_code = 0;
1336 m_itemOld = 0;
1337 }
1338
1339 #endif // __WIN95__
1340