]> git.saurik.com Git - wxWidgets.git/blame - src/common/windowid.cpp
Make mouse capture checking asserts stronger and more detailed.
[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
cf2810aa
VZ
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
535a0e08
VZ
24#include "wx/hashmap.h"
25
cf2810aa
VZ
26// Not needed, included in defs.h
27// #include "wx/windowid.h"
28
9a83f860 29#define wxTRACE_WINDOWID wxT("windowid")
cf2810aa
VZ
30
31namespace
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
40static const wxUint8 ID_FREE = 0;
41static const wxUint8 ID_STARTCOUNT = 1;
535a0e08 42static const wxUint8 ID_COUNTTOOLARGE = 254;
cf2810aa
VZ
43static const wxUint8 ID_RESERVED = 255;
44
535a0e08
VZ
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
cf2810aa
VZ
49wxUint8 gs_autoIdsRefCount[wxID_AUTO_HIGHEST - wxID_AUTO_LOWEST + 1] = { 0 };
50
535a0e08
VZ
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)
56wxLongToLongHashMap *gs_autoIdsLargeRefCount = NULL;
57
cf2810aa
VZ
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
61wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
62
63// Reserve an ID
e3b2f973 64void ReserveIdRefCount(wxWindowID winid)
cf2810aa 65{
e3b2f973 66 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
67 wxT("invalid id range"));
68
e3b2f973 69 winid -= wxID_AUTO_LOWEST;
cf2810aa 70
e3b2f973 71 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_FREE,
cf2810aa 72 wxT("id already in use or already reserved"));
e3b2f973 73 gs_autoIdsRefCount[winid] = ID_RESERVED;
cf2810aa
VZ
74}
75
76// Unreserve and id
e3b2f973 77void UnreserveIdRefCount(wxWindowID winid)
cf2810aa 78{
e3b2f973 79 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
80 wxT("invalid id range"));
81
e3b2f973 82 winid -= wxID_AUTO_LOWEST;
cf2810aa 83
e3b2f973 84 wxCHECK_RET(gs_autoIdsRefCount[winid] == ID_RESERVED,
cf2810aa 85 wxT("id already in use or not reserved"));
e3b2f973 86 gs_autoIdsRefCount[winid] = ID_FREE;
cf2810aa
VZ
87}
88
89// Get the usage count of an id
e3b2f973 90int GetIdRefCount(wxWindowID winid)
cf2810aa 91{
e3b2f973 92 wxCHECK_MSG(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST, 0,
cf2810aa
VZ
93 wxT("invalid id range"));
94
e3b2f973 95 winid -= wxID_AUTO_LOWEST;
535a0e08
VZ
96 int refCount = gs_autoIdsRefCount[winid];
97 if (refCount == ID_COUNTTOOLARGE)
98 refCount = (*gs_autoIdsLargeRefCount)[winid];
99 return refCount;
cf2810aa
VZ
100}
101
102// Increase the count for an id
e3b2f973 103void IncIdRefCount(wxWindowID winid)
cf2810aa 104{
e3b2f973 105 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
106 wxT("invalid id range"));
107
e3b2f973 108 winid -= wxID_AUTO_LOWEST;
cf2810aa 109
e3b2f973 110 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id should first be reserved"));
cf2810aa 111
e3b2f973 112 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
535a0e08 113 {
e3b2f973 114 gs_autoIdsRefCount[winid] = ID_STARTCOUNT;
535a0e08
VZ
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 }
cf2810aa 129 else
535a0e08 130 {
e3b2f973 131 gs_autoIdsRefCount[winid]++;
535a0e08 132 }
cf2810aa
VZ
133
134 wxLogTrace(wxTRACE_WINDOWID, wxT("Increasing ref count of ID %d to %d"),
535a0e08 135 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
cf2810aa
VZ
136}
137
138// Decrease the count for an id
e3b2f973 139void DecIdRefCount(wxWindowID winid)
cf2810aa 140{
e3b2f973 141 wxCHECK_RET(winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST,
cf2810aa
VZ
142 wxT("invalid id range"));
143
e3b2f973 144 winid -= wxID_AUTO_LOWEST;
cf2810aa 145
e3b2f973 146 wxCHECK_RET(gs_autoIdsRefCount[winid] != ID_FREE, wxT("id count already 0"));
cf2810aa
VZ
147
148 // DecIdRefCount is only called on an ID that has been IncIdRefCount'ed'
149 // so it should never be reserved, but test anyway
e3b2f973 150 if(gs_autoIdsRefCount[winid] == ID_RESERVED)
cf2810aa 151 {
e54266ad 152 wxFAIL_MSG(wxT("reserve id being decreased"));
e3b2f973 153 gs_autoIdsRefCount[winid] = ID_FREE;
cf2810aa 154 }
535a0e08
VZ
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 }
cf2810aa 168 else
e3b2f973 169 gs_autoIdsRefCount[winid]--;
cf2810aa
VZ
170
171 wxLogTrace(wxTRACE_WINDOWID, wxT("Decreasing ref count of ID %d to %d"),
535a0e08 172 winid + wxID_AUTO_LOWEST, GetIdRefCount(winid + wxID_AUTO_LOWEST));
cf2810aa
VZ
173}
174
175#else // wxUSE_AUTOID_MANAGEMENT
176
177static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
178
179#endif
180
181} // anonymous namespace
182
183
184#if wxUSE_AUTOID_MANAGEMENT
185
e3b2f973 186void wxWindowIDRef::Assign(wxWindowID winid)
cf2810aa 187{
e3b2f973 188 if ( winid != m_id )
cf2810aa
VZ
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
e3b2f973 194 m_id = winid;
cf2810aa
VZ
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
206wxWindowID 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 {
e3b2f973 214 wxWindowID winid = gs_nextAutoId;
cf2810aa
VZ
215
216 while(count--)
217 {
218 ReserveIdRefCount(gs_nextAutoId++);
219 }
220
e3b2f973 221 return winid;
cf2810aa
VZ
222 }
223 else
224 {
225 int found = 0;
226
e3b2f973 227 for(wxWindowID winid = wxID_AUTO_LOWEST; winid <= wxID_AUTO_HIGHEST; winid++)
cf2810aa 228 {
e3b2f973 229 if(GetIdRefCount(winid) == 0)
cf2810aa
VZ
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
e3b2f973
SC
243 if(winid >= gs_nextAutoId)
244 gs_nextAutoId = winid + 1;
cf2810aa
VZ
245
246 while(count--)
e3b2f973 247 ReserveIdRefCount(winid--);
cf2810aa 248
e3b2f973 249 return winid + 1;
cf2810aa
VZ
250 }
251 }
252 else
253 {
254 found = 0;
255 }
256 }
257 }
258
34a083c9 259 wxLogError(_("Out of window IDs. Recommend shutting down application."));
cf2810aa
VZ
260 return wxID_NONE;
261#else // !wxUSE_AUTOID_MANAGEMENT
262 // Make sure enough in the range
e3b2f973 263 wxWindowID winid;
cf2810aa 264
e3b2f973 265 winid = gs_nextAutoId - count + 1;
cf2810aa 266
e3b2f973 267 if ( winid >= wxID_AUTO_LOWEST && winid <= wxID_AUTO_HIGHEST )
cf2810aa
VZ
268 {
269 // There is enough, but it may be time to wrap
e3b2f973 270 if(winid == wxID_AUTO_LOWEST)
cf2810aa
VZ
271 gs_nextAutoId = wxID_AUTO_HIGHEST;
272 else
e3b2f973 273 gs_nextAutoId = winid - 1;
cf2810aa 274
e3b2f973 275 return winid;
cf2810aa
VZ
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
e3b2f973 288void wxIdManager::UnreserveId(wxWindowID winid, int count)
cf2810aa
VZ
289{
290 wxASSERT_MSG(count > 0, wxT("can't unreserve less than 1 id"));
291
292#if wxUSE_AUTOID_MANAGEMENT
293 while (count--)
e3b2f973 294 UnreserveIdRefCount(winid++);
cf2810aa 295#else
e3b2f973 296 wxUnusedVar(winid);
cf2810aa
VZ
297 wxUnusedVar(count);
298#endif
299}
300