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 "CGUITabControl.h"
6 #ifdef _IRR_COMPILE_WITH_GUI_
7 
8 #include "CGUIButton.h"
9 #include "IGUISkin.h"
10 #include "IGUIEnvironment.h"
11 #include "IGUIFont.h"
12 #include "IVideoDriver.h"
13 #include "rect.h"
14 #include "os.h"
15 
16 namespace irr
17 {
18 namespace gui
19 {
20 
21 // ------------------------------------------------------------------
22 // Tab
23 // ------------------------------------------------------------------
24 
25 //! constructor
CGUITab(s32 number,IGUIEnvironment * environment,IGUIElement * parent,const core::rect<s32> & rectangle,s32 id)26 CGUITab::CGUITab(s32 number, IGUIEnvironment* environment,
27 	IGUIElement* parent, const core::rect<s32>& rectangle,
28 	s32 id)
29 	: IGUITab(environment, parent, id, rectangle), Number(number),
30 		BackColor(0,0,0,0), OverrideTextColorEnabled(false), TextColor(255,0,0,0),
31 		DrawBackground(false)
32 {
33 	#ifdef _DEBUG
34 	setDebugName("CGUITab");
35 	#endif
36 
37 	const IGUISkin* const skin = environment->getSkin();
38 	if (skin)
39 		TextColor = skin->getColor(EGDC_BUTTON_TEXT);
40 }
41 
42 
43 //! Returns number of tab in tabcontrol. Can be accessed
44 //! later IGUITabControl::getTab() by this number.
getNumber() const45 s32 CGUITab::getNumber() const
46 {
47 	return Number;
48 }
49 
50 
51 //! Sets the number
setNumber(s32 n)52 void CGUITab::setNumber(s32 n)
53 {
54 	Number = n;
55 }
56 
refreshSkinColors()57 void CGUITab::refreshSkinColors()
58 {
59 	if ( !OverrideTextColorEnabled )
60 	{
61 		TextColor = Environment->getSkin()->getColor(EGDC_BUTTON_TEXT);
62 	}
63 }
64 
65 //! draws the element and its children
draw()66 void CGUITab::draw()
67 {
68 	if (!IsVisible)
69 		return;
70 
71 	IGUISkin *skin = Environment->getSkin();
72 
73 	if (skin && DrawBackground)
74 		skin->draw2DRectangle(this, BackColor, AbsoluteRect, &AbsoluteClippingRect);
75 
76 	IGUIElement::draw();
77 }
78 
79 
80 //! sets if the tab should draw its background
setDrawBackground(bool draw)81 void CGUITab::setDrawBackground(bool draw)
82 {
83 	DrawBackground = draw;
84 }
85 
86 
87 //! sets the color of the background, if it should be drawn.
setBackgroundColor(video::SColor c)88 void CGUITab::setBackgroundColor(video::SColor c)
89 {
90 	BackColor = c;
91 }
92 
93 
94 //! sets the color of the text
setTextColor(video::SColor c)95 void CGUITab::setTextColor(video::SColor c)
96 {
97 	OverrideTextColorEnabled = true;
98 	TextColor = c;
99 }
100 
101 
getTextColor() const102 video::SColor CGUITab::getTextColor() const
103 {
104 	return TextColor;
105 }
106 
107 
108 //! returns true if the tab is drawing its background, false if not
isDrawingBackground() const109 bool CGUITab::isDrawingBackground() const
110 {
111 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
112 	return DrawBackground;
113 }
114 
115 
116 //! returns the color of the background
getBackgroundColor() const117 video::SColor CGUITab::getBackgroundColor() const
118 {
119 	return BackColor;
120 }
121 
122 
123 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const124 void CGUITab::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
125 {
126 	IGUITab::serializeAttributes(out,options);
127 
128 	out->addInt		("TabNumber",		Number);
129 	out->addBool	("DrawBackground",	DrawBackground);
130 	out->addColor	("BackColor",		BackColor);
131 	out->addBool	("OverrideTextColorEnabled", OverrideTextColorEnabled);
132 	out->addColor	("TextColor",		TextColor);
133 
134 }
135 
136 
137 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)138 void CGUITab::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
139 {
140 	IGUITab::deserializeAttributes(in,options);
141 
142 	setNumber(in->getAttributeAsInt("TabNumber"));
143 	setDrawBackground(in->getAttributeAsBool("DrawBackground"));
144 	setBackgroundColor(in->getAttributeAsColor("BackColor"));
145 	if ( in->existsAttribute("OverrideTextColorEnabled") )
146 		OverrideTextColorEnabled = in->getAttributeAsBool("OverrideTextColorEnabled");
147 	setTextColor(in->getAttributeAsColor("TextColor"));
148 
149 	if (Parent && Parent->getType() == EGUIET_TAB_CONTROL)
150 	{
151 		((CGUITabControl*)Parent)->addTab(this);
152 		if (isVisible())
153 			((CGUITabControl*)Parent)->setActiveTab(this);
154 	}
155 }
156 
157 
158 // ------------------------------------------------------------------
159 // Tabcontrol
160 // ------------------------------------------------------------------
161 
162 //! constructor
CGUITabControl(IGUIEnvironment * environment,IGUIElement * parent,const core::rect<s32> & rectangle,bool fillbackground,bool border,s32 id)163 CGUITabControl::CGUITabControl(IGUIEnvironment* environment,
164 	IGUIElement* parent, const core::rect<s32>& rectangle,
165 	bool fillbackground, bool border, s32 id)
166 	: IGUITabControl(environment, parent, id, rectangle), ActiveTab(-1),
167 	Border(border), FillBackground(fillbackground), ScrollControl(false), TabHeight(0), VerticalAlignment(EGUIA_UPPERLEFT),
168 	UpButton(0), DownButton(0), TabMaxWidth(0), CurrentScrollTabIndex(0), TabExtraWidth(20)
169 {
170 	#ifdef _DEBUG
171 	setDebugName("CGUITabControl");
172 	#endif
173 
174 	IGUISkin* skin = Environment->getSkin();
175 	IGUISpriteBank* sprites = 0;
176 
177 	TabHeight = 32;
178 
179 	if (skin)
180 	{
181 		sprites = skin->getSpriteBank();
182 		TabHeight = skin->getSize(gui::EGDS_BUTTON_HEIGHT) + 2;
183 	}
184 
185 	UpButton = Environment->addButton(core::rect<s32>(0,0,10,10), this);
186 
187 	if (UpButton)
188 	{
189 		UpButton->setSpriteBank(sprites);
190 		UpButton->setVisible(false);
191 		UpButton->setSubElement(true);
192 		UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
193 		UpButton->setOverrideFont(Environment->getBuiltInFont());
194 		UpButton->grab();
195 	}
196 
197 	DownButton = Environment->addButton(core::rect<s32>(0,0,10,10), this);
198 
199 	if (DownButton)
200 	{
201 		DownButton->setSpriteBank(sprites);
202 		DownButton->setVisible(false);
203 		DownButton->setSubElement(true);
204 		DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
205 		DownButton->setOverrideFont(Environment->getBuiltInFont());
206 		DownButton->grab();
207 	}
208 
209 	setTabVerticalAlignment(EGUIA_UPPERLEFT);
210 	refreshSprites();
211 }
212 
213 //! destructor
~CGUITabControl()214 CGUITabControl::~CGUITabControl()
215 {
216 	for (u32 i=0; i<Tabs.size(); ++i)
217 	{
218 		if (Tabs[i])
219 			Tabs[i]->drop();
220 	}
221 
222 	if (UpButton)
223 		UpButton->drop();
224 
225 	if (DownButton)
226 		DownButton->drop();
227 }
228 
refreshSprites()229 void CGUITabControl::refreshSprites()
230 {
231 	video::SColor color(255,255,255,255);
232 	IGUISkin* skin = Environment->getSkin();
233 	if (skin)
234 	{
235 		color = skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL);
236 	}
237 
238 	if (UpButton)
239 	{
240 		UpButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_LEFT), color);
241 		UpButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_LEFT), color);
242 	}
243 
244 	if (DownButton)
245 	{
246 		DownButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_RIGHT), color);
247 		DownButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_RIGHT), color);
248 	}
249 }
250 
251 //! Adds a tab
addTab(const wchar_t * caption,s32 id)252 IGUITab* CGUITabControl::addTab(const wchar_t* caption, s32 id)
253 {
254 	CGUITab* tab = new CGUITab(Tabs.size(), Environment, this, calcTabPos(), id);
255 
256 	tab->setText(caption);
257 	tab->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
258 	tab->setVisible(false);
259 	Tabs.push_back(tab);
260 
261 	if (ActiveTab == -1)
262 	{
263 		ActiveTab = 0;
264 		tab->setVisible(true);
265 	}
266 
267 	recalculateScrollBar();
268 
269 	return tab;
270 }
271 
272 
273 //! adds a tab which has been created elsewhere
addTab(CGUITab * tab)274 void CGUITabControl::addTab(CGUITab* tab)
275 {
276 	if (!tab)
277 		return;
278 
279 	// check if its already added
280 	for (u32 i=0; i < Tabs.size(); ++i)
281 	{
282 		if (Tabs[i] == tab)
283 			return;
284 	}
285 
286 	tab->grab();
287 
288 	if (tab->getNumber() == -1)
289 		tab->setNumber((s32)Tabs.size());
290 
291 	while (tab->getNumber() >= (s32)Tabs.size())
292 		Tabs.push_back(0);
293 
294 	if (Tabs[tab->getNumber()])
295 	{
296 		Tabs.push_back(Tabs[tab->getNumber()]);
297 		Tabs[Tabs.size()-1]->setNumber(Tabs.size());
298 	}
299 	Tabs[tab->getNumber()] = tab;
300 
301 	if (ActiveTab == -1)
302 		ActiveTab = tab->getNumber();
303 
304 
305 	if (tab->getNumber() == ActiveTab)
306 	{
307 		setActiveTab(ActiveTab);
308 	}
309 }
310 
311 //! Insert the tab at the given index
insertTab(s32 idx,const wchar_t * caption,s32 id)312 IGUITab* CGUITabControl::insertTab(s32 idx, const wchar_t* caption, s32 id)
313 {
314 	if ( idx < 0 || idx > (s32)Tabs.size() )	// idx == Tabs.size() is indeed ok here as core::array can handle that
315 		return NULL;
316 
317 	CGUITab* tab = new CGUITab(idx, Environment, this, calcTabPos(), id);
318 
319 	tab->setText(caption);
320 	tab->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
321 	tab->setVisible(false);
322 	Tabs.insert(tab, (u32)idx);
323 
324 	if (ActiveTab == -1)
325 	{
326 		ActiveTab = 0;
327 		tab->setVisible(true);
328 	}
329 
330 	for ( u32 i=(u32)idx+1; i < Tabs.size(); ++i )
331 	{
332 		Tabs[i]->setNumber(i);
333 	}
334 
335 	recalculateScrollBar();
336 
337 	return tab;
338 }
339 
340 //! Removes a tab from the tabcontrol
removeTab(s32 idx)341 void CGUITabControl::removeTab(s32 idx)
342 {
343 	if ( idx < 0 || idx >= (s32)Tabs.size() )
344 		return;
345 
346 	Tabs[(u32)idx]->drop();
347 	Tabs.erase((u32)idx);
348 	for ( u32 i=(u32)idx; i < Tabs.size(); ++i )
349 	{
350 		Tabs[i]->setNumber(i);
351 	}
352 }
353 
354 //! Clears the tabcontrol removing all tabs
clear()355 void CGUITabControl::clear()
356 {
357 	for (u32 i=0; i<Tabs.size(); ++i)
358 	{
359 		if (Tabs[i])
360 			Tabs[i]->drop();
361 	}
362 	Tabs.clear();
363 }
364 
365 //! Returns amount of tabs in the tabcontrol
getTabCount() const366 s32 CGUITabControl::getTabCount() const
367 {
368 	return Tabs.size();
369 }
370 
371 
372 //! Returns a tab based on zero based index
getTab(s32 idx) const373 IGUITab* CGUITabControl::getTab(s32 idx) const
374 {
375 	if ((u32)idx >= Tabs.size())
376 		return 0;
377 
378 	return Tabs[idx];
379 }
380 
381 
382 //! called if an event happened.
OnEvent(const SEvent & event)383 bool CGUITabControl::OnEvent(const SEvent& event)
384 {
385 	if (isEnabled())
386 	{
387 
388 		switch(event.EventType)
389 		{
390 		case EET_GUI_EVENT:
391 			switch(event.GUIEvent.EventType)
392 			{
393 			case EGET_BUTTON_CLICKED:
394 				if (event.GUIEvent.Caller == UpButton)
395 				{
396 					scrollLeft();
397 					return true;
398 				}
399 				else if (event.GUIEvent.Caller == DownButton)
400 				{
401 					scrollRight();
402 					return true;
403 				}
404 
405 			break;
406 			default:
407 			break;
408 			}
409 			break;
410 		case EET_MOUSE_INPUT_EVENT:
411 			switch(event.MouseInput.Event)
412 			{
413 			case EMIE_LMOUSE_PRESSED_DOWN:
414 				// todo: dragging tabs around
415 				return true;
416 			case EMIE_LMOUSE_LEFT_UP:
417 			{
418 				s32 idx = getTabAt(event.MouseInput.X, event.MouseInput.Y);
419 				if ( idx >= 0 )
420 				{
421 					setActiveTab(idx);
422 					return true;
423 				}
424 				break;
425 			}
426 			default:
427 				break;
428 			}
429 			break;
430 		default:
431 			break;
432 		}
433 	}
434 
435 	return IGUIElement::OnEvent(event);
436 }
437 
438 
scrollLeft()439 void CGUITabControl::scrollLeft()
440 {
441 	if ( CurrentScrollTabIndex > 0 )
442 		--CurrentScrollTabIndex;
443 	recalculateScrollBar();
444 }
445 
446 
scrollRight()447 void CGUITabControl::scrollRight()
448 {
449 	if ( CurrentScrollTabIndex < (s32)(Tabs.size()) - 1 )
450 	{
451 		if ( needScrollControl(CurrentScrollTabIndex, true) )
452 			++CurrentScrollTabIndex;
453 	}
454 	recalculateScrollBar();
455 }
456 
calcTabWidth(s32 pos,IGUIFont * font,const wchar_t * text,bool withScrollControl) const457 s32 CGUITabControl::calcTabWidth(s32 pos, IGUIFont* font, const wchar_t* text, bool withScrollControl) const
458 {
459 	if ( !font )
460 		return 0;
461 
462 	s32 len = font->getDimension(text).Width + TabExtraWidth;
463 	if ( TabMaxWidth > 0 && len > TabMaxWidth )
464 		len = TabMaxWidth;
465 
466 	// check if we miss the place to draw the tab-button
467 	if ( withScrollControl && ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )
468 	{
469 		s32 tabMinWidth = font->getDimension(L"A").Width;
470 		if ( TabExtraWidth > 0 && TabExtraWidth > tabMinWidth )
471 			tabMinWidth = TabExtraWidth;
472 
473 		if ( ScrollControl && pos+tabMinWidth <= UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )
474 		{
475 			len = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;
476 		}
477 	}
478 	return len;
479 }
480 
needScrollControl(s32 startIndex,bool withScrollControl)481 bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl)
482 {
483 	if ( startIndex >= (s32)Tabs.size() )
484 		startIndex -= 1;
485 
486 	if ( startIndex < 0 )
487 		startIndex = 0;
488 
489 	IGUISkin* skin = Environment->getSkin();
490 	if (!skin)
491 		return false;
492 
493 	IGUIFont* font = skin->getFont();
494 
495 	core::rect<s32> frameRect(AbsoluteRect);
496 
497 	if (Tabs.empty())
498 		return false;
499 
500 	if (!font)
501 		return false;
502 
503 	s32 pos = frameRect.UpperLeftCorner.X + 2;
504 
505 	for (s32 i=startIndex; i<(s32)Tabs.size(); ++i)
506 	{
507 		// get Text
508 		const wchar_t* text = 0;
509 		if (Tabs[i])
510 			text = Tabs[i]->getText();
511 
512 		// get text length
513 		s32 len = calcTabWidth(pos, font, text, false);	// always without withScrollControl here or len would be shortened
514 
515 		frameRect.LowerRightCorner.X += len;
516 
517 		frameRect.UpperLeftCorner.X = pos;
518 		frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;
519 		pos += len;
520 
521 		if ( withScrollControl && pos > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2)
522 			return true;
523 
524 		if ( !withScrollControl && pos > AbsoluteRect.LowerRightCorner.X )
525 			return true;
526 	}
527 
528 	return false;
529 }
530 
531 
calcTabPos()532 core::rect<s32> CGUITabControl::calcTabPos()
533 {
534 	core::rect<s32> r;
535 	r.UpperLeftCorner.X = 0;
536 	r.LowerRightCorner.X = AbsoluteRect.getWidth();
537 	if ( Border )
538 	{
539 		++r.UpperLeftCorner.X;
540 		--r.LowerRightCorner.X;
541 	}
542 
543 	if ( VerticalAlignment == EGUIA_UPPERLEFT )
544 	{
545 		r.UpperLeftCorner.Y = TabHeight+2;
546 		r.LowerRightCorner.Y = AbsoluteRect.getHeight()-1;
547 		if ( Border )
548 		{
549 			--r.LowerRightCorner.Y;
550 		}
551 	}
552 	else
553 	{
554 		r.UpperLeftCorner.Y = 0;
555 		r.LowerRightCorner.Y = AbsoluteRect.getHeight()-(TabHeight+2);
556 		if ( Border )
557 		{
558 			++r.UpperLeftCorner.Y;
559 		}
560 	}
561 
562 	return r;
563 }
564 
565 
566 //! draws the element and its children
draw()567 void CGUITabControl::draw()
568 {
569 	if (!IsVisible)
570 		return;
571 
572 	IGUISkin* skin = Environment->getSkin();
573 	if (!skin)
574 		return;
575 
576 	IGUIFont* font = skin->getFont();
577 	video::IVideoDriver* driver = Environment->getVideoDriver();
578 
579 	core::rect<s32> frameRect(AbsoluteRect);
580 
581 	if (Tabs.empty())
582 		driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), frameRect, &AbsoluteClippingRect);
583 
584 	if (!font)
585 		return;
586 
587 	if ( VerticalAlignment == EGUIA_UPPERLEFT )
588 	{
589 		frameRect.UpperLeftCorner.Y += 2;
590 		frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + TabHeight;
591 	}
592 	else
593 	{
594 		frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - TabHeight - 1;
595 		frameRect.LowerRightCorner.Y -= 2;
596 	}
597 
598 	core::rect<s32> tr;
599 	s32 pos = frameRect.UpperLeftCorner.X + 2;
600 
601 	bool needLeftScroll = CurrentScrollTabIndex > 0;
602 	bool needRightScroll = false;
603 
604 	// left and right pos of the active tab
605 	s32 left = 0;
606 	s32 right = 0;
607 
608 	//const wchar_t* activetext = 0;
609 	CGUITab *activeTab = 0;
610 
611 	for (u32 i=CurrentScrollTabIndex; i<Tabs.size(); ++i)
612 	{
613 		// get Text
614 		const wchar_t* text = 0;
615 		if (Tabs[i])
616 			text = Tabs[i]->getText();
617 
618 		// get text length
619 		s32 len = calcTabWidth(pos, font, text, true);
620 		if ( ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )
621 		{
622 			needRightScroll = true;
623 			break;
624 		}
625 
626 		frameRect.LowerRightCorner.X += len;
627 		frameRect.UpperLeftCorner.X = pos;
628 		frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;
629 
630 		pos += len;
631 
632 		if ( text )
633 			Tabs[i]->refreshSkinColors();
634 
635 		if ((s32)i == ActiveTab)
636 		{
637 			left = frameRect.UpperLeftCorner.X;
638 			right = frameRect.LowerRightCorner.X;
639 			//activetext = text;
640 			activeTab = Tabs[i];
641 		}
642 		else
643 		{
644 			skin->draw3DTabButton(this, false, frameRect, &AbsoluteClippingRect, VerticalAlignment);
645 
646 			// draw text
647 			core::rect<s32> textClipRect(frameRect);	// TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface
648 			textClipRect.clipAgainst(AbsoluteClippingRect);
649 			font->draw(text, frameRect, Tabs[i]->getTextColor(),
650 				true, true, &textClipRect);
651 		}
652 	}
653 
654 	// draw active tab
655 	if (left != 0 && right != 0 && activeTab != 0)
656 	{
657 		// draw upper highlight frame
658 		if ( VerticalAlignment == EGUIA_UPPERLEFT )
659 		{
660 			frameRect.UpperLeftCorner.X = left-2;
661 			frameRect.LowerRightCorner.X = right+2;
662 			frameRect.UpperLeftCorner.Y -= 2;
663 
664 			skin->draw3DTabButton(this, true, frameRect, &AbsoluteClippingRect, VerticalAlignment);
665 
666 			// draw text
667 			core::rect<s32> textClipRect(frameRect);	// TODO: exact size depends on borders in draw3DTabButton which we don't get with current interface
668 			textClipRect.clipAgainst(AbsoluteClippingRect);
669 			font->draw(activeTab->getText(), frameRect, activeTab->getTextColor(),
670 				true, true, &textClipRect);
671 
672 			tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
673 			tr.LowerRightCorner.X = left - 1;
674 			tr.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - 1;
675 			tr.LowerRightCorner.Y = frameRect.LowerRightCorner.Y;
676 			driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
677 
678 			tr.UpperLeftCorner.X = right;
679 			tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
680 			driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
681 		}
682 		else
683 		{
684 
685 			frameRect.UpperLeftCorner.X = left-2;
686 			frameRect.LowerRightCorner.X = right+2;
687 			frameRect.LowerRightCorner.Y += 2;
688 
689 			skin->draw3DTabButton(this, true, frameRect, &AbsoluteClippingRect, VerticalAlignment);
690 
691 			// draw text
692 			font->draw(activeTab->getText(), frameRect, activeTab->getTextColor(),
693 				true, true, &frameRect);
694 
695 			tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
696 			tr.LowerRightCorner.X = left - 1;
697 			tr.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - 1;
698 			tr.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y;
699 			driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
700 
701 			tr.UpperLeftCorner.X = right;
702 			tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
703 			driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
704 		}
705 	}
706 	else
707 	{
708 		if ( VerticalAlignment == EGUIA_UPPERLEFT )
709 		{
710 			tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
711 			tr.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
712 			tr.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - 1;
713 			tr.LowerRightCorner.Y = frameRect.LowerRightCorner.Y;
714 			driver->draw2DRectangle(skin->getColor(EGDC_3D_HIGH_LIGHT), tr, &AbsoluteClippingRect);
715 		}
716 		else
717 		{
718 			tr.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X;
719 			tr.LowerRightCorner.X = 1000;
720 			tr.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - 1;
721 			tr.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y;
722 			driver->draw2DRectangle(skin->getColor(EGDC_3D_DARK_SHADOW), tr, &AbsoluteClippingRect);
723 		}
724 	}
725 
726 	skin->draw3DTabBody(this, Border, FillBackground, AbsoluteRect, &AbsoluteClippingRect, TabHeight, VerticalAlignment);
727 
728 	// enable scrollcontrols on need
729 	if ( UpButton )
730 		UpButton->setEnabled(needLeftScroll);
731 	if ( DownButton )
732 		DownButton->setEnabled(needRightScroll);
733 	refreshSprites();
734 
735 	IGUIElement::draw();
736 }
737 
738 
739 //! Set the height of the tabs
setTabHeight(s32 height)740 void CGUITabControl::setTabHeight( s32 height )
741 {
742 	if ( height < 0 )
743 		height = 0;
744 
745 	TabHeight = height;
746 
747 	recalculateScrollButtonPlacement();
748 	recalculateScrollBar();
749 }
750 
751 
752 //! Get the height of the tabs
getTabHeight() const753 s32 CGUITabControl::getTabHeight() const
754 {
755 	return TabHeight;
756 }
757 
758 //! set the maximal width of a tab. Per default width is 0 which means "no width restriction".
setTabMaxWidth(s32 width)759 void CGUITabControl::setTabMaxWidth(s32 width )
760 {
761 	TabMaxWidth = width;
762 }
763 
764 //! get the maximal width of a tab
getTabMaxWidth() const765 s32 CGUITabControl::getTabMaxWidth() const
766 {
767 	return TabMaxWidth;
768 }
769 
770 
771 //! Set the extra width added to tabs on each side of the text
setTabExtraWidth(s32 extraWidth)772 void CGUITabControl::setTabExtraWidth( s32 extraWidth )
773 {
774 	if ( extraWidth < 0 )
775 		extraWidth = 0;
776 
777 	TabExtraWidth = extraWidth;
778 
779 	recalculateScrollBar();
780 }
781 
782 
783 //! Get the extra width added to tabs on each side of the text
getTabExtraWidth() const784 s32 CGUITabControl::getTabExtraWidth() const
785 {
786 	return TabExtraWidth;
787 }
788 
789 
recalculateScrollBar()790 void CGUITabControl::recalculateScrollBar()
791 {
792 	if (!UpButton || !DownButton)
793 		return;
794 
795 	ScrollControl = needScrollControl() || CurrentScrollTabIndex > 0;
796 
797 	if (ScrollControl)
798 	{
799 		UpButton->setVisible( true );
800 		DownButton->setVisible( true );
801 	}
802 	else
803 	{
804 		UpButton->setVisible( false );
805 		DownButton->setVisible( false );
806 	}
807 
808 	bringToFront( UpButton );
809 	bringToFront( DownButton );
810 }
811 
812 //! Set the alignment of the tabs
setTabVerticalAlignment(EGUI_ALIGNMENT alignment)813 void CGUITabControl::setTabVerticalAlignment( EGUI_ALIGNMENT alignment )
814 {
815 	VerticalAlignment = alignment;
816 
817 	recalculateScrollButtonPlacement();
818 	recalculateScrollBar();
819 
820 	core::rect<s32> r(calcTabPos());
821 	for ( u32 i=0; i<Tabs.size(); ++i )
822 	{
823 		Tabs[i]->setRelativePosition(r);
824 	}
825 }
826 
recalculateScrollButtonPlacement()827 void CGUITabControl::recalculateScrollButtonPlacement()
828 {
829 	IGUISkin* skin = Environment->getSkin();
830 	s32 ButtonSize = 16;
831 	s32 ButtonHeight = TabHeight - 2;
832 	if ( ButtonHeight < 0 )
833 		ButtonHeight = TabHeight;
834 	if (skin)
835 	{
836 		ButtonSize = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH);
837 		if (ButtonSize > TabHeight)
838 			ButtonSize = TabHeight;
839 	}
840 
841 	s32 ButtonX = RelativeRect.getWidth() - (s32)(2.5f*(f32)ButtonSize) - 1;
842 	s32 ButtonY = 0;
843 
844 	if (VerticalAlignment == EGUIA_UPPERLEFT)
845 	{
846 		ButtonY = 2 + (TabHeight / 2) - (ButtonHeight / 2);
847 		UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
848 		DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_UPPERLEFT);
849 	}
850 	else
851 	{
852 		ButtonY = RelativeRect.getHeight() - (TabHeight / 2) - (ButtonHeight / 2) - 2;
853 		UpButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
854 		DownButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
855 	}
856 
857 	UpButton->setRelativePosition(core::rect<s32>(ButtonX, ButtonY, ButtonX+ButtonSize, ButtonY+ButtonHeight));
858 	ButtonX += ButtonSize + 1;
859 	DownButton->setRelativePosition(core::rect<s32>(ButtonX, ButtonY, ButtonX+ButtonSize, ButtonY+ButtonHeight));
860 }
861 
862 //! Get the alignment of the tabs
getTabVerticalAlignment() const863 EGUI_ALIGNMENT CGUITabControl::getTabVerticalAlignment() const
864 {
865 	return VerticalAlignment;
866 }
867 
868 
getTabAt(s32 xpos,s32 ypos) const869 s32 CGUITabControl::getTabAt(s32 xpos, s32 ypos) const
870 {
871 	core::position2di p(xpos, ypos);
872 	IGUISkin* skin = Environment->getSkin();
873 	IGUIFont* font = skin->getFont();
874 
875 	core::rect<s32> frameRect(AbsoluteRect);
876 
877 	if ( VerticalAlignment == EGUIA_UPPERLEFT )
878 	{
879 		frameRect.UpperLeftCorner.Y += 2;
880 		frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + TabHeight;
881 	}
882 	else
883 	{
884 		frameRect.UpperLeftCorner.Y = frameRect.LowerRightCorner.Y - TabHeight;
885 	}
886 
887 	s32 pos = frameRect.UpperLeftCorner.X + 2;
888 
889 	if (!frameRect.isPointInside(p))
890 		return -1;
891 
892 	for (s32 i=CurrentScrollTabIndex; i<(s32)Tabs.size(); ++i)
893 	{
894 		// get Text
895 		const wchar_t* text = 0;
896 		if (Tabs[i])
897 			text = Tabs[i]->getText();
898 
899 		// get text length
900 		s32 len = calcTabWidth(pos, font, text, true);
901 		if ( ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )
902 			return -1;
903 
904 		frameRect.UpperLeftCorner.X = pos;
905 		frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;
906 
907 		pos += len;
908 
909 		if (frameRect.isPointInside(p))
910 		{
911 			return i;
912 		}
913 	}
914 	return -1;
915 }
916 
917 //! Returns which tab is currently active
getActiveTab() const918 s32 CGUITabControl::getActiveTab() const
919 {
920 	return ActiveTab;
921 }
922 
923 
924 //! Brings a tab to front.
setActiveTab(s32 idx)925 bool CGUITabControl::setActiveTab(s32 idx)
926 {
927 	if ((u32)idx >= Tabs.size())
928 		return false;
929 
930 	bool changed = (ActiveTab != idx);
931 
932 	ActiveTab = idx;
933 
934 	for (s32 i=0; i<(s32)Tabs.size(); ++i)
935 		if (Tabs[i])
936 			Tabs[i]->setVisible( i == ActiveTab );
937 
938 	if (changed)
939 	{
940 		SEvent event;
941 		event.EventType = EET_GUI_EVENT;
942 		event.GUIEvent.Caller = this;
943 		event.GUIEvent.Element = 0;
944 		event.GUIEvent.EventType = EGET_TAB_CHANGED;
945 		Parent->OnEvent(event);
946 	}
947 
948 	return true;
949 }
950 
951 
setActiveTab(IGUITab * tab)952 bool CGUITabControl::setActiveTab(IGUITab *tab)
953 {
954 	for (s32 i=0; i<(s32)Tabs.size(); ++i)
955 		if (Tabs[i] == tab)
956 			return setActiveTab(i);
957 	return false;
958 }
959 
960 
961 //! Removes a child.
removeChild(IGUIElement * child)962 void CGUITabControl::removeChild(IGUIElement* child)
963 {
964 	bool isTab = false;
965 
966 	u32 i=0;
967 	// check if it is a tab
968 	while (i<Tabs.size())
969 	{
970 		if (Tabs[i] == child)
971 		{
972 			Tabs[i]->drop();
973 			Tabs.erase(i);
974 			isTab = true;
975 		}
976 		else
977 			++i;
978 	}
979 
980 	// reassign numbers
981 	if (isTab)
982 	{
983 		for (i=0; i<Tabs.size(); ++i)
984 			if (Tabs[i])
985 				Tabs[i]->setNumber(i);
986 	}
987 
988 	// remove real element
989 	IGUIElement::removeChild(child);
990 
991 	recalculateScrollBar();
992 }
993 
994 
995 //! Update the position of the element, decides scroll button status
updateAbsolutePosition()996 void CGUITabControl::updateAbsolutePosition()
997 {
998 	IGUIElement::updateAbsolutePosition();
999 	recalculateScrollBar();
1000 }
1001 
1002 
1003 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const1004 void CGUITabControl::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
1005 {
1006 	IGUITabControl::serializeAttributes(out,options);
1007 
1008 	out->addInt ("ActiveTab",	ActiveTab);
1009 	out->addBool("Border",		Border);
1010 	out->addBool("FillBackground",	FillBackground);
1011 	out->addInt ("TabHeight",	TabHeight);
1012 	out->addInt ("TabMaxWidth", TabMaxWidth);
1013 	out->addEnum("TabVerticalAlignment", s32(VerticalAlignment), GUIAlignmentNames);
1014 }
1015 
1016 
1017 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)1018 void CGUITabControl::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
1019 {
1020 	Border          = in->getAttributeAsBool("Border");
1021 	FillBackground  = in->getAttributeAsBool("FillBackground");
1022 
1023 	ActiveTab = -1;
1024 
1025 	setTabHeight(in->getAttributeAsInt("TabHeight"));
1026 	TabMaxWidth     = in->getAttributeAsInt("TabMaxWidth");
1027 
1028 	IGUITabControl::deserializeAttributes(in,options);
1029 
1030 	setActiveTab(in->getAttributeAsInt("ActiveTab"));
1031 	setTabVerticalAlignment( static_cast<EGUI_ALIGNMENT>(in->getAttributeAsEnumeration("TabVerticalAlignment" , GUIAlignmentNames)) );
1032 }
1033 
1034 
1035 } // end namespace irr
1036 } // end namespace gui
1037 
1038 #endif // _IRR_COMPILE_WITH_GUI_
1039 
1040