]> git.saurik.com Git - wxWidgets.git/blame - src/common/windowid.cpp
wxTextCtrk::GetRange() shouldn't crash on out of range request
[wxWidgets.git] / src / common / windowid.cpp
CommitLineData
cf2810aa
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: src/common/windowid.cpp
3// Purpose: wxWindowID class - a class for managing window ids
4// Author: Brian Vanderburg II
5// Created: 2007-09-21
6// RCS-ID: $Id$
7// Copyright: (c) 2007 Brian Vanderburg II
8// Licence: wxWindows licence
9///////////////////////////////////////////////////////////////////////////////
10
11// ----------------------------------------------------------------------------
12// Needed headers
13// ----------------------------------------------------------------------------
14#include "wx/wxprec.h"
15
16#ifdef __BORLANDC__
17 #pragma hdrstop
18#endif
19
20#ifndef WX_PRECOMP
21 #include "wx/log.h"
22 #include "wx/intl.h"
23#endif //WX_PRECOMP
24
25// Not needed, included in defs.h
26// #include "wx/windowid.h"
27
28#define wxTRACE_WINDOWID _T("windowid")
29
30namespace
31{
32
33#if wxUSE_AUTOID_MANAGEMENT
34
35
36// initially no ids are in use and we allocate them consecutively, but after we
37// exhaust the entire range, we wrap around and reuse the ids freed in the
38// meanwhile
39static const wxUint8 ID_FREE = 0;
40static const wxUint8 ID_STARTCOUNT = 1;
41static const wxUint8 ID_MAXCOUNT = 254;
42static const wxUint8 ID_RESERVED = 255;
43
44wxUint8 gs_autoIdsRefCount[wxID_AUTO_HIGHEST - wxID_AUTO_LOWEST + 1] = { 0 };
45
46// this is an optimization used until we wrap around wxID_AUTO_HIGHEST: if this
47// value is < wxID_AUTO_HIGHEST we know that we haven't wrapped yet and so can
48// allocate the ids simply by incrementing it
49wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
50
51// Reserve an ID
52void ReserveIdRefCount(wxWindowID id)
53{
54 wxCHECK_RET(id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST,
55 wxT("invalid id range"));
56
57 id -= wxID_AUTO_LOWEST;
58
59 wxCHECK_RET(gs_autoIdsRefCount[id] == ID_FREE,
60 wxT("id already in use or already reserved"));
61 gs_autoIdsRefCount[id] = ID_RESERVED;
62}
63
64// Unreserve and id
65void UnreserveIdRefCount(wxWindowID id)
66{
67 wxCHECK_RET(id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST,
68 wxT("invalid id range"));
69
70 id -= wxID_AUTO_LOWEST;
71
72 wxCHECK_RET(gs_autoIdsRefCount[id] == ID_RESERVED,
73 wxT("id already in use or not reserved"));
74 gs_autoIdsRefCount[id] = ID_FREE;
75}
76
77// Get the usage count of an id
78int GetIdRefCount(wxWindowID id)
79{
80 wxCHECK_MSG(id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST, 0,
81 wxT("invalid id range"));
82
83 id -= wxID_AUTO_LOWEST;
84 return gs_autoIdsRefCount[id];
85}
86
87// Increase the count for an id
88void IncIdRefCount(wxWindowID id)
89{
90 wxCHECK_RET(id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST,
91 wxT("invalid id range"));
92
93 id -= wxID_AUTO_LOWEST;
94
95 wxCHECK_RET(gs_autoIdsRefCount[id] != ID_MAXCOUNT, wxT("id count at max"));
96 wxCHECK_RET(gs_autoIdsRefCount[id] != ID_FREE, wxT("id should first be reserved"));
97
98 if(gs_autoIdsRefCount[id] == ID_RESERVED)
99 gs_autoIdsRefCount[id] = ID_STARTCOUNT;
100 else
101 gs_autoIdsRefCount[id]++;
102
103 wxLogTrace(wxTRACE_WINDOWID, wxT("Increasing ref count of ID %d to %d"),
104 id + wxID_AUTO_LOWEST, gs_autoIdsRefCount[id]);
105}
106
107// Decrease the count for an id
108void DecIdRefCount(wxWindowID id)
109{
110 wxCHECK_RET(id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST,
111 wxT("invalid id range"));
112
113 id -= wxID_AUTO_LOWEST;
114
115 wxCHECK_RET(gs_autoIdsRefCount[id] != ID_FREE, wxT("id count already 0"));
116
117 // DecIdRefCount is only called on an ID that has been IncIdRefCount'ed'
118 // so it should never be reserved, but test anyway
119 if(gs_autoIdsRefCount[id] == ID_RESERVED)
120 {
e54266ad 121 wxFAIL_MSG(wxT("reserve id being decreased"));
cf2810aa
VZ
122 gs_autoIdsRefCount[id] = ID_FREE;
123 }
124 else
125 gs_autoIdsRefCount[id]--;
126
127 wxLogTrace(wxTRACE_WINDOWID, wxT("Decreasing ref count of ID %d to %d"),
128 id + wxID_AUTO_LOWEST, gs_autoIdsRefCount[id]);
129}
130
131#else // wxUSE_AUTOID_MANAGEMENT
132
133static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
134
135#endif
136
137} // anonymous namespace
138
139
140#if wxUSE_AUTOID_MANAGEMENT
141
142void wxWindowIDRef::Assign(wxWindowID id)
143{
144 if ( id != m_id )
145 {
146 // decrease count if it is in the managed range
147 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
148 DecIdRefCount(m_id);
149
150 m_id = id;
151
152 // increase count if it is in the managed range
153 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
154 IncIdRefCount(m_id);
155 }
156}
157
158#endif // wxUSE_AUTOID_MANAGEMENT
159
160
161
162wxWindowID wxIdManager::ReserveId(int count)
163{
164 wxASSERT_MSG(count > 0, wxT("can't allocate less than 1 id"));
165
166
167#if wxUSE_AUTOID_MANAGEMENT
168 if ( gs_nextAutoId + count - 1 <= wxID_AUTO_HIGHEST )
169 {
170 wxWindowID id = gs_nextAutoId;
171
172 while(count--)
173 {
174 ReserveIdRefCount(gs_nextAutoId++);
175 }
176
177 return id;
178 }
179 else
180 {
181 int found = 0;
182
183 for(wxWindowID id = wxID_AUTO_LOWEST; id <= wxID_AUTO_HIGHEST; id++)
184 {
185 if(GetIdRefCount(id) == 0)
186 {
187 found++;
188 if(found == count)
189 {
190 // Imagine this: 100 free IDs left. Then NewId(50) takes 50
191 // so 50 left. Then, the 25 before that last 50 are freed, but
192 // gs_nextAutoId does not decrement and stays where it is at
193 // with 50 free. Then NewId(75) gets called, and since there
194 // are only 50 left according to gs_nextAutoId, it does a
195 // search and finds the 75 at the end. Then NewId(10) gets
196 // called, and accorind to gs_nextAutoId, their are still
197 // 50 at the end so it returns them without testing the ref
198 // To fix this, the next ID is also updated here as needed
199 if(id >= gs_nextAutoId)
200 gs_nextAutoId = id + 1;
201
202 while(count--)
203 ReserveIdRefCount(id--);
204
205 return id;
206 }
207 }
208 else
209 {
210 found = 0;
211 }
212 }
213 }
214
215 ::wxLogError(_("Out of window IDs. Recommend shutting down application."));
216 return wxID_NONE;
217#else // !wxUSE_AUTOID_MANAGEMENT
218 // Make sure enough in the range
219 wxWindowID id;
220
221 id = gs_nextAutoId - count + 1;
222
223 if ( id >= wxID_AUTO_LOWEST && id <= wxID_AUTO_HIGHEST )
224 {
225 // There is enough, but it may be time to wrap
226 if(id == wxID_AUTO_LOWEST)
227 gs_nextAutoId = wxID_AUTO_HIGHEST;
228 else
229 gs_nextAutoId = id - 1;
230
231 return id;
232 }
233 else
234 {
235 // There is not enough at the low end of the range or
236 // count was big enough to wrap around to the positive
237 // Surely 'count' is not so big to take up much of the range
238 gs_nextAutoId = wxID_AUTO_HIGHEST - count;
239 return gs_nextAutoId + 1;
240 }
241#endif // wxUSE_AUTOID_MANAGEMENT/!wxUSE_AUTOID_MANAGEMENT
242}
243
244void wxIdManager::UnreserveId(wxWindowID id, int count)
245{
246 wxASSERT_MSG(count > 0, wxT("can't unreserve less than 1 id"));
247
248#if wxUSE_AUTOID_MANAGEMENT
249 while (count--)
250 UnreserveIdRefCount(id++);
251#else
252 wxUnusedVar(id);
253 wxUnusedVar(count);
254#endif
255}
256