]> git.saurik.com Git - wxWidgets.git/blob - src/msw/treectrl.cpp
028f749428fe8ef377fa95e542b69c6b348ae3ac
[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 #ifndef WX_PRECOMP
34 #include "wx/settings.h"
35 #endif
36
37 // Mingw32 is a bit mental even though this is done in winundef
38 #ifdef GetFirstChild
39 #undef GetFirstChild
40 #endif
41
42 #ifdef GetNextSibling
43 #undef GetNextSibling
44 #endif
45
46 #if defined(__WIN95__)
47
48 #include "wx/log.h"
49 #include "wx/dynarray.h"
50 #include "wx/imaglist.h"
51 #include "wx/msw/treectrl.h"
52
53 #ifdef __GNUWIN32__
54 #include "wx/msw/gnuwin32/extra.h"
55 #endif
56
57 #if (defined(__WIN95__) && !defined(__GNUWIN32__)) || defined(__TWIN32__)
58 #include <commctrl.h>
59 #endif
60
61 // Bug in headers, sometimes
62 #ifndef TVIS_FOCUSED
63 #define TVIS_FOCUSED 0x0001
64 #endif
65
66 // ----------------------------------------------------------------------------
67 // private classes
68 // ----------------------------------------------------------------------------
69
70 // a convenient wrapper around TV_ITEM struct which adds a ctor
71 struct wxTreeViewItem : public TV_ITEM
72 {
73 wxTreeViewItem(const wxTreeItemId& item,
74 UINT mask_, UINT stateMask_ = 0)
75 {
76 mask = mask_;
77 stateMask = stateMask_;
78 hItem = (HTREEITEM) (WXHTREEITEM) item;
79 }
80 };
81
82 // ----------------------------------------------------------------------------
83 // macros
84 // ----------------------------------------------------------------------------
85
86 #if !USE_SHARED_LIBRARY
87 IMPLEMENT_DYNAMIC_CLASS(wxTreeCtrl, wxControl)
88 #endif
89
90 // ----------------------------------------------------------------------------
91 // variables
92 // ----------------------------------------------------------------------------
93
94 // handy table for sending events
95 static const wxEventType g_events[2][2] =
96 {
97 { wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxEVT_COMMAND_TREE_ITEM_COLLAPSING },
98 { wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxEVT_COMMAND_TREE_ITEM_EXPANDING }
99 };
100
101 // ============================================================================
102 // implementation
103 // ============================================================================
104
105 // ----------------------------------------------------------------------------
106 // construction and destruction
107 // ----------------------------------------------------------------------------
108
109 void wxTreeCtrl::Init()
110 {
111 m_imageListNormal = NULL;
112 m_imageListState = NULL;
113 m_textCtrl = NULL;
114 }
115
116 bool wxTreeCtrl::Create(wxWindow *parent, wxWindowID id,
117 const wxPoint& pos, const wxSize& size,
118 long style, const wxValidator& validator,
119 const wxString& name)
120 {
121 Init();
122
123 wxSystemSettings settings;
124
125 SetName(name);
126 SetValidator(validator);
127
128 m_windowStyle = style;
129
130 SetParent(parent);
131
132 m_windowId = (id == -1) ? NewControlId() : id;
133
134 DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | TVS_HASLINES | TVS_SHOWSELALWAYS ;
135
136
137 bool want3D;
138 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D) ;
139
140 // Even with extended styles, need to combine with WS_BORDER
141 // for them to look right.
142 if ( want3D || wxStyleHasBorder(m_windowStyle) )
143 {
144 wstyle |= WS_BORDER;
145 }
146
147 if ( m_windowStyle & wxTR_HAS_BUTTONS )
148 wstyle |= TVS_HASBUTTONS;
149
150 if ( m_windowStyle & wxTR_EDIT_LABELS )
151 wstyle |= TVS_EDITLABELS;
152
153 if ( m_windowStyle & wxTR_LINES_AT_ROOT )
154 wstyle |= TVS_LINESATROOT;
155
156 // Create the tree control.
157 m_hWnd = (WXHWND)::CreateWindowEx
158 (
159 exStyle,
160 WC_TREEVIEW,
161 _T(""),
162 wstyle,
163 pos.x, pos.y, size.x, size.y,
164 (HWND)parent->GetHWND(),
165 (HMENU)m_windowId,
166 wxGetInstance(),
167 NULL
168 );
169
170 wxCHECK_MSG( m_hWnd, FALSE, _T("Failed to create tree ctrl") );
171
172 if ( parent )
173 parent->AddChild(this);
174
175 SubclassWin(m_hWnd);
176
177 return TRUE;
178 }
179
180 wxTreeCtrl::~wxTreeCtrl()
181 {
182 DeleteTextCtrl();
183
184 // delete user data to prevent memory leaks
185 DeleteAllItems();
186 }
187
188 // ----------------------------------------------------------------------------
189 // accessors
190 // ----------------------------------------------------------------------------
191
192 // simple wrappers which add error checking in debug mode
193
194 bool wxTreeCtrl::DoGetItem(wxTreeViewItem* tvItem) const
195 {
196 if ( !TreeView_GetItem(GetHwnd(), tvItem) )
197 {
198 wxLogLastError("TreeView_GetItem");
199
200 return FALSE;
201 }
202
203 return TRUE;
204 }
205
206 void wxTreeCtrl::DoSetItem(wxTreeViewItem* tvItem)
207 {
208 if ( TreeView_SetItem(GetHwnd(), tvItem) == -1 )
209 {
210 wxLogLastError("TreeView_SetItem");
211 }
212 }
213
214 size_t wxTreeCtrl::GetCount() const
215 {
216 return (size_t)TreeView_GetCount(GetHwnd());
217 }
218
219 unsigned int wxTreeCtrl::GetIndent() const
220 {
221 return TreeView_GetIndent(GetHwnd());
222 }
223
224 void wxTreeCtrl::SetIndent(unsigned int indent)
225 {
226 TreeView_SetIndent(GetHwnd(), indent);
227 }
228
229 wxImageList *wxTreeCtrl::GetImageList() const
230 {
231 return m_imageListNormal;
232 }
233
234 wxImageList *wxTreeCtrl::GetStateImageList() const
235 {
236 return m_imageListNormal;
237 }
238
239 void wxTreeCtrl::SetAnyImageList(wxImageList *imageList, int which)
240 {
241 // no error return
242 TreeView_SetImageList(GetHwnd(),
243 imageList ? imageList->GetHIMAGELIST() : 0,
244 which);
245 }
246
247 void wxTreeCtrl::SetImageList(wxImageList *imageList)
248 {
249 SetAnyImageList(m_imageListNormal = imageList, TVSIL_NORMAL);
250 }
251
252 void wxTreeCtrl::SetStateImageList(wxImageList *imageList)
253 {
254 SetAnyImageList(m_imageListState = imageList, TVSIL_STATE);
255 }
256
257 size_t wxTreeCtrl::GetChildrenCount(const wxTreeItemId& item, bool recursively)
258 {
259 long cookie;
260
261 size_t result = 0;
262
263 wxArrayLong children;
264 wxTreeItemId child = GetFirstChild(item, cookie);
265 while ( child.IsOk() )
266 {
267 if ( recursively )
268 {
269 // recursive call
270 result += GetChildrenCount(child, TRUE);
271 }
272
273 // add the child to the result in any case
274 result++;
275
276 child = GetNextChild(item, cookie);
277 }
278
279 return result;
280 }
281
282 // ----------------------------------------------------------------------------
283 // Item access
284 // ----------------------------------------------------------------------------
285
286 wxString wxTreeCtrl::GetItemText(const wxTreeItemId& item) const
287 {
288 wxChar buf[512]; // the size is arbitrary...
289
290 wxTreeViewItem tvItem(item, TVIF_TEXT);
291 tvItem.pszText = buf;
292 tvItem.cchTextMax = WXSIZEOF(buf);
293 if ( !DoGetItem(&tvItem) )
294 {
295 // don't return some garbage which was on stack, but an empty string
296 buf[0] = _T('\0');
297 }
298
299 return wxString(buf);
300 }
301
302 void wxTreeCtrl::SetItemText(const wxTreeItemId& item, const wxString& text)
303 {
304 wxTreeViewItem tvItem(item, TVIF_TEXT);
305 tvItem.pszText = (wxChar *)text.c_str(); // conversion is ok
306 DoSetItem(&tvItem);
307 }
308
309 int wxTreeCtrl::GetItemImage(const wxTreeItemId& item) const
310 {
311 wxTreeViewItem tvItem(item, TVIF_IMAGE);
312 DoGetItem(&tvItem);
313
314 return tvItem.iImage;
315 }
316
317 void wxTreeCtrl::SetItemImage(const wxTreeItemId& item, int image)
318 {
319 wxTreeViewItem tvItem(item, TVIF_IMAGE);
320 tvItem.iImage = image;
321 DoSetItem(&tvItem);
322 }
323
324 int wxTreeCtrl::GetItemSelectedImage(const wxTreeItemId& item) const
325 {
326 wxTreeViewItem tvItem(item, TVIF_SELECTEDIMAGE);
327 DoGetItem(&tvItem);
328
329 return tvItem.iSelectedImage;
330 }
331
332 void wxTreeCtrl::SetItemSelectedImage(const wxTreeItemId& item, int image)
333 {
334 wxTreeViewItem tvItem(item, TVIF_SELECTEDIMAGE);
335 tvItem.iSelectedImage = image;
336 DoSetItem(&tvItem);
337 }
338
339 wxTreeItemData *wxTreeCtrl::GetItemData(const wxTreeItemId& item) const
340 {
341 wxTreeViewItem tvItem(item, TVIF_PARAM);
342 if ( !DoGetItem(&tvItem) )
343 {
344 return NULL;
345 }
346
347 return (wxTreeItemData *)tvItem.lParam;
348 }
349
350 void wxTreeCtrl::SetItemData(const wxTreeItemId& item, wxTreeItemData *data)
351 {
352 wxTreeViewItem tvItem(item, TVIF_PARAM);
353 tvItem.lParam = (LPARAM)data;
354 DoSetItem(&tvItem);
355 }
356
357 void wxTreeCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has)
358 {
359 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
360 tvItem.cChildren = (int)has;
361 DoSetItem(&tvItem);
362 }
363
364 void wxTreeCtrl::SetItemBold(const wxTreeItemId& item, bool bold)
365 {
366 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
367 tvItem.state = bold ? TVIS_BOLD : 0;
368 DoSetItem(&tvItem);
369 }
370
371 void wxTreeCtrl::SetItemDropHighlight(const wxTreeItemId& item, bool highlight)
372 {
373 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_DROPHILITED);
374 tvItem.state = highlight ? TVIS_DROPHILITED : 0;
375 DoSetItem(&tvItem);
376 }
377
378 // ----------------------------------------------------------------------------
379 // Item status
380 // ----------------------------------------------------------------------------
381
382 bool wxTreeCtrl::IsVisible(const wxTreeItemId& item) const
383 {
384 // Bug in Gnu-Win32 headers, so don't use the macro TreeView_GetItemRect
385 RECT rect;
386 return SendMessage(GetHwnd(), TVM_GETITEMRECT, FALSE, (LPARAM)&rect) != 0;
387
388 }
389
390 bool wxTreeCtrl::ItemHasChildren(const wxTreeItemId& item) const
391 {
392 wxTreeViewItem tvItem(item, TVIF_CHILDREN);
393 DoGetItem(&tvItem);
394
395 return tvItem.cChildren != 0;
396 }
397
398 bool wxTreeCtrl::IsExpanded(const wxTreeItemId& item) const
399 {
400 // probably not a good idea to put it here
401 //wxASSERT( ItemHasChildren(item) );
402
403 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDED);
404 DoGetItem(&tvItem);
405
406 return (tvItem.state & TVIS_EXPANDED) != 0;
407 }
408
409 bool wxTreeCtrl::IsSelected(const wxTreeItemId& item) const
410 {
411 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_SELECTED);
412 DoGetItem(&tvItem);
413
414 return (tvItem.state & TVIS_SELECTED) != 0;
415 }
416
417 bool wxTreeCtrl::IsBold(const wxTreeItemId& item) const
418 {
419 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_BOLD);
420 DoGetItem(&tvItem);
421
422 return (tvItem.state & TVIS_BOLD) != 0;
423 }
424
425 // ----------------------------------------------------------------------------
426 // navigation
427 // ----------------------------------------------------------------------------
428
429 wxTreeItemId wxTreeCtrl::GetRootItem() const
430 {
431 return wxTreeItemId((WXHTREEITEM) TreeView_GetRoot(GetHwnd()));
432 }
433
434 wxTreeItemId wxTreeCtrl::GetSelection() const
435 {
436 return wxTreeItemId((WXHTREEITEM) TreeView_GetSelection(GetHwnd()));
437 }
438
439 wxTreeItemId wxTreeCtrl::GetParent(const wxTreeItemId& item) const
440 {
441 return wxTreeItemId((WXHTREEITEM) TreeView_GetParent(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
442 }
443
444 wxTreeItemId wxTreeCtrl::GetFirstChild(const wxTreeItemId& item,
445 long& _cookie) const
446 {
447 // remember the last child returned in 'cookie'
448 _cookie = (long)TreeView_GetChild(GetHwnd(), (HTREEITEM) (WXHTREEITEM)item);
449
450 return wxTreeItemId((WXHTREEITEM)_cookie);
451 }
452
453 wxTreeItemId wxTreeCtrl::GetNextChild(const wxTreeItemId& WXUNUSED(item),
454 long& _cookie) const
455 {
456 wxTreeItemId l = wxTreeItemId((WXHTREEITEM)TreeView_GetNextSibling(GetHwnd(),
457 (HTREEITEM)(WXHTREEITEM)_cookie));
458 _cookie = (long)l;
459
460 return l;
461 }
462
463 wxTreeItemId wxTreeCtrl::GetLastChild(const wxTreeItemId& item) const
464 {
465 // can this be done more efficiently?
466 long cookie;
467
468 wxTreeItemId childLast,
469 child = GetFirstChild(item, cookie);
470 while ( child.IsOk() )
471 {
472 childLast = child;
473 child = GetNextChild(item, cookie);
474 }
475
476 return childLast;
477 }
478
479 wxTreeItemId wxTreeCtrl::GetNextSibling(const wxTreeItemId& item) const
480 {
481 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
482 }
483
484 wxTreeItemId wxTreeCtrl::GetPrevSibling(const wxTreeItemId& item) const
485 {
486 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevSibling(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
487 }
488
489 wxTreeItemId wxTreeCtrl::GetFirstVisibleItem() const
490 {
491 return wxTreeItemId((WXHTREEITEM) TreeView_GetFirstVisible(GetHwnd()));
492 }
493
494 wxTreeItemId wxTreeCtrl::GetNextVisible(const wxTreeItemId& item) const
495 {
496 wxASSERT_MSG( IsVisible(item), _T("The item you call GetNextVisible() "
497 "for must be visible itself!"));
498
499 return wxTreeItemId((WXHTREEITEM) TreeView_GetNextVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
500 }
501
502 wxTreeItemId wxTreeCtrl::GetPrevVisible(const wxTreeItemId& item) const
503 {
504 wxASSERT_MSG( IsVisible(item), _T("The item you call GetPrevVisible() "
505 "for must be visible itself!"));
506
507 return wxTreeItemId((WXHTREEITEM) TreeView_GetPrevVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item));
508 }
509
510 // ----------------------------------------------------------------------------
511 // Usual operations
512 // ----------------------------------------------------------------------------
513
514 wxTreeItemId wxTreeCtrl::DoInsertItem(const wxTreeItemId& parent,
515 wxTreeItemId hInsertAfter,
516 const wxString& text,
517 int image, int selectedImage,
518 wxTreeItemData *data)
519 {
520 TV_INSERTSTRUCT tvIns;
521 tvIns.hParent = (HTREEITEM) (WXHTREEITEM)parent;
522 tvIns.hInsertAfter = (HTREEITEM) (WXHTREEITEM) hInsertAfter;
523
524 // This is how we insert the item as the first child: supply a NULL hInsertAfter
525 if (tvIns.hInsertAfter == (HTREEITEM) 0)
526 {
527 tvIns.hInsertAfter = TVI_FIRST;
528 }
529
530 UINT mask = 0;
531 if ( !text.IsEmpty() )
532 {
533 mask |= TVIF_TEXT;
534 tvIns.item.pszText = (wxChar *)text.c_str(); // cast is ok
535 }
536
537 if ( image != -1 )
538 {
539 mask |= TVIF_IMAGE;
540 tvIns.item.iImage = image;
541
542 if ( selectedImage == -1 )
543 {
544 // take the same image for selected icon if not specified
545 selectedImage = image;
546 }
547 }
548
549 if ( selectedImage != -1 )
550 {
551 mask |= TVIF_SELECTEDIMAGE;
552 tvIns.item.iSelectedImage = selectedImage;
553 }
554
555 if ( data != NULL )
556 {
557 mask |= TVIF_PARAM;
558 tvIns.item.lParam = (LPARAM)data;
559 }
560
561 tvIns.item.mask = mask;
562
563 HTREEITEM id = (HTREEITEM) TreeView_InsertItem(GetHwnd(), &tvIns);
564 if ( id == 0 )
565 {
566 wxLogLastError("TreeView_InsertItem");
567 }
568
569 if ( data != NULL )
570 {
571 // associate the application tree item with Win32 tree item handle
572 data->SetId((WXHTREEITEM)id);
573 }
574
575 return wxTreeItemId((WXHTREEITEM)id);
576 }
577
578 // for compatibility only
579 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
580 const wxString& text,
581 int image, int selImage,
582 long insertAfter)
583 {
584 return DoInsertItem(parent, (WXHTREEITEM)insertAfter, text,
585 image, selImage, NULL);
586 }
587
588 wxTreeItemId wxTreeCtrl::AddRoot(const wxString& text,
589 int image, int selectedImage,
590 wxTreeItemData *data)
591 {
592 return DoInsertItem(wxTreeItemId((WXHTREEITEM) 0), (WXHTREEITEM) 0,
593 text, image, selectedImage, data);
594 }
595
596 wxTreeItemId wxTreeCtrl::PrependItem(const wxTreeItemId& parent,
597 const wxString& text,
598 int image, int selectedImage,
599 wxTreeItemData *data)
600 {
601 return DoInsertItem(parent, (WXHTREEITEM) TVI_FIRST,
602 text, image, selectedImage, data);
603 }
604
605 wxTreeItemId wxTreeCtrl::InsertItem(const wxTreeItemId& parent,
606 const wxTreeItemId& idPrevious,
607 const wxString& text,
608 int image, int selectedImage,
609 wxTreeItemData *data)
610 {
611 return DoInsertItem(parent, idPrevious, text, image, selectedImage, data);
612 }
613
614 wxTreeItemId wxTreeCtrl::AppendItem(const wxTreeItemId& parent,
615 const wxString& text,
616 int image, int selectedImage,
617 wxTreeItemData *data)
618 {
619 return DoInsertItem(parent, (WXHTREEITEM) TVI_LAST,
620 text, image, selectedImage, data);
621 }
622
623 void wxTreeCtrl::Delete(const wxTreeItemId& item)
624 {
625 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item) )
626 {
627 wxLogLastError("TreeView_DeleteItem");
628 }
629 }
630
631 // delete all children (but don't delete the item itself)
632 void wxTreeCtrl::DeleteChildren(const wxTreeItemId& item)
633 {
634 long cookie;
635
636 wxArrayLong children;
637 wxTreeItemId child = GetFirstChild(item, cookie);
638 while ( child.IsOk() )
639 {
640 children.Add((long)(WXHTREEITEM)child);
641
642 child = GetNextChild(item, cookie);
643 }
644
645 size_t nCount = children.Count();
646 for ( size_t n = 0; n < nCount; n++ )
647 {
648 if ( !TreeView_DeleteItem(GetHwnd(), (HTREEITEM)children[n]) )
649 {
650 wxLogLastError("TreeView_DeleteItem");
651 }
652 }
653 }
654
655 void wxTreeCtrl::DeleteAllItems()
656 {
657 if ( !TreeView_DeleteAllItems(GetHwnd()) )
658 {
659 wxLogLastError("TreeView_DeleteAllItems");
660 }
661 }
662
663 void wxTreeCtrl::DoExpand(const wxTreeItemId& item, int flag)
664 {
665 wxASSERT_MSG( flag == TVE_COLLAPSE ||
666 flag == (TVE_COLLAPSE | TVE_COLLAPSERESET) ||
667 flag == TVE_EXPAND ||
668 flag == TVE_TOGGLE,
669 _T("Unknown flag in wxTreeCtrl::DoExpand") );
670
671 // TreeView_Expand doesn't send TVN_ITEMEXPAND(ING) messages, so we must
672 // emulate them. This behaviour has changed slightly with comctl32.dll
673 // v 4.70 - now it does send them but only the first time. To maintain
674 // compatible behaviour and also in order to not have surprises with the
675 // future versions, don't rely on this and still do everything ourselves.
676 // To avoid that the messages be sent twice when the item is expanded for
677 // the first time we must clear TVIS_EXPANDEDONCE style manually.
678
679 wxTreeViewItem tvItem(item, TVIF_STATE, TVIS_EXPANDEDONCE);
680 tvItem.state = 0;
681 DoSetItem(&tvItem);
682
683 if ( TreeView_Expand(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item, flag) != 0 )
684 {
685 wxTreeEvent event(wxEVT_NULL, m_windowId);
686 event.m_item = item;
687
688 bool isExpanded = IsExpanded(item);
689
690 event.SetEventObject(this);
691
692 // FIXME return value of {EXPAND|COLLAPS}ING event handler is discarded
693 event.SetEventType(g_events[isExpanded][TRUE]);
694 GetEventHandler()->ProcessEvent(event);
695
696 event.SetEventType(g_events[isExpanded][FALSE]);
697 GetEventHandler()->ProcessEvent(event);
698 }
699 //else: change didn't took place, so do nothing at all
700 }
701
702 void wxTreeCtrl::Expand(const wxTreeItemId& item)
703 {
704 DoExpand(item, TVE_EXPAND);
705 }
706
707 void wxTreeCtrl::Collapse(const wxTreeItemId& item)
708 {
709 DoExpand(item, TVE_COLLAPSE);
710 }
711
712 void wxTreeCtrl::CollapseAndReset(const wxTreeItemId& item)
713 {
714 DoExpand(item, TVE_COLLAPSE | TVE_COLLAPSERESET);
715 }
716
717 void wxTreeCtrl::Toggle(const wxTreeItemId& item)
718 {
719 DoExpand(item, TVE_TOGGLE);
720 }
721
722 void wxTreeCtrl::ExpandItem(const wxTreeItemId& item, int action)
723 {
724 DoExpand(item, action);
725 }
726
727 void wxTreeCtrl::Unselect()
728 {
729 SelectItem(wxTreeItemId((WXHTREEITEM) 0));
730 }
731
732 void wxTreeCtrl::SelectItem(const wxTreeItemId& item)
733 {
734 // inspite of the docs (MSDN Jan 99 edition), we don't seem to receive
735 // the notification from the control (i.e. TVN_SELCHANG{ED|ING}), so
736 // send them ourselves
737
738 wxTreeEvent event(wxEVT_NULL, m_windowId);
739 event.m_item = item;
740 event.SetEventObject(this);
741
742 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGING);
743 if ( !GetEventHandler()->ProcessEvent(event) || event.IsAllowed() )
744 {
745 if ( !TreeView_SelectItem(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
746 {
747 wxLogLastError("TreeView_SelectItem");
748 }
749 else
750 {
751 event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED);
752 (void)GetEventHandler()->ProcessEvent(event);
753 }
754 }
755 //else: program vetoed the change
756 }
757
758 void wxTreeCtrl::EnsureVisible(const wxTreeItemId& item)
759 {
760 // no error return
761 TreeView_EnsureVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
762 }
763
764 void wxTreeCtrl::ScrollTo(const wxTreeItemId& item)
765 {
766 if ( !TreeView_SelectSetFirstVisible(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item) )
767 {
768 wxLogLastError("TreeView_SelectSetFirstVisible");
769 }
770 }
771
772 wxTextCtrl* wxTreeCtrl::GetEditControl() const
773 {
774 return m_textCtrl;
775 }
776
777 void wxTreeCtrl::DeleteTextCtrl()
778 {
779 if ( m_textCtrl )
780 {
781 m_textCtrl->UnsubclassWin();
782 m_textCtrl->SetHWND(0);
783 delete m_textCtrl;
784 m_textCtrl = NULL;
785 }
786 }
787
788 wxTextCtrl* wxTreeCtrl::EditLabel(const wxTreeItemId& item,
789 wxClassInfo* textControlClass)
790 {
791 wxASSERT( textControlClass->IsKindOf(CLASSINFO(wxTextCtrl)) );
792
793 HWND hWnd = (HWND) TreeView_EditLabel(GetHwnd(), (HTREEITEM) (WXHTREEITEM) item);
794
795 wxCHECK_MSG( hWnd, NULL, _T("Can't edit tree ctrl label") );
796
797 DeleteTextCtrl();
798
799 m_textCtrl = (wxTextCtrl *)textControlClass->CreateObject();
800 m_textCtrl->SetHWND((WXHWND)hWnd);
801 m_textCtrl->SubclassWin((WXHWND)hWnd);
802
803 return m_textCtrl;
804 }
805
806 // End label editing, optionally cancelling the edit
807 void wxTreeCtrl::EndEditLabel(const wxTreeItemId& item, bool discardChanges)
808 {
809 TreeView_EndEditLabelNow(GetHwnd(), discardChanges);
810
811 DeleteTextCtrl();
812 }
813
814 wxTreeItemId wxTreeCtrl::HitTest(const wxPoint& point, int& flags)
815 {
816 TV_HITTESTINFO hitTestInfo;
817 hitTestInfo.pt.x = (int)point.x;
818 hitTestInfo.pt.y = (int)point.y;
819
820 TreeView_HitTest(GetHwnd(), &hitTestInfo);
821
822 flags = 0;
823
824 // avoid repetition
825 #define TRANSLATE_FLAG(flag) if ( hitTestInfo.flags & TVHT_##flag ) \
826 flags |= wxTREE_HITTEST_##flag
827
828 TRANSLATE_FLAG(ABOVE);
829 TRANSLATE_FLAG(BELOW);
830 TRANSLATE_FLAG(NOWHERE);
831 TRANSLATE_FLAG(ONITEMBUTTON);
832 TRANSLATE_FLAG(ONITEMICON);
833 TRANSLATE_FLAG(ONITEMINDENT);
834 TRANSLATE_FLAG(ONITEMLABEL);
835 TRANSLATE_FLAG(ONITEMRIGHT);
836 TRANSLATE_FLAG(ONITEMSTATEICON);
837 TRANSLATE_FLAG(TOLEFT);
838 TRANSLATE_FLAG(TORIGHT);
839
840 #undef TRANSLATE_FLAG
841
842 return wxTreeItemId((WXHTREEITEM) hitTestInfo.hItem);
843 }
844
845 bool wxTreeCtrl::GetBoundingRect(const wxTreeItemId& item,
846 wxRect& rect,
847 bool textOnly) const
848 {
849 RECT rc;
850 if ( TreeView_GetItemRect(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item,
851 &rc, textOnly) )
852 {
853 rect = wxRect(wxPoint(rc.left, rc.top), wxPoint(rc.right, rc.bottom));
854
855 return TRUE;
856 }
857 else
858 {
859 // couldn't retrieve rect: for example, item isn't visible
860 return FALSE;
861 }
862 }
863
864 // ----------------------------------------------------------------------------
865 // sorting stuff
866 // ----------------------------------------------------------------------------
867
868 static int CALLBACK TreeView_CompareCallback(wxTreeItemData *pItem1,
869 wxTreeItemData *pItem2,
870 wxTreeCtrl *tree)
871 {
872 wxCHECK_MSG( pItem1 && pItem2, 0,
873 _T("sorting tree without data doesn't make sense") );
874
875 return tree->OnCompareItems(pItem1->GetId(), pItem2->GetId());
876 }
877
878 int wxTreeCtrl::OnCompareItems(const wxTreeItemId& item1,
879 const wxTreeItemId& item2)
880 {
881 return wxStrcmp(GetItemText(item1), GetItemText(item2));
882 }
883
884 void wxTreeCtrl::SortChildren(const wxTreeItemId& item)
885 {
886 // rely on the fact that TreeView_SortChildren does the same thing as our
887 // default behaviour, i.e. sorts items alphabetically and so call it
888 // directly if we're not in derived class (much more efficient!)
889 if ( GetClassInfo() == CLASSINFO(wxTreeCtrl) )
890 {
891 TreeView_SortChildren(GetHwnd(), (HTREEITEM)(WXHTREEITEM)item, 0);
892 }
893 else
894 {
895 TV_SORTCB tvSort;
896 tvSort.hParent = (HTREEITEM)(WXHTREEITEM)item;
897 tvSort.lpfnCompare = (PFNTVCOMPARE)TreeView_CompareCallback;
898 tvSort.lParam = (LPARAM)this;
899 TreeView_SortChildrenCB(GetHwnd(), &tvSort, 0 /* reserved */);
900 }
901 }
902
903 // ----------------------------------------------------------------------------
904 // implementation
905 // ----------------------------------------------------------------------------
906
907 bool wxTreeCtrl::MSWCommand(WXUINT cmd, WXWORD id)
908 {
909 if ( cmd == EN_UPDATE )
910 {
911 wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, id);
912 event.SetEventObject( this );
913 ProcessCommand(event);
914 }
915 else if ( cmd == EN_KILLFOCUS )
916 {
917 wxCommandEvent event(wxEVT_KILL_FOCUS, id);
918 event.SetEventObject( this );
919 ProcessCommand(event);
920 }
921 else
922 {
923 // nothing done
924 return FALSE;
925 }
926
927 // command processed
928 return TRUE;
929 }
930
931 // process WM_NOTIFY Windows message
932 bool wxTreeCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
933 {
934 wxTreeEvent event(wxEVT_NULL, m_windowId);
935 wxEventType eventType = wxEVT_NULL;
936 NMHDR *hdr = (NMHDR *)lParam;
937
938 switch ( hdr->code )
939 {
940 case TVN_BEGINDRAG:
941 eventType = wxEVT_COMMAND_TREE_BEGIN_DRAG;
942 // fall through
943
944 case TVN_BEGINRDRAG:
945 {
946 if ( eventType == wxEVT_NULL )
947 eventType = wxEVT_COMMAND_TREE_BEGIN_RDRAG;
948 //else: left drag, already set above
949
950 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
951
952 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
953 event.m_pointDrag = wxPoint(tv->ptDrag.x, tv->ptDrag.y);
954 break;
955 }
956
957 case TVN_BEGINLABELEDIT:
958 {
959 eventType = wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT;
960 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
961
962 event.m_item = (WXHTREEITEM) info->item.hItem;
963 break;
964 }
965
966 case TVN_DELETEITEM:
967 {
968 eventType = wxEVT_COMMAND_TREE_DELETE_ITEM;
969 NM_TREEVIEW *tv = (NM_TREEVIEW *)lParam;
970
971 event.m_item = (WXHTREEITEM) tv->itemOld.hItem;
972 break;
973 }
974
975 case TVN_ENDLABELEDIT:
976 {
977 eventType = wxEVT_COMMAND_TREE_END_LABEL_EDIT;
978 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
979
980 event.m_item = (WXHTREEITEM) info->item.hItem;
981 break;
982 }
983
984 case TVN_GETDISPINFO:
985 eventType = wxEVT_COMMAND_TREE_GET_INFO;
986 // fall through
987
988 case TVN_SETDISPINFO:
989 {
990 if ( eventType == wxEVT_NULL )
991 eventType = wxEVT_COMMAND_TREE_SET_INFO;
992 //else: get, already set above
993
994 TV_DISPINFO *info = (TV_DISPINFO *)lParam;
995
996 event.m_item = (WXHTREEITEM) info->item.hItem;
997 break;
998 }
999
1000 case TVN_ITEMEXPANDING:
1001 event.m_code = FALSE;
1002 // fall through
1003
1004 case TVN_ITEMEXPANDED:
1005 {
1006 NM_TREEVIEW* tv = (NM_TREEVIEW*)lParam;
1007
1008 bool expand = FALSE;
1009 switch ( tv->action )
1010 {
1011 case TVE_EXPAND:
1012 expand = TRUE;
1013 break;
1014
1015 case TVE_COLLAPSE:
1016 expand = FALSE;
1017 break;
1018
1019 default:
1020 wxLogDebug(_T("unexpected code %d in TVN_ITEMEXPAND "
1021 "message"), tv->action);
1022 }
1023
1024 bool ing = (hdr->code == TVN_ITEMEXPANDING);
1025 eventType = g_events[expand][ing];
1026
1027 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1028 break;
1029 }
1030
1031 case TVN_KEYDOWN:
1032 {
1033 eventType = wxEVT_COMMAND_TREE_KEY_DOWN;
1034 TV_KEYDOWN *info = (TV_KEYDOWN *)lParam;
1035
1036 event.m_code = wxCharCodeMSWToWX(info->wVKey);
1037
1038 // a separate event for this case
1039 if ( info->wVKey == VK_SPACE || info->wVKey == VK_RETURN )
1040 {
1041 wxTreeEvent event2(wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
1042 m_windowId);
1043 event2.SetEventObject(this);
1044
1045 GetEventHandler()->ProcessEvent(event2);
1046 }
1047 break;
1048 }
1049
1050 case TVN_SELCHANGED:
1051 eventType = wxEVT_COMMAND_TREE_SEL_CHANGED;
1052 // fall through
1053
1054 case TVN_SELCHANGING:
1055 {
1056 if ( eventType == wxEVT_NULL )
1057 eventType = wxEVT_COMMAND_TREE_SEL_CHANGING;
1058 //else: already set above
1059
1060 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1061
1062 event.m_item = (WXHTREEITEM) tv->itemNew.hItem;
1063 event.m_itemOld = (WXHTREEITEM) tv->itemOld.hItem;
1064 break;
1065 }
1066
1067 default:
1068 return wxControl::MSWOnNotify(idCtrl, lParam, result);
1069 }
1070
1071 event.SetEventObject(this);
1072 event.SetEventType(eventType);
1073
1074 bool processed = GetEventHandler()->ProcessEvent(event);
1075
1076 // post processing
1077 if ( hdr->code == TVN_DELETEITEM )
1078 {
1079 // NB: we might process this message using wxWindows event tables, but
1080 // due to overhead of wxWin event system we prefer to do it here
1081 // (otherwise deleting a tree with many items is just too slow)
1082 NM_TREEVIEW* tv = (NM_TREEVIEW *)lParam;
1083 wxTreeItemData *data = (wxTreeItemData *)tv->itemOld.lParam;
1084 delete data; // may be NULL, ok
1085 processed = TRUE; // Make sure we don't get called twice
1086 }
1087
1088 *result = !event.IsAllowed();
1089
1090 return processed;
1091 }
1092
1093 // ----------------------------------------------------------------------------
1094 // Tree event
1095 // ----------------------------------------------------------------------------
1096
1097 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
1098
1099 wxTreeEvent::wxTreeEvent(wxEventType commandType, int id)
1100 : wxNotifyEvent(commandType, id)
1101 {
1102 m_code = 0;
1103 m_itemOld = 0;
1104 }
1105
1106 #endif // __WIN95__
1107