Retour
Version Originale

./aip/1.8aipmod/source/Irrlicht/CGUIContextMenu.cpp :


// Copyright (C) 2002-2011 Nikolaus Gebhardt

// This file is part of the "Irrlicht Engine".

// For conditions of distribution and use, see copyright notice in irrlicht.h


#include "CGUIContextMenu.h"

#ifdef _IRR_COMPILE_WITH_GUI_

#include "IGUISkin.h"
#include "IGUIEnvironment.h"
#include "IVideoDriver.h"
#include "IGUIFont.h"
#include "IGUISpriteBank.h"
#include "os.h"

namespace irr
{
namespace gui
{


//! constructor

CGUIContextMenu::CGUIContextMenu(IGUIEnvironment* environment,
				IGUIElement* parent, s32 id,
				core::rect<s32> rectangle, bool getFocus, bool allowFocus)
	: IGUIContextMenu(environment, parent, id, rectangle), EventParent(0), LastFont(0),
		CloseHandling(ECMC_REMOVE), HighLighted(-1), ChangeTime(0), AllowFocus(allowFocus)
{
	#ifdef _DEBUG
	setDebugName("CGUIContextMenu");
	#endif

	Pos = rectangle.UpperLeftCorner;
	recalculateSize();

	if (getFocus)
		Environment->setFocus(this);

	setNotClipped(true);
}


//! destructor

CGUIContextMenu::~CGUIContextMenu()
{
	for (u32 i=0; i<Items.size(); ++i)
		if (Items[i].SubMenu)
			Items[i].SubMenu->drop();

	if (LastFont)
		LastFont->drop();
}

//! set behaviour when menus are closed

void CGUIContextMenu::setCloseHandling(ECONTEXT_MENU_CLOSE onClose)
{
	CloseHandling = onClose;
}

//! get current behaviour when the menue will be closed

ECONTEXT_MENU_CLOSE CGUIContextMenu::getCloseHandling() const
{
	return CloseHandling;
}

//! Returns amount of menu items

u32 CGUIContextMenu::getItemCount() const
{
	return Items.size();
}


//! Adds a menu item.

u32 CGUIContextMenu::addItem(const wchar_t* text, s32 commandId, bool enabled, bool hasSubMenu, bool checked, bool autoChecking)
{
    return insertItem(Items.size(), text, commandId, enabled, hasSubMenu, checked, autoChecking);
}

//! Insert a menu item at specified position.

u32 CGUIContextMenu::insertItem(u32 idx, const wchar_t* text, s32 commandId, bool enabled,
    bool hasSubMenu, bool checked, bool autoChecking)
{
	SItem s;
	s.Enabled = enabled;
	s.Checked = checked;
	s.AutoChecking = autoChecking;
	s.Text = text;
	s.IsSeparator = (text == 0);
	s.SubMenu = 0;
	s.CommandId = commandId;

	if (hasSubMenu)
	{
		s.SubMenu = new CGUIContextMenu(Environment, this, commandId,
			core::rect<s32>(0,0,100,100), false, false);
		s.SubMenu->setVisible(false);
	}

    u32 result = idx;
    if ( idx < Items.size() )
    {
        Items.insert(s, idx);
    }
    else
    {
        Items.push_back(s);
        result = Items.size() - 1;
    }

	recalculateSize();
	return result;
}

s32 CGUIContextMenu::findItemWithCommandId(s32 commandId, u32 idxStartSearch) const
{
	for ( u32 i=idxStartSearch; i<Items.size(); ++i )
	{
		if ( Items[i].CommandId == commandId )
		{
			return (s32)i;
		}
	}
	return -1;
}

//! Adds a sub menu from an element that already exists.

void CGUIContextMenu::setSubMenu(u32 index, CGUIContextMenu* menu)
{
	if (index >= Items.size())
		return;

    if (menu)
        menu->grab();
	if (Items[index].SubMenu)
		Items[index].SubMenu->drop();

	Items[index].SubMenu = menu;
	menu->setVisible(false);

	if (Items[index].SubMenu)
	{
		menu->AllowFocus = false;
		if ( Environment->getFocus() == menu )
		{
			Environment->setFocus( this );
		}
	}

	recalculateSize();
}


//! Adds a separator item to the menu

void CGUIContextMenu::addSeparator()
{
	addItem(0, -1, true, false, false, false);
}


//! Returns text of the menu item.

const wchar_t* CGUIContextMenu::getItemText(u32 idx) const
{
	if (idx >= Items.size())
		return 0;

	return Items[idx].Text.c_str();
}


//! Sets text of the menu item.

void CGUIContextMenu::setItemText(u32 idx, const wchar_t* text)
{
	if (idx >= Items.size())
		return;

	Items[idx].Text = text;
	recalculateSize();
}

//! should the element change the checked status on clicking

void CGUIContextMenu::setItemAutoChecking(u32 idx, bool autoChecking)
{
	if ( idx >= Items.size())
		return;

	Items[idx].AutoChecking = autoChecking;
}

//! does the element change the checked status on clicking

bool CGUIContextMenu::getItemAutoChecking(u32 idx) const
{
	if (idx >= Items.size())
		return false;

	return Items[idx].AutoChecking;
}


//! Returns if a menu item is enabled

bool CGUIContextMenu::isItemEnabled(u32 idx) const
{
	if (idx >= Items.size())
	{
		_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
		return false;
	}

	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return Items[idx].Enabled;
}


//! Returns if a menu item is checked

bool CGUIContextMenu::isItemChecked(u32 idx) const
{
	if (idx >= Items.size())
	{
		_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
		return false;
	}

	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
	return Items[idx].Checked;
}


//! Sets if the menu item should be enabled.

void CGUIContextMenu::setItemEnabled(u32 idx, bool enabled)
{
	if (idx >= Items.size())
		return;

	Items[idx].Enabled = enabled;
}


//! Sets if the menu item should be checked.

void CGUIContextMenu::setItemChecked(u32 idx, bool checked )
{
	if (idx >= Items.size())
		return;

	Items[idx].Checked = checked;
}


//! Removes a menu item

void CGUIContextMenu::removeItem(u32 idx)
{
	if (idx >= Items.size())
		return;

	if (Items[idx].SubMenu)
	{
		Items[idx].SubMenu->drop();
		Items[idx].SubMenu = 0;
	}

	Items.erase(idx);
	recalculateSize();
}


//! Removes all menu items

void CGUIContextMenu::removeAllItems()
{
	for (u32 i=0; i<Items.size(); ++i)
		if (Items[i].SubMenu)
			Items[i].SubMenu->drop();

	Items.clear();
	recalculateSize();
}


//! called if an event happened.

bool CGUIContextMenu::OnEvent(const SEvent& event)
{
	if (isEnabled())
	{

		switch(event.EventType)
		{
		case EET_GUI_EVENT:
			switch(event.GUIEvent.EventType)
			{
			case EGET_ELEMENT_FOCUS_LOST:
				if (event.GUIEvent.Caller == this && !isMyChild(event.GUIEvent.Element) && AllowFocus)
				{
					// set event parent of submenus

					IGUIElement * p =  EventParent ? EventParent : Parent;
					setEventParent(p);

					SEvent event;
					event.EventType = EET_GUI_EVENT;
					event.GUIEvent.Caller = this;
					event.GUIEvent.Element = 0;
					event.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
					if ( !p->OnEvent(event) )
					{
						if ( CloseHandling & ECMC_HIDE )
						{
							setVisible(false);
						}
						if ( CloseHandling & ECMC_REMOVE )
						{
							remove();
						}
					}

					return false;
				}
				break;
			case EGET_ELEMENT_FOCUSED:
				if (event.GUIEvent.Caller == this && !AllowFocus)
				{
					return true;
				}
				break;
			default:
				break;
			}
			break;
		case EET_MOUSE_INPUT_EVENT:
			switch(event.MouseInput.Event)
			{
			case EMIE_LMOUSE_LEFT_UP:
				{
					// menu might be removed if it loses focus in sendClick, so grab a reference

					grab();
					const u32 t = sendClick(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
					if ((t==0 || t==1) && Environment->hasFocus(this))
						Environment->removeFocus(this);
					drop();
				}
				return true;
			case EMIE_LMOUSE_PRESSED_DOWN:
				return true;
			case EMIE_MOUSE_MOVED:
				if (Environment->hasFocus(this))
					highlight(core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y), true);
				return true;
			default:
				break;
			}
			break;
		default:
			break;
		}
	}

	return IGUIElement::OnEvent(event);
}


//! Sets the visible state of this element.

void CGUIContextMenu::setVisible(bool visible)
{
	HighLighted = -1;
	ChangeTime = os::Timer::getTime();
	for (u32 j=0; j<Items.size(); ++j)
		if (Items[j].SubMenu)
			Items[j].SubMenu->setVisible(false);

	IGUIElement::setVisible(visible);
}


//! sends a click Returns:

//! 0 if click went outside of the element,

//! 1 if a valid button was clicked,

//! 2 if a nonclickable element was clicked

u32 CGUIContextMenu::sendClick(const core::position2d<s32>& p)
{
	u32 t = 0;

	// get number of open submenu

	s32 openmenu = -1;
	s32 j;
	for (j=0; j<(s32)Items.size(); ++j)
		if (Items[j].SubMenu && Items[j].SubMenu->isVisible())
		{
			openmenu = j;
			break;
		}

	// delegate click operation to submenu

	if (openmenu != -1)
	{
		t = Items[j].SubMenu->sendClick(p);
		if (t != 0)
			return t; // clicked something

	}

	// check click on myself

	if (isPointInside(p) &&
		(u32)HighLighted < Items.size())
	{
		if (!Items[HighLighted].Enabled ||
			Items[HighLighted].IsSeparator ||
			Items[HighLighted].SubMenu)
			return 2;

		if ( Items[HighLighted].AutoChecking )
		{
			Items[HighLighted].Checked = Items[HighLighted].Checked ? false : true;
		}

		SEvent event;
		event.EventType = EET_GUI_EVENT;
		event.GUIEvent.Caller = this;
		event.GUIEvent.Element = 0;
		event.GUIEvent.EventType = EGET_MENU_ITEM_SELECTED;
		if (EventParent)
 			EventParent->OnEvent(event);
		else if (Parent)
			Parent->OnEvent(event);

		return 1;
	}

	return 0;
}


//! returns true, if an element was highligted

bool CGUIContextMenu::highlight(const core::position2d<s32>& p, bool canOpenSubMenu)
{
	if (!isEnabled())
	{
		return false;
	}

	// get number of open submenu

	s32 openmenu = -1;
	s32 i;
	for (i=0; i<(s32)Items.size(); ++i)
		if (Items[i].Enabled && Items[i].SubMenu && Items[i].SubMenu->isVisible())
		{
			openmenu = i;
			break;
		}

	// delegate highlight operation to submenu

	if (openmenu != -1)
	{
		if (Items[openmenu].Enabled && Items[openmenu].SubMenu->highlight(p, canOpenSubMenu))
		{
			HighLighted = openmenu;
			ChangeTime = os::Timer::getTime();
			return true;
		}
	}

	// highlight myself

	for (i=0; i<(s32)Items.size(); ++i)
	{
		if (Items[i].Enabled && getHRect(Items[i], AbsoluteRect).isPointInside(p))
		{
			HighLighted = i;
			ChangeTime = os::Timer::getTime();

			// make submenus visible/invisible

				for (s32 j=0; j<(s32)Items.size(); ++j)
					if (Items[j].SubMenu)
					{
						if ( j == i && canOpenSubMenu && Items[j].Enabled )
							Items[j].SubMenu->setVisible(true);
						else if ( j != i )
							Items[j].SubMenu->setVisible(false);
					}
			return true;
		}
	}

	HighLighted = openmenu;
	return false;
}


//! returns the item highlight-area

core::rect<s32> CGUIContextMenu::getHRect(const SItem& i, const core::rect<s32>& absolute) const
{
	core::rect<s32> r = absolute;
	r.UpperLeftCorner.Y += i.PosY;
	r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
	return r;
}


//! Gets drawing rect of Item

core::rect<s32> CGUIContextMenu::getRect(const SItem& i, const core::rect<s32>& absolute) const
{
	core::rect<s32> r = absolute;
	r.UpperLeftCorner.Y += i.PosY;
	r.LowerRightCorner.Y = r.UpperLeftCorner.Y + i.Dim.Height;
	r.UpperLeftCorner.X += 20;
	return r;
}


//! draws the element and its children

void CGUIContextMenu::draw()
{
	if (!IsVisible)
		return;

	IGUISkin* skin = Environment->getSkin();

	if (!skin)
		return;

	IGUIFont* font = skin->getFont(EGDF_MENU);
	if (font != LastFont)
	{
		if (LastFont)
			LastFont->drop();
		LastFont = font;
		if (LastFont)
			LastFont->grab();

		recalculateSize();
	}

	IGUISpriteBank* sprites = skin->getSpriteBank();

	core::rect<s32> rect = AbsoluteRect;
	core::rect<s32>* clip = 0;

	// draw frame

	skin->draw3DMenuPane(this, AbsoluteRect, clip);

	// loop through all menu items


	rect = AbsoluteRect;
	s32 y = AbsoluteRect.UpperLeftCorner.Y;

	for (s32 i=0; i<(s32)Items.size(); ++i)
	{
		if (Items[i].IsSeparator)
		{
			// draw separator

			rect = AbsoluteRect;
			rect.UpperLeftCorner.Y += Items[i].PosY + 3;
			rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1;
			rect.UpperLeftCorner.X += 5;
			rect.LowerRightCorner.X -= 5;
			skin->draw2DRectangle(this, skin->getColor(EGDC_3D_SHADOW), rect, clip);

			rect.LowerRightCorner.Y += 1;
			rect.UpperLeftCorner.Y += 1;
			skin->draw2DRectangle(this, skin->getColor(EGDC_3D_HIGH_LIGHT), rect, clip);

			y += 10;
		}
		else
		{
			rect = getRect(Items[i], AbsoluteRect);

			// draw highlighted


			if (i == HighLighted && Items[i].Enabled)
			{
				core::rect<s32> r = AbsoluteRect;
				r.LowerRightCorner.Y = rect.LowerRightCorner.Y;
				r.UpperLeftCorner.Y = rect.UpperLeftCorner.Y;
				r.LowerRightCorner.X -= 5;
				r.UpperLeftCorner.X += 5;
				skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), r, clip);
			}

			// draw text


			EGUI_DEFAULT_COLOR c = EGDC_BUTTON_TEXT;

			if (i == HighLighted)
				c = EGDC_HIGH_LIGHT_TEXT;

			if (!Items[i].Enabled)
				c = EGDC_GRAY_TEXT;

			if (font)
				font->draw(Items[i].Text.c_str(), rect,
					skin->getColor(c), false, true, clip);

			// draw submenu symbol

			if (Items[i].SubMenu && sprites)
			{
				core::rect<s32> r = rect;
				r.UpperLeftCorner.X = r.LowerRightCorner.X - 15;

				sprites->draw2DSprite(skin->getIcon(EGDI_CURSOR_RIGHT),
					r.getCenter(), clip, skin->getColor(c),
					(i == HighLighted) ? ChangeTime : 0,
					(i == HighLighted) ? os::Timer::getTime() : 0,
					(i == HighLighted), true);
			}

			// draw checked symbol

			if (Items[i].Checked && sprites)
			{
				core::rect<s32> r = rect;
				r.LowerRightCorner.X = r.UpperLeftCorner.X - 15;
				r.UpperLeftCorner.X = r.LowerRightCorner.X + 15;
				sprites->draw2DSprite(skin->getIcon(EGDI_CHECK_BOX_CHECKED),
					r.getCenter(), clip, skin->getColor(c),
					(i == HighLighted) ? ChangeTime : 0,
					(i == HighLighted) ? os::Timer::getTime() : 0,
					(i == HighLighted), true);
			}
		}
	}

