1 /************************************************************************
2     created:    Tue Sep 27 2005
3     author:     Tomas Lindquist Olsen
4 *************************************************************************/
5 /***************************************************************************
6  *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
7  *
8  *   Permission is hereby granted, free of charge, to any person obtaining
9  *   a copy of this software and associated documentation files (the
10  *   "Software"), to deal in the Software without restriction, including
11  *   without limitation the rights to use, copy, modify, merge, publish,
12  *   distribute, sublicense, and/or sell copies of the Software, and to
13  *   permit persons to whom the Software is furnished to do so, subject to
14  *   the following conditions:
15  *
16  *   The above copyright notice and this permission notice shall be
17  *   included in all copies or substantial portions of the Software.
18  *
19  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  ***************************************************************************/
27 #include "CEGUI/widgets/ItemListbox.h"
28 #include "CEGUI/Exceptions.h"
29 
30 // begin CEGUI namespace
31 namespace CEGUI
32 {
33 
34 /*************************************************************************
35     Constants
36 *************************************************************************/
37 // event strings
38 const String ItemListbox::EventNamespace("ItemListbox");
39 const String ItemListbox::WidgetTypeName("CEGUI/ItemListbox");
40 const String ItemListbox::EventSelectionChanged("SelectionChanged");
41 const String ItemListbox::EventMultiSelectModeChanged("MultiSelectModeChanged");
42 
43 /************************************************************************
44     Constructor
45 ************************************************************************/
ItemListbox(const String & type,const String & name)46 ItemListbox::ItemListbox(const String& type, const String& name) :
47     ScrolledItemListBase(type, name),
48     d_multiSelect(false),
49     d_lastSelected(0),
50     d_nextSelectionIndex(0)
51 {
52     addItemListboxProperties();
53 }
54 
55 /************************************************************************
56     Layout items
57 ************************************************************************/
layoutItemWidgets()58 void ItemListbox::layoutItemWidgets()
59 {
60     float y = 0;
61     float widest = 0;
62 
63     ItemEntryList::iterator i = d_listItems.begin();
64     ItemEntryList::iterator end = d_listItems.end();
65 
66     while (i!=end)
67     {
68         ItemEntry* entry = *i;
69         const Sizef pxs = entry->getItemPixelSize();
70         if (pxs.d_width > widest)
71         {
72             widest = pxs.d_width;
73         }
74 
75         entry->setArea(URect(
76             UVector2(cegui_absdim(0), cegui_absdim(y)),
77             UVector2(cegui_reldim(1), cegui_absdim(y + pxs.d_height))
78             ));
79 
80         y += pxs.d_height;
81 
82         ++i;
83     }
84 
85     // reconfigure scrollbars
86     configureScrollbars(Sizef(widest, y));
87 }
88 
89 /************************************************************************
90     Get size of items
91 ************************************************************************/
getContentSize() const92 Sizef ItemListbox::getContentSize() const
93 {
94     float h = 0;
95 
96     ItemEntryList::const_iterator i = d_listItems.begin();
97     ItemEntryList::const_iterator end = d_listItems.end();
98     while (i!=end)
99     {
100         h += (*i)->getItemPixelSize().d_height;
101         ++i;
102     }
103 
104     return Sizef(getItemRenderArea().getWidth(), h);
105 }
106 
107 /************************************************************************
108     Get the number of selected items
109 ************************************************************************/
getSelectedCount() const110 size_t ItemListbox::getSelectedCount() const
111 {
112     if (!d_multiSelect)
113     {
114         return d_lastSelected ? 1 : 0;
115     }
116 
117     size_t count = 0;
118     size_t max = d_listItems.size();
119     for (size_t i=0; i<max; ++i)
120     {
121         if (d_listItems[i]->isSelected())
122         {
123             ++count;
124         }
125     }
126 
127     return count;
128 }
129 
130 /************************************************************************
131     Get a pointer to the first selected item starting from the given index
132 ************************************************************************/
findSelectedItem(size_t start_index) const133 ItemEntry* ItemListbox::findSelectedItem(size_t start_index) const
134 {
135     const size_t max = d_listItems.size();
136 
137     for (size_t i = start_index; i < max; ++i)
138     {
139         ItemEntry* li = d_listItems[i];
140         if (li->isSelected())
141         {
142             d_nextSelectionIndex = i + 1;
143             return li;
144         }
145     }
146 
147     return 0;
148 }
149 
150 /************************************************************************
151     Get a pointer to the first selected item
152 ************************************************************************/
getFirstSelectedItem(size_t start_index) const153 ItemEntry* ItemListbox::getFirstSelectedItem(size_t start_index) const
154 {
155     if (!d_multiSelect)
156     {
157         return d_lastSelected;
158     }
159     return findSelectedItem(start_index);
160 }
161 
162 /************************************************************************
163     Get a pointer to the next selected item using internal counter
164 ************************************************************************/
getNextSelectedItem() const165 ItemEntry* ItemListbox::getNextSelectedItem() const
166 {
167     if (!d_multiSelect)
168     {
169         return 0;
170     }
171     return findSelectedItem(d_nextSelectionIndex);
172 }
173 
174 /************************************************************************
175     Get a pointer to the next selected item after the given item
176 ************************************************************************/
getNextSelectedItemAfter(const ItemEntry * start_item) const177 ItemEntry* ItemListbox::getNextSelectedItemAfter(const ItemEntry* start_item) const
178 {
179     if (start_item==0||!d_multiSelect)
180     {
181         return 0;
182     }
183 
184     size_t max = d_listItems.size();
185     size_t i = getItemIndex(start_item);
186 
187     while (i<max)
188     {
189         ItemEntry* li = d_listItems[i];
190         if (li->isSelected())
191         {
192             return li;
193         }
194         ++i;
195     }
196 
197     return 0;
198 }
199 
200 /************************************************************************
201     Set whether multiple selections should be allowed
202 ************************************************************************/
setMultiSelectEnabled(bool state)203 void ItemListbox::setMultiSelectEnabled(bool state)
204 {
205     if (state != d_multiSelect)
206     {
207         d_multiSelect = state;
208         WindowEventArgs e(this);
209         onMultiSelectModeChanged(e);
210     }
211 }
212 
213 /************************************************************************
214     Notify item clicked
215 ************************************************************************/
notifyItemClicked(ItemEntry * li)216 void ItemListbox::notifyItemClicked(ItemEntry* li)
217 {
218     bool sel_state = !(li->isSelected() && d_multiSelect);
219     bool skip = false;
220 
221     // multiselect enabled
222     if (d_multiSelect)
223     {
224         uint syskeys = getGUIContext().getSystemKeys().get();
225         ItemEntry* last = d_lastSelected;
226 
227         // no Control? clear others
228         if (!(syskeys & Control))
229         {
230             clearAllSelections();
231             if (!sel_state)
232             {
233                 sel_state=true;
234             }
235         }
236 
237         // select range if Shift if held, and we have a 'last selection'
238         if (last && (syskeys & Shift))
239         {
240             selectRange(getItemIndex(last),getItemIndex(li));
241             skip = true;
242         }
243     }
244     else
245     {
246         clearAllSelections();
247     }
248 
249     if (!skip)
250     {
251         li->setSelected_impl(sel_state,false);
252         if (sel_state)
253         {
254             d_lastSelected = li;
255         }
256         else if (d_lastSelected == li)
257         {
258             d_lastSelected = 0;
259         }
260     }
261 
262     WindowEventArgs e(this);
263     onSelectionChanged(e);
264 }
265 
266 /************************************************************************
267     Notify item select state change
268 ************************************************************************/
notifyItemSelectState(ItemEntry * li,bool state)269 void ItemListbox::notifyItemSelectState(ItemEntry* li, bool state)
270 {
271     // deselect
272     if (!state)
273     {
274         // clear last selection if this one was it
275         if (d_lastSelected == li)
276         {
277             d_lastSelected = 0;
278         }
279     }
280     // if we dont support multiselect, we must clear all the other selections
281     else if (!d_multiSelect)
282     {
283         clearAllSelections();
284         li->setSelected_impl(true,false);
285         d_lastSelected = li;
286     }
287 
288     WindowEventArgs e(this);
289     onSelectionChanged(e);
290 }
291 
292 /*************************************************************************
293     Add ItemListbox specific properties
294 *************************************************************************/
addItemListboxProperties()295 void ItemListbox::addItemListboxProperties()
296 {
297     const String propertyOrigin("ItemListbox");
298     CEGUI_DEFINE_PROPERTY(ItemListbox, bool,
299         "MultiSelect","Property to get/set the state of the multiselect enabled setting for the ItemListbox.  Value is either \"true\" or \"false\".",
300         &ItemListbox::setMultiSelectEnabled, &ItemListbox::isMultiSelectEnabled, false
301     );
302 }
303 
304 /*************************************************************************
305     Query item selection state
306 *************************************************************************/
isItemSelected(size_t index) const307 bool ItemListbox::isItemSelected(size_t index) const
308 {
309     if (index >= d_listItems.size())
310     {
311         CEGUI_THROW(InvalidRequestException(
312             "The index given is out of range for this ItemListbox"));
313     }
314     ItemEntry *li = d_listItems[index];
315     return li->isSelected();
316 }
317 
318 /*************************************************************************
319     Clear all selections
320 *************************************************************************/
clearAllSelections()321 void ItemListbox::clearAllSelections()
322 {
323     size_t max = d_listItems.size();
324     for (size_t i=0; i<max; ++i)
325     {
326         d_listItems[i]->setSelected_impl(false,false);
327     }
328     d_lastSelected = 0;
329 
330     WindowEventArgs e(this);
331     onSelectionChanged(e);
332 }
333 
334 /*************************************************************************
335     Select range of items
336 *************************************************************************/
selectRange(size_t a,size_t z)337 void ItemListbox::selectRange(size_t a, size_t z)
338 {
339     // do nothing if the list is empty
340     if (d_listItems.empty())
341     {
342         return;
343     }
344 
345     size_t max = d_listItems.size();
346     if (a >= max)
347     {
348         a = 0;
349     }
350     if (z >= max)
351     {
352         z = max-1;
353     }
354 
355     if (a>z)
356     {
357         size_t tmp = a;
358         a = z;
359         z = tmp;
360     }
361 
362     for (size_t i=a; i<=z; ++i)
363     {
364         d_listItems[i]->setSelected_impl(true,false);
365     }
366     d_lastSelected = d_listItems[z];
367 
368 
369     WindowEventArgs e(this);
370     onSelectionChanged(e);
371 }
372 
373 /************************************************************************
374     Select all items if allowed
375 ************************************************************************/
selectAllItems()376 void ItemListbox::selectAllItems()
377 {
378     if (!d_multiSelect)
379     {
380         return;
381     }
382 
383     size_t max = d_listItems.size();
384     for (size_t i=0; i<max; ++i)
385     {
386         d_lastSelected = d_listItems[i];
387         d_lastSelected->setSelected_impl(true,false);
388     }
389 
390     WindowEventArgs e(this);
391     onSelectionChanged(e);
392 }
393 
394 /************************************************************************
395     Handle selection changed
396 ************************************************************************/
onSelectionChanged(WindowEventArgs & e)397 void ItemListbox::onSelectionChanged(WindowEventArgs& e)
398 {
399     fireEvent(EventSelectionChanged, e);
400 }
401 
402 /************************************************************************
403     Handle multiselect mode changed
404 ************************************************************************/
onMultiSelectModeChanged(WindowEventArgs & e)405 void ItemListbox::onMultiSelectModeChanged(WindowEventArgs& e)
406 {
407     fireEvent(EventMultiSelectModeChanged, e);
408 }
409 
410 /************************************************************************
411     Handle key down event
412 ************************************************************************/
onKeyDown(KeyEventArgs & e)413 void ItemListbox::onKeyDown(KeyEventArgs& e)
414 {
415     ScrolledItemListBase::onKeyDown(e);
416 
417     // select all (if allowed) on Ctrl+A
418     if (d_multiSelect)
419     {
420         uint sysKeys = getGUIContext().getSystemKeys().get();
421         if (e.scancode == Key::A && (sysKeys&Control))
422         {
423             selectAllItems();
424             ++e.handled;
425         }
426     }
427 }
428 
429 //----------------------------------------------------------------------------//
handle_PaneChildRemoved(const EventArgs & e)430 bool ItemListbox::handle_PaneChildRemoved(const EventArgs& e)
431 {
432     ItemListBase::handle_PaneChildRemoved(e);
433 
434     // get the window that's being removed
435     const Window* w = static_cast<const WindowEventArgs&>(e).window;
436     // Clear last selected pointer if that item was just removed.
437     if (w == d_lastSelected)
438         d_lastSelected = 0;
439 
440     return true;
441 }
442 //----------------------------------------------------------------------------//
443 
444 } // end CEGUI namespace
445