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