	IGUIElement::draw();
}


void CGUIContextMenu::recalculateSize()
{
	IGUIFont* font = Environment->getSkin()->getFont(EGDF_MENU);

	if (!font)
		return;

	core::rect<s32> rect;
	rect.UpperLeftCorner = RelativeRect.UpperLeftCorner;
	u32 width = 100;
	u32 height = 3;

	u32 i;
	for (i=0; i<Items.size(); ++i)
	{
		if (Items[i].IsSeparator)
		{
			Items[i].Dim.Width = 100;
			Items[i].Dim.Height = 10;
		}
		else
		{
			Items[i].Dim = font->getDimension(Items[i].Text.c_str());
			Items[i].Dim.Width += 40;

			if (Items[i].Dim.Width > width)
				width = Items[i].Dim.Width;
		}

		Items[i].PosY = height;
		height += Items[i].Dim.Height;
	}

	height += 5;

	if (height < 10)
		height = 10;

	rect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + width;
	rect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + height;

	setRelativePosition(rect);

	// recalculate submenus

	for (i=0; i<Items.size(); ++i)
	{
		if (Items[i].SubMenu)
		{
			// move submenu

			const s32 w = Items[i].SubMenu->getAbsolutePosition().getWidth();
			const s32 h = Items[i].SubMenu->getAbsolutePosition().getHeight();

            core::rect<s32> subRect(width-5, Items[i].PosY, width+w-5, Items[i].PosY+h);

            // if it would be drawn beyond the right border, then add it to the left side

            gui::IGUIElement * root = Environment->getRootGUIElement();
            if ( root )
            {
                core::rect<s32> rectRoot( root->getAbsolutePosition() );
                if ( getAbsolutePosition().UpperLeftCorner.X+subRect.LowerRightCorner.X > rectRoot.LowerRightCorner.X )
                {
                    subRect.UpperLeftCorner.X = -w;
                    subRect.LowerRightCorner.X = 0;
                }
            }

			Items[i].SubMenu->setRelativePosition(subRect);
		}
	}
}


