]> git.saurik.com Git - wxWidgets.git/blob - src/common/windowid.cpp
Applied #15375 to stop event-sending in generic wxSpinCtrl ctor (eco)
[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 // Copyright: (c) 2007 Brian Vanderburg II
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // Needed headers
12 // ----------------------------------------------------------------------------
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #ifndef WX_PRECOMP
20 #include "wx/log.h"
21 #include "wx/intl.h"
22 #endif //WX_PRECOMP
23
24 #include "wx/hashmap.h"
25
26 // Not needed, included in defs.h
27 // #include "wx/windowid.h"
28
29 #define wxTRACE_WINDOWID wxT("windowid")
30
31 namespace
32 {
33
34 #if wxUSE_AUTOID_MANAGEMENT
35
36
37 // initially no ids are in use and we allocate them consecutively, but after we
38 // exhaust the entire range, we wrap around and reuse the ids freed in the
39 // meanwhile
40 static const wxUint8 ID_FREE = 0;
41 static const wxUint8 ID_STARTCOUNT = 1;
42 static const wxUint8 ID_COUNTTOOLARGE = 254;
43 static const wxUint8 ID_RESERVED = 255;
44
45 // we use a two level count, most IDs will be used less than ID_COUNTTOOLARGE-1
46 // thus we store their count directly in this array, however when the same ID
47 // is reused a great number of times (more than or equal to ID_COUNTTOOLARGE),
48 // the hash map stores the actual count
49 wxUint8 gs_autoIdsRefCount[wxID_AUTO_HIGHEST - wxID_AUTO_LOWEST + 1] = { 0 };
50
51 // NB: this variable is allocated (again) only when an ID gets at least
52 // ID_COUNTTOOLARGE refs, and is freed when the latest entry in the map gets
53 // freed. The cell storing the count for an ID is freed only when its count
54 // gets to zero (not when it goes below ID_COUNTTOOLARGE, so as to avoid
55 // degenerate cases)
56 wxLongToLongHashMap *gs_autoIdsLargeRefCount = NULL;
57
58 // this is an optimization used until we wrap around wxID_AUTO_HIGHEST: if this
59 // value is < wxID_AUTO_HIGHEST we know that we haven't wrapped yet and so can
60 // allocate the ids simply by incrementing it
61 wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
62
63 // Reserve an ID
64 void ReserveIdRefCount(wxWindowID winid)
65 {
66 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
67 wxT("invalid id range"));
68
69 winid -= wxID_AUTO_LOWEST;
70
71 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_FREE,
72 wxT("id already in use or already reserved"));
73 gs_autoIdsRefCount[winid] = ID_RESERVED;
74 }
75
76 // Unreserve and id
77 void UnreserveIdRefCount(wxWindowID winid)
78 {
79 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
80 wxT("invalid id range"));
81
82 winid -= wxID_AUTO_LOWEST;
83
84 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_RESERVED,
85 wxT("id already in use or not reserved"));
86 gs_autoIdsRefCount[winid] = ID_FREE;
87 }
88
89 // Get the usage count of an id
90 int GetIdRefCount(wxWindowID winid)
91 {
92 wxCHECK_MSG(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST, 0,
93 wxT("invalid id range"));
94
95 winid -= wxID_AUTO_LOWEST;
96 int refCount = gs_autoIdsRefCount[winid];
97 if (refCount == ID_COUNTTOOLARGE)
98 refCount = (*gs_autoIdsLargeRefCount)[winid];
99 return refCount;
100 }
101
102 // Increase the count for an id
103 void IncIdRefCount(wxWindowID winid)
104 {
105 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
106 wxT("invalid id range"));
107
108 winid -= wxID_AUTO_LOWEST;
109
110 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id should first be reserved"));
111
112 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
113 {
114 gs_autoIdsRefCount[winid] = ID_STARTCOUNT;
115 }
116 else if (gs_autoIdsRefCount[winid] >= ID_COUNTTOOLARGE-1)
117 {
118 if (gs_autoIdsRefCount[winid] == ID_COUNTTOOLARGE-1)
119 {
120 // we need to allocate a cell, and maybe the hash map itself
121 if (!gs_autoIdsLargeRefCount)
122 gs_autoIdsLargeRefCount = new wxLongToLongHashMap;
123 (*gs_autoIdsLargeRefCount)[winid] = ID_COUNTTOOLARGE-1;
124
125 gs_autoIdsRefCount[winid] = ID_COUNTTOOLARGE;
126 }
127 ++(*gs_autoIdsLargeRefCount)[winid];
128 }
129 else
130 {
131 gs_autoIdsRefCount[winid]++;
132 }
133
134 wxLogTrace(wxTRACE_WINDOWID, wxT("Increasing ref count of ID %d to %d"),
135 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
136 }
137
138 // Decrease the count for an id
139 void DecIdRefCount(wxWindowID winid)
140 {
141 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
142 wxT("invalid id range"));
143
144 winid -= wxID_AUTO_LOWEST;
145
146 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id count already 0"));
147
148 // DecIdRefCount is only called on an ID that has been IncIdRefCount'ed'
149 // so it should never be reserved, but test anyway
150 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
151 {
152 wxFAIL_MSG(wxT("reserve id being decreased"));
153 gs_autoIdsRefCount[winid] = ID_FREE;
154 }
155 else if(gs_autoIdsRefCount[winid] == ID_COUNTTOOLARGE)
156 {
157 long &largeCount = (*gs_autoIdsLargeRefCount)[winid];
158 --largeCount;
159 if (largeCount == 0)
160 {
161 gs_autoIdsLargeRefCount->erase (winid);
162 gs_autoIdsRefCount[winid] = ID_FREE;
163
164 if (gs_autoIdsLargeRefCount->empty())
165 wxDELETE (gs_autoIdsLargeRefCount);
166 }
167 }
168 else
169 gs_autoIdsRefCount[winid]--;
170
171 wxLogTrace(wxTRACE_WINDOWID, wxT("Decreasing ref count of ID %d to %d"),
172 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
173 }
174
175 #else // wxUSE_AUTOID_MANAGEMENT
176
177 static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
178
179 #endif
180
181 } // anonymous namespace
182
183
184 #if wxUSE_AUTOID_MANAGEMENT
185
186 void wxWindowIDRef::Assign(wxWindowID winid)
187 {
188 if ( winid != m_id )
189 {
190 // decrease count if it is in the managed range
191 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
192 DecIdRefCount(m_id);
193
194 m_id = winid;
195
196 // increase count if it is in the managed range
197 if ( m_id >= wxID_AUTO_LOWEST && m_id <= wxID_AUTO_HIGHEST )
198 IncIdRefCount(m_id);
199 }
200 }
201
202 #endif // wxUSE_AUTOID_MANAGEMENT
203
204
205
206 wxWindowID wxIdManager::ReserveId(int count)
207 {
208 wxASSERT_MSG(count > 0, wxT("can't allocate less than 1 id"));
209
210
211 #if wxUSE_AUTOID_MANAGEMENT
212 if ( gs_nextAutoId + count - 1 <= wxID_AUTO_HIGHEST )
213 {
214 wxWindowID winid = gs_nextAutoId;
215
216 while(count--)
217 {
218 ReserveIdRefCount(gs_nextAutoId++);
219 }
220
221 return winid;
222 }
223 else
224 {
225 int found = 0;
226
227 for(wxWindowID winid = wxID_AUTO_LOWEST; winid <= wxID_AUTO_HIGHEST; winid++)
228 {
229 if(GetIdRefCount(winid) == 0)
230 {
231 found++;
232 if(found == count)
233 {
234 // Imagine this: 100 free IDs left. Then NewId(50) takes 50
235 // so 50 left. Then, the 25 before that last 50 are freed, but
236 // gs_nextAutoId does not decrement and stays where it is at
237 // with 50 free. Then NewId(75) gets called, and since there
238 // are only 50 left according to gs_nextAutoId, it does a
239 // search and finds the 75 at the end. Then NewId(10) gets
240 // called, and accorind to gs_nextAutoId, their are still
241 // 50 at the end so it returns them without testing the ref
242 // To fix this, the next ID is also updated here as needed
243 if(winid >= gs_nextAutoId)
244 gs_nextAutoId = winid + 1;
245
246 while(count--)
247 ReserveIdRefCount(winid--);
248
249 return winid + 1;
250 }
251 }
252 else
253 {
254 found = 0;
255 }
256 }
257 }
258
259 wxLogError(_("Out of window IDs. Recommend shutting down application."));
260 return wxID_NONE;
261 #else // !wxUSE_AUTOID_MANAGEMENT
262 // Make sure enough in the range
263 wxWindowID winid;
264
265 winid = gs_nextAutoId - count + 1;
266
267 if ( winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST )
268 {
269 // There is enough, but it may be time to wrap
270 if(winid == wxID_AUTO_LOWEST)
271 gs_nextAutoId = wxID_AUTO_HIGHEST;
272 else
273 gs_nextAutoId = winid - 1;
274
275 return winid;
276 }
277 else
278 {
279 // There is not enough at the low end of the range or
280 // count was big enough to wrap around to the positive
281 // Surely 'count' is not so big to take up much of the range
282 gs_nextAutoId = wxID_AUTO_HIGHEST - count;
283 return gs_nextAutoId + 1;
284 }
285 #endif // wxUSE_AUTOID_MANAGEMENT/!wxUSE_AUTOID_MANAGEMENT
286 }
287
288 void wxIdManager::UnreserveId(wxWindowID winid, int count)
289 {
290 wxASSERT_MSG(count > 0, wxT("can't unreserve less than 1 id"));
291
292 #if wxUSE_AUTOID_MANAGEMENT
293 while (count--)
294 UnreserveIdRefCount(winid++);
295 #else
296 wxUnusedVar(winid);
297 wxUnusedVar(count);
298 #endif
299 }
300