1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "CGUIListBox.h"
6 #ifdef _IRR_COMPILE_WITH_GUI_
7 
8 #include "CGUIListBox.h"
9 #include "IGUISkin.h"
10 #include "IGUIEnvironment.h"
11 #include "IVideoDriver.h"
12 #include "IGUIFont.h"
13 #include "IGUISpriteBank.h"
14 #include "CGUIScrollBar.h"
15 #include "os.h"
16 
17 namespace irr
18 {
19 namespace gui
20 {
21 
22 //! constructor
CGUIListBox(IGUIEnvironment * environment,IGUIElement * parent,s32 id,core::rect<s32> rectangle,bool clip,bool drawBack,bool moveOverSelect)23 CGUIListBox::CGUIListBox(IGUIEnvironment* environment, IGUIElement* parent,
24 			s32 id, core::rect<s32> rectangle, bool clip,
25 			bool drawBack, bool moveOverSelect)
26 : IGUIListBox(environment, parent, id, rectangle), Selected(-1),
27 	ItemHeight(0),ItemHeightOverride(0),
28 	TotalItemHeight(0), ItemsIconWidth(0), Font(0), IconBank(0),
29 	ScrollBar(0), selectTime(0), LastKeyTime(0), Selecting(false), DrawBack(drawBack),
30 	MoveOverSelect(moveOverSelect), AutoScroll(true), HighlightWhenNotFocused(true)
31 {
32 	#ifdef _DEBUG
33 	setDebugName("CGUIListBox");
34 	#endif
35 
36 	IGUISkin* skin = Environment->getSkin();
37 	const s32 s = skin->getSize(EGDS_SCROLLBAR_SIZE);
38 
39 	ScrollBar = new CGUIScrollBar(false, Environment, this, -1,
40 		core::rect<s32>(RelativeRect.getWidth() - s, 0, RelativeRect.getWidth(), RelativeRect.getHeight()),
41 		!clip);
42 	ScrollBar->setSubElement(true);
43 	ScrollBar->setTabStop(false);
44 	ScrollBar->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
45 	ScrollBar->setVisible(false);
46 	ScrollBar->setPos(0);
47 
48 	setNotClipped(!clip);
49 
50 	// this element can be tabbed to
51 	setTabStop(true);
52 	setTabOrder(-1);
53 
54 	updateAbsolutePosition();
55 }
56 
57 
58 //! destructor
~CGUIListBox()59 CGUIListBox::~CGUIListBox()
60 {
61 	if (ScrollBar)
62 		ScrollBar->drop();
63 
64 	if (Font)
65 		Font->drop();
66 
67 	if (IconBank)
68 		IconBank->drop();
69 }
70 
71 
72 //! returns amount of list items
getItemCount() const73 u32 CGUIListBox::getItemCount() const
74 {
75 	return Items.size();
76 }
77 
78 
79 //! returns string of a list item. the may be a value from 0 to itemCount-1
getListItem(u32 id) const80 const wchar_t* CGUIListBox::getListItem(u32 id) const
81 {
82 	if (id>=Items.size())
83 		return 0;
84 
85 	return Items[id].text.c_str();
86 }
87 
88 
89 //! Returns the icon of an item
getIcon(u32 id) const90 s32 CGUIListBox::getIcon(u32 id) const
91 {
92 	if (id>=Items.size())
93 		return -1;
94 
95 	return Items[id].icon;
96 }
97 
98 
99 //! adds a list item, returns id of item
addItem(const wchar_t * text)100 u32 CGUIListBox::addItem(const wchar_t* text)
101 {
102 	return addItem(text, -1);
103 }
104 
105 
106 //! adds a list item, returns id of item
removeItem(u32 id)107 void CGUIListBox::removeItem(u32 id)
108 {
109 	if (id >= Items.size())
110 		return;
111 
112 	if ((u32)Selected==id)
113 	{
114 		Selected = -1;
115 	}
116 	else if ((u32)Selected > id)
117 	{
118 		Selected -= 1;
119 		selectTime = os::Timer::getTime();
120 	}
121 
122 	Items.erase(id);
123 
124 	recalculateItemHeight();
125 }
126 
127 
getItemAt(s32 xpos,s32 ypos) const128 s32 CGUIListBox::getItemAt(s32 xpos, s32 ypos) const
129 {
130 	if ( 	xpos < AbsoluteRect.UpperLeftCorner.X || xpos >= AbsoluteRect.LowerRightCorner.X
131 		||	ypos < AbsoluteRect.UpperLeftCorner.Y || ypos >= AbsoluteRect.LowerRightCorner.Y
132 		)
133 		return -1;
134 
135 	if ( ItemHeight == 0 )
136 		return -1;
137 
138 	s32 item = ((ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + ScrollBar->getPos()) / ItemHeight;
139 	if ( item < 0 || item >= (s32)Items.size())
140 		return -1;
141 
142 	return item;
143 }
144 
145 //! clears the list
clear()146 void CGUIListBox::clear()
147 {
148 	Items.clear();
149 	ItemsIconWidth = 0;
150 	Selected = -1;
151 
152 	if (ScrollBar)
153 		ScrollBar->setPos(0);
154 
155 	recalculateItemHeight();
156 }
157 
158 
recalculateItemHeight()159 void CGUIListBox::recalculateItemHeight()
160 {
161 	IGUISkin* skin = Environment->getSkin();
162 
163 	if (Font != skin->getFont())
164 	{
165 		if (Font)
166 			Font->drop();
167 
168 		Font = skin->getFont();
169 		if ( 0 == ItemHeightOverride )
170 			ItemHeight = 0;
171 
172 		if (Font)
173 		{
174 			if ( 0 == ItemHeightOverride )
175 				ItemHeight = Font->getDimension(L"A").Height + 4;
176 
177 			Font->grab();
178 		}
179 	}
180 
181 	TotalItemHeight = ItemHeight * Items.size();
182 	ScrollBar->setMax( core::max_(0, TotalItemHeight - AbsoluteRect.getHeight()) );
183 	s32 minItemHeight = ItemHeight > 0 ? ItemHeight : 1;
184 	ScrollBar->setSmallStep ( minItemHeight );
185 	ScrollBar->setLargeStep ( 2*minItemHeight );
186 
187 	if ( TotalItemHeight <= AbsoluteRect.getHeight() )
188 		ScrollBar->setVisible(false);
189 	else
190 		ScrollBar->setVisible(true);
191 }
192 
193 
194 //! returns id of selected item. returns -1 if no item is selected.
getSelected() const195 s32 CGUIListBox::getSelected() const
196 {
197 	return Selected;
198 }
199 
200 
201 //! sets the selected item. Set this to -1 if no item should be selected
setSelected(s32 id)202 void CGUIListBox::setSelected(s32 id)
203 {
204 	if ((u32)id>=Items.size())
205 		Selected = -1;
206 	else
207 		Selected = id;
208 
209 	selectTime = os::Timer::getTime();
210 
211 	recalculateScrollPos();
212 }
213 
214 //! sets the selected item. Set this to -1 if no item should be selected
setSelected(const wchar_t * item)215 void CGUIListBox::setSelected(const wchar_t *item)
216 {
217 	s32 index = -1;
218 
219 	if ( item )
220 	{
221 		for ( index = 0; index < (s32) Items.size(); ++index )
222 		{
223 			if ( Items[index].text == item )
224 				break;
225 		}
226 	}
227 	setSelected ( index );
228 }
229 
230 //! called if an event happened.
OnEvent(const SEvent & event)231 bool CGUIListBox::OnEvent(const SEvent& event)
232 {
233 	if (isEnabled())
234 	{
235 		switch(event.EventType)
236 		{
237 		case EET_KEY_INPUT_EVENT:
238 			if (event.KeyInput.PressedDown &&
239 				(event.KeyInput.Key == KEY_DOWN ||
240 				event.KeyInput.Key == KEY_UP   ||
241 				event.KeyInput.Key == KEY_HOME ||
242 				event.KeyInput.Key == KEY_END  ||
243 				event.KeyInput.Key == KEY_NEXT ||
244 				event.KeyInput.Key == KEY_PRIOR ) )
245 			{
246 				s32 oldSelected = Selected;
247 				switch (event.KeyInput.Key)
248 				{
249 					case KEY_DOWN:
250 						Selected += 1;
251 						break;
252 					case KEY_UP:
253 						Selected -= 1;
254 						break;
255 					case KEY_HOME:
256 						Selected = 0;
257 						break;
258 					case KEY_END:
259 						Selected = (s32)Items.size()-1;
260 						break;
261 					case KEY_NEXT:
262 						Selected += AbsoluteRect.getHeight() / ItemHeight;
263 						break;
264 					case KEY_PRIOR:
265 						Selected -= AbsoluteRect.getHeight() / ItemHeight;
266 						break;
267 					default:
268 						break;
269 				}
270 				if (Selected<0)
271 					Selected = 0;
272 				if (Selected >= (s32)Items.size())
273 					Selected = Items.size() - 1;	// will set Selected to -1 for empty listboxes which is correct
274 
275 
276 				recalculateScrollPos();
277 
278 				// post the news
279 
280 				if (oldSelected != Selected && Parent && !Selecting && !MoveOverSelect)
281 				{
282 					SEvent e;
283 					e.EventType = EET_GUI_EVENT;
284 					e.GUIEvent.Caller = this;
285 					e.GUIEvent.Element = 0;
286 					e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
287 					Parent->OnEvent(e);
288 				}
289 
290 				return true;
291 			}
292 			else
293 			if (!event.KeyInput.PressedDown && ( event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE ) )
294 			{
295 				if (Parent)
296 				{
297 					SEvent e;
298 					e.EventType = EET_GUI_EVENT;
299 					e.GUIEvent.Caller = this;
300 					e.GUIEvent.Element = 0;
301 					e.GUIEvent.EventType = EGET_LISTBOX_SELECTED_AGAIN;
302 					Parent->OnEvent(e);
303 				}
304 				return true;
305 			}
306 			else if (event.KeyInput.PressedDown && event.KeyInput.Char)
307 			{
308 				// change selection based on text as it is typed.
309 				u32 now = os::Timer::getTime();
310 
311 				if (now - LastKeyTime < 500)
312 				{
313 					// add to key buffer if it isn't a key repeat
314 					if (!(KeyBuffer.size() == 1 && KeyBuffer[0] == event.KeyInput.Char))
315 					{
316 						KeyBuffer += L" ";
317 						KeyBuffer[KeyBuffer.size()-1] = event.KeyInput.Char;
318 					}
319 				}
320 				else
321 				{
322 					KeyBuffer = L" ";
323 					KeyBuffer[0] = event.KeyInput.Char;
324 				}
325 				LastKeyTime = now;
326 
327 				// find the selected item, starting at the current selection
328 				s32 start = Selected;
329 				// dont change selection if the key buffer matches the current item
330 				if (Selected > -1 && KeyBuffer.size() > 1)
331 				{
332 					if (Items[Selected].text.size() >= KeyBuffer.size() &&
333 						KeyBuffer.equals_ignore_case(Items[Selected].text.subString(0,KeyBuffer.size())))
334 						return true;
335 				}
336 
337 				s32 current;
338 				for (current = start+1; current < (s32)Items.size(); ++current)
339 				{
340 					if (Items[current].text.size() >= KeyBuffer.size())
341 					{
342 						if (KeyBuffer.equals_ignore_case(Items[current].text.subString(0,KeyBuffer.size())))
343 						{
344 							if (Parent && Selected != current && !Selecting && !MoveOverSelect)
345 							{
346 								SEvent e;
347 								e.EventType = EET_GUI_EVENT;
348 								e.GUIEvent.Caller = this;
349 								e.GUIEvent.Element = 0;
350 								e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
351 								Parent->OnEvent(e);
352 							}
353 							setSelected(current);
354 							return true;
355 						}
356 					}
357 				}
358 				for (current = 0; current <= start; ++current)
359 				{
360 					if (Items[current].text.size() >= KeyBuffer.size())
361 					{
362 						if (KeyBuffer.equals_ignore_case(Items[current].text.subString(0,KeyBuffer.size())))
363 						{
364 							if (Parent && Selected != current && !Selecting && !MoveOverSelect)
365 							{
366 								Selected = current;
367 								SEvent e;
368 								e.EventType = EET_GUI_EVENT;
369 								e.GUIEvent.Caller = this;
370 								e.GUIEvent.Element = 0;
371 								e.GUIEvent.EventType = EGET_LISTBOX_CHANGED;
372 								Parent->OnEvent(e);
373 							}
374 							setSelected(current);
375 							return true;
376 						}
377 					}
378 				}
379 
380 				return true;
381 			}
382 			break;
383 
384 		case EET_GUI_EVENT:
385 			switch(event.GUIEvent.EventType)
386 			{
387 			case gui::EGET_SCROLL_BAR_CHANGED:
388 				if (event.GUIEvent.Caller == ScrollBar)
389 					return true;
390 				break;
391 			case gui::EGET_ELEMENT_FOCUS_LOST:
392 				{
393 					if (event.GUIEvent.Caller == this)
394 						Selecting = false;
395 				}
396 			default:
397 			break;
398 			}
399 			break;
400 
401 		case EET_MOUSE_INPUT_EVENT:
402 			{
403 				core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
404 
405 				switch(event.MouseInput.Event)
406 				{
407 				case EMIE_MOUSE_WHEEL:
408 					ScrollBar->setPos(ScrollBar->getPos() + (event.MouseInput.Wheel < 0 ? -1 : 1)*-ItemHeight/2);
409 					return true;
410 
411 				case EMIE_LMOUSE_PRESSED_DOWN:
412 				{
413 					Selecting = true;
414 					return true;
415 				}
416 
417 				case EMIE_LMOUSE_LEFT_UP:
418 				{
419 					Selecting = false;
420 
421 					if (isPointInside(p))
422 						selectNew(event.MouseInput.Y);
423 
424 					return true;
425 				}
426 
427 				case EMIE_MOUSE_MOVED:
428 					if (Selecting || MoveOverSelect)
429 					{
430 						if (isPointInside(p))
431 						{
432 							selectNew(event.MouseInput.Y, true);
433 							return true;
434 						}
435 					}
436 				default:
437 				break;
438 				}
439 			}
440 			break;
441 		case EET_LOG_TEXT_EVENT:
442 		case EET_USER_EVENT:
443 		case EET_JOYSTICK_INPUT_EVENT:
444 		case EGUIET_FORCE_32_BIT:
445 			break;
446 		}
447 	}
448 
449 	return IGUIElement::OnEvent(event);
450 }
451 
452 
selectNew(s32 ypos,bool onlyHover)453 void CGUIListBox::selectNew(s32 ypos, bool onlyHover)
454 {
455 	u32 now = os::Timer::getTime();
456 	s32 oldSelected = Selected;
457 
458 	Selected = getItemAt(AbsoluteRect.UpperLeftCorner.X, ypos);
459 	if (Selected<0 && !Items.empty())
460 		Selected = 0;
461 
462 	recalculateScrollPos();
463 
464 	gui::EGUI_EVENT_TYPE eventType = (Selected == oldSelected && now < selectTime + 500) ? EGET_LISTBOX_SELECTED_AGAIN : EGET_LISTBOX_CHANGED;
465 	selectTime = now;
466 	// post the news
467 	if (Parent && !onlyHover)
468 	{
469 		SEvent event;
470 		event.EventType = EET_GUI_EVENT;
471 		event.GUIEvent.Caller = this;
472 		event.GUIEvent.Element = 0;
473 		event.GUIEvent.EventType = eventType;
474 		Parent->OnEvent(event);
475 	}
476 }
477 
478 
479 //! Update the position and size of the listbox, and update the scrollbar
updateAbsolutePosition()480 void CGUIListBox::updateAbsolutePosition()
481 {
482 	IGUIElement::updateAbsolutePosition();
483 
484 	recalculateItemHeight();
485 }
486 
487 
488 //! draws the element and its children
draw()489 void CGUIListBox::draw()
490 {
491 	if (!IsVisible)
492 		return;
493 
494 	recalculateItemHeight(); // if the font changed
495 
496 	IGUISkin* skin = Environment->getSkin();
497 
498 	core::rect<s32>* clipRect = 0;
499 
500 	// draw background
501 	core::rect<s32> frameRect(AbsoluteRect);
502 
503 	// draw items
504 
505 	core::rect<s32> clientClip(AbsoluteRect);
506 	clientClip.UpperLeftCorner.Y += 1;
507 	clientClip.UpperLeftCorner.X += 1;
508 	if (ScrollBar->isVisible())
509 		clientClip.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE);
510 	clientClip.LowerRightCorner.Y -= 1;
511 	clientClip.clipAgainst(AbsoluteClippingRect);
512 
513 	skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true,
514 		DrawBack, frameRect, &AbsoluteClippingRect);
515 
516 	if (clipRect)
517 		clientClip.clipAgainst(*clipRect);
518 
519 	frameRect = AbsoluteRect;
520 	frameRect.UpperLeftCorner.X += 1;
521 	if (ScrollBar->isVisible())
522 		frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize(EGDS_SCROLLBAR_SIZE);
523 
524 	frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight;
525 
526 	frameRect.UpperLeftCorner.Y -= ScrollBar->getPos();
527 	frameRect.LowerRightCorner.Y -= ScrollBar->getPos();
528 
529 	bool hl = (HighlightWhenNotFocused || Environment->hasFocus(this) || Environment->hasFocus(ScrollBar));
530 
531 	for (s32 i=0; i<(s32)Items.size(); ++i)
532 	{
533 		if (frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y &&
534 			frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y)
535 		{
536 			if (i == Selected && hl)
537 				skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), frameRect, &clientClip);
538 
539 			core::rect<s32> textRect = frameRect;
540 			textRect.UpperLeftCorner.X += 3;
541 
542 			if (Font)
543 			{
544 				if (IconBank && (Items[i].icon > -1))
545 				{
546 					core::position2di iconPos = textRect.UpperLeftCorner;
547 					iconPos.Y += textRect.getHeight() / 2;
548 					iconPos.X += ItemsIconWidth/2;
549 
550 					if ( i==Selected && hl )
551 					{
552 						IconBank->draw2DSprite( (u32)Items[i].icon, iconPos, &clientClip,
553 							hasItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) ?
554 							getItemOverrideColor(i, EGUI_LBC_ICON_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_ICON_HIGHLIGHT),
555 							selectTime, os::Timer::getTime(), false, true);
556 					}
557 					else
558 					{
559 						IconBank->draw2DSprite( (u32)Items[i].icon, iconPos, &clientClip,
560 							hasItemOverrideColor(i, EGUI_LBC_ICON) ? getItemOverrideColor(i, EGUI_LBC_ICON) : getItemDefaultColor(EGUI_LBC_ICON),
561 							0 , (i==Selected) ? os::Timer::getTime() : 0, false, true);
562 					}
563 				}
564 
565 				textRect.UpperLeftCorner.X += ItemsIconWidth+3;
566 
567 				if ( i==Selected && hl )
568 				{
569 					Font->draw(Items[i].text.c_str(), textRect,
570 						hasItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) ?
571 						getItemOverrideColor(i, EGUI_LBC_TEXT_HIGHLIGHT) : getItemDefaultColor(EGUI_LBC_TEXT_HIGHLIGHT),
572 						false, true, &clientClip);
573 				}
574 				else
575 				{
576 					Font->draw(Items[i].text.c_str(), textRect,
577 						hasItemOverrideColor(i, EGUI_LBC_TEXT) ? getItemOverrideColor(i, EGUI_LBC_TEXT) : getItemDefaultColor(EGUI_LBC_TEXT),
578 						false, true, &clientClip);
579 				}
580 
581 				textRect.UpperLeftCorner.X -= ItemsIconWidth+3;
582 			}
583 		}
584 
585 		frameRect.UpperLeftCorner.Y += ItemHeight;
586 		frameRect.LowerRightCorner.Y += ItemHeight;
587 	}
588 
589 	IGUIElement::draw();
590 }
591 
592 
593 //! adds an list item with an icon
addItem(const wchar_t * text,s32 icon)594 u32 CGUIListBox::addItem(const wchar_t* text, s32 icon)
595 {
596 	ListItem i;
597 	i.text = text;
598 	i.icon = icon;
599 
600 	Items.push_back(i);
601 	recalculateItemHeight();
602 	recalculateItemWidth(icon);
603 
604 	return Items.size() - 1;
605 }
606 
607 
setSpriteBank(IGUISpriteBank * bank)608 void CGUIListBox::setSpriteBank(IGUISpriteBank* bank)
609 {
610 	if ( bank == IconBank )
611 		return;
612 	if (IconBank)
613 		IconBank->drop();
614 
615 	IconBank = bank;
616 	if (IconBank)
617 		IconBank->grab();
618 }
619 
620 
recalculateScrollPos()621 void CGUIListBox::recalculateScrollPos()
622 {
623 	if (!AutoScroll)
624 		return;
625 
626 	const s32 selPos = (Selected == -1 ? TotalItemHeight : Selected * ItemHeight) - ScrollBar->getPos();
627 
628 	if (selPos < 0)
629 	{
630 		ScrollBar->setPos(ScrollBar->getPos() + selPos);
631 	}
632 	else
633 	if (selPos > AbsoluteRect.getHeight() - ItemHeight)
634 	{
635 		ScrollBar->setPos(ScrollBar->getPos() + selPos - AbsoluteRect.getHeight() + ItemHeight);
636 	}
637 }
638 
639 
setAutoScrollEnabled(bool scroll)640 void CGUIListBox::setAutoScrollEnabled(bool scroll)
641 {
642 	AutoScroll = scroll;
643 }
644 
645 
isAutoScrollEnabled() const646 bool CGUIListBox::isAutoScrollEnabled() const
647 {
648 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
649 	return AutoScroll;
650 }
651 
652 
getSerializationLabels(EGUI_LISTBOX_COLOR colorType,core::stringc & useColorLabel,core::stringc & colorLabel) const653 bool CGUIListBox::getSerializationLabels(EGUI_LISTBOX_COLOR colorType, core::stringc & useColorLabel, core::stringc & colorLabel) const
654 {
655 	switch ( colorType )
656 	{
657 	case EGUI_LBC_TEXT:
658 		useColorLabel = "UseColText";
659 		colorLabel = "ColText";
660 		break;
661 	case EGUI_LBC_TEXT_HIGHLIGHT:
662 		useColorLabel = "UseColTextHl";
663 		colorLabel = "ColTextHl";
664 		break;
665 	case EGUI_LBC_ICON:
666 		useColorLabel = "UseColIcon";
667 		colorLabel = "ColIcon";
668 		break;
669 	case EGUI_LBC_ICON_HIGHLIGHT:
670 		useColorLabel = "UseColIconHl";
671 		colorLabel = "ColIconHl";
672 		break;
673 	default:
674 		return false;
675 	}
676 	return true;
677 }
678 
679 
680 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const681 void CGUIListBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
682 {
683 	IGUIListBox::serializeAttributes(out,options);
684 
685 	// todo: out->addString	("IconBank",		IconBank->getName?);
686 	out->addBool    ("DrawBack",        DrawBack);
687 	out->addBool    ("MoveOverSelect",  MoveOverSelect);
688 	out->addBool    ("AutoScroll",      AutoScroll);
689 
690 	out->addInt("ItemCount", Items.size());
691 	for (u32 i=0;i<Items.size(); ++i)
692 	{
693 		core::stringc label("text");
694 		label += i;
695 		out->addString(label.c_str(), Items[i].text.c_str() );
696 
697 		for ( s32 c=0; c < (s32)EGUI_LBC_COUNT; ++c )
698 		{
699 			core::stringc useColorLabel, colorLabel;
700 			if ( !getSerializationLabels((EGUI_LISTBOX_COLOR)c, useColorLabel, colorLabel) )
701 				return;
702 			label = useColorLabel; label += i;
703 			if ( Items[i].OverrideColors[c].Use )
704 			{
705 				out->addBool(label.c_str(), true );
706 				label = colorLabel; label += i;
707 				out->addColor(label.c_str(), Items[i].OverrideColors[c].Color);
708 			}
709 			else
710 			{
711 				out->addBool(label.c_str(), false );
712 			}
713 		}
714 	}
715 }
716 
717 
718 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)719 void CGUIListBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
720 {
721 	clear();
722 
723 	DrawBack        = in->getAttributeAsBool("DrawBack");
724 	MoveOverSelect  = in->getAttributeAsBool("MoveOverSelect");
725 	AutoScroll      = in->getAttributeAsBool("AutoScroll");
726 
727 	IGUIListBox::deserializeAttributes(in,options);
728 
729 	const s32 count = in->getAttributeAsInt("ItemCount");
730 	for (s32 i=0; i<count; ++i)
731 	{
732 		core::stringc label("text");
733 		ListItem item;
734 
735 		label += i;
736 		item.text = in->getAttributeAsStringW(label.c_str());
737 
738 		addItem(item.text.c_str(), item.icon);
739 
740 		for ( u32 c=0; c < EGUI_LBC_COUNT; ++c )
741 		{
742 			core::stringc useColorLabel, colorLabel;
743 			if ( !getSerializationLabels((EGUI_LISTBOX_COLOR)c, useColorLabel, colorLabel) )
744 				return;
745 			label = useColorLabel; label += i;
746 			Items[i].OverrideColors[c].Use = in->getAttributeAsBool(label.c_str());
747 			if ( Items[i].OverrideColors[c].Use )
748 			{
749 				label = colorLabel; label += i;
750 				Items[i].OverrideColors[c].Color = in->getAttributeAsColor(label.c_str());
751 			}
752 		}
753 	}
754 }
755 
756 
recalculateItemWidth(s32 icon)757 void CGUIListBox::recalculateItemWidth(s32 icon)
758 {
759 	if (IconBank && icon > -1 &&
760 		IconBank->getSprites().size() > (u32)icon &&
761 		IconBank->getSprites()[(u32)icon].Frames.size())
762 	{
763 		u32 rno = IconBank->getSprites()[(u32)icon].Frames[0].rectNumber;
764 		if (IconBank->getPositions().size() > rno)
765 		{
766 			const s32 w = IconBank->getPositions()[rno].getWidth();
767 			if (w > ItemsIconWidth)
768 				ItemsIconWidth = w;
769 		}
770 	}
771 }
772 
773 
setItem(u32 index,const wchar_t * text,s32 icon)774 void CGUIListBox::setItem(u32 index, const wchar_t* text, s32 icon)
775 {
776 	if ( index >= Items.size() )
777 		return;
778 
779 	Items[index].text = text;
780 	Items[index].icon = icon;
781 
782 	recalculateItemHeight();
783 	recalculateItemWidth(icon);
784 }
785 
786 
787 //! Insert the item at the given index
788 //! Return the index on success or -1 on failure.
insertItem(u32 index,const wchar_t * text,s32 icon)789 s32 CGUIListBox::insertItem(u32 index, const wchar_t* text, s32 icon)
790 {
791 	ListItem i;
792 	i.text = text;
793 	i.icon = icon;
794 
795 	Items.insert(i, index);
796 	recalculateItemHeight();
797 	recalculateItemWidth(icon);
798 
799 	return index;
800 }
801 
802 
swapItems(u32 index1,u32 index2)803 void CGUIListBox::swapItems(u32 index1, u32 index2)
804 {
805 	if ( index1 >= Items.size() || index2 >= Items.size() )
806 		return;
807 
808 	ListItem dummmy = Items[index1];
809 	Items[index1] = Items[index2];
810 	Items[index2] = dummmy;
811 }
812 
813 
setItemOverrideColor(u32 index,video::SColor color)814 void CGUIListBox::setItemOverrideColor(u32 index, video::SColor color)
815 {
816 	for ( u32 c=0; c < EGUI_LBC_COUNT; ++c )
817 	{
818 		Items[index].OverrideColors[c].Use = true;
819 		Items[index].OverrideColors[c].Color = color;
820 	}
821 }
822 
823 
setItemOverrideColor(u32 index,EGUI_LISTBOX_COLOR colorType,video::SColor color)824 void CGUIListBox::setItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType, video::SColor color)
825 {
826 	if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT )
827 		return;
828 
829 	Items[index].OverrideColors[colorType].Use = true;
830 	Items[index].OverrideColors[colorType].Color = color;
831 }
832 
833 
clearItemOverrideColor(u32 index)834 void CGUIListBox::clearItemOverrideColor(u32 index)
835 {
836 	for (u32 c=0; c < (u32)EGUI_LBC_COUNT; ++c )
837 	{
838 		Items[index].OverrideColors[c].Use = false;
839 	}
840 }
841 
842 
clearItemOverrideColor(u32 index,EGUI_LISTBOX_COLOR colorType)843 void CGUIListBox::clearItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType)
844 {
845 	if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT )
846 		return;
847 
848 	Items[index].OverrideColors[colorType].Use = false;
849 }
850 
851 
hasItemOverrideColor(u32 index,EGUI_LISTBOX_COLOR colorType) const852 bool CGUIListBox::hasItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const
853 {
854 	if ( index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT )
855 		return false;
856 
857 	return Items[index].OverrideColors[colorType].Use;
858 }
859 
860 
getItemOverrideColor(u32 index,EGUI_LISTBOX_COLOR colorType) const861 video::SColor CGUIListBox::getItemOverrideColor(u32 index, EGUI_LISTBOX_COLOR colorType) const
862 {
863 	if ( (u32)index >= Items.size() || colorType < 0 || colorType >= EGUI_LBC_COUNT )
864 		return video::SColor();
865 
866 	return Items[index].OverrideColors[colorType].Color;
867 }
868 
869 
getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const870 video::SColor CGUIListBox::getItemDefaultColor(EGUI_LISTBOX_COLOR colorType) const
871 {
872 	IGUISkin* skin = Environment->getSkin();
873 	if ( !skin )
874 		return video::SColor();
875 
876 	switch ( colorType )
877 	{
878 		case EGUI_LBC_TEXT:
879 			return skin->getColor(EGDC_BUTTON_TEXT);
880 		case EGUI_LBC_TEXT_HIGHLIGHT:
881 			return skin->getColor(EGDC_HIGH_LIGHT_TEXT);
882 		case EGUI_LBC_ICON:
883 			return skin->getColor(EGDC_ICON);
884 		case EGUI_LBC_ICON_HIGHLIGHT:
885 			return skin->getColor(EGDC_ICON_HIGH_LIGHT);
886 		default:
887 			return video::SColor();
888 	}
889 }
890 
891 //! set global itemHeight
setItemHeight(s32 height)892 void CGUIListBox::setItemHeight( s32 height )
893 {
894 	ItemHeight = height;
895 	ItemHeightOverride = 1;
896 }
897 
898 
899 //! Sets whether to draw the background
setDrawBackground(bool draw)900 void CGUIListBox::setDrawBackground(bool draw)
901 {
902     DrawBack = draw;
903 }
904 
905 
906 } // end namespace gui
907 } // end namespace irr
908 
909 #endif // _IRR_COMPILE_WITH_GUI_
910 
911