//! Returns the selected item in the menu

s32 CGUIContextMenu::getSelectedItem() const
{
	return HighLighted;
}


//! \return Returns a pointer to the submenu of an item.

IGUIContextMenu* CGUIContextMenu::getSubMenu(u32 idx) const
{
	if (idx >= Items.size())
		return 0;

	return Items[idx].SubMenu;
}


//! Returns command id of a menu item

s32 CGUIContextMenu::getItemCommandId(u32 idx) const
{
	if (idx >= Items.size())
		return -1;

	return Items[idx].CommandId;
}


//! Sets the command id of a menu item

void CGUIContextMenu::setItemCommandId(u32 idx, s32 id)
{
	if (idx >= Items.size())
		return;

	Items[idx].CommandId = id;
}


//! Writes attributes of the element.

void CGUIContextMenu::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
{
	IGUIElement::serializeAttributes(out,options);
	out->addPosition2d("Position", Pos);

	if (Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU )
	{
		const IGUIContextMenu* const ptr = (const IGUIContextMenu*)Parent;
		// find the position of this item in its parent's list

		u32 i;
		// VC6 needs the cast for this

		for (i=0; (i<ptr->getItemCount()) && (ptr->getSubMenu(i) != (const IGUIContextMenu*)this); ++i)
			; // do nothing


		out->addInt("ParentItem", i);
	}

	out->addInt("CloseHandling", (s32)CloseHandling);

	// write out the item list

	out->addInt("ItemCount", Items.size());

	core::stringc tmp;

	for (u32 i=0; i < Items.size(); ++i)
	{
		tmp = "IsSeparator"; tmp += i;
		out->addBool(tmp.c_str(), Items[i].IsSeparator);

		if (!Items[i].IsSeparator)
		{
			tmp = "Text"; tmp += i;
			out->addString(tmp.c_str(), Items[i].Text.c_str());
			tmp = "CommandID"; tmp += i;
			out->addInt(tmp.c_str(), Items[i].CommandId);
			tmp = "Enabled"; tmp += i;
			out->addBool(tmp.c_str(), Items[i].Enabled);
			tmp = "Checked"; tmp += i;
			out->addBool(tmp.c_str(), Items[i].Checked);
			tmp = "AutoChecking"; tmp += i;
			out->addBool(tmp.c_str(), Items[i].AutoChecking);
		}
	}
}


