1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/windowid.cpp
3 // Purpose: wxWindowID class - a class for managing window ids
4 // Author: Brian Vanderburg II
7 // Copyright: (c) 2007 Brian Vanderburg II
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
11 // ----------------------------------------------------------------------------
13 // ----------------------------------------------------------------------------
14 #include "wx/wxprec.h"
25 #include "wx/hashmap.h"
27 // Not needed, included in defs.h
28 // #include "wx/windowid.h"
30 #define wxTRACE_WINDOWID wxT("windowid")
35 #if wxUSE_AUTOID_MANAGEMENT
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
41 static const wxUint8 ID_FREE
= 0;
42 static const wxUint8 ID_STARTCOUNT
= 1;
43 static const wxUint8 ID_COUNTTOOLARGE
= 254;
44 static const wxUint8 ID_RESERVED
= 255;
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
50 wxUint8 gs_autoIdsRefCount
[wxID_AUTO_HIGHEST
- wxID_AUTO_LOWEST
+ 1] = { 0 };
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
57 wxLongToLongHashMap
*gs_autoIdsLargeRefCount
= NULL
;
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
62 wxWindowID gs_nextAutoId
= wxID_AUTO_LOWEST
;
65 void ReserveIdRefCount(wxWindowID winid
)
67 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
68 wxT("invalid id range"));
70 winid
-= wxID_AUTO_LOWEST
;
72 wxCHECK_RET(gs_autoIdsRefCount
[winid
] == ID_FREE
,
73 wxT("id already in use or already reserved"));
74 gs_autoIdsRefCount
[winid
] = ID_RESERVED
;
78 void UnreserveIdRefCount(wxWindowID winid
)
80 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
81 wxT("invalid id range"));
83 winid
-= wxID_AUTO_LOWEST
;
85 wxCHECK_RET(gs_autoIdsRefCount
[winid
] == ID_RESERVED
,
86 wxT("id already in use or not reserved"));
87 gs_autoIdsRefCount
[winid
] = ID_FREE
;
90 // Get the usage count of an id
91 int GetIdRefCount(wxWindowID winid
)
93 wxCHECK_MSG(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
, 0,
94 wxT("invalid id range"));
96 winid
-= wxID_AUTO_LOWEST
;
97 int refCount
= gs_autoIdsRefCount
[winid
];
98 if (refCount
== ID_COUNTTOOLARGE
)
99 refCount
= (*gs_autoIdsLargeRefCount
)[winid
];
103 // Increase the count for an id
104 void IncIdRefCount(wxWindowID winid
)
106 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
107 wxT("invalid id range"));
109 winid
-= wxID_AUTO_LOWEST
;
111 wxCHECK_RET(gs_autoIdsRefCount
[winid
] != ID_FREE
, wxT("id should first be reserved"));
113 if(gs_autoIdsRefCount
[winid
] == ID_RESERVED
)
115 gs_autoIdsRefCount
[winid
] = ID_STARTCOUNT
;
117 else if (gs_autoIdsRefCount
[winid
] >= ID_COUNTTOOLARGE
-1)
119 if (gs_autoIdsRefCount
[winid
] == ID_COUNTTOOLARGE
-1)
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;
126 gs_autoIdsRefCount
[winid
] = ID_COUNTTOOLARGE
;
128 ++(*gs_autoIdsLargeRefCount
)[winid
];
132 gs_autoIdsRefCount
[winid
]++;
135 wxLogTrace(wxTRACE_WINDOWID
, wxT("Increasing ref count of ID %d to %d"),
136 winid
+ wxID_AUTO_LOWEST
, GetIdRefCount(winid
+ wxID_AUTO_LOWEST
));
139 // Decrease the count for an id
140 void DecIdRefCount(wxWindowID winid
)
142 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
143 wxT("invalid id range"));
145 winid
-= wxID_AUTO_LOWEST
;
147 wxCHECK_RET(gs_autoIdsRefCount
[winid
] != ID_FREE
, wxT("id count already 0"));
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
)
153 wxFAIL_MSG(wxT("reserve id being decreased"));
154 gs_autoIdsRefCount
[winid
] = ID_FREE
;
156 else if(gs_autoIdsRefCount
[winid
] == ID_COUNTTOOLARGE
)
158 long &largeCount
= (*gs_autoIdsLargeRefCount
)[winid
];
162 gs_autoIdsLargeRefCount
->erase (winid
);
163 gs_autoIdsRefCount
[winid
] = ID_FREE
;
165 if (gs_autoIdsLargeRefCount
->empty())
166 wxDELETE (gs_autoIdsLargeRefCount
);
170 gs_autoIdsRefCount
[winid
]--;
172 wxLogTrace(wxTRACE_WINDOWID
, wxT("Decreasing ref count of ID %d to %d"),
173 winid
+ wxID_AUTO_LOWEST
, GetIdRefCount(winid
+ wxID_AUTO_LOWEST
));
176 #else // wxUSE_AUTOID_MANAGEMENT
178 static wxWindowID gs_nextAutoId
= wxID_AUTO_HIGHEST
;
182 } // anonymous namespace
185 #if wxUSE_AUTOID_MANAGEMENT
187 void wxWindowIDRef::Assign(wxWindowID winid
)
191 // decrease count if it is in the managed range
192 if ( m_id
>= wxID_AUTO_LOWEST
&& m_id
<= wxID_AUTO_HIGHEST
)
197 // increase count if it is in the managed range
198 if ( m_id
>= wxID_AUTO_LOWEST
&& m_id
<= wxID_AUTO_HIGHEST
)
203 #endif // wxUSE_AUTOID_MANAGEMENT
207 wxWindowID
wxIdManager::ReserveId(int count
)
209 wxASSERT_MSG(count
> 0, wxT("can't allocate less than 1 id"));
212 #if wxUSE_AUTOID_MANAGEMENT
213 if ( gs_nextAutoId
+ count
- 1 <= wxID_AUTO_HIGHEST
)
215 wxWindowID winid
= gs_nextAutoId
;
219 ReserveIdRefCount(gs_nextAutoId
++);
228 for(wxWindowID winid
= wxID_AUTO_LOWEST
; winid
<= wxID_AUTO_HIGHEST
; winid
++)
230 if(GetIdRefCount(winid
) == 0)
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;
248 ReserveIdRefCount(winid
--);
260 wxLogError(_("Out of window IDs. Recommend shutting down application."));
262 #else // !wxUSE_AUTOID_MANAGEMENT
263 // Make sure enough in the range
266 winid
= gs_nextAutoId
- count
+ 1;
268 if ( winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
)
270 // There is enough, but it may be time to wrap
271 if(winid
== wxID_AUTO_LOWEST
)
272 gs_nextAutoId
= wxID_AUTO_HIGHEST
;
274 gs_nextAutoId
= winid
- 1;
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;
286 #endif // wxUSE_AUTOID_MANAGEMENT/!wxUSE_AUTOID_MANAGEMENT
289 void wxIdManager::UnreserveId(wxWindowID winid
, int count
)
291 wxASSERT_MSG(count
> 0, wxT("can't unreserve less than 1 id"));
293 #if wxUSE_AUTOID_MANAGEMENT
295 UnreserveIdRefCount(winid
++);