]> git.saurik.com Git - wxWidgets.git/blob - src/common/windowid.cpp
wxTextCtrk::GetRange() shouldn't crash on out of range request
[wxWidgets.git] / src / common / windowid.cpp
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
30 namespace
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
39 static const wxUint8 ID_FREE = 0;
40 static const wxUint8 ID_STARTCOUNT = 1;
41 static const wxUint8 ID_MAXCOUNT = 254;
42 static const wxUint8 ID_RESERVED = 255;
43
44 wxUint8 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
49 wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
50
51 // Reserve an ID
52 void 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
65 void 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
78 int 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
88 void 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
108 void 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 {
121 wxFAIL_MSG(wxT("reserve id being decreased"));
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
133 static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
134
135 #endif
136
137 } // anonymous namespace
138
139
140 #if wxUSE_AUTOID_MANAGEMENT
141
142 void 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
162 wxWindowID 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
244 void 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