1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/windowid.cpp
3 // Purpose: wxWindowID class - a class for managing window ids
4 // Author: Brian Vanderburg II
6 // Copyright: (c) 2007 Brian Vanderburg II
7 // Licence: wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
10 // ----------------------------------------------------------------------------
12 // ----------------------------------------------------------------------------
13 #include "wx/wxprec.h"
24 #include "wx/hashmap.h"
26 // Not needed, included in defs.h
27 // #include "wx/windowid.h"
29 #define wxTRACE_WINDOWID wxT("windowid")
34 #if wxUSE_AUTOID_MANAGEMENT
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
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;
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 };
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
56 wxLongToLongHashMap
*gs_autoIdsLargeRefCount
= NULL
;
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
;
64 void ReserveIdRefCount(wxWindowID winid
)
66 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
67 wxT("invalid id range"));
69 winid
-= wxID_AUTO_LOWEST
;
71 wxCHECK_RET(gs_autoIdsRefCount
[winid
] == ID_FREE
,
72 wxT("id already in use or already reserved"));
73 gs_autoIdsRefCount
[winid
] = ID_RESERVED
;
77 void UnreserveIdRefCount(wxWindowID winid
)
79 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
80 wxT("invalid id range"));
82 winid
-= wxID_AUTO_LOWEST
;
84 wxCHECK_RET(gs_autoIdsRefCount
[winid
] == ID_RESERVED
,
85 wxT("id already in use or not reserved"));
86 gs_autoIdsRefCount
[winid
] = ID_FREE
;
89 // Get the usage count of an id
90 int GetIdRefCount(wxWindowID winid
)
92 wxCHECK_MSG(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
, 0,
93 wxT("invalid id range"));
95 winid
-= wxID_AUTO_LOWEST
;
96 int refCount
= gs_autoIdsRefCount
[winid
];
97 if (refCount
== ID_COUNTTOOLARGE
)
98 refCount
= (*gs_autoIdsLargeRefCount
)[winid
];
102 // Increase the count for an id
103 void IncIdRefCount(wxWindowID winid
)
105 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
106 wxT("invalid id range"));
108 winid
-= wxID_AUTO_LOWEST
;
110 wxCHECK_RET(gs_autoIdsRefCount
[winid
] != ID_FREE
, wxT("id should first be reserved"));
112 if(gs_autoIdsRefCount
[winid
] == ID_RESERVED
)
114 gs_autoIdsRefCount
[winid
] = ID_STARTCOUNT
;
116 else if (gs_autoIdsRefCount
[winid
] >= ID_COUNTTOOLARGE
-1)
118 if (gs_autoIdsRefCount
[winid
] == ID_COUNTTOOLARGE
-1)
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;
125 gs_autoIdsRefCount
[winid
] = ID_COUNTTOOLARGE
;
127 ++(*gs_autoIdsLargeRefCount
)[winid
];
131 gs_autoIdsRefCount
[winid
]++;
134 wxLogTrace(wxTRACE_WINDOWID
, wxT("Increasing ref count of ID %d to %d"),
135 winid
+ wxID_AUTO_LOWEST
, GetIdRefCount(winid
+ wxID_AUTO_LOWEST
));
138 // Decrease the count for an id
139 void DecIdRefCount(wxWindowID winid
)
141 wxCHECK_RET(winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
,
142 wxT("invalid id range"));
144 winid
-= wxID_AUTO_LOWEST
;
146 wxCHECK_RET(gs_autoIdsRefCount
[winid
] != ID_FREE
, wxT("id count already 0"));
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
)
152 wxFAIL_MSG(wxT("reserve id being decreased"));
153 gs_autoIdsRefCount
[winid
] = ID_FREE
;
155 else if(gs_autoIdsRefCount
[winid
] == ID_COUNTTOOLARGE
)
157 long &largeCount
= (*gs_autoIdsLargeRefCount
)[winid
];
161 gs_autoIdsLargeRefCount
->erase (winid
);
162 gs_autoIdsRefCount
[winid
] = ID_FREE
;
164 if (gs_autoIdsLargeRefCount
->empty())
165 wxDELETE (gs_autoIdsLargeRefCount
);
169 gs_autoIdsRefCount
[winid
]--;
171 wxLogTrace(wxTRACE_WINDOWID
, wxT("Decreasing ref count of ID %d to %d"),
172 winid
+ wxID_AUTO_LOWEST
, GetIdRefCount(winid
+ wxID_AUTO_LOWEST
));
175 #else // wxUSE_AUTOID_MANAGEMENT
177 static wxWindowID gs_nextAutoId
= wxID_AUTO_HIGHEST
;
181 } // anonymous namespace
184 #if wxUSE_AUTOID_MANAGEMENT
186 void wxWindowIDRef::Assign(wxWindowID winid
)
190 // decrease count if it is in the managed range
191 if ( m_id
>= wxID_AUTO_LOWEST
&& m_id
<= wxID_AUTO_HIGHEST
)
196 // increase count if it is in the managed range
197 if ( m_id
>= wxID_AUTO_LOWEST
&& m_id
<= wxID_AUTO_HIGHEST
)
202 #endif // wxUSE_AUTOID_MANAGEMENT
206 wxWindowID
wxIdManager::ReserveId(int count
)
208 wxASSERT_MSG(count
> 0, wxT("can't allocate less than 1 id"));
211 #if wxUSE_AUTOID_MANAGEMENT
212 if ( gs_nextAutoId
+ count
- 1 <= wxID_AUTO_HIGHEST
)
214 wxWindowID winid
= gs_nextAutoId
;
218 ReserveIdRefCount(gs_nextAutoId
++);
227 for(wxWindowID winid
= wxID_AUTO_LOWEST
; winid
<= wxID_AUTO_HIGHEST
; winid
++)
229 if(GetIdRefCount(winid
) == 0)
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;
247 ReserveIdRefCount(winid
--);
259 wxLogError(_("Out of window IDs. Recommend shutting down application."));
261 #else // !wxUSE_AUTOID_MANAGEMENT
262 // Make sure enough in the range
265 winid
= gs_nextAutoId
- count
+ 1;
267 if ( winid
>= wxID_AUTO_LOWEST
&& winid
<= wxID_AUTO_HIGHEST
)
269 // There is enough, but it may be time to wrap
270 if(winid
== wxID_AUTO_LOWEST
)
271 gs_nextAutoId
= wxID_AUTO_HIGHEST
;
273 gs_nextAutoId
= winid
- 1;
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;
285 #endif // wxUSE_AUTOID_MANAGEMENT/!wxUSE_AUTOID_MANAGEMENT
288 void wxIdManager::UnreserveId(wxWindowID winid
, int count
)
290 wxASSERT_MSG(count
> 0, wxT("can't unreserve less than 1 id"));
292 #if wxUSE_AUTOID_MANAGEMENT
294 UnreserveIdRefCount(winid
++);