//! Reads attributes of the element

void CGUIContextMenu::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
{
	IGUIElement::deserializeAttributes(in,options);

	Pos = in->getAttributeAsPosition2d("Position");

	// link to this item's parent

	if (Parent && ( Parent->getType() == EGUIET_CONTEXT_MENU || Parent->getType() == EGUIET_MENU ) )
		((CGUIContextMenu*)Parent)->setSubMenu(in->getAttributeAsInt("ParentItem"),this);

	CloseHandling = (ECONTEXT_MENU_CLOSE)in->getAttributeAsInt("CloseHandling");

	removeAllItems();

	// read the item list

	const s32 count = in->getAttributeAsInt("ItemCount");

	for (s32 i=0; i<count; ++i)
	{
		core::stringc tmp;
		core::stringw txt;
		s32 commandid=-1;
		bool enabled=true;
		bool checked=false;
		bool autochecking=false;

		tmp = "IsSeparator"; tmp += i;
		if ( in->existsAttribute(tmp.c_str()) && in->getAttributeAsBool(tmp.c_str()) )
			addSeparator();
		else
		{
			tmp = "Text"; tmp += i;
			if ( in->existsAttribute(tmp.c_str()) )
				txt = in->getAttributeAsStringW(tmp.c_str());

			tmp = "CommandID"; tmp += i;
			if ( in->existsAttribute(tmp.c_str()) )
				commandid = in->getAttributeAsInt(tmp.c_str());

			tmp = "Enabled"; tmp += i;
			if ( in->existsAttribute(tmp.c_str()) )
				enabled = in->getAttributeAsBool(tmp.c_str());

			tmp = "Checked"; tmp += i;
			if ( in->existsAttribute(tmp.c_str()) )
				checked = in->getAttributeAsBool(tmp.c_str());

 			tmp = "AutoChecking"; tmp += i;
			if ( in->existsAttribute(tmp.c_str()) )
				autochecking = in->getAttributeAsBool(tmp.c_str());

 			addItem(core::stringw(txt.c_str()).c_str(), commandid, enabled, false, checked, autochecking);
		}
	}

	recalculateSize();
}


// because sometimes the element has no parent at click time

void CGUIContextMenu::setEventParent(IGUIElement *parent)
{
	EventParent = parent;

	for (u32 i=0; i<Items.size(); ++i)
		if (Items[i].SubMenu)
			Items[i].SubMenu->setEventParent(parent);
}


bool CGUIContextMenu::hasOpenSubMenu() const
{
	for (u32 i=0; i<Items.size(); ++i)
		if (Items[i].SubMenu && Items[i].SubMenu->isVisible())
			return true;

	return false;
}


void CGUIContextMenu::closeAllSubMenus()
{
	for (u32 i=0; i<Items.size(); ++i)
		if (Items[i].SubMenu)
			Items[i].SubMenu->setVisible(false);

	//HighLighted = -1;

}


} // end namespace

} // end namespace


#endif // _IRR_COMPILE_WITH_GUI_


Options Liens officiels Caractéristiques Statistiques Communauté
Corrections
irrlicht
irrklang
irredit
irrxml
xhtml 1.0
css 2.1
Propulsé par FluxBB
Traduit par FluxBB.fr
Analysé par
880 membres
1424 sujets
11113 messages
Dernier membre inscrit: mandrifidy
20 invités en ligne
Aucun membre connecté
RSS Feed