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