]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/common/windowid.cpp
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / common / windowid.cpp
... / ...
CommitLineData
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
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;
42static const wxUint8 ID_COUNTTOOLARGE = 254;
43static 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
49wxUint8 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)
56wxLongToLongHashMap *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
61wxWindowID gs_nextAutoId = wxID_AUTO_LOWEST;
62
63// Reserve an ID
64void 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
77void 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
90int 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
103void 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
139void 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
177static wxWindowID gs_nextAutoId = wxID_AUTO_HIGHEST;
178
179#endif
180
181} // anonymous namespace
182
183
184#if wxUSE_AUTOID_MANAGEMENT
185
186void 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
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 {
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
288void 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