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