]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/treebase.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / treebase.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/common/treebase.cpp
3// Purpose: Base wxTreeCtrl classes
4// Author: Julian Smart
5// Created: 01/02/97
6// Modified:
7// Id: $Id$
8// Copyright: (c) 1998 Robert Roebling, Julian Smart et al
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// =============================================================================
13// declarations
14// =============================================================================
15
16// -----------------------------------------------------------------------------
17// headers
18// -----------------------------------------------------------------------------
19
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
27#if wxUSE_TREECTRL
28
29#include "wx/treectrl.h"
30#include "wx/imaglist.h"
31
32extern WXDLLEXPORT_DATA(const char) wxTreeCtrlNameStr[] = "treeCtrl";
33
34// ----------------------------------------------------------------------------
35// events
36// ----------------------------------------------------------------------------
37
38wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEvent );
39wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_RDRAG, wxTreeEvent );
40wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, wxTreeEvent );
41wxDEFINE_EVENT( wxEVT_COMMAND_TREE_END_LABEL_EDIT, wxTreeEvent );
42wxDEFINE_EVENT( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEvent );
43wxDEFINE_EVENT( wxEVT_COMMAND_TREE_GET_INFO, wxTreeEvent );
44wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SET_INFO, wxTreeEvent );
45wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEvent );
46wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_EXPANDING, wxTreeEvent );
47wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEvent );
48wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, wxTreeEvent );
49wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEvent );
50wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEvent );
51wxDEFINE_EVENT( wxEVT_COMMAND_TREE_KEY_DOWN, wxTreeEvent );
52wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEvent );
53wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, wxTreeEvent );
54wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, wxTreeEvent );
55wxDEFINE_EVENT( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEvent );
56wxDEFINE_EVENT( wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, wxTreeEvent );
57wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEvent );
58wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEvent );
59
60// ----------------------------------------------------------------------------
61// XTI
62// ----------------------------------------------------------------------------
63
64wxDEFINE_FLAGS( wxTreeCtrlStyle )
65wxBEGIN_FLAGS( wxTreeCtrlStyle )
66// new style border flags, we put them first to
67// use them for streaming out
68wxFLAGS_MEMBER(wxBORDER_SIMPLE)
69wxFLAGS_MEMBER(wxBORDER_SUNKEN)
70wxFLAGS_MEMBER(wxBORDER_DOUBLE)
71wxFLAGS_MEMBER(wxBORDER_RAISED)
72wxFLAGS_MEMBER(wxBORDER_STATIC)
73wxFLAGS_MEMBER(wxBORDER_NONE)
74
75// old style border flags
76wxFLAGS_MEMBER(wxSIMPLE_BORDER)
77wxFLAGS_MEMBER(wxSUNKEN_BORDER)
78wxFLAGS_MEMBER(wxDOUBLE_BORDER)
79wxFLAGS_MEMBER(wxRAISED_BORDER)
80wxFLAGS_MEMBER(wxSTATIC_BORDER)
81wxFLAGS_MEMBER(wxBORDER)
82
83// standard window styles
84wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
85wxFLAGS_MEMBER(wxCLIP_CHILDREN)
86wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
87wxFLAGS_MEMBER(wxWANTS_CHARS)
88wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
89wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
90wxFLAGS_MEMBER(wxVSCROLL)
91wxFLAGS_MEMBER(wxHSCROLL)
92
93wxFLAGS_MEMBER(wxTR_EDIT_LABELS)
94wxFLAGS_MEMBER(wxTR_NO_BUTTONS)
95wxFLAGS_MEMBER(wxTR_HAS_BUTTONS)
96wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS)
97wxFLAGS_MEMBER(wxTR_NO_LINES)
98wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT)
99wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT)
100wxFLAGS_MEMBER(wxTR_HIDE_ROOT)
101wxFLAGS_MEMBER(wxTR_ROW_LINES)
102wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT)
103wxFLAGS_MEMBER(wxTR_SINGLE)
104wxFLAGS_MEMBER(wxTR_MULTIPLE)
105#if WXWIN_COMPATIBILITY_2_8
106wxFLAGS_MEMBER(wxTR_EXTENDED)
107#endif
108wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE)
109wxEND_FLAGS( wxTreeCtrlStyle )
110
111wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl, "wx/treectrl.h")
112
113wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl)
114wxEVENT_PROPERTY( TextUpdated, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEvent )
115wxEVENT_RANGE_PROPERTY( TreeEvent, wxEVT_COMMAND_TREE_BEGIN_DRAG, \
116 wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, wxTreeEvent )
117
118wxPROPERTY_FLAGS( WindowStyle, wxTreeCtrlStyle, long, SetWindowStyleFlag, \
119 GetWindowStyleFlag, wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, \
120 wxT("Helpstring"), wxT("group")) // style
121wxEND_PROPERTIES_TABLE()
122
123wxEMPTY_HANDLERS_TABLE(wxTreeCtrl)
124
125wxCONSTRUCTOR_5( wxTreeCtrl, wxWindow*, Parent, wxWindowID, Id, \
126 wxPoint, Position, wxSize, Size, long, WindowStyle )
127
128// ----------------------------------------------------------------------------
129// Tree event
130// ----------------------------------------------------------------------------
131
132IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
133
134wxTreeEvent::wxTreeEvent(wxEventType commandType,
135 wxTreeCtrlBase *tree,
136 const wxTreeItemId& item)
137 : wxNotifyEvent(commandType, tree->GetId()),
138 m_item(item)
139{
140 m_editCancelled = false;
141
142 SetEventObject(tree);
143
144 if ( item.IsOk() )
145 SetClientObject(tree->GetItemData(item));
146}
147
148wxTreeEvent::wxTreeEvent(wxEventType commandType, int id)
149 : wxNotifyEvent(commandType, id)
150{
151 m_itemOld = 0l;
152 m_editCancelled = false;
153}
154
155wxTreeEvent::wxTreeEvent(const wxTreeEvent & event)
156 : wxNotifyEvent(event)
157{
158 m_evtKey = event.m_evtKey;
159 m_item = event.m_item;
160 m_itemOld = event.m_itemOld;
161 m_pointDrag = event.m_pointDrag;
162 m_label = event.m_label;
163 m_editCancelled = event.m_editCancelled;
164}
165
166// ----------------------------------------------------------------------------
167// wxTreeCtrlBase
168// ----------------------------------------------------------------------------
169
170wxTreeCtrlBase::wxTreeCtrlBase()
171{
172 m_imageListNormal =
173 m_imageListState = NULL;
174 m_ownsImageListNormal =
175 m_ownsImageListState = false;
176
177 // arbitrary default
178 m_spacing = 18;
179
180 // quick DoGetBestSize calculation
181 m_quickBestSize = true;
182
183 Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(wxTreeCtrlBase::OnCharHook));
184}
185
186wxTreeCtrlBase::~wxTreeCtrlBase()
187{
188 if (m_ownsImageListNormal)
189 delete m_imageListNormal;
190 if (m_ownsImageListState)
191 delete m_imageListState;
192}
193
194void wxTreeCtrlBase::SetItemState(const wxTreeItemId& item, int state)
195{
196 if ( state == wxTREE_ITEMSTATE_NEXT )
197 {
198 int current = GetItemState(item);
199 if ( current == wxTREE_ITEMSTATE_NONE )
200 return;
201 state = current + 1;
202 if ( m_imageListState && state >= m_imageListState->GetImageCount() )
203 state = 0;
204 }
205 else if ( state == wxTREE_ITEMSTATE_PREV )
206 {
207 int current = GetItemState(item);
208 if ( current == wxTREE_ITEMSTATE_NONE )
209 return;
210 state = current - 1;
211 if ( state == -1 )
212 state = m_imageListState ? m_imageListState->GetImageCount() - 1 : 0;
213 }
214 // else: wxTREE_ITEMSTATE_NONE depending on platform
215
216 DoSetItemState(item, state);
217}
218
219static void
220wxGetBestTreeSize(const wxTreeCtrlBase* treeCtrl, wxTreeItemId id, wxSize& size)
221{
222 wxRect rect;
223
224 if ( treeCtrl->GetBoundingRect(id, rect, true /* just the item */) )
225 {
226 // Translate to logical position so we get the full extent
227#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__)
228 rect.x += treeCtrl->GetScrollPos(wxHORIZONTAL);
229 rect.y += treeCtrl->GetScrollPos(wxVERTICAL);
230#endif
231
232 size.IncTo(wxSize(rect.GetRight(), rect.GetBottom()));
233 }
234
235 wxTreeItemIdValue cookie;
236 for ( wxTreeItemId item = treeCtrl->GetFirstChild(id, cookie);
237 item.IsOk();
238 item = treeCtrl->GetNextChild(id, cookie) )
239 {
240 wxGetBestTreeSize(treeCtrl, item, size);
241 }
242}
243
244wxSize wxTreeCtrlBase::DoGetBestSize() const
245{
246 wxSize size;
247
248 // this doesn't really compute the total bounding rectangle of all items
249 // but a not too bad guess of it which has the advantage of not having to
250 // examine all (potentially hundreds or thousands) items in the control
251
252 if (GetQuickBestSize())
253 {
254 for ( wxTreeItemId item = GetRootItem();
255 item.IsOk();
256 item = GetLastChild(item) )
257 {
258 wxRect rect;
259
260 // last parameter is "true" to get only the dimensions of the text
261 // label, we don't want to get the entire item width as it's determined
262 // by the current size
263 if ( GetBoundingRect(item, rect, true) )
264 {
265 if ( size.x < rect.x + rect.width )
266 size.x = rect.x + rect.width;
267 if ( size.y < rect.y + rect.height )
268 size.y = rect.y + rect.height;
269 }
270 }
271 }
272 else // use precise, if potentially slow, size computation method
273 {
274 // iterate over all items recursively
275 wxTreeItemId idRoot = GetRootItem();
276 if ( idRoot.IsOk() )
277 wxGetBestTreeSize(this, idRoot, size);
278 }
279
280 // need some minimal size even for empty tree
281 if ( !size.x || !size.y )
282 size = wxControl::DoGetBestSize();
283 else
284 {
285 // Add border size
286 size += GetWindowBorderSize();
287
288 CacheBestSize(size);
289 }
290
291 return size;
292}
293
294void wxTreeCtrlBase::ExpandAll()
295{
296 if ( IsEmpty() )
297 return;
298
299 ExpandAllChildren(GetRootItem());
300}
301
302void wxTreeCtrlBase::ExpandAllChildren(const wxTreeItemId& item)
303{
304 Freeze();
305 // expand this item first, this might result in its children being added on
306 // the fly
307 if ( item != GetRootItem() || !HasFlag(wxTR_HIDE_ROOT) )
308 Expand(item);
309 //else: expanding hidden root item is unsupported and unnecessary
310
311 // then (recursively) expand all the children
312 wxTreeItemIdValue cookie;
313 for ( wxTreeItemId idCurr = GetFirstChild(item, cookie);
314 idCurr.IsOk();
315 idCurr = GetNextChild(item, cookie) )
316 {
317 ExpandAllChildren(idCurr);
318 }
319 Thaw();
320}
321
322void wxTreeCtrlBase::CollapseAll()
323{
324 if ( IsEmpty() )
325 return;
326
327 CollapseAllChildren(GetRootItem());
328}
329
330void wxTreeCtrlBase::CollapseAllChildren(const wxTreeItemId& item)
331{
332 Freeze();
333 // first (recursively) collapse all the children
334 wxTreeItemIdValue cookie;
335 for ( wxTreeItemId idCurr = GetFirstChild(item, cookie);
336 idCurr.IsOk();
337 idCurr = GetNextChild(item, cookie) )
338 {
339 CollapseAllChildren(idCurr);
340 }
341
342 // then collapse this element too unless it's the hidden root which can't
343 // be collapsed
344 if ( item != GetRootItem() || !HasFlag(wxTR_HIDE_ROOT) )
345 Collapse(item);
346 Thaw();
347}
348
349bool wxTreeCtrlBase::IsEmpty() const
350{
351 return !GetRootItem().IsOk();
352}
353
354void wxTreeCtrlBase::OnCharHook(wxKeyEvent& event)
355{
356 if ( GetEditControl() )
357 {
358 bool discardChanges = false;
359 switch ( event.GetKeyCode() )
360 {
361 case WXK_ESCAPE:
362 discardChanges = true;
363 // fall through
364
365 case WXK_RETURN:
366 EndEditLabel(GetFocusedItem(), discardChanges);
367
368 // Do not call Skip() below.
369 return;
370 }
371 }
372
373 event.Skip();
374}
375
376#endif // wxUSE_TREECTRL