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