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@wxwindows.org>
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #include "wx/selstore.h"
26 
27 // ============================================================================
28 // wxSelectionStore
29 // ============================================================================
30 
31 // ----------------------------------------------------------------------------
32 // tests
33 // ----------------------------------------------------------------------------
34 
IsSelected(unsigned item) const35 bool wxSelectionStore::IsSelected(unsigned item) const
36 {
37     bool isSel = m_itemsSel.Index(item) != wxNOT_FOUND;
38 
39     // if the default state is to be selected, being in m_itemsSel means that
40     // the item is not selected, so we have to inverse the logic
41     return m_defaultState ? !isSel : isSel;
42 }
43 
44 // ----------------------------------------------------------------------------
45 // Select*()
46 // ----------------------------------------------------------------------------
47 
SelectItem(unsigned item,bool select)48 bool wxSelectionStore::SelectItem(unsigned item, bool select)
49 {
50     // search for the item ourselves as like this we get the index where to
51     // insert it later if needed, so we do only one search in the array instead
52     // of two (adding item to a sorted array requires a search)
53     size_t index = m_itemsSel.IndexForInsert(item);
54     bool isSel = index < m_itemsSel.GetCount() && m_itemsSel[index] == item;
55 
56     if ( select != m_defaultState )
57     {
58         if ( !isSel )
59         {
60             m_itemsSel.AddAt(item, index);
61 
62             return true;
63         }
64     }
65     else // reset to default state
66     {
67         if ( isSel )
68         {
69             m_itemsSel.RemoveAt(index);
70             return true;
71         }
72     }
73 
74     return false;
75 }
76 
SelectRange(unsigned itemFrom,unsigned itemTo,bool select,wxArrayInt * itemsChanged)77 bool wxSelectionStore::SelectRange(unsigned itemFrom, unsigned itemTo,
78                                    bool select,
79                                    wxArrayInt *itemsChanged)
80 {
81     // 100 is hardcoded but it shouldn't matter much: the important thing is
82     // that we don't refresh everything when really few (e.g. 1 or 2) items
83     // change state
84     static const unsigned MANY_ITEMS = 100;
85 
86     wxASSERT_MSG( itemFrom <= itemTo, wxT("should be in order") );
87 
88     // are we going to have more [un]selected items than the other ones?
89     if ( itemTo - itemFrom > m_count/2 )
90     {
91         if ( select != m_defaultState )
92         {
93             // the default state now becomes the same as 'select'
94             m_defaultState = select;
95 
96             // so all the old selections (which had state select) shouldn't be
97             // selected any more, but all the other ones should
98             wxSelectedIndices selOld = m_itemsSel;
99             m_itemsSel.Empty();
100 
101             // TODO: it should be possible to optimize the searches a bit
102             //       knowing the possible range
103 
104             unsigned item;
105             for ( item = 0; item < itemFrom; item++ )
106             {
107                 if ( selOld.Index(item) == wxNOT_FOUND )
108                     m_itemsSel.Add(item);
109             }
110 
111             for ( item = itemTo + 1; item < m_count; item++ )
112             {
113                 if ( selOld.Index(item) == wxNOT_FOUND )
114                     m_itemsSel.Add(item);
115             }
116 
117             // many items (> half) changed state
118             itemsChanged = NULL;
119         }
120         else // select == m_defaultState
121         {
122             // get the inclusive range of items between itemFrom and itemTo
123             size_t count = m_itemsSel.GetCount(),
124                    start = m_itemsSel.IndexForInsert(itemFrom),
125                    end = m_itemsSel.IndexForInsert(itemTo);
126 
127             if ( start == count || m_itemsSel[start] < itemFrom )
128             {
129                 start++;
130             }
131 
132             if ( end == count || m_itemsSel[end] > itemTo )
133             {
134                 end--;
135             }
136 
137             if ( start <= end )
138             {
139                 // delete all of them (from end to avoid changing indices)
140                 for ( int i = end; i >= (int)start; i-- )
141                 {
142                     if ( itemsChanged )
143                     {
144                         if ( itemsChanged->GetCount() > MANY_ITEMS )
145                         {
146                             // stop counting (see comment below)
147                             itemsChanged = NULL;
148                         }
149                         else
150                         {
151                             itemsChanged->Add(m_itemsSel[i]);
152                         }
153                     }
154 
155                     m_itemsSel.RemoveAt(i);
156                 }
157             }
158         }
159     }
160     else // "few" items change state
161     {
162         if ( itemsChanged )
163         {
164             itemsChanged->Empty();
165         }
166 
167         // just add the items to the selection
168         for ( unsigned item = itemFrom; item <= itemTo; item++ )
169         {
170             if ( SelectItem(item, select) && itemsChanged )
171             {
172                 itemsChanged->Add(item);
173 
174                 if ( itemsChanged->GetCount() > MANY_ITEMS )
175                 {
176                     // stop counting them, we'll just eat gobs of memory
177                     // for nothing at all - faster to refresh everything in
178                     // this case
179                     itemsChanged = NULL;
180                 }
181             }
182         }
183     }
184 
185     // we set it to NULL if there are many items changing state
186     return itemsChanged != NULL;
187 }
188 
189 // ----------------------------------------------------------------------------
190 // callbacks
191 // ----------------------------------------------------------------------------
192 
OnItemDelete(unsigned item)193 void wxSelectionStore::OnItemDelete(unsigned item)
194 {
195     size_t count = m_itemsSel.GetCount(),
196            i = m_itemsSel.IndexForInsert(item);
197 
198     if ( i < count && m_itemsSel[i] == item )
199     {
200         // this item itself was in m_itemsSel, remove it from there
201         m_itemsSel.RemoveAt(i);
202 
203         count--;
204     }
205 
206     // and adjust the index of all which follow it
207     while ( i < count )
208     {
209         // all following elements must be greater than the one we deleted
210         wxASSERT_MSG( m_itemsSel[i] > item, wxT("logic error") );
211 
212         m_itemsSel[i++]--;
213     }
214 }
215 
SetItemCount(unsigned count)216 void wxSelectionStore::SetItemCount(unsigned count)
217 {
218     // forget about all items whose indices are now invalid if the size
219     // decreased
220     if ( count < m_count )
221     {
222         for ( size_t i = m_itemsSel.GetCount(); i > 0; i-- )
223         {
224             if ( m_itemsSel[i - 1] >= count )
225                 m_itemsSel.RemoveAt(i - 1);
226         }
227     }
228 
229     // remember the new number of items
230     m_count = count;
231 }
232