]> git.saurik.com Git - wxWidgets.git/blob - src/common/treebase.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / treebase.cpp
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
32 extern WXDLLEXPORT_DATA(const char) wxTreeCtrlNameStr[] = "treeCtrl";
33
34 // ----------------------------------------------------------------------------
35 // events
36 // ----------------------------------------------------------------------------
37
38 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_DRAG, wxTreeEvent );
39 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_RDRAG, wxTreeEvent );
40 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, wxTreeEvent );
41 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_END_LABEL_EDIT, wxTreeEvent );
42 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEvent );
43 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_GET_INFO, wxTreeEvent );
44 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SET_INFO, wxTreeEvent );
45 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_EXPANDED, wxTreeEvent );
46 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_EXPANDING, wxTreeEvent );
47 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_COLLAPSED, wxTreeEvent );
48 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_COLLAPSING, wxTreeEvent );
49 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEvent );
50 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_SEL_CHANGING, wxTreeEvent );
51 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_KEY_DOWN, wxTreeEvent );
52 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_ACTIVATED, wxTreeEvent );
53 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, wxTreeEvent );
54 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, wxTreeEvent );
55 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_END_DRAG, wxTreeEvent );
56 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, wxTreeEvent );
57 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP, wxTreeEvent );
58 wxDEFINE_EVENT( wxEVT_COMMAND_TREE_ITEM_MENU, wxTreeEvent );
59
60 // ----------------------------------------------------------------------------
61 // XTI
62 // ----------------------------------------------------------------------------
63
64 wxDEFINE_FLAGS( wxTreeCtrlStyle )
65 wxBEGIN_FLAGS( wxTreeCtrlStyle )
66 // new style border flags, we put them first to
67 // use them for streaming out
68 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
69 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
70 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
71 wxFLAGS_MEMBER(wxBORDER_RAISED)
72 wxFLAGS_MEMBER(wxBORDER_STATIC)
73 wxFLAGS_MEMBER(wxBORDER_NONE)
74
75 // old style border flags
76 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
77 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
78 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
79 wxFLAGS_MEMBER(wxRAISED_BORDER)
80 wxFLAGS_MEMBER(wxSTATIC_BORDER)
81 wxFLAGS_MEMBER(wxBORDER)
82
83 // standard window styles
84 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
85 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
86 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
87 wxFLAGS_MEMBER(wxWANTS_CHARS)
88 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
89 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
90 wxFLAGS_MEMBER(wxVSCROLL)
91 wxFLAGS_MEMBER(wxHSCROLL)
92
93 wxFLAGS_MEMBER(wxTR_EDIT_LABELS)
94 wxFLAGS_MEMBER(wxTR_NO_BUTTONS)
95 wxFLAGS_MEMBER(wxTR_HAS_BUTTONS)
96 wxFLAGS_MEMBER(wxTR_TWIST_BUTTONS)
97 wxFLAGS_MEMBER(wxTR_NO_LINES)
98 wxFLAGS_MEMBER(wxTR_FULL_ROW_HIGHLIGHT)
99 wxFLAGS_MEMBER(wxTR_LINES_AT_ROOT)
100 wxFLAGS_MEMBER(wxTR_HIDE_ROOT)
101 wxFLAGS_MEMBER(wxTR_ROW_LINES)
102 wxFLAGS_MEMBER(wxTR_HAS_VARIABLE_ROW_HEIGHT)
103 wxFLAGS_MEMBER(wxTR_SINGLE)
104 wxFLAGS_MEMBER(wxTR_MULTIPLE)
105 #if WXWIN_COMPATIBILITY_2_8
106 wxFLAGS_MEMBER(wxTR_EXTENDED)
107 #endif
108 wxFLAGS_MEMBER(wxTR_DEFAULT_STYLE)
109 wxEND_FLAGS( wxTreeCtrlStyle )
110
111 wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxTreeCtrl, wxControl, "wx/treectrl.h")
112
113 wxBEGIN_PROPERTIES_TABLE(wxTreeCtrl)
114 wxEVENT_PROPERTY( TextUpdated, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEvent )
115 wxEVENT_RANGE_PROPERTY( TreeEvent, wxEVT_COMMAND_TREE_BEGIN_DRAG, \
116 wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK, wxTreeEvent )
117
118 wxPROPERTY_FLAGS( WindowStyle, wxTreeCtrlStyle, long, SetWindowStyleFlag, \
119 GetWindowStyleFlag, wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, \
120 wxT("Helpstring"), wxT("group")) // style
121 wxEND_PROPERTIES_TABLE()
122
123 wxEMPTY_HANDLERS_TABLE(wxTreeCtrl)
124
125 wxCONSTRUCTOR_5( wxTreeCtrl, wxWindow*, Parent, wxWindowID, Id, \
126 wxPoint, Position, wxSize, Size, long, WindowStyle )
127
128 // ----------------------------------------------------------------------------
129 // Tree event
130 // ----------------------------------------------------------------------------
131
132 IMPLEMENT_DYNAMIC_CLASS(wxTreeEvent, wxNotifyEvent)
133
134 wxTreeEvent::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
148 wxTreeEvent::wxTreeEvent(wxEventType commandType, int id)
149 : wxNotifyEvent(commandType, id)
150 {
151 m_itemOld = 0l;
152 m_editCancelled = false;
153 }
154
155 wxTreeEvent::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
170 wxTreeCtrlBase::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
186 wxTreeCtrlBase::~wxTreeCtrlBase()
187 {
188 if (m_ownsImageListNormal)
189 delete m_imageListNormal;
190 if (m_ownsImageListState)
191 delete m_imageListState;
192 }
193
194 void 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
219 static void
220 wxGetBestTreeSize(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
244 wxSize 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
294 void wxTreeCtrlBase::ExpandAll()
295 {
296 if ( IsEmpty() )
297 return;
298
299 ExpandAllChildren(GetRootItem());
300 }
301
302 void 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
322 void wxTreeCtrlBase::CollapseAll()
323 {
324 if ( IsEmpty() )
325 return;
326
327 CollapseAllChildren(GetRootItem());
328 }
329
330 void 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
349 bool wxTreeCtrlBase::IsEmpty() const
350 {
351 return !GetRootItem().IsOk();
352 }
353
354 void 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