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 "CGUIContextMenu.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 "IGUISpriteBank.h"
14 #include "os.h"
15 
16 namespace irr
17 {
18 namespace gui
19 {
20 
21 
22 //! constructor
CGUIContextMenu(IGUIEnvironment * environment,IGUIElement * parent,s32 id,core::rect<s32> rectangle,bool getFocus,bool allowFocus)23 CGUIContextMenu::CGUIContextMenu(IGUIEnvironment* environment,
24 				IGUIElement* parent, s32 id,
25 				core::rect<s32> rectangle, bool getFocus, bool allowFocus)
26 	: IGUIContextMenu(environment, parent, id, rectangle), EventParent(0), LastFont(0),
27 		CloseHandling(ECMC_REMOVE), HighLighted(-1), ChangeTime(0), AllowFocus(allowFocus)
28 {
29 	#ifdef _DEBUG
30 	setDebugName("CGUIContextMenu");
31 	#endif
32 
33 	Pos = rectangle.UpperLeftCorner;
34 	recalculateSize();
35 
36 	if (getFocus)
37 		Environment->setFocus(this);
38 
39 	setNotClipped(true);
40 }
41 
42 
43 //! destructor
~CGUIContextMenu()44 CGUIContextMenu::~CGUIContextMenu()
45 {
46 	for (u32 i=0; i<Items.size(); ++i)
47 		if (Items[i].SubMenu)
48 			Items[i].SubMenu->drop();
49 
50 	if (LastFont)
51 		LastFont->drop();
52 }
53 
54 //! set behavior when menus are closed
setCloseHandling(ECONTEXT_MENU_CLOSE onClose)55 void CGUIContextMenu::setCloseHandling(ECONTEXT_MENU_CLOSE onClose)
56 {
57 	CloseHandling = onClose;
58 }
59 
60 //! get current behavior when the menue will be closed
getCloseHandling() const61 ECONTEXT_MENU_CLOSE CGUIContextMenu::getCloseHandling() const
62 {
63 	return CloseHandling;
64 }
65 
66 //! Returns amount of menu items
getItemCount() const67 u32 CGUIContextMenu::getItemCount() const
68 {
69 	return Items.size();
70 }
71 
72 
73 //! Adds a menu item.
addItem(const wchar_t * text,s32 commandId,bool enabled,bool hasSubMenu,bool checked,bool autoChecking)74 u32 CGUIContextMenu::addItem(const wchar_t* text, s32 commandId, bool enabled, bool hasSubMenu, bool checked, bool autoChecking)
75 {
76     return insertItem(Items.size(), text, commandId, enabled, hasSubMenu, checked, autoChecking);
77 }
78 
79 //! Insert a menu item at specified position.
insertItem(u32 idx,const wchar_t * text,s32 commandId,bool enabled,bool hasSubMenu,bool checked,bool autoChecking)80 u32 CGUIContextMenu::insertItem(u32 idx, const wchar_t* text, s32 commandId, bool enabled,
81     bool hasSubMenu, bool checked, bool autoChecking)
82 {
83 	SItem s;
84 	s.Enabled = enabled;
85 	s.Checked = checked;
86 	s.AutoChecking = autoChecking;
87 	s.Text = text;
88 	s.IsSeparator = (text == 0);
89 	s.SubMenu = 0;
90 	s.CommandId = commandId;
91 
92 	if (hasSubMenu)
93 	{
94 		s.SubMenu = new CGUIContextMenu(Environment, this, commandId,
95 			core::rect<s32>(0,0,100,100), false, false);
96 		s.SubMenu->setVisible(false);
97 	}
98 
99     u32 result = idx;
100     if ( idx < Items.size() )
101     {
102         Items.insert(s, idx);
103     }
104     else
105     {
106         Items.push_back(s);
107         result = Items.size() - 1;
108     }
109 
110 	recalculateSize();
111 	return result;
112 }
113 
findItemWithCommandId(s32 commandId,u32 idxStartSearch) const114 s32 CGUIContextMenu::findItemWithCommandId(s32 commandId, u32 idxStartSearch) const
115 {
116 	for ( u32 i=idxStartSearch; i<Items.size(); ++i )
117 	{
118 		if ( Items[i].CommandId == commandId )
119 		{
120 			return (s32)i;
121 		}
122 	}
123 	return -1;
124 }
125 
126 //! Adds a sub menu from an element that already exists.
setSubMenu(u32 index,CGUIContextMenu * menu)127 void CGUIContextMenu::setSubMenu(u32 index, CGUIContextMenu* menu)
128 {
129 	if (index >= Items.size())
130 		return;
131 
132 	if (menu)
133 		menu->grab();
134 	if (Items[index].SubMenu)
135 		Items[index].SubMenu->drop();
136 
137 	Items[index].SubMenu = menu;
138 	menu->setVisible(false);
139 
140 	if (Items[index].SubMenu)
141 	{
142 		menu->AllowFocus = false;
143 		if ( Environment->getFocus() == menu )
144 		{
145 			Environment->setFocus( this );
146 		}
147 	}
148 
149 	recalculateSize();
150 }
151 
152 
153 //! Adds a separator item to the menu
addSeparator()154 void CGUIContextMenu::addSeparator()
155 {
156 	addItem(0, -1, true, false, false, false);
157 }
158 
159 
160 //! Returns text of the menu item.
getItemText(u32 idx) const161 const wchar_t* CGUIContextMenu::getItemText(u32 idx) const
162 {
163 	if (idx >= Items.size())
164 		return 0;
165 
166 	return Items[idx].Text.c_str();
167 }
168 
169 
170 //! Sets text of the menu item.
setItemText(u32 idx,const wchar_t * text)171 void CGUIContextMenu::setItemText(u32 idx, const wchar_t* text)
172 {
173 	if (idx >= Items.size())
174 		return;
175 
176 	Items[idx].Text = text;
177 	recalculateSize();
178 }
179 
180 //! should the element change the checked status on clicking
setItemAutoChecking(u32 idx,bool autoChecking)181 void CGUIContextMenu::setItemAutoChecking(u32 idx, bool autoChecking)
182 {
183 	if ( idx >= Items.size())
184 		return;
185 
186 	Items[idx].AutoChecking = autoChecking;
187 }
188 
189 //! does the element change the checked status on clicking
getItemAutoChecking(u32 idx) const190 bool CGUIContextMenu::getItemAutoChecking(u32 idx) const
191 {
192 	if (idx >= Items.size())
193 		return false;
194 
195 	return Items[idx].AutoChecking;
196 }
197 
198 
199 //! Returns if a menu item is enabled
isItemEnabled(u32 idx) const200 bool CGUIContextMenu::isItemEnabled(u32 idx) const
201 {
202 	if (idx >= Items.size())
203 	{
204 		_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
205 		return false;
206 	}
207 
208 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
209 	return Items[idx].Enabled;
210 }
211 
212 
213 //! Returns if a menu item is checked
isItemChecked(u32 idx) const214 bool CGUIContextMenu::isItemChecked(u32 idx) const
215 {
216 	if (idx >= Items.size())
217 	{
218 		_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
219 		return false;
220 	}
221 
222 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
223 	return Items[idx].Checked;
224 }
225 
226 
227 //! Sets if the menu item should be enabled.
setItemEnabled(u32 idx,bool enabled)228 void CGUIContextMenu::setItemEnabled(u32 idx, bool enabled)
229 {
230 	if (idx >= Items.size())
231 		return;
232 
233 	Items[idx].Enabled = enabled;
234 }
235 
236 
237 //! Sets if the menu item should be checked.
setItemChecked(u32 idx,bool checked)238 void CGUIContextMenu::setItemChecked(u32 idx, bool checked )
239 {
240 	if (idx >= Items.size())
241 		return;
242 
243 	Items[idx].Checked = checked;
244 }
245 
246 
247 //! Removes a menu item
removeItem(u32 idx)248 void CGUIContextMenu::removeItem(u32 idx)
249 {
250 	if (idx >= Items.size())
251 		return;
252 
253 	if (Items[idx].SubMenu)
254 	{
255 		Items[idx].SubMenu->drop();
256 		Items[idx].SubMenu = 0;
257 	}
258 
259 	Items.erase(idx);
260 	recalculateSize();
261 }
262 
263 
264 //! Removes all menu items
removeAllItems()265 void CGUIContextMenu::removeAllItems()
266 {
267 	for (u32 i=0; i<Items.size(); ++i)
268 		if (Items[i].SubMenu)
269 			Items[i].SubMenu->drop();
270 
271 	Items.clear();
272 	recalculateSize();
273 }
274 
275 
276 //! called if an event happened.
OnEvent(const SEvent & event)277 bool CGUIContextMenu::OnEvent(const SEvent& event)
278 {
279 	if (isEnabled())
280 	{
281 
282 		switch(event.EventType)
283 		{
284 		case EET_GUI_EVENT:
285 			switch(event.GUIEvent.EventType)
286 			{
287 			case EGET_ELEMENT_FOCUS_LOST:
288 				if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element) && AllowFocus)
289 				{
290 					// set event parent of submenus
291 					IGUIElement * p =  EventParent ? EventParent : Parent;
292 					if ( p )	// can be 0 when element got removed already
293 					{
294 						setEventParent(p);
295 
296 						SEvent event;
297 						event.EventType = EET_GUI_EVENT;
298 						event.GUIEvent.Caller = this;
299 						event.GUIEvent.Element = 0;
300 						event.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
301 						if ( !p->OnEvent(event) )
302 						{
303 							if ( CloseHandling & ECMC_HIDE )
304 							{
305 								setVisible(false);
306 							}
307 							if ( CloseHandling & ECMC_REMOVE )
308 							{
309 								remove();
310 							}
311 						}
312 					}
313 
314 					return false;
315 				}
316 				break;
317 			case EGET_ELEMENT_FOCUSED:
318 				if (event.GUIEvent.Caller == this && !AllowFocus)
319 				{
320 					return true;
321 				}
322 				break;
323 			default:
324 				break;
325 			}
326 			break;
327 		case EET_MOUSE_INPUT_EVENT:
328 			switch(event.MouseInput.Event)
329 			{
330 			case EMIE_LMOUSE_LEFT_UP:
331 				{
332 					// menu might be removed if it loses focus in sendClick, so grab a reference
333 					grab();
334 					const u32 t = sendClick(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
335 					if ((t==0 || t==1) && Environment->hasFocus(this))
336 						Environment->removeFocus(this);
337 					drop();
338 				}
339 				return true;
340 			case EMIE_LMOUSE_PRESSED_DOWN:
341 				return true;
342 			case EMIE_MOUSE_MOVED:
343 				if (Environment->hasFocus(this))
344 					highlight(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y), true);
345 				return true;
346 			default:
347 				break;
348 			}
349 			break;
350 		default:
351 			break;
352 		}
353 	}
354 
355 	return IGUIElement::OnEvent(event);
356 }
357 
358 
359 //! Sets the visible state of this element.
setVisible(bool visible)360 void CGUIContextMenu::setVisible(bool visible)
361 {
362 	HighLighted = -1;
363 	ChangeTime = os::Timer::getTime();
364 	for (u32 j=0; j<Items.size(); ++j)
365 		if (Items[j].SubMenu)
366 			Items[j].SubMenu->setVisible(false);
367 
368 	IGUIElement::setVisible(visible);
369 }
370 
371 
372 //! sends a click Returns:
373 //! 0 if click went outside of the element,
374 //! 1 if a valid button was clicked,
375 //! 2 if a nonclickable element was clicked
sendClick(const core::position2d<s32> & p)376 u32 CGUIContextMenu::sendClick(const core::position2d<s32>& p)
377 {
378 	u32 t = 0;
379 
380 	// get number of open submenu
381 	s32 openmenu = -1;
382 	s32 j;
383 	for (j=0; j<(s32)Items.size(); ++j)
384 		if (Items[j].SubMenu && Items[j].SubMenu->isVisible())
385 		{
386 			openmenu = j;
387 			break;
388 		}
389 
390 	// delegate click operation to submenu
391 	if (openmenu != -1)
392 	{
393 		t = Items[j].SubMenu->sendClick(p);
394 		if (t != 0)
395 			return t; // clicked something
396 	}
397 
398 	// check click on myself
399 	if (isPointInside(p) &&
400 		(u32)HighLighted < Items.size())
401 	{
402 		if (!Items[HighLighted].Enabled ||
403 			Items[HighLighted].IsSeparator ||
404 			Items[HighLighted].SubMenu)
405 			return 2;
406 
407 		if ( Items[HighLighted].AutoChecking )
408 		{
409 			Items[HighLighted].Checked = Items[HighLighted].Checked ? false : true;
410 		}
411 
412 		SEvent event;
413 		event.EventType = EET_GUI_EVENT;
414 		event.GUIEvent.Caller = this;
415 		event.GUIEvent.Element = 0;
416 		event.GUIEvent.EventType = EGET_MENU_ITEM_SELECTED;
417 		if (EventParent)
418  			EventParent->OnEvent(event);
419 		else if (Parent)
420 			Parent->OnEvent(event);
421 
422 		return 1;
423 	}
424 
425 	return 0;
426 }
427 
428 
429 //! returns true, if an element was highligted
highlight(const core::position2d<s32> & p,bool canOpenSubMenu)430 bool CGUIContextMenu::highlight(const core::position2d<s32>& p, bool canOpenSubMenu)
431 {
432 	if (!isEnabled())
433 	{
434 		return false;
435 	}
436 
437 	// get number of open submenu
438 	s32 openmenu = -1;
439 	s32 i;
440 	for (i=0; i<(s32)Items.size(); ++i)
441 		if (Items[i].Enabled && Items[i].SubMenu && Items[i].SubMenu->isVisible())
442 		{
443 			openmenu = i;
444 			break;
445 		}
446 
447 	// delegate highlight operation to submenu
448 	if (openmenu != -1)
449 	{
450 		if (Items[openmenu].Enabled && Items[openmenu].SubMenu->highlight(p, canOpenSubMenu))
451 		{
452 			HighLighted = openmenu;
453 			ChangeTime = os::Timer::getTime();
454 			return true;
455 		}
456 	}
457 
458 	// highlight myself
459 	for (i=0; i<(s32)Items.size(); ++i)
460 	{
461 		if (Items[i].Enabled && getHRect(Items[i], AbsoluteRect).isPointInside(p))
462 		{
463 			HighLighted = i;
464 			ChangeTime = os::Timer::getTime();
465 
466 			// make submenus visible/invisible
467 				for (s32 j=0; j<(s32)Items.size(); ++j)
468 					if (Items[j].SubMenu)
469 					{
470 						if ( j == i && canOpenSubMenu && Items[j].Enabled )
471 							Items[j].SubMenu->setVisible(true);
472 						else if ( j != i )
473 							Items[j].SubMenu->setVisible(false);
474 					}
475 			return true;
476 		}
477 	}
478 
479 	HighLighted = openmenu;
480 	return false;
481 }
482 
483 
484 //! returns the item highlight-area
getHRect(const SItem & i,const core::rect<s32> & absolute) const485 core::rect<s32> CGUIContextMenu::getHRect(const SItem& i, const core::rect<s32>& absolute) const
486 {
487 	core::rect<s32> r = absolute;
488 	r.UpperLeftCorner.Y += i.PosY;
489 	r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
490 	return r;
491 }
492 
493 
494 //! Gets drawing rect of Item
getRect(const SItem & i,const core::rect<s32> & absolute) const495 core::rect<s32> CGUIContextMenu::getRect(const SItem& i, const core::rect<s32>& absolute) const
496 {
497 	core::rect<s32> r = absolute;
498 	r.UpperLeftCorner.Y += i.PosY;
499 	r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
500 	r.UpperLeftCorner.X += 20;
501 	return r;
502 }
503 
504 
505 //! draws the element and its children
draw()506 void CGUIContextMenu::draw()
507 {
508 	if (!IsVisible)
509 		return;
510 
511 	IGUISkin* skin = Environment->getSkin();
512 
513 	if (!skin)
514 		return;
515 
516 	IGUIFont* font = skin->getFont(EGDF_MENU);
517 	if (font != LastFont)
518 	{
519 		if (LastFont)
520 			LastFont->drop();
521 		LastFont = font;
522 		if (LastFont)
523 			LastFont->grab();
524 
525 		recalculateSize();
526 	}
527 
528 	IGUISpriteBank* sprites = skin->getSpriteBank();
529 
530 	core::rect<s32> rect = AbsoluteRect;
531 	core::rect<s32>* clip = 0;
532 
533 	// draw frame
534 	skin->draw3DMenuPane(this, AbsoluteRect, clip);
535 
536 	// loop through all menu items
537 
538 	rect = AbsoluteRect;
539 	s32 y = AbsoluteRect.UpperLeftCorner.Y;
540 
541 	for (s32 i=0; i<(s32)Items.size(); ++i)
542 	{
543 		if (Items[i].IsSeparator)
544 		{
545 			// draw separator
546 			rect = AbsoluteRect;
547 			rect.UpperLeftCorner.Y += Items[i].PosY + 3;
548 			rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
549 			rect.UpperLeftCorner.X += 5;
550 			rect.LowerRightCorner.X -= 5;
551 			skin->draw2DRectangle(this, skin->getColor(EGDC_3D_SHADOW), rect, clip);
552 
553 			rect.LowerRightCorner.Y += 1;
554 			rect.UpperLeftCorner.Y += 1;
555 			skin->draw2DRectangle(this, skin->getColor(EGDC_3D_HIGH_LIGHT), rect, clip);
556 
557 			y += 10;
558 		}
559 		else
560 		{
561 			rect = getRect(Items[i], AbsoluteRect);
562 
563 			// draw highlighted
564 
565 			if (i == HighLighted && Items[i].Enabled)
566 			{
567 				core::rect<s32> r = AbsoluteRect;
568 				r.LowerRightCorner.Y = rect.LowerRightCorner.Y;
569 				r.UpperLeftCorner.Y = rect.UpperLeftCorner.Y;
570 				r.LowerRightCorner.X -= 5;
571 				r.UpperLeftCorner.X += 5;
572 				skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), r, clip);
573 			}
574 
575 			// draw text
576 
577 			EGUI_DEFAULT_COLOR c = EGDC_BUTTON_TEXT;
578 
579 			if (i == HighLighted)
580 				c = EGDC_HIGH_LIGHT_TEXT;
581 
582 			if (!Items[i].Enabled)
583 				c = EGDC_GRAY_TEXT;
584 
585 			if (font)
586 				font->draw(Items[i].Text.c_str(), rect,
587 					skin->getColor(c), false, true, clip);
588 
589 			// draw submenu symbol
590 			if (Items[i].SubMenu && sprites)
591 			{
592 				core::rect<s32> r = rect;
593 				r.UpperLeftCorner.X = r.LowerRightCorner.X - 15;
594 
595 				sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_RIGHT),
596 					r.getCenter(), clip, skin->getColor(c),
597 					(i == HighLighted) ? ChangeTime : 0,
598 					(i == HighLighted) ? os::Timer::getTime() : 0,
599 					(i == HighLighted), true);
600 			}
601 
602 			// draw checked symbol
603 			if (Items[i].Checked && sprites)
604 			{
605 				core::rect<s32> r = rect;
606 				r.LowerRightCorner.X = r.UpperLeftCorner.X - 15;
607 				r.UpperLeftCorner.X = r.LowerRightCorner.X + 15;
608 				sprites->draw2DSprite(skin->getIcon(EGDI_CHECK_BOX_CHECKED),
609 					r.getCenter(), clip, skin->getColor(c),
610 					(i == HighLighted) ? ChangeTime : 0,
611 					(i == HighLighted) ? os::Timer::getTime() : 0,
612 					(i == HighLighted), true);
613 			}
614 		}
615 	}
616 
617 	IGUIElement::draw();
618 }
619 
620 
recalculateSize()621 void CGUIContextMenu::recalculateSize()
622 {
623 	IGUIFont* font = Environment->getSkin()->getFont(EGDF_MENU);
624 
625 	if (!font)
626 		return;
627 
628 	core::rect<s32> rect;
629 	rect.UpperLeftCorner = RelativeRect.UpperLeftCorner;
630 	u32 width = 100;
631 	u32 height = 3;
632 
633 	u32 i;
634 	for (i=0; i<Items.size(); ++i)
635 	{
636 		if (Items[i].IsSeparator)
637 		{
638 			Items[i].Dim.Width = 100;
639 			Items[i].Dim.Height = 10;
640 		}
641 		else
642 		{
643 			Items[i].Dim = font->getDimension(Items[i].Text.c_str());
644 			Items[i].Dim.Width += 40;
645 
646 			if (Items[i].Dim.Width > width)
647 				width = Items[i].Dim.Width;
648 		}
649 
650 		Items[i].PosY = height;
651 		height += Items[i].Dim.Height;
652 	}
653 
654 	height += 5;
655 
656 	if (height < 10)
657 		height = 10;
658 
659 	rect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + width;
660 	rect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + height;
661 
662 	setRelativePosition(rect);
663 
664 	// recalculate submenus
665 	for (i=0; i<Items.size(); ++i)
666 	{
667 		if (Items[i].SubMenu)
668 		{
669 			// move submenu
670 			const s32 w = Items[i].SubMenu->getAbsolutePosition().getWidth();
671 			const s32 h = Items[i].SubMenu->getAbsolutePosition().getHeight();
672 
673             core::rect<s32> subRect(width-5, Items[i].PosY, width+w-5, Items[i].PosY+h);
674 
675             // if it would be drawn beyond the right border, then add it to the left side
676             gui::IGUIElement * root = Environment->getRootGUIElement();
677             if ( root )
678             {
679                 core::rect<s32> rectRoot( root->getAbsolutePosition() );
680                 if ( getAbsolutePosition().UpperLeftCorner.X+subRect.LowerRightCorner.X > rectRoot.LowerRightCorner.X )
681                 {
682                     subRect.UpperLeftCorner.X = -w;
683                     subRect.LowerRightCorner.X = 0;
684                 }
685             }
686 
687 			Items[i].SubMenu->setRelativePosition(subRect);
688 		}
689 	}
690 }
691 
692 
693 //! Returns the selected item in the menu
getSelectedItem() const694 s32 CGUIContextMenu::getSelectedItem() const
695 {
696 	return HighLighted;
697 }
698 
699 
700 //! \return Returns a pointer to the submenu of an item.
getSubMenu(u32 idx) const701 IGUIContextMenu* CGUIContextMenu::getSubMenu(u32 idx) const
702 {
703 	if (idx >= Items.size())
704 		return 0;
705 
706 	return Items[idx].SubMenu;
707 }
708 
709 
710 //! Returns command id of a menu item
getItemCommandId(u32 idx) const711 s32 CGUIContextMenu::getItemCommandId(u32 idx) const
712 {
713 	if (idx >= Items.size())
714 		return -1;
715 
716 	return Items[idx].CommandId;
717 }
718 
719 
720 //! Sets the command id of a menu item
setItemCommandId(u32 idx,s32 id)721 void CGUIContextMenu::setItemCommandId(u32 idx, s32 id)
722 {
723 	if (idx >= Items.size())
724 		return;
725 
726 	Items[idx].CommandId = id;
727 }
728 
729 
730 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const731 void CGUIContextMenu::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
732 {
733 	IGUIElement::serializeAttributes(out,options);
734 	out->addPosition2d("Position", Pos);
735 
736 	if (Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU )
737 	{
738 		const IGUIContextMenu* const ptr = (const IGUIContextMenu*)Parent;
739 		// find the position of this item in its parent's list
740 		u32 i;
741 		// VC6 needs the cast for this
742 		for (i=0; (i<ptr->getItemCount()) && (ptr->getSubMenu(i) != (const IGUIContextMenu*)this); ++i)
743 			; // do nothing
744 
745 		out->addInt("ParentItem", i);
746 	}
747 
748 	out->addInt("CloseHandling", (s32)CloseHandling);
749 
750 	// write out the item list
751 	out->addInt("ItemCount", Items.size());
752 
753 	core::stringc tmp;
754 
755 	for (u32 i=0; i < Items.size(); ++i)
756 	{
757 		tmp = "IsSeparator"; tmp += i;
758 		out->addBool(tmp.c_str(), Items[i].IsSeparator);
759 
760 		if (!Items[i].IsSeparator)
761 		{
762 			tmp = "Text"; tmp += i;
763 			out->addString(tmp.c_str(), Items[i].Text.c_str());
764 			tmp = "CommandID"; tmp += i;
765 			out->addInt(tmp.c_str(), Items[i].CommandId);
766 			tmp = "Enabled"; tmp += i;
767 			out->addBool(tmp.c_str(), Items[i].Enabled);
768 			tmp = "Checked"; tmp += i;
769 			out->addBool(tmp.c_str(), Items[i].Checked);
770 			tmp = "AutoChecking"; tmp += i;
771 			out->addBool(tmp.c_str(), Items[i].AutoChecking);
772 		}
773 	}
774 }
775 
776 
777 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)778 void CGUIContextMenu::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
779 {
780 	IGUIElement::deserializeAttributes(in,options);
781 
782 	Pos = in->getAttributeAsPosition2d("Position");
783 
784 	// link to this item's parent
785 	if (Parent && ( Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU ) )
786 		((CGUIContextMenu*)Parent)->setSubMenu(in->getAttributeAsInt("ParentItem"),this);
787 
788 	CloseHandling = (ECONTEXT_MENU_CLOSE)in->getAttributeAsInt("CloseHandling");
789 
790 	removeAllItems();
791 
792 	// read the item list
793 	const s32 count = in->getAttributeAsInt("ItemCount");
794 
795 	for (s32 i=0; i<count; ++i)
796 	{
797 		core::stringc tmp;
798 		core::stringw txt;
799 		s32 commandid=-1;
800 		bool enabled=true;
801 		bool checked=false;
802 		bool autochecking=false;
803 
804 		tmp = "IsSeparator"; tmp += i;
805 		if ( in->existsAttribute(tmp.c_str()) && in->getAttributeAsBool(tmp.c_str()) )
806 			addSeparator();
807 		else
808 		{
809 			tmp = "Text"; tmp += i;
810 			if ( in->existsAttribute(tmp.c_str()) )
811 				txt = in->getAttributeAsStringW(tmp.c_str());
812 
813 			tmp = "CommandID"; tmp += i;
814 			if ( in->existsAttribute(tmp.c_str()) )
815 				commandid = in->getAttributeAsInt(tmp.c_str());
816 
817 			tmp = "Enabled"; tmp += i;
818 			if ( in->existsAttribute(tmp.c_str()) )
819 				enabled = in->getAttributeAsBool(tmp.c_str());
820 
821 			tmp = "Checked"; tmp += i;
822 			if ( in->existsAttribute(tmp.c_str()) )
823 				checked = in->getAttributeAsBool(tmp.c_str());
824 
825  			tmp = "AutoChecking"; tmp += i;
826 			if ( in->existsAttribute(tmp.c_str()) )
827 				autochecking = in->getAttributeAsBool(tmp.c_str());
828 
829  			addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked, autochecking);
830 		}
831 	}
832 
833 	recalculateSize();
834 }
835 
836 
837 // because sometimes the element has no parent at click time
setEventParent(IGUIElement * parent)838 void CGUIContextMenu::setEventParent(IGUIElement *parent)
839 {
840 	EventParent = parent;
841 
842 	for (u32 i=0; i<Items.size(); ++i)
843 		if (Items[i].SubMenu)
844 			Items[i].SubMenu->setEventParent(parent);
845 }
846 
847 
hasOpenSubMenu() const848 bool CGUIContextMenu::hasOpenSubMenu() const
849 {
850 	for (u32 i=0; i<Items.size(); ++i)
851 		if (Items[i].SubMenu && Items[i].SubMenu->isVisible())
852 			return true;
853 
854 	return false;
855 }
856 
857 
closeAllSubMenus()858 void CGUIContextMenu::closeAllSubMenus()
859 {
860 	for (u32 i=0; i<Items.size(); ++i)
861 		if (Items[i].SubMenu)
862 			Items[i].SubMenu->setVisible(false);
863 
864 	//HighLighted = -1;
865 }
866 
867 
868 } // end namespace
869 } // end namespace
870 
871 #endif // _IRR_COMPILE_WITH_GUI_
872 
873