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