1 // This file is part of the "Irrlicht Engine".
2 // written by Reinhard Ostermeier, reinhard@nospam.r-ostermeier.de
3 // expanded by burningwater
4 
5 #include "CGUITreeView.h"
6 
7 #ifdef _IRR_COMPILE_WITH_GUI_
8 
9 #include "IGUISkin.h"
10 #include "IGUIEnvironment.h"
11 #include "IVideoDriver.h"
12 #include "IGUIFont.h"
13 #include "CGUIScrollBar.h"
14 #include "os.h"
15 
16 namespace irr
17 {
18 namespace gui
19 {
20 
CGUITreeViewNode(CGUITreeView * owner,CGUITreeViewNode * parent)21 CGUITreeViewNode::CGUITreeViewNode( CGUITreeView* owner, CGUITreeViewNode* parent )
22 	: Owner(owner), Parent(parent), ImageIndex(-1), SelectedImageIndex(-1),
23 	Data(0), Data2(0), Expanded(false)
24 {
25 #ifdef _DEBUG
26 	setDebugName( "CGUITreeView" );
27 #endif
28 }
29 
~CGUITreeViewNode()30 CGUITreeViewNode::~CGUITreeViewNode()
31 {
32 	if( Owner && this == Owner->getSelected() )
33 	{
34 		setSelected( false );
35 	}
36 
37 	clearChildren();
38 
39 	if( Data2 )
40 	{
41 		Data2->drop();
42 	}
43 }
44 
getOwner() const45 IGUITreeView* CGUITreeViewNode::getOwner() const
46 {
47 	return Owner;
48 }
49 
getParent() const50 IGUITreeViewNode* CGUITreeViewNode::getParent() const
51 {
52 	return Parent;
53 }
54 
setText(const wchar_t * text)55 void CGUITreeViewNode::setText( const wchar_t* text )
56 {
57 	Text = text;
58 }
59 
setIcon(const wchar_t * icon)60 void CGUITreeViewNode::setIcon( const wchar_t* icon )
61 {
62 	Icon = icon;
63 }
64 
clearChildren()65 void CGUITreeViewNode::clearChildren()
66 {
67 	core::list<CGUITreeViewNode*>::Iterator	it;
68 
69 	for( it = Children.begin(); it != Children.end(); it++ )
70 	{
71 		( *it )->drop();
72 	}
73 	Children.clear();
74 }
75 
addChildBack(const wchar_t * text,const wchar_t * icon,s32 imageIndex,s32 selectedImageIndex,void * data,IReferenceCounted * data2)76 IGUITreeViewNode* CGUITreeViewNode::addChildBack(
77 	const wchar_t*		text,
78 	const wchar_t*		icon /*= 0*/,
79 	s32					imageIndex /*= -1*/,
80 	s32					selectedImageIndex /*= -1*/,
81 	void*					data /*= 0*/,
82 	IReferenceCounted*			data2 /*= 0*/ )
83 {
84 	CGUITreeViewNode*	newChild = new CGUITreeViewNode( Owner, this );
85 
86 	Children.push_back( newChild );
87 	newChild->Text = text;
88 	newChild->Icon = icon;
89 	newChild->ImageIndex = imageIndex;
90 	newChild->SelectedImageIndex = selectedImageIndex;
91 	newChild->Data = data;
92 	newChild->Data2 = data2;
93 	if( data2 )
94 	{
95 		data2->grab();
96 	}
97 	return newChild;
98 }
99 
addChildFront(const wchar_t * text,const wchar_t * icon,s32 imageIndex,s32 selectedImageIndex,void * data,IReferenceCounted * data2)100 IGUITreeViewNode* CGUITreeViewNode::addChildFront(
101 	const wchar_t*		text,
102 	const wchar_t*		icon /*= 0*/,
103 	s32					imageIndex /*= -1*/,
104 	s32					selectedImageIndex /*= -1*/,
105 	void*					data /*= 0*/,
106 	IReferenceCounted*			data2 /*= 0*/ )
107 {
108 	CGUITreeViewNode*	newChild = new CGUITreeViewNode( Owner, this );
109 
110 	Children.push_front( newChild );
111 	newChild->Text = text;
112 	newChild->Icon = icon;
113 	newChild->ImageIndex = imageIndex;
114 	newChild->SelectedImageIndex = selectedImageIndex;
115 	newChild->Data = data;
116 	newChild->Data2 = data2;
117 	if( data2 )
118 	{
119 		data2->grab();
120 	}
121 	return newChild;
122 }
123 
insertChildAfter(IGUITreeViewNode * other,const wchar_t * text,const wchar_t * icon,s32 imageIndex,s32 selectedImageIndex,void * data,IReferenceCounted * data2)124 IGUITreeViewNode* CGUITreeViewNode::insertChildAfter(
125 	IGUITreeViewNode*	other,
126 	const wchar_t*		text,
127 	const wchar_t*		icon /*= 0*/,
128 	s32					imageIndex /*= -1*/,
129 	s32					selectedImageIndex /*= -1*/,
130 	void*					data /*= 0*/,
131 	IReferenceCounted*			data2/* = 0*/ )
132 {
133 	core::list<CGUITreeViewNode*>::Iterator	itOther;
134 	CGUITreeViewNode*									newChild = 0;
135 
136 	for( itOther = Children.begin(); itOther != Children.end(); itOther++ )
137 	{
138 		if( other == *itOther )
139 		{
140 			newChild = new CGUITreeViewNode( Owner, this );
141 			newChild->Text = text;
142 			newChild->Icon = icon;
143 			newChild->ImageIndex = imageIndex;
144 			newChild->SelectedImageIndex = selectedImageIndex;
145 			newChild->Data = data;
146 			newChild->Data2 = data2;
147 			if( data2 )
148 			{
149 				data2->grab();
150 			}
151 			Children.insert_after( itOther, newChild );
152 			break;
153 		}
154 	}
155 	return newChild;
156 }
157 
insertChildBefore(IGUITreeViewNode * other,const wchar_t * text,const wchar_t * icon,s32 imageIndex,s32 selectedImageIndex,void * data,IReferenceCounted * data2)158 IGUITreeViewNode* CGUITreeViewNode::insertChildBefore(
159 	IGUITreeViewNode*	other,
160 	const wchar_t*		text,
161 	const wchar_t*		icon /*= 0*/,
162 	s32					imageIndex /*= -1*/,
163 	s32					selectedImageIndex /*= -1*/,
164 	void*					data /*= 0*/,
165 	IReferenceCounted*			data2/* = 0*/ )
166 {
167 	core::list<CGUITreeViewNode*>::Iterator	itOther;
168 	CGUITreeViewNode*									newChild = 0;
169 
170 	for( itOther = Children.begin(); itOther != Children.end(); itOther++ )
171 	{
172 		if( other == *itOther )
173 		{
174 			newChild = new CGUITreeViewNode( Owner, this );
175 			newChild->Text = text;
176 			newChild->Icon = icon;
177 			newChild->ImageIndex = imageIndex;
178 			newChild->SelectedImageIndex = selectedImageIndex;
179 			newChild->Data = data;
180 			newChild->Data2 = data2;
181 			if( data2 )
182 			{
183 				data2->grab();
184 			}
185 			Children.insert_before( itOther, newChild );
186 			break;
187 		}
188 	}
189 	return newChild;
190 }
191 
getFirstChild() const192 IGUITreeViewNode* CGUITreeViewNode::getFirstChild() const
193 {
194 	if( Children.empty() )
195 	{
196 		return 0;
197 	}
198 	else
199 	{
200 		return *( Children.begin() );
201 	}
202 }
203 
getLastChild() const204 IGUITreeViewNode* CGUITreeViewNode::getLastChild() const
205 {
206 	if( Children.empty() )
207 	{
208 		return 0;
209 	}
210 	else
211 	{
212 		return *( Children.getLast() );
213 	}
214 }
215 
getPrevSibling() const216 IGUITreeViewNode* CGUITreeViewNode::getPrevSibling() const
217 {
218 	core::list<CGUITreeViewNode*>::Iterator	itThis;
219 	core::list<CGUITreeViewNode*>::Iterator	itOther;
220 	CGUITreeViewNode*									other = 0;
221 
222 	if( Parent )
223 	{
224 		for( itThis = Parent->Children.begin(); itThis != Parent->Children.end(); itThis++ )
225 		{
226 			if( this == *itThis )
227 			{
228 				if( itThis != Parent->Children.begin() )
229 				{
230 					other = *itOther;
231 				}
232 				break;
233 			}
234 			itOther = itThis;
235 		}
236 	}
237 	return other;
238 }
239 
getNextSibling() const240 IGUITreeViewNode* CGUITreeViewNode::getNextSibling() const
241 {
242 	core::list<CGUITreeViewNode*>::Iterator	itThis;
243 	CGUITreeViewNode*									other = 0;
244 
245 	if( Parent )
246 	{
247 		for( itThis = Parent->Children.begin(); itThis != Parent->Children.end(); itThis++ )
248 		{
249 			if( this == *itThis )
250 			{
251 				if( itThis != Parent->Children.getLast() )
252 				{
253 					other = *( ++itThis );
254 				}
255 				break;
256 			}
257 		}
258 	}
259 	return other;
260 }
261 
getNextVisible() const262 IGUITreeViewNode* CGUITreeViewNode::getNextVisible() const
263 {
264 	IGUITreeViewNode*	next = 0;
265 	IGUITreeViewNode*	node = 0;
266 
267 	node = const_cast<CGUITreeViewNode*>( this );
268 
269 	if( node->getExpanded() && node->hasChildren() )
270 	{
271 		next = node->getFirstChild();
272 	}
273 	else
274 	{
275 		next = node->getNextSibling();
276 	}
277 	while( !next && node->getParent() )
278 	{
279 		next = node->getParent()->getNextSibling();
280 		if( !next )
281 		{
282 			node = node->getParent();
283 		}
284 	}
285 
286 	return next;
287 }
288 
deleteChild(IGUITreeViewNode * child)289 bool CGUITreeViewNode::deleteChild( IGUITreeViewNode* child )
290 {
291 	core::list<CGUITreeViewNode*>::Iterator	itChild;
292 	bool	deleted = false;
293 
294 	for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
295 	{
296 		if( child == *itChild )
297 		{
298 			child->drop();
299 			Children.erase( itChild );
300 			deleted = true;
301 			break;
302 		}
303 	}
304 	return deleted;
305 }
306 
moveChildUp(IGUITreeViewNode * child)307 bool CGUITreeViewNode::moveChildUp( IGUITreeViewNode* child )
308 {
309 	core::list<CGUITreeViewNode*>::Iterator	itChild;
310 	core::list<CGUITreeViewNode*>::Iterator	itOther;
311 	CGUITreeViewNode*									nodeTmp;
312 	bool													moved = false;
313 
314 	for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
315 	{
316 		if( child == *itChild )
317 		{
318 			if( itChild != Children.begin() )
319 			{
320 				nodeTmp = *itChild;
321 				*itChild = *itOther;
322 				*itOther = nodeTmp;
323 				moved = true;
324 			}
325 			break;
326 		}
327 		itOther = itChild;
328 	}
329 	return moved;
330 }
331 
moveChildDown(IGUITreeViewNode * child)332 bool CGUITreeViewNode::moveChildDown( IGUITreeViewNode* child )
333 {
334 	core::list<CGUITreeViewNode*>::Iterator	itChild;
335 	core::list<CGUITreeViewNode*>::Iterator	itOther;
336 	CGUITreeViewNode*									nodeTmp;
337 	bool													moved = false;
338 
339 	for( itChild = Children.begin(); itChild != Children.end(); itChild++ )
340 	{
341 		if( child == *itChild )
342 		{
343 			if( itChild != Children.getLast() )
344 			{
345 				itOther = itChild;
346 				++itOther;
347 				nodeTmp = *itChild;
348 				*itChild = *itOther;
349 				*itOther = nodeTmp;
350 				moved = true;
351 			}
352 			break;
353 		}
354 	}
355 	return moved;
356 }
357 
setExpanded(bool expanded)358 void CGUITreeViewNode::setExpanded( bool expanded )
359 {
360 	Expanded = expanded;
361 }
362 
setSelected(bool selected)363 void CGUITreeViewNode::setSelected( bool selected )
364 {
365 	if( Owner )
366 	{
367 		if( selected )
368 		{
369 			Owner->Selected = this;
370 		}
371 		else
372 		{
373 			if( Owner->Selected == this )
374 			{
375 				Owner->Selected = 0;
376 			}
377 		}
378 	}
379 }
380 
getSelected() const381 bool CGUITreeViewNode::getSelected() const
382 {
383 	if( Owner )
384 	{
385 		return Owner->Selected == (IGUITreeViewNode*)this;
386 	}
387 	else
388 	{
389 		return false;
390 	}
391 }
392 
isRoot() const393 bool CGUITreeViewNode::isRoot() const
394 {
395 	return ( Owner && ( this == Owner->Root ) );
396 }
397 
getLevel() const398 s32 CGUITreeViewNode::getLevel() const
399 {
400 	if( Parent )
401 	{
402 		return Parent->getLevel() + 1;
403 	}
404 	else
405 	{
406 		return 0;
407 	}
408 }
409 
isVisible() const410 bool CGUITreeViewNode::isVisible() const
411 {
412 	if( Parent )
413 	{
414 		return Parent->getExpanded() && Parent->isVisible();
415 	}
416 	else
417 	{
418 		return true;
419 	}
420 }
421 
422 
423 //! constructor
CGUITreeView(IGUIEnvironment * environment,IGUIElement * parent,s32 id,core::rect<s32> rectangle,bool clip,bool drawBack,bool scrollBarVertical,bool scrollBarHorizontal)424 CGUITreeView::CGUITreeView(IGUIEnvironment* environment, IGUIElement* parent,
425 	s32 id, core::rect<s32> rectangle, bool clip,
426 	bool drawBack,bool scrollBarVertical, bool scrollBarHorizontal)
427 	: IGUITreeView( environment, parent, id, rectangle ),
428 	Root(0), Selected(0),
429 	ItemHeight( 0 ),
430 	IndentWidth( 0 ),
431 	TotalItemHeight( 0 ),
432 	TotalItemWidth ( 0 ),
433 	Font( 0 ),
434 	IconFont( 0 ),
435 	ScrollBarH( 0 ),
436 	ScrollBarV( 0 ),
437 	ImageList( 0 ),
438 	LastEventNode( 0 ),
439 	LinesVisible( true ),
440 	Selecting( false ),
441 	Clip( clip ),
442 	DrawBack( drawBack ),
443 	ImageLeftOfIcon( true )
444 {
445 #ifdef _DEBUG
446 	setDebugName( "CGUITreeView" );
447 #endif
448 
449 	IGUISkin* skin = Environment->getSkin();
450 	s32 s = skin->getSize( EGDS_SCROLLBAR_SIZE );
451 
452 	if ( scrollBarVertical )
453 	{
454 		ScrollBarV = new CGUIScrollBar( false, Environment, this, 0,
455 			core::rect<s32>(	RelativeRect.getWidth() - s,
456 			0,
457 			RelativeRect.getWidth(),
458 			RelativeRect.getHeight() - (scrollBarHorizontal ? s : 0 )
459 			),
460 			!clip );
461 		ScrollBarV->drop();
462 
463 		ScrollBarV->setSubElement(true);
464 		ScrollBarV->setPos( 0 );
465 		ScrollBarV->grab();
466 	}
467 
468 	if ( scrollBarHorizontal )
469 	{
470 		ScrollBarH = new CGUIScrollBar( true, Environment, this, 1,
471 			core::rect<s32>( 0, RelativeRect.getHeight() - s, RelativeRect.getWidth() - s, RelativeRect.getHeight() ),
472 			!clip );
473 		ScrollBarH->drop();
474 
475 		ScrollBarH->setSubElement(true);
476 		ScrollBarH->setPos( 0 );
477 		ScrollBarH->grab();
478 	}
479 
480 	Root = new CGUITreeViewNode( this, 0 );
481 	Root->Expanded = true;
482 
483 	recalculateItemHeight();
484 }
485 
486 
487 //! destructor
~CGUITreeView()488 CGUITreeView::~CGUITreeView()
489 {
490 	if( ScrollBarV )
491 	{
492 		ScrollBarV->drop();
493 	}
494 
495 	if( ScrollBarH )
496 	{
497 		ScrollBarH->drop();
498 	}
499 
500 	if( Font )
501 	{
502 		Font->drop();
503 	}
504 
505 	if( IconFont )
506 	{
507 		IconFont->drop();
508 	}
509 
510 	if( ImageList )
511 	{
512 		ImageList->drop();
513 	}
514 
515 	if( Root )
516 	{
517 		Root->drop();
518 	}
519 }
520 
recalculateItemHeight()521 void CGUITreeView::recalculateItemHeight()
522 {
523 	IGUISkin*		skin = Environment->getSkin();
524 	IGUITreeViewNode*	node;
525 
526 	if( Font != skin->getFont() )
527 	{
528 		if( Font )
529 		{
530 			Font->drop();
531 		}
532 
533 		Font = skin->getFont();
534 		ItemHeight = 0;
535 
536 		if( Font )
537 		{
538 			ItemHeight = Font->getDimension( L"A" ).Height + 4;
539 			Font->grab();
540 		}
541 
542 		if( IconFont )
543 		{
544 			s32 height = IconFont->getDimension( L" " ).Height;
545 			if( height > ItemHeight )
546 			{
547 				ItemHeight = height;
548 			}
549 		}
550 		if( ImageList )
551 		{
552 			if( ImageList->getImageSize().Height + 1 > ItemHeight )
553 			{
554 				ItemHeight = ImageList->getImageSize().Height + 1;
555 			}
556 		}
557 	}
558 
559 	IndentWidth = ItemHeight;
560 	if( IndentWidth < 9 )
561 	{
562 		IndentWidth = 9;
563 	}
564 	else if( IndentWidth > 15 )
565 	{
566 		IndentWidth = 15;
567 	}
568 	else
569 	{
570 		if( ( ( IndentWidth >> 1 ) << 1 ) - IndentWidth == 0 )
571 		{
572 			--IndentWidth;
573 		}
574 	}
575 
576 	TotalItemHeight = 0;
577 	TotalItemWidth = AbsoluteRect.getWidth() * 2;
578 	node = Root->getFirstChild();
579 	while( node )
580 	{
581 		TotalItemHeight += ItemHeight;
582 		node = node->getNextVisible();
583 	}
584 
585 	if ( ScrollBarV )
586 		ScrollBarV->setMax( core::max_(0,TotalItemHeight - AbsoluteRect.getHeight()) );
587 
588 	if ( ScrollBarH )
589 		ScrollBarH->setMax( core::max_(0, TotalItemWidth - AbsoluteRect.getWidth()) );
590 
591 }
592 
593 //! called if an event happened.
OnEvent(const SEvent & event)594 bool CGUITreeView::OnEvent( const SEvent &event )
595 {
596 	if ( isEnabled() )
597 	{
598 		switch( event.EventType )
599 		{
600 		case EET_GUI_EVENT:
601 			switch( event.GUIEvent.EventType )
602 			{
603 			case gui::EGET_SCROLL_BAR_CHANGED:
604 				if( event.GUIEvent.Caller == ScrollBarV || event.GUIEvent.Caller == ScrollBarH )
605 				{
606 					//s32 pos = ( ( gui::IGUIScrollBar* )event.GUIEvent.Caller )->getPos();
607 					return true;
608 				}
609 				break;
610 			case gui::EGET_ELEMENT_FOCUS_LOST:
611 				{
612 					Selecting = false;
613 					return false;
614 				}
615 				break;
616 			default:
617 				break;
618 			}
619 			break;
620 		case EET_MOUSE_INPUT_EVENT:
621 			{
622 				core::position2d<s32> p( event.MouseInput.X, event.MouseInput.Y );
623 
624 				switch( event.MouseInput.Event )
625 				{
626 				case EMIE_MOUSE_WHEEL:
627 					if ( ScrollBarV )
628 						ScrollBarV->setPos( ScrollBarV->getPos() + (event.MouseInput.Wheel < 0 ? -1 : 1) * -10 );
629 					return true;
630 					break;
631 
632 				case EMIE_LMOUSE_PRESSED_DOWN:
633 
634 					if (Environment->hasFocus(this) && !AbsoluteClippingRect.isPointInside(p) )
635 					{
636 						Environment->removeFocus(this);
637 						return false;
638 					}
639 
640 					if( Environment->hasFocus( this ) &&
641 						(	( ScrollBarV && ScrollBarV->getAbsolutePosition().isPointInside( p ) && ScrollBarV->OnEvent( event ) ) ||
642 						( ScrollBarH && ScrollBarH->getAbsolutePosition().isPointInside( p ) &&	ScrollBarH->OnEvent( event ) )
643 						)
644 						)
645 					{
646 						return true;
647 					}
648 
649 					Selecting = true;
650 					Environment->setFocus( this );
651 					return true;
652 					break;
653 
654 				case EMIE_LMOUSE_LEFT_UP:
655 					if( Environment->hasFocus( this ) &&
656 						(	( ScrollBarV && ScrollBarV->getAbsolutePosition().isPointInside( p ) && ScrollBarV->OnEvent( event ) ) ||
657 						( ScrollBarH && ScrollBarH->getAbsolutePosition().isPointInside( p ) &&	ScrollBarH->OnEvent( event ) )
658 						)
659 						)
660 					{
661 						return true;
662 					}
663 
664 					Selecting = false;
665 					Environment->removeFocus( this );
666 					mouseAction( event.MouseInput.X, event.MouseInput.Y );
667 					return true;
668 					break;
669 
670 				case EMIE_MOUSE_MOVED:
671 					if( Selecting )
672 					{
673 						if( getAbsolutePosition().isPointInside( p ) )
674 						{
675 							mouseAction( event.MouseInput.X, event.MouseInput.Y, true );
676 							return true;
677 						}
678 					}
679 					break;
680 				default:
681 					break;
682 				}
683 			}
684 			break;
685 		default:
686 			break;
687 		}
688 	}
689 
690 	return Parent ? Parent->OnEvent( event ) : false;
691 }
692 
693 /*!
694 */
mouseAction(s32 xpos,s32 ypos,bool onlyHover)695 void CGUITreeView::mouseAction( s32 xpos, s32 ypos, bool onlyHover /*= false*/ )
696 {
697 	IGUITreeViewNode*		oldSelected = Selected;
698 	IGUITreeViewNode*		hitNode = 0;
699 	s32						selIdx=-1;
700 	s32						n;
701 	IGUITreeViewNode*		node;
702 	SEvent					event;
703 
704 	event.EventType			= EET_GUI_EVENT;
705 	event.GUIEvent.Caller	= this;
706 	event.GUIEvent.Element = 0;
707 
708 	xpos -= AbsoluteRect.UpperLeftCorner.X;
709 	ypos -= AbsoluteRect.UpperLeftCorner.Y;
710 
711 	// find new selected item.
712 	if( ItemHeight != 0 && ScrollBarV )
713 	{
714 		selIdx = ( ( ypos - 1 ) + ScrollBarV->getPos() ) / ItemHeight;
715 	}
716 
717 	hitNode = 0;
718 	node = Root->getFirstChild();
719 	n = 0;
720 	while( node )
721 	{
722 		if( selIdx == n )
723 		{
724 			hitNode = node;
725 			break;
726 		}
727 		node = node->getNextVisible();
728 		++n;
729 	}
730 
731 	if( hitNode && xpos > hitNode->getLevel() * IndentWidth )
732 	{
733 		Selected = hitNode;
734 	}
735 
736 	if( hitNode && !onlyHover
737 		&& xpos < hitNode->getLevel() * IndentWidth
738 		&& xpos > ( hitNode->getLevel() - 1 ) * IndentWidth
739 		&& hitNode->hasChildren() )
740 	{
741 		hitNode->setExpanded( !hitNode->getExpanded() );
742 
743 		// post expand/collaps news
744 		if( hitNode->getExpanded() )
745 		{
746 			event.GUIEvent.EventType = EGET_TREEVIEW_NODE_EXPAND;
747 		}
748 		else
749 		{
750 			event.GUIEvent.EventType = EGET_TREEVIEW_NODE_COLLAPS;
751 		}
752 		LastEventNode = hitNode;
753 		Parent->OnEvent( event );
754 		LastEventNode = 0;
755 	}
756 
757 	if( Selected && !Selected->isVisible() )
758 	{
759 		Selected = 0;
760 	}
761 
762 	// post selection news
763 
764 	if( Parent && !onlyHover && Selected != oldSelected )
765 	{
766 		if( oldSelected )
767 		{
768 			event.GUIEvent.EventType = EGET_TREEVIEW_NODE_DESELECT;
769 			LastEventNode = oldSelected;
770 			Parent->OnEvent( event );
771 			LastEventNode = 0;
772 		}
773 		if( Selected )
774 		{
775 			event.GUIEvent.EventType = EGET_TREEVIEW_NODE_SELECT;
776 			LastEventNode = Selected;
777 			Parent->OnEvent( event );
778 			LastEventNode = 0;
779 		}
780 	}
781 }
782 
783 
784 //! draws the element and its children
draw()785 void CGUITreeView::draw()
786 {
787 	if( !IsVisible )
788 	{
789 		return;
790 	}
791 
792 	recalculateItemHeight(); // if the font changed
793 
794 	IGUISkin* skin = Environment->getSkin();
795 	irr::video::IVideoDriver* driver = Environment->getVideoDriver();
796 
797 	core::rect<s32>* clipRect = 0;
798 	if( Clip )
799 	{
800 		clipRect = &AbsoluteClippingRect;
801 	}
802 
803 	// draw background
804 	core::rect<s32> frameRect( AbsoluteRect );
805 
806 	if( DrawBack )
807 	{
808 		driver->draw2DRectangle( skin->getColor( EGDC_3D_HIGH_LIGHT ), frameRect,
809 			clipRect );
810 	}
811 
812 	// draw the border
813 	frameRect.LowerRightCorner.Y = frameRect.UpperLeftCorner.Y + 1;
814 	driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), frameRect,
815 		clipRect );
816 
817 	frameRect.LowerRightCorner.Y = AbsoluteRect.LowerRightCorner.Y;
818 	frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + 1;
819 	driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), frameRect,
820 		clipRect );
821 
822 	frameRect = AbsoluteRect;
823 	frameRect.UpperLeftCorner.X = frameRect.LowerRightCorner.X - 1;
824 	driver->draw2DRectangle( skin->getColor( EGDC_3D_HIGH_LIGHT ), frameRect,
825 		clipRect );
826 
827 	frameRect = AbsoluteRect;
828 	frameRect.UpperLeftCorner.Y = AbsoluteRect.LowerRightCorner.Y - 1;
829 	frameRect.LowerRightCorner.Y = AbsoluteRect.LowerRightCorner.Y;
830 	driver->draw2DRectangle( skin->getColor( EGDC_3D_HIGH_LIGHT ), frameRect,
831 		clipRect );
832 
833 
834 	// draw items
835 
836 	core::rect<s32> clientClip( AbsoluteRect );
837 	clientClip.UpperLeftCorner.Y += 1;
838 	clientClip.UpperLeftCorner.X += 1;
839 	clientClip.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X;
840 	clientClip.LowerRightCorner.Y -= 1;
841 
842 	if ( ScrollBarV )
843 		clientClip.LowerRightCorner.X -= skin->getSize( EGDS_SCROLLBAR_SIZE );
844 	if ( ScrollBarH )
845 		clientClip.LowerRightCorner.Y -= skin->getSize( EGDS_SCROLLBAR_SIZE );
846 
847 	if( clipRect )
848 	{
849 		clientClip.clipAgainst( *clipRect );
850 	}
851 
852 	frameRect = AbsoluteRect;
853 	frameRect.LowerRightCorner.X = AbsoluteRect.LowerRightCorner.X - skin->getSize( EGDS_SCROLLBAR_SIZE );
854 	frameRect.LowerRightCorner.Y = AbsoluteRect.UpperLeftCorner.Y + ItemHeight;
855 
856 	if ( ScrollBarV )
857 	{
858 		frameRect.UpperLeftCorner.Y -= ScrollBarV->getPos();
859 		frameRect.LowerRightCorner.Y -= ScrollBarV->getPos();
860 	}
861 
862 	if ( ScrollBarH )
863 	{
864 		frameRect.UpperLeftCorner.X -= ScrollBarH->getPos();
865 		frameRect.LowerRightCorner.X -= ScrollBarH->getPos();
866 	}
867 
868 	IGUITreeViewNode* node = Root->getFirstChild();
869 	while( node )
870 	{
871 		frameRect.UpperLeftCorner.X = AbsoluteRect.UpperLeftCorner.X + 1 + node->getLevel() * IndentWidth;
872 
873 		if( frameRect.LowerRightCorner.Y >= AbsoluteRect.UpperLeftCorner.Y
874 			&& frameRect.UpperLeftCorner.Y <= AbsoluteRect.LowerRightCorner.Y )
875 		{
876 			if( node == Selected )
877 			{
878 				driver->draw2DRectangle( skin->getColor( EGDC_HIGH_LIGHT ), frameRect, &clientClip );
879 			}
880 
881 			if( node->hasChildren() )
882 			{
883 				core::rect<s32> rc;
884 				core::rect<s32> expanderRect;
885 
886 				expanderRect.UpperLeftCorner.X = frameRect.UpperLeftCorner.X - IndentWidth + 2;
887 				expanderRect.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y + ( ( frameRect.getHeight() - ( IndentWidth - 4 ) ) >> 1 );
888 				expanderRect.LowerRightCorner.X = expanderRect.UpperLeftCorner.X + IndentWidth - 4;
889 				expanderRect.LowerRightCorner.Y = expanderRect.UpperLeftCorner.Y + IndentWidth - 4;
890 
891 				// box upper line
892 				rc.UpperLeftCorner.X = expanderRect.UpperLeftCorner.X;
893 				rc.UpperLeftCorner.Y = expanderRect.UpperLeftCorner.Y;
894 				rc.LowerRightCorner.X = expanderRect.LowerRightCorner.X;
895 				rc.LowerRightCorner.Y = rc.UpperLeftCorner.Y + 1;
896 				driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
897 					clipRect );
898 
899 				// box left line
900 				rc.UpperLeftCorner.X = expanderRect.UpperLeftCorner.X;
901 				rc.UpperLeftCorner.Y = expanderRect.UpperLeftCorner.Y;
902 				rc.LowerRightCorner.X = rc.UpperLeftCorner.X + 1;
903 				rc.LowerRightCorner.Y = expanderRect.LowerRightCorner.Y;
904 				driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
905 					clipRect );
906 
907 				// box right line
908 				rc.UpperLeftCorner.X = expanderRect.LowerRightCorner.X - 1;
909 				rc.UpperLeftCorner.Y = expanderRect.UpperLeftCorner.Y;
910 				rc.LowerRightCorner.X = rc.UpperLeftCorner.X + 1;
911 				rc.LowerRightCorner.Y = expanderRect.LowerRightCorner.Y;
912 				driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
913 					clipRect );
914 
915 				// box bottom line
916 				rc.UpperLeftCorner.X = expanderRect.UpperLeftCorner.X;
917 				rc.UpperLeftCorner.Y = expanderRect.LowerRightCorner.Y - 1;
918 				rc.LowerRightCorner.X = expanderRect.LowerRightCorner.X;
919 				rc.LowerRightCorner.Y = rc.UpperLeftCorner.Y + 1;
920 				driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
921 					clipRect );
922 
923 				// horizontal '-' line
924 				rc.UpperLeftCorner.X = expanderRect.UpperLeftCorner.X + 2;
925 				rc.UpperLeftCorner.Y = expanderRect.UpperLeftCorner.Y + ( expanderRect.getHeight() >> 1 );
926 				rc.LowerRightCorner.X = rc.UpperLeftCorner.X + expanderRect.getWidth() - 4;
927 				rc.LowerRightCorner.Y = rc.UpperLeftCorner.Y + 1;
928 				driver->draw2DRectangle( skin->getColor( EGDC_BUTTON_TEXT ), rc,
929 					clipRect );
930 
931 				if( !node->getExpanded() )
932 				{
933 					// vertical '+' line
934 					rc.UpperLeftCorner.X = expanderRect.UpperLeftCorner.X + ( expanderRect.getWidth() >> 1 );
935 					rc.UpperLeftCorner.Y = expanderRect.UpperLeftCorner.Y + 2;
936 					rc.LowerRightCorner.X = rc.UpperLeftCorner.X + 1;
937 					rc.LowerRightCorner.Y = rc.UpperLeftCorner.Y + expanderRect.getHeight() - 4;
938 					driver->draw2DRectangle( skin->getColor( EGDC_BUTTON_TEXT ), rc,
939 						clipRect );
940 				}
941 			}
942 
943 			core::rect<s32> textRect = frameRect;
944 
945 			if( Font )
946 			{
947 				EGUI_DEFAULT_COLOR textCol = EGDC_GRAY_TEXT;
948 				if ( isEnabled() )
949 					textCol = ( node == Selected ) ? EGDC_HIGH_LIGHT_TEXT : EGDC_BUTTON_TEXT;
950 
951 				s32 iconWidth = 0;
952 				for( s32 n = 0; n < 2; ++n )
953 				{
954 					s32 index = node->getImageIndex();
955 					if( ( ImageList && index >= 0 )
956 						&& ( ( ImageLeftOfIcon && n == 0 )
957 						|| ( !ImageLeftOfIcon && n == 1 ) ) )
958 					{
959 						index = node->getSelectedImageIndex();
960 						if( node != Selected || index < 0 )
961 						{
962 							index = node->getImageIndex();
963 						}
964 						ImageList->draw(
965 							index,
966 							core::position2d<s32>(
967 							textRect.UpperLeftCorner.X,
968 							textRect.UpperLeftCorner.Y + ( ( textRect.getHeight() - ImageList->getImageSize().Height ) >> 1 ) ),
969 							&clientClip );
970 						iconWidth += ImageList->getImageSize().Width + 3;
971 						textRect.UpperLeftCorner.X += ImageList->getImageSize().Width + 3;
972 					}
973 					else if( ( IconFont && reinterpret_cast<CGUITreeViewNode*>( node )->Icon.size() )
974 						&& ( ( ImageLeftOfIcon && n == 1 )
975 						|| ( !ImageLeftOfIcon && n == 0 ) ) )
976 					{
977 						IconFont->draw( node->getIcon(), textRect, skin->getColor(textCol), false, true, &clientClip );
978 						iconWidth += IconFont->getDimension( node->getIcon() ).Width + 3;
979 						textRect.UpperLeftCorner.X += IconFont->getDimension( node->getIcon() ).Width + 3;
980 					}
981 				}
982 
983 				Font->draw( node->getText(), textRect, skin->getColor(textCol), false, true, &clientClip );
984 
985 				textRect.UpperLeftCorner.X -= iconWidth;
986 			}
987 
988 			// draw the lines if neccessary
989 			if( LinesVisible )
990 			{
991 				core::rect<s32> rc;
992 
993 				// horizontal line
994 				rc.UpperLeftCorner.X = frameRect.UpperLeftCorner.X - IndentWidth - ( IndentWidth >> 1 ) - 1;
995 				rc.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y + ( ( frameRect.getHeight() ) >> 1 );
996 				if( node->hasChildren() )
997 				{
998 					rc.LowerRightCorner.X = frameRect.UpperLeftCorner.X - IndentWidth;
999 				}
1000 				else
1001 				{
1002 					rc.LowerRightCorner.X = frameRect.UpperLeftCorner.X - 2;
1003 				}
1004 				rc.LowerRightCorner.Y = rc.UpperLeftCorner.Y + 1;
1005 				driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
1006 					clipRect );
1007 
1008 				if( node->getParent() != Root )
1009 				{
1010 					// vertical line
1011 					if( node == node->getParent()->getFirstChild() )
1012 					{
1013 						rc.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - ( ( frameRect.getHeight() - IndentWidth ) >> 1 );
1014 					}
1015 					else
1016 					{
1017 						rc.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - ( frameRect.getHeight() >> 1 );
1018 					}
1019 					rc.LowerRightCorner.X = rc.UpperLeftCorner.X + 1;
1020 					driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
1021 						clipRect );
1022 
1023 					// the vertical lines of all parents
1024 					IGUITreeViewNode* nodeTmp = node->getParent();
1025 					rc.UpperLeftCorner.Y = frameRect.UpperLeftCorner.Y - ( frameRect.getHeight() >> 1 );
1026 					for( s32 n = 0; n < node->getLevel() - 2; ++n )
1027 					{
1028 						rc.UpperLeftCorner.X -= IndentWidth;
1029 						rc.LowerRightCorner.X -= IndentWidth;
1030 						if( nodeTmp != nodeTmp->getParent()->getLastChild() )
1031 						{
1032 							driver->draw2DRectangle( skin->getColor( EGDC_3D_SHADOW ), rc,
1033 								clipRect );
1034 						}
1035 						nodeTmp = nodeTmp->getParent();
1036 					}
1037 				}
1038 			}
1039 		}
1040 
1041 		frameRect.UpperLeftCorner.Y += ItemHeight;
1042 		frameRect.LowerRightCorner.Y += ItemHeight;
1043 
1044 		node = node->getNextVisible();
1045 	}
1046 
1047 	IGUIElement::draw();
1048 }
1049 
1050 //! Sets the font which should be used as icon font. This font is set to the Irrlicht engine
1051 //! built-in-font by default. Icons can be displayed in front of every list item.
1052 //! An icon is a string, displayed with the icon font. When using the build-in-font of the
1053 //! Irrlicht engine as icon font, the icon strings defined in GUIIcons.h can be used.
setIconFont(IGUIFont * font)1054 void CGUITreeView::setIconFont( IGUIFont* font )
1055 {
1056 	s32	height;
1057 
1058 	if ( font )
1059 		font->grab();
1060 	if ( IconFont )
1061 	{
1062 		IconFont->drop();
1063 	}
1064 
1065 	IconFont = font;
1066 	if( IconFont )
1067 	{
1068 		height = IconFont->getDimension( L" " ).Height;
1069 		if( height > ItemHeight )
1070 		{
1071 			ItemHeight = height;
1072 		}
1073 	}
1074 }
1075 
1076 //! Sets the image list which should be used for the image and selected image of every node.
1077 //! The default is 0 (no images).
setImageList(IGUIImageList * imageList)1078 void CGUITreeView::setImageList( IGUIImageList* imageList )
1079 {
1080 	if (imageList )
1081 		imageList->grab();
1082 	if( ImageList )
1083 	{
1084 		ImageList->drop();
1085 	}
1086 
1087 	ImageList = imageList;
1088 	if( ImageList )
1089 	{
1090 		if( ImageList->getImageSize().Height + 1 > ItemHeight )
1091 		{
1092 			ItemHeight = ImageList->getImageSize().Height + 1;
1093 		}
1094 	}
1095 }
1096 
1097 } // end namespace gui
1098 } // end namespace irr
1099 
1100 
1101 #endif // _IRR_COMPILE_WITH_GUI_
1102