1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/selstore.cpp
3 // Purpose: wxSelectionStore implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 08.06.03 (extracted from src/generic/listctrl.cpp)
7 // Copyright: (c) 2000-2003 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #include "wx/wxprec.h"
20
21
22 #include "wx/selstore.h"
23
24 // ============================================================================
25 // wxSelectionStore
26 // ============================================================================
27
28 const unsigned wxSelectionStore::NO_SELECTION = static_cast<unsigned>(-1);
29
30 // ----------------------------------------------------------------------------
31 // tests
32 // ----------------------------------------------------------------------------
33
IsSelected(unsigned item) const34 bool wxSelectionStore::IsSelected(unsigned item) const
35 {
36 bool isSel = m_itemsSel.Index(item) != wxNOT_FOUND;
37
38 // if the default state is to be selected, being in m_itemsSel means that
39 // the item is not selected, so we have to inverse the logic
40 return m_defaultState ? !isSel : isSel;
41 }
42
43 // ----------------------------------------------------------------------------
44 // Select*()
45 // ----------------------------------------------------------------------------
46
SelectItem(unsigned item,bool select)47 bool wxSelectionStore::SelectItem(unsigned item, bool select)
48 {
49 // search for the item ourselves as like this we get the index where to
50 // insert it later if needed, so we do only one search in the array instead
51 // of two (adding item to a sorted array requires a search)
52 size_t index = m_itemsSel.IndexForInsert(item);
53 bool isSel = index < m_itemsSel.GetCount() && m_itemsSel[index] == item;
54
55 if ( select != m_defaultState )
56 {
57 if ( !isSel )
58 {
59 m_itemsSel.AddAt(item, index);
60
61 return true;
62 }
63 }
64 else // reset to default state
65 {
66 if ( isSel )
67 {
68 m_itemsSel.RemoveAt(index);
69 return true;
70 }
71 }
72
73 return false;
74 }
75
SelectRange(unsigned itemFrom,unsigned itemTo,bool select,wxArrayInt * itemsChanged)76 bool wxSelectionStore::SelectRange(unsigned itemFrom, unsigned itemTo,
77 bool select,
78 wxArrayInt *itemsChanged)
79 {
80 // 100 is hardcoded but it shouldn't matter much: the important thing is
81 // that we don't refresh everything when really few (e.g. 1 or 2) items
82 // change state
83 static const unsigned MANY_ITEMS = 100;
84
85 wxASSERT_MSG( itemFrom <= itemTo, wxT("should be in order") );
86
87 // are we going to have more [un]selected items than the other ones?
88 if ( itemTo - itemFrom > m_count/2 )
89 {
90 if ( select != m_defaultState )
91 {
92 // the default state now becomes the same as 'select'
93 m_defaultState = select;
94
95 // so all the old selections (which had state select) shouldn't be
96 // selected any more, but all the other ones should
97 wxSelectedIndices selOld = m_itemsSel;
98 m_itemsSel.Empty();
99
100 // TODO: it should be possible to optimize the searches a bit
101 // knowing the possible range
102
103 unsigned item;
104 for ( item = 0; item < itemFrom; item++ )
105 {
106 if ( selOld.Index(item) == wxNOT_FOUND )
107 m_itemsSel.Add(item);
108 }
109
110 for ( item = itemTo + 1; item < m_count; item++ )
111 {
112 if ( selOld.Index(item) == wxNOT_FOUND )
113 m_itemsSel.Add(item);
114 }
115
116 // many items (> half) changed state
117 itemsChanged = NULL;
118 }
119 else // select == m_defaultState
120 {
121 // get the inclusive range of items between itemFrom and itemTo
122 size_t count = m_itemsSel.GetCount(),
123 start = m_itemsSel.IndexForInsert(itemFrom),
124 end = m_itemsSel.IndexForInsert(itemTo);
125
126 if ( start == count || m_itemsSel[start] < itemFrom )
127 {
128 start++;
129 }
130
131 if ( end == count || m_itemsSel[end] > itemTo )
132 {
133 end--;
134 }
135
136 if ( start <= end )
137 {
138 // delete all of them (from end to avoid changing indices)
139 for ( int i = end; i >= (int)start; i-- )
140 {
141 if ( itemsChanged )
142 {
143 if ( itemsChanged->GetCount() > MANY_ITEMS )
144 {
145 // stop counting (see comment below)
146 itemsChanged = NULL;
147 }
148 else
149 {
150 itemsChanged->Add(m_itemsSel[i]);
151 }
152 }
153
154 m_itemsSel.RemoveAt(i);
155 }
156 }
157 }
158 }
159 else // "few" items change state
160 {
161 if ( itemsChanged )
162 {
163 itemsChanged->Empty();
164 }
165
166 // just add the items to the selection
167 for ( unsigned item = itemFrom; item <= itemTo; item++ )
168 {
169 if ( SelectItem(item, select) && itemsChanged )
170 {
171 itemsChanged->Add(item);
172
173 if ( itemsChanged->GetCount() > MANY_ITEMS )
174 {
175 // stop counting them, we'll just eat gobs of memory
176 // for nothing at all - faster to refresh everything in
177 // this case
178 itemsChanged = NULL;
179 }
180 }
181 }
182 }
183
184 // we set it to NULL if there are many items changing state
185 return itemsChanged != NULL;
186 }
187
188 // ----------------------------------------------------------------------------
189 // callbacks
190 // ----------------------------------------------------------------------------
191
OnItemsInserted(unsigned item,unsigned numItems)192 void wxSelectionStore::OnItemsInserted(unsigned item, unsigned numItems)
193 {
194 const size_t count = m_itemsSel.GetCount();
195
196 size_t idx = m_itemsSel.IndexForInsert(item);
197
198 for ( size_t i = idx; i < count; i++ )
199 {
200 m_itemsSel[i] += numItems;
201 }
202
203 if ( m_defaultState )
204 {
205 // All newly inserted items are not selected, so if the default state
206 // is to be selected, we need to manually add them to the deselected
207 // items indices.
208 for ( unsigned n = item; n < item + numItems; n++ )
209 {
210 m_itemsSel.AddAt(n, idx++);
211 }
212 }
213
214 m_count += numItems;
215 }
216
OnItemDelete(unsigned item)217 void wxSelectionStore::OnItemDelete(unsigned item)
218 {
219 size_t count = m_itemsSel.GetCount(),
220 i = m_itemsSel.IndexForInsert(item);
221
222 if ( i < count && m_itemsSel[i] == item )
223 {
224 // this item itself was in m_itemsSel, remove it from there
225 m_itemsSel.RemoveAt(i);
226
227 count--;
228 }
229
230 // and adjust the index of all which follow it
231 while ( i < count )
232 {
233 // all following elements must be greater than the one we deleted
234 wxASSERT_MSG( m_itemsSel[i] > item, wxT("logic error") );
235
236 m_itemsSel[i++]--;
237 }
238
239 m_count--;
240 }
241
OnItemsDeleted(unsigned item,unsigned numItems)242 bool wxSelectionStore::OnItemsDeleted(unsigned item, unsigned numItems)
243 {
244 bool anyDeletedInSelItems = false,
245 allDeletedInSelItems = true;
246
247 size_t i = m_itemsSel.IndexForInsert(item);
248
249 const unsigned firstAfterDeleted = item + numItems;
250 while ( i < m_itemsSel.size() )
251 {
252 if ( m_itemsSel[i] < firstAfterDeleted )
253 {
254 // This item is going to be deleted, so remove it from the
255 // selected indices entirely. Notice that we do not update i here
256 // as it now refers to the next element.
257 m_itemsSel.RemoveAt(i);
258
259 anyDeletedInSelItems = true;
260 }
261 else
262 {
263 // This item remains, just update its index.
264 m_itemsSel[i++] -= numItems;
265
266 allDeletedInSelItems = false;
267 }
268 }
269
270 m_count -= numItems;
271
272 return m_defaultState ? allDeletedInSelItems : anyDeletedInSelItems;
273 }
274
275
SetItemCount(unsigned count)276 void wxSelectionStore::SetItemCount(unsigned count)
277 {
278 // forget about all items whose indices are now invalid if the size
279 // decreased
280 if ( count < m_count )
281 {
282 for ( size_t i = m_itemsSel.GetCount(); i > 0; i-- )
283 {
284 if ( m_itemsSel[i - 1] >= count )
285 m_itemsSel.RemoveAt(i - 1);
286 }
287 }
288
289 // remember the new number of items
290 m_count = count;
291 }
292
293 // ----------------------------------------------------------------------------
294 // Iteration
295 // ----------------------------------------------------------------------------
296
GetFirstSelectedItem(IterationState & cookie) const297 unsigned wxSelectionStore::GetFirstSelectedItem(IterationState& cookie) const
298 {
299 cookie = 0;
300
301 return GetNextSelectedItem(cookie);
302 }
303
GetNextSelectedItem(IterationState & cookie) const304 unsigned wxSelectionStore::GetNextSelectedItem(IterationState& cookie) const
305 {
306 if ( m_defaultState )
307 {
308 // We have no choice but to iterate over all items in this case. It
309 // shouldn't be that bad in practice because (almost) all items are
310 // supposed to be selected if m_defaultState == true anyhow.
311 for ( unsigned item = cookie; ; item++ )
312 {
313 if ( item >= m_count )
314 return NO_SELECTION;
315
316 if ( IsSelected(item) )
317 {
318 cookie = item + 1;
319 return item;
320 }
321 }
322 }
323 else // Simple case when we directly have the selected items.
324 {
325 if ( cookie >= m_itemsSel.size() )
326 return NO_SELECTION;
327
328 return m_itemsSel[cookie++];
329 }
330 }
331