Index: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp
===================================================================
--- ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 8656)
+++ ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 8657)
@@ -1,651 +1,649 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "JSInterface_IGUIObject.h"
#include "JSInterface_GUITypes.h"
#include "gui/IGUIObject.h"
#include "gui/CGUI.h"
#include "gui/IGUIScrollBar.h"
#include "gui/CList.h"
#include "gui/GUIManager.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
JSClass JSI_IGUIObject::JSI_class = {
"GUIObject", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JSI_IGUIObject::getProperty, JSI_IGUIObject::setProperty,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, JSI_IGUIObject::construct
};
JSPropertySpec JSI_IGUIObject::JSI_props[] =
{
{ 0 }
};
JSFunctionSpec JSI_IGUIObject::JSI_methods[] =
{
{ "toString", JSI_IGUIObject::toString, 0, 0 },
{ "focus", JSI_IGUIObject::focus, 0, 0 },
{ "blur", JSI_IGUIObject::blur, 0, 0 },
{ 0 }
};
JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp)
{
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
jsval idval;
if (!JS_IdToValue(cx, id, &idval))
return JS_FALSE;
std::string propName;
if (!ScriptInterface::FromJSVal(cx, idval, propName))
return JS_FALSE;
// Skip some things which are known to be functions rather than properties.
// ("constructor" *must* be here, else it'll try to GetSettingType before
// the private IGUIObject* has been set (and thus crash). The others are
// partly for efficiency, and also to allow correct reporting of attempts to
// access nonexistent properties.)
if (propName == "constructor" ||
propName == "prototype" ||
propName == "toString" ||
propName == "focus" ||
propName == "blur"
)
return JS_TRUE;
// Use onWhatever to access event handlers
if (propName.substr(0, 2) == "on")
{
CStr eventName (CStr(propName.substr(2)).LowerCase());
std::map::iterator it = e->m_ScriptHandlers.find(eventName);
if (it == e->m_ScriptHandlers.end())
*vp = JSVAL_NULL;
else
*vp = OBJECT_TO_JSVAL(*(it->second));
return JS_TRUE;
}
// Handle the "parent" property specially
if (propName == "parent")
{
IGUIObject* parent = e->GetParent();
if (parent)
{
// If the object isn't parentless, return a new object
- JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL);
- JS_SetPrivate(cx, entity, parent);
- *vp = OBJECT_TO_JSVAL(entity);
+ *vp = OBJECT_TO_JSVAL(parent->GetJSObject());
}
else
{
// Return null if there's no parent
*vp = JSVAL_NULL;
}
return JS_TRUE;
}
// Also handle "name" specially
else if (propName == "name")
{
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName()));
return JS_TRUE;
}
// Handle all other properties
else
{
// Retrieve the setting's type (and make sure it actually exists)
EGUISettingType Type;
if (e->GetSettingType(propName, Type) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid GUIObject property '%s'", propName.c_str());
return JS_FALSE;
}
// (All the cases are in {...} to avoid scoping problems)
switch (Type)
{
case GUIST_bool:
{
bool value;
GUI::GetSetting(e, propName, value);
*vp = value ? JSVAL_TRUE : JSVAL_FALSE;
break;
}
case GUIST_int:
{
int value;
GUI::GetSetting(e, propName, value);
*vp = INT_TO_JSVAL(value);
break;
}
case GUIST_float:
{
float value;
GUI::GetSetting(e, propName, value);
// Create a garbage-collectable double
return JS_NewNumberValue(cx, value, vp);
}
case GUIST_CColor:
{
CColor colour;
GUI::GetSetting(e, propName, colour);
JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
jsval c;
// Attempt to minimise ugliness through macrosity
#define P(x) if (!JS_NewNumberValue(cx, colour.x, &c)) return JS_FALSE; JS_SetProperty(cx, obj, #x, &c)
P(r);
P(g);
P(b);
P(a);
#undef P
break;
}
case GUIST_CClientArea:
{
CClientArea area;
GUI::GetSetting(e, propName, area);
JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
try
{
#define P(x, y, z) g_ScriptingHost.SetObjectProperty_Double(obj, #z, area.x.y)
P(pixel, left, left);
P(pixel, top, top);
P(pixel, right, right);
P(pixel, bottom, bottom);
P(percent, left, rleft);
P(percent, top, rtop);
P(percent, right, rright);
P(percent, bottom, rbottom);
#undef P
}
catch (PSERROR_Scripting_ConversionFailed)
{
debug_warn(L"Error creating size object!");
break;
}
break;
}
case GUIST_CGUIString:
{
CGUIString value;
GUI::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value.GetRawString());
break;
}
case GUIST_CStr:
{
CStr value;
GUI::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value);
break;
}
case GUIST_CStrW:
{
CStrW value;
GUI::GetSetting(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value);
break;
}
case GUIST_CGUISpriteInstance:
{
CGUISpriteInstance *value;
GUI::GetSettingPointer(e, propName, value);
*vp = ScriptInterface::ToJSVal(cx, value->GetName());
break;
}
case GUIST_EAlign:
{
EAlign value;
GUI::GetSetting(e, propName, value);
CStr word;
switch (value)
{
case EAlign_Left: word = "left"; break;
case EAlign_Right: word = "right"; break;
case EAlign_Center: word = "center"; break;
default: debug_warn(L"Invalid EAlign!"); word = "error"; break;
}
*vp = ScriptInterface::ToJSVal(cx, word);
break;
}
case GUIST_EVAlign:
{
EVAlign value;
GUI::GetSetting(e, propName, value);
CStr word;
switch (value)
{
case EVAlign_Top: word = "top"; break;
case EVAlign_Bottom: word = "bottom"; break;
case EVAlign_Center: word = "center"; break;
default: debug_warn(L"Invalid EVAlign!"); word = "error"; break;
}
*vp = ScriptInterface::ToJSVal(cx, word);
break;
}
case GUIST_CGUIList:
{
CGUIList value;
GUI::GetSetting(e, propName, value);
JSObject *obj = JS_NewArrayObject(cx, 0, NULL);
*vp = OBJECT_TO_JSVAL(obj); // root it
for (size_t i = 0; i < value.m_Items.size(); ++i)
{
jsval val = ScriptInterface::ToJSVal(cx, value.m_Items[i].GetRawString());
JS_SetElement(cx, obj, (jsint)i, &val);
}
break;
}
default:
JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
debug_assert(0);
return JS_FALSE;
}
return JS_TRUE;
}
}
JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp)
{
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
jsval idval;
if (!JS_IdToValue(cx, id, &idval))
return JS_FALSE;
std::string propName;
if (!ScriptInterface::FromJSVal(cx, idval, propName))
return JS_FALSE;
if (propName == "name")
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
e->SetName(value);
return JS_TRUE;
}
// Use onWhatever to set event handlers
if (propName.substr(0, 2) == "on")
{
if (!JSVAL_IS_OBJECT(*vp) || !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(*vp)))
{
JS_ReportError(cx, "on- event-handlers must be functions");
return JS_FALSE;
}
CStr eventName (CStr(propName.substr(2)).LowerCase());
e->SetScriptHandler(eventName, JSVAL_TO_OBJECT(*vp));
return JS_TRUE;
}
// Retrieve the setting's type (and make sure it actually exists)
EGUISettingType Type;
if (e->GetSettingType(propName, Type) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid setting '%s'", propName.c_str());
return JS_TRUE;
}
switch (Type)
{
case GUIST_CStr:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
GUI::SetSetting(e, propName, value);
break;
}
case GUIST_CStrW:
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
GUI::SetSetting(e, propName, value);
break;
}
case GUIST_CGUISpriteInstance:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
GUI::SetSetting(e, propName, CGUISpriteInstance(value));
break;
}
case GUIST_CGUIString:
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
CGUIString str;
str.SetValue(value);
GUI::SetSetting(e, propName, str);
break;
}
case GUIST_EAlign:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
EAlign a;
if (value == "left") a = EAlign_Left;
else if (value == "right") a = EAlign_Right;
else if (value == "center" || value == "centre") a = EAlign_Center;
else
{
JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')");
return JS_FALSE;
}
GUI::SetSetting(e, propName, a);
break;
}
case GUIST_EVAlign:
{
std::string value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
EVAlign a;
if (value == "top") a = EVAlign_Top;
else if (value == "bottom") a = EVAlign_Bottom;
else if (value == "center" || value == "centre") a = EVAlign_Center;
else
{
JS_ReportError(cx, "Invalid alignment (should be 'top', 'bottom' or 'center')");
return JS_FALSE;
}
GUI::SetSetting(e, propName, a);
break;
}
case GUIST_int:
{
int32 value;
if (JS_ValueToInt32(cx, *vp, &value) == JS_TRUE)
GUI::SetSetting(e, propName, value);
else
{
JS_ReportError(cx, "Cannot convert value to int");
return JS_FALSE;
}
break;
}
case GUIST_float:
{
jsdouble value;
if (JS_ValueToNumber(cx, *vp, &value) == JS_TRUE)
GUI::SetSetting(e, propName, (float)value);
else
{
JS_ReportError(cx, "Cannot convert value to float");
return JS_FALSE;
}
break;
}
case GUIST_bool:
{
JSBool value;
if (JS_ValueToBoolean(cx, *vp, &value) == JS_TRUE)
GUI::SetSetting(e, propName, value||0); // ||0 to avoid int-to-bool compiler warnings
else
{
JS_ReportError(cx, "Cannot convert value to bool");
return JS_FALSE;
}
break;
}
case GUIST_CClientArea:
{
if (JSVAL_IS_STRING(*vp))
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
if (e->SetSetting(propName, value) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str());
return JS_FALSE;
}
}
else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUISize::JSI_class, NULL))
{
CClientArea area;
GUI::GetSetting(e, propName, area);
JSObject* obj = JSVAL_TO_OBJECT(*vp);
#define P(x, y, z) area.x.y = (float)g_ScriptingHost.GetObjectProperty_Double(obj, #z)
P(pixel, left, left);
P(pixel, top, top);
P(pixel, right, right);
P(pixel, bottom, bottom);
P(percent, left, rleft);
P(percent, top, rtop);
P(percent, right, rright);
P(percent, bottom, rbottom);
#undef P
GUI::SetSetting(e, propName, area);
}
else
{
JS_ReportError(cx, "Size only accepts strings or GUISize objects");
return JS_FALSE;
}
break;
}
case GUIST_CColor:
{
if (JSVAL_IS_STRING(*vp))
{
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, *vp, value))
return JS_FALSE;
if (e->SetSetting(propName, value) != PSRETURN_OK)
{
JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str());
return JS_FALSE;
}
}
else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUIColor::JSI_class, NULL))
{
CColor colour;
JSObject* obj = JSVAL_TO_OBJECT(*vp);
jsval t; double s;
#define PROP(x) JS_GetProperty(cx, obj, #x, &t); \
JS_ValueToNumber(cx, t, &s); \
colour.x = (float)s
PROP(r); PROP(g); PROP(b); PROP(a);
#undef PROP
GUI::SetSetting(e, propName, colour);
}
else
{
JS_ReportError(cx, "Color only accepts strings or GUIColor objects");
return JS_FALSE;
}
break;
}
case GUIST_CGUIList:
{
JSObject* obj = JSVAL_TO_OBJECT(*vp);
jsuint length;
if (JSVAL_IS_OBJECT(*vp) && JS_GetArrayLength(cx, obj, &length) == JS_TRUE)
{
CGUIList list;
for (int i=0; i<(int)length; ++i)
{
jsval element;
if (! JS_GetElement(cx, obj, i, &element))
{
JS_ReportError(cx, "Failed to get list element");
return JS_FALSE;
}
std::wstring value;
if (!ScriptInterface::FromJSVal(cx, element, value))
return JS_FALSE;
CGUIString str;
str.SetValue(value);
list.m_Items.push_back(str);
}
GUI::SetSetting(e, propName, list);
}
else
{
JS_ReportError(cx, "List only accepts a GUIList object");
return JS_FALSE;
}
break;
}
// TODO Gee: (2004-09-01) EAlign and EVAlign too.
default:
JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str());
break;
}
return JS_TRUE;
}
JSBool JSI_IGUIObject::construct(JSContext* cx, uintN argc, jsval* vp)
{
if (argc == 0)
{
JS_ReportError(cx, "GUIObject has no default constructor");
return JS_FALSE;
}
JSObject* obj = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL);
// Store the IGUIObject in the JS object's 'private' area
IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(JS_ARGV(cx, vp)[0]);
JS_SetPrivate(cx, obj, guiObject);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj));
return JS_TRUE;
}
void JSI_IGUIObject::init()
{
g_ScriptingHost.DefineCustomObjectType(&JSI_class, construct, 1, JSI_props, JSI_methods, NULL, NULL);
}
JSBool JSI_IGUIObject::toString(JSContext* cx, uintN argc, jsval* vp)
{
UNUSED2(argc);
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
char buffer[256];
snprintf(buffer, 256, "[GUIObject: %s]", e->GetName().c_str());
buffer[255] = 0;
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)));
return JS_TRUE;
}
JSBool JSI_IGUIObject::focus(JSContext* cx, uintN argc, jsval* vp)
{
UNUSED2(argc);
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
e->GetGUI()->SetFocusedObject(e);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
JSBool JSI_IGUIObject::blur(JSContext* cx, uintN argc, jsval* vp)
{
UNUSED2(argc);
IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL);
if (!e)
return JS_FALSE;
e->GetGUI()->SetFocusedObject(NULL);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
Index: ps/trunk/source/gui/CGUI.cpp
===================================================================
--- ps/trunk/source/gui/CGUI.cpp (revision 8656)
+++ ps/trunk/source/gui/CGUI.cpp (revision 8657)
@@ -1,1877 +1,1941 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
CGUI
*/
#include "precompiled.h"
#include
#include
#include "lib/res/graphics/unifont.h"
#include "GUI.h"
// Types - when including them into the engine.
#include "CButton.h"
#include "CImage.h"
#include "CText.h"
#include "CCheckBox.h"
#include "CRadioButton.h"
#include "CInput.h"
#include "CList.h"
#include "CDropDown.h"
#include "CProgressBar.h"
#include "CTooltip.h"
#include "MiniMap.h"
#include "scripting/JSInterface_GUITypes.h"
#include "ps/XML/Xeromyces.h"
#include "ps/Font.h"
#include "ps/Pyrogenesis.h"
#include "lib/input.h"
#include "lib/bits.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "lib/sysdep/sysdep.h"
// TODO Gee: Whatever include CRect/CPos/CSize
#include "ps/Overlay.h"
#include "ps/Profile.h"
#include "scripting/ScriptingHost.h"
#include "scriptinterface/ScriptInterface.h"
#include "ps/Hotkey.h"
#include "ps/Globals.h"
#include "ps/Filesystem.h"
const double SELECT_DBLCLICK_RATE = 0.5;
#include "ps/CLogger.h"
#define LOG_CATEGORY L"gui"
void CGUI::ScriptingInit()
{
JSI_IGUIObject::init();
JSI_GUITypes::init();
}
InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
InReaction ret = IN_PASS;
if (ev->ev.type == SDL_HOTKEYDOWN)
{
const char* hotkey = static_cast(ev->ev.user.data1);
std::map >::iterator it = m_HotkeyObjects.find(hotkey);
if (it != m_HotkeyObjects.end())
{
for (size_t i = 0; i < it->second.size(); ++i)
{
it->second[i]->HandleMessage(SGUIMessage(GUIM_PRESSED));
it->second[i]->ScriptEvent("press");
}
}
}
else if (ev->ev.type == SDL_MOUSEMOTION)
{
// Yes the mouse position is stored as float to avoid
// constant conversions when operating in a
// float-based environment.
m_MousePos = CPos((float)ev->ev.motion.x, (float)ev->ev.motion.y);
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::HandleMessage,
SGUIMessage(GUIM_MOUSE_MOTION));
}
// Update m_MouseButtons. (BUTTONUP is handled later.)
else if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
{
switch (ev->ev.button.button)
{
case SDL_BUTTON_LEFT:
case SDL_BUTTON_RIGHT:
case SDL_BUTTON_MIDDLE:
m_MouseButtons |= Bit(ev->ev.button.button);
break;
default:
break;
}
}
// Update m_MousePos (for delayed mouse button events)
CPos oldMousePos = m_MousePos;
if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
{
m_MousePos = CPos((float)ev->ev.button.x, (float)ev->ev.button.y);
}
// Only one object can be hovered
IGUIObject *pNearest = NULL;
// TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress!
try
{
PROFILE( "mouse events" );
// TODO Gee: Optimizations needed!
// these two recursive function are quite overhead heavy.
// pNearest will after this point at the hovered object, possibly NULL
pNearest = FindObjectUnderMouse();
// Is placed in the UpdateMouseOver function
//if (ev->ev.type == SDL_MOUSEMOTION && pNearest)
// pNearest->ScriptEvent("mousemove");
// Now we'll call UpdateMouseOver on *all* objects,
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::UpdateMouseOver,
pNearest);
if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
{
switch (ev->ev.button.button)
{
case SDL_BUTTON_LEFT:
// Focus the clicked object (or focus none if nothing clicked on)
SetFocusedObject(pNearest);
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_PRESS_LEFT));
pNearest->ScriptEvent("mouseleftpress");
// Block event, so things on the map (behind the GUI) won't be pressed
ret = IN_HANDLED;
}
break;
case SDL_BUTTON_RIGHT:
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_PRESS_RIGHT));
pNearest->ScriptEvent("mouserightpress");
ret = IN_HANDLED;
}
break;
case SDL_BUTTON_WHEELDOWN: // wheel down
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_DOWN));
pNearest->ScriptEvent("mousewheeldown");
ret = IN_HANDLED;
}
break;
case SDL_BUTTON_WHEELUP: // wheel up
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_UP));
pNearest->ScriptEvent("mousewheelup");
ret = IN_HANDLED;
}
break;
default:
break;
}
}
else
if (ev->ev.type == SDL_MOUSEBUTTONUP)
{
switch (ev->ev.button.button)
{
case SDL_BUTTON_LEFT:
if (pNearest)
{
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT];
pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time();
//Double click?
if (timeElapsed < SELECT_DBLCLICK_RATE)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_DBLCLICK_LEFT));
pNearest->ScriptEvent("mouseleftdoubleclick");
}
else
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_RELEASE_LEFT));
pNearest->ScriptEvent("mouseleftrelease");
}
ret = IN_HANDLED;
}
break;
case SDL_BUTTON_RIGHT:
if (pNearest)
{
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT];
pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time();
//Double click?
if (timeElapsed < SELECT_DBLCLICK_RATE)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_DBLCLICK_RIGHT));
//pNearest->ScriptEvent("mouserightdoubleclick");
}
else
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_RELEASE_RIGHT));
//pNearest->ScriptEvent("mouserightrelease");
}
ret = IN_HANDLED;
}
break;
}
// Reset all states on all visible objects
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject,
&IGUIObject::ResetStates);
// It will have reset the mouse over of the current hovered, so we'll
// have to restore that
if (pNearest)
pNearest->m_MouseHovering = true;
}
}
catch (PSERROR_GUI& e)
{
UNUSED2(e);
debug_warn(L"CGUI::HandleEvent error");
// TODO Gee: Handle
}
// JW: what's the difference between mPress and mDown? what's the code below responsible for?
/*
// Generally if just mouse is clicked
if (m_pInput->mDown(NEMM_BUTTON1) && pNearest)
{
pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT);
}
*/
// BUTTONUP's effect on m_MouseButtons is handled after
// everything else, so that e.g. 'press' handlers (activated
// on button up) see which mouse button had been pressed.
if (ev->ev.type == SDL_MOUSEBUTTONUP)
{
switch (ev->ev.button.button)
{
case SDL_BUTTON_LEFT:
case SDL_BUTTON_RIGHT:
case SDL_BUTTON_MIDDLE:
m_MouseButtons &= ~Bit(ev->ev.button.button);
break;
default:
break;
}
}
// Restore m_MousePos (for delayed mouse button events)
if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP)
{
m_MousePos = oldMousePos;
}
// Handle keys for input boxes
if (GetFocusedObject())
{
if (
(ev->ev.type == SDL_KEYDOWN &&
ev->ev.key.keysym.sym != SDLK_ESCAPE &&
!g_keys[SDLK_LCTRL] && !g_keys[SDLK_RCTRL] &&
!g_keys[SDLK_LALT] && !g_keys[SDLK_RALT])
|| ev->ev.type == SDL_HOTKEYDOWN
)
{
ret = GetFocusedObject()->ManuallyHandleEvent(ev);
}
// else will return IN_PASS because we never used the button.
}
return ret;
}
void CGUI::TickObjects()
{
CStr action = "tick";
GUI::RecurseObject(0, m_BaseObject,
&IGUIObject::ScriptEvent, action);
// Also update tooltips:
m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, this);
}
void CGUI::SendEventToAll(const CStr& EventName)
{
// janwas 2006-03-03: spoke with Ykkrosh about EventName case.
// when registering, case is converted to lower - this avoids surprise
// if someone were to get the case wrong and then not notice their
// handler is never called. however, until now, the other end
// (sending events here) wasn't converting to lower case,
// leading to a similar problem.
// now fixed; case is irrelevant since all are converted to lower.
GUI::RecurseObject(0, m_BaseObject,
&IGUIObject::ScriptEvent, EventName.LowerCase());
}
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
+
+// To isolate the vars declared by each GUI page, we need to create
+// a pseudo-global object to declare them in. In particular, it must
+// have no parent object, so it must be declared with JS_NewGlobalObject.
+// But GUI scripts should have access to the real global's properties
+// (Array, undefined, getGUIObjectByName, etc), so we add a custom resolver
+// that defers to the real global object when necessary.
+
+static JSBool GetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, jsval* vp)
+{
+ return JS_GetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp);
+}
+
+static JSBool SetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, jsval* vp)
+{
+ return JS_SetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp);
+}
+
+static JSBool ResolveGlobalProperty(JSContext* cx, JSObject* obj, jsid id, uintN flags, JSObject** objp)
+{
+ // This gets called when the property can't be resolved in the page_global object.
+
+ // Warning: The interaction between this resolution stuff and the JITs appears
+ // to be quite fragile, and I don't quite understand what the constraints are.
+ // If changing it, be careful to test with each JIT to make sure it works.
+ // (This code is somewhat based on GPSEE's module system.)
+
+ // Declarations and assignments shouldn't affect the real global
+ if (flags & (JSRESOLVE_DECLARING | JSRESOLVE_ASSIGNING))
+ {
+ // Can't be resolved - return NULL
+ *objp = NULL;
+ return JS_TRUE;
+ }
+
+ // Check whether the real global object defined this property
+ uintN attrs;
+ JSBool found;
+ if (!JS_GetPropertyAttrsGetterAndSetterById(cx, g_ScriptingHost.GetGlobalObject(), id, &attrs, &found, NULL, NULL))
+ return JS_FALSE;
+
+ if (!found)
+ {
+ // Not found on real global, so can't be resolved - return NULL
+ *objp = NULL;
+ return JS_TRUE;
+ }
+
+ // Retrieve the property value from the global
+ jsval v;
+ if (!JS_GetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, &v))
+ return JS_FALSE;
+
+ // Add the global's property value onto this object, with getter/setter that will
+ // update the global's copy instead of this copy, and then return this object
+ if (!JS_DefinePropertyById(cx, obj, id, v, GetGlobalProperty, SetGlobalProperty, attrs))
+ return JS_FALSE;
+
+ *objp = obj;
+ return JS_TRUE;
+}
+
+static JSClass page_global_class = {
+ "page_global", JSCLASS_GLOBAL_FLAGS | JSCLASS_NEW_RESOLVE,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, (JSResolveOp)ResolveGlobalProperty, JS_ConvertStub, JS_FinalizeStub,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
CGUI::CGUI() : m_MouseButtons(0), m_FocusedObject(NULL), m_InternalNameNumber(0)
{
m_BaseObject = new CGUIDummyObject;
m_BaseObject->SetGUI(this);
- // Construct the root object for all GUI JavaScript things.
- // (We need an object with no parent, so functions defined by scripts get
- // put onto this object and not its parent. In the current version of SpiderMonkey
- // that means it must be a global object. Then we adjust its prototype so it
- // can still read standard properties from the context's standard global object.)
- m_ScriptObject = JS_NewGlobalObject(g_ScriptingHost.getContext(), g_ScriptingHost.GetScriptInterface().GetGlobalClass());
+ // Construct the root object for all GUI JavaScript things
+ m_ScriptObject = JS_NewGlobalObject(g_ScriptingHost.getContext(), &page_global_class);
JS_AddObjectRoot(g_ScriptingHost.getContext(), &m_ScriptObject);
-
- JS_SetPrototype(g_ScriptingHost.getContext(), m_ScriptObject, g_ScriptingHost.GetGlobalObject());
}
CGUI::~CGUI()
{
Destroy();
if (m_BaseObject)
delete m_BaseObject;
if (m_ScriptObject)
{
// Let it be garbage-collected
JS_RemoveObjectRoot(g_ScriptingHost.getContext(), &m_ScriptObject);
}
}
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
IGUIObject *CGUI::ConstructObject(const CStr& str)
{
if (m_ObjectTypes.count(str) > 0)
return (*m_ObjectTypes[str])();
else
{
// Error reporting will be handled with the NULL return.
return NULL;
}
}
void CGUI::Initialize()
{
// Add base types!
// You can also add types outside the GUI to extend the flexibility of the GUI.
// Pyrogenesis though will have all the object types inserted from here.
AddObjectType("empty", &CGUIDummyObject::ConstructObject);
AddObjectType("button", &CButton::ConstructObject);
AddObjectType("image", &CImage::ConstructObject);
AddObjectType("text", &CText::ConstructObject);
AddObjectType("checkbox", &CCheckBox::ConstructObject);
AddObjectType("radiobutton", &CRadioButton::ConstructObject);
AddObjectType("progressbar", &CProgressBar::ConstructObject);
AddObjectType("minimap", &CMiniMap::ConstructObject);
AddObjectType("input", &CInput::ConstructObject);
AddObjectType("list", &CList::ConstructObject);
AddObjectType("dropdown", &CDropDown::ConstructObject);
}
void CGUI::Draw()
{
// Clear the depth buffer, so the GUI is
// drawn on top of everything else
glClear(GL_DEPTH_BUFFER_BIT);
glPushMatrix();
guiLoadIdentity();
try
{
// Recurse IGUIObject::Draw() with restriction: hidden
// meaning all hidden objects won't call Draw (nor will it recurse its children)
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::Draw);
}
catch (PSERROR_GUI& e)
{
LOGERROR(L"GUI draw error: %hs", e.what());
}
glPopMatrix();
}
void CGUI::DrawSprite(const CGUISpriteInstance& Sprite,
int CellID,
const float& Z,
const CRect& Rect,
const CRect& UNUSED(Clipping))
{
// If the sprite doesn't exist (name == ""), don't bother drawing anything
if (Sprite.IsEmpty())
return;
// TODO: Clipping?
glPushMatrix();
glTranslatef(0.0f, 0.0f, Z);
Sprite.Draw(Rect, CellID, m_Sprites);
glPopMatrix();
}
void CGUI::Destroy()
{
// We can use the map to delete all
// now we don't want to cancel all if one Destroy fails
map_pObjects::iterator it;
for (it = m_pAllObjects.begin(); it != m_pAllObjects.end(); ++it)
{
try
{
it->second->Destroy();
}
catch (PSERROR_GUI& e)
{
UNUSED2(e);
debug_warn(L"CGUI::Destroy error");
// TODO Gee: Handle
}
delete it->second;
}
for (std::map::iterator it2 = m_Sprites.begin(); it2 != m_Sprites.end(); ++it2)
for (std::vector::iterator it3 = it2->second.m_Images.begin(); it3 != it2->second.m_Images.end(); ++it3)
delete it3->m_Effects;
// Clear all
m_pAllObjects.clear();
m_Sprites.clear();
m_Icons.clear();
}
void CGUI::UpdateResolution()
{
// Update ALL cached
GUI<>::RecurseObject(0, m_BaseObject, &IGUIObject::UpdateCachedSize );
}
void CGUI::AddObject(IGUIObject* pObject)
{
try
{
// Add CGUI pointer
GUI::RecurseObject(0, pObject, &IGUIObject::SetGUI, this);
// Add child to base object
m_BaseObject->AddChild(pObject); // can throw
// Cache tree
GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize);
// Loaded
GUI::RecurseObject(0, pObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_LOAD));
}
catch (PSERROR_GUI&)
{
throw;
}
}
void CGUI::UpdateObjects()
{
// We'll fill a temporary map until we know everything
// succeeded
map_pObjects AllObjects;
try
{
// Fill freshly
GUI< map_pObjects >::RecurseObject(0, m_BaseObject, &IGUIObject::AddToPointersMap, AllObjects );
}
catch (PSERROR_GUI&)
{
// Throw the same error
throw;
}
// Else actually update the real one
m_pAllObjects.swap(AllObjects);
}
bool CGUI::ObjectExists(const CStr& Name) const
{
return m_pAllObjects.count(Name) != 0;
}
IGUIObject* CGUI::FindObjectByName(const CStr& Name) const
{
map_pObjects::const_iterator it = m_pAllObjects.find(Name);
if (it == m_pAllObjects.end())
return NULL;
else
return it->second;
}
IGUIObject* CGUI::FindObjectUnderMouse() const
{
IGUIObject* pNearest = NULL;
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::ChooseMouseOverAndClosest,
pNearest);
return pNearest;
}
void CGUI::SetFocusedObject(IGUIObject* pObject)
{
if (pObject == m_FocusedObject)
return;
if (m_FocusedObject)
m_FocusedObject->HandleMessage(SGUIMessage(GUIM_LOST_FOCUS));
m_FocusedObject = pObject;
if (m_FocusedObject)
m_FocusedObject->HandleMessage(SGUIMessage(GUIM_GOT_FOCUS));
}
// private struct used only in GenerateText(...)
struct SGenerateTextImage
{
float m_YFrom, // The image's starting location in Y
m_YTo, // The image's end location in Y
m_Indentation; // The image width in other words
// Some help functions
// TODO Gee: CRect => CPoint ?
void SetupSpriteCall(const bool Left, SGUIText::SSpriteCall &SpriteCall,
const float width, const float y,
const CSize &Size, const CStr& TextureName,
const float BufferZone, const int CellID)
{
// TODO Gee: Temp hardcoded values
SpriteCall.m_Area.top = y+BufferZone;
SpriteCall.m_Area.bottom = y+BufferZone + Size.cy;
if (Left)
{
SpriteCall.m_Area.left = BufferZone;
SpriteCall.m_Area.right = Size.cx+BufferZone;
}
else
{
SpriteCall.m_Area.left = width-BufferZone - Size.cx;
SpriteCall.m_Area.right = width-BufferZone;
}
SpriteCall.m_CellID = CellID;
SpriteCall.m_Sprite = TextureName;
m_YFrom = SpriteCall.m_Area.top-BufferZone;
m_YTo = SpriteCall.m_Area.bottom+BufferZone;
m_Indentation = Size.cx+BufferZone*2;
}
};
SGUIText CGUI::GenerateText(const CGUIString &string,
const CStr& Font, const float &Width, const float &BufferZone,
const IGUIObject *pObject)
{
SGUIText Text; // object we're generating
if (string.m_Words.size() == 0)
return Text;
float x=BufferZone, y=BufferZone; // drawing pointer
int from=0;
bool done=false;
bool FirstLine = true; // Necessary because text in the first line is shorter
// (it doesn't count the line spacing)
// Images on the left or the right side.
std::vector Images[2];
int pos_last_img=-1; // Position in the string where last img (either left or right) were encountered.
// in order to avoid duplicate processing.
// Easier to read.
bool WordWrapping = (Width != 0);
// Go through string word by word
for (int i=0; i<(int)string.m_Words.size()-1 && !done; ++i)
{
// Pre-process each line one time, so we know which floating images
// will be added for that line.
// Generated stuff is stored in Feedback.
CGUIString::SFeedback Feedback;
// Preliminary line_height, used for word-wrapping with floating images.
float prelim_line_height=0.f;
// Width and height of all text calls generated.
string.GenerateTextCall(Feedback, Font,
string.m_Words[i], string.m_Words[i+1],
FirstLine);
// Loop through our images queues, to see if images has been added.
// Check if this has already been processed.
// Also, floating images are only applicable if Word-Wrapping is on
if (WordWrapping && i > pos_last_img)
{
// Loop left/right
for (int j=0; j<2; ++j)
{
for (std::vector::const_iterator it = Feedback.m_Images[j].begin();
it != Feedback.m_Images[j].end();
++it)
{
SGUIText::SSpriteCall SpriteCall;
SGenerateTextImage Image;
// Y is if no other floating images is above, y. Else it is placed
// after the last image, like a stack downwards.
float _y;
if (Images[j].size() > 0)
_y = std::max(y, Images[j].back().m_YTo);
else
_y = y;
// Get Size from Icon database
SGUIIcon icon = GetIcon(*it);
CSize size = icon.m_Size;
Image.SetupSpriteCall((j==CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, icon.m_SpriteName, BufferZone, icon.m_CellID);
// Check if image is the lowest thing.
Text.m_Size.cy = std::max(Text.m_Size.cy, Image.m_YTo);
Images[j].push_back(Image);
Text.m_SpriteCalls.push_back(SpriteCall);
}
}
}
pos_last_img = std::max(pos_last_img, i);
x += Feedback.m_Size.cx;
prelim_line_height = std::max(prelim_line_height, Feedback.m_Size.cy);
// If Width is 0, then there's no word-wrapping, disable NewLine.
if ((WordWrapping && (x > Width-BufferZone || Feedback.m_NewLine)) || i == (int)string.m_Words.size()-2)
{
// Change 'from' to 'i', but first keep a copy of its value.
int temp_from = from;
from = i;
static const int From=0, To=1;
//int width_from=0, width_to=width;
float width_range[2];
width_range[From] = BufferZone;
width_range[To] = Width - BufferZone;
// Floating images are only applicable if word-wrapping is enabled.
if (WordWrapping)
{
// Decide width of the line. We need to iterate our floating images.
// this won't be exact because we're assuming the line_height
// will be as our preliminary calculation said. But that may change,
// although we'd have to add a couple of more loops to try straightening
// this problem out, and it is very unlikely to happen noticeably if one
// structures his text in a stylistically pure fashion. Even if not, it
// is still quite unlikely it will happen.
// Loop through left and right side, from and to.
for (int j=0; j<2; ++j)
{
for (std::vector::const_iterator it = Images[j].begin();
it != Images[j].end();
++it)
{
// We're working with two intervals here, the image's and the line height's.
// let's find the union of these two.
float union_from, union_to;
union_from = std::max(y, it->m_YFrom);
union_to = std::min(y+prelim_line_height, it->m_YTo);
// The union is not empty
if (union_to > union_from)
{
if (j == From)
width_range[From] = std::max(width_range[From], it->m_Indentation);
else
width_range[To] = std::min(width_range[To], Width - it->m_Indentation);
}
}
}
}
// Reset X for the next loop
x = width_range[From];
// Now we'll do another loop to figure out the height of
// the line (the height of the largest character). This
// couldn't be determined in the first loop (main loop)
// because it didn't regard images, so we don't know
// if all characters processed, will actually be involved
// in that line.
float line_height=0.f;
for (int j=temp_from; j<=i; ++j)
{
// We don't want to use Feedback now, so we'll have to use
// another.
CGUIString::SFeedback Feedback2;
// Don't attach object, it'll suppress the errors
// we want them to be reported in the final GenerateTextCall()
// so that we don't get duplicates.
string.GenerateTextCall(Feedback2, Font,
string.m_Words[j], string.m_Words[j+1],
FirstLine);
// Append X value.
x += Feedback2.m_Size.cx;
if (WordWrapping && x > width_range[To] && j!=temp_from && !Feedback2.m_NewLine)
break;
// Let line_height be the maximum m_Height we encounter.
line_height = std::max(line_height, Feedback2.m_Size.cy);
if (WordWrapping && Feedback2.m_NewLine)
break;
}
// Reset x once more
x = width_range[From];
// Move down, because font drawing starts from the baseline
y += line_height;
// Do the real processing now
for (int j=temp_from; j<=i; ++j)
{
// We don't want to use Feedback now, so we'll have to use
// another one.
CGUIString::SFeedback Feedback2;
// Defaults
string.GenerateTextCall(Feedback2, Font,
string.m_Words[j], string.m_Words[j+1],
FirstLine, pObject);
// Iterate all and set X/Y values
// Since X values are not set, we need to make an internal
// iteration with an increment that will append the internal
// x, that is what x_pointer is for.
float x_pointer=0.f;
std::vector::iterator it;
for (it = Feedback2.m_TextCalls.begin(); it != Feedback2.m_TextCalls.end(); ++it)
{
it->m_Pos = CPos(x + x_pointer, y);
x_pointer += it->m_Size.cx;
if (it->m_pSpriteCall)
{
it->m_pSpriteCall->m_Area += it->m_Pos - CSize(0,it->m_pSpriteCall->m_Area.GetHeight());
}
}
// Append X value.
x += Feedback2.m_Size.cx;
Text.m_Size.cx = std::max(Text.m_Size.cx, x+BufferZone);
// The first word overrides the width limit, what we
// do, in those cases, are just drawing that word even
// though it'll extend the object.
if (WordWrapping) // only if word-wrapping is applicable
{
if (Feedback2.m_NewLine)
{
from = j+1;
// Sprite call can exist within only a newline segment,
// therefore we need this.
Text.m_SpriteCalls.insert(Text.m_SpriteCalls.end(), Feedback2.m_SpriteCalls.begin(), Feedback2.m_SpriteCalls.end());
break;
}
else
if (x > width_range[To] && j==temp_from)
{
from = j+1;
// do not break, since we want it to be added to m_TextCalls
}
else
if (x > width_range[To])
{
from = j;
break;
}
}
// Add the whole Feedback2.m_TextCalls to our m_TextCalls.
Text.m_TextCalls.insert(Text.m_TextCalls.end(), Feedback2.m_TextCalls.begin(), Feedback2.m_TextCalls.end());
Text.m_SpriteCalls.insert(Text.m_SpriteCalls.end(), Feedback2.m_SpriteCalls.begin(), Feedback2.m_SpriteCalls.end());
if (j == (int)string.m_Words.size()-2)
done = true;
}
// Reset X
x = 0.f;
// Update height of all
Text.m_Size.cy = std::max(Text.m_Size.cy, y+BufferZone);
FirstLine = false;
// Now if we entered as from = i, then we want
// i being one minus that, so that it will become
// the same i in the next loop. The difference is that
// we're on a new line now.
i = from-1;
}
}
return Text;
}
void CGUI::DrawText(SGUIText &Text, const CColor &DefaultColor,
const CPos &pos, const float &z, const CRect &clipping)
{
// TODO Gee: All these really necessary? Some
// are defaults and if you changed them
// the opposite value at the end of the functions,
// some things won't be drawn correctly.
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
if (clipping != CRect())
{
double eq[4][4] =
{
{ 0.0, 1.0, 0.0, -clipping.top },
{ 1.0, 0.0, 0.0, -clipping.left },
{ 0.0, -1.0, 0.0, clipping.bottom },
{ -1.0, 0.0, 0.0, clipping.right }
};
for (int i=0; i<4; ++i)
{
glClipPlane(GL_CLIP_PLANE0+i, eq[i]);
glEnable(GL_CLIP_PLANE0+i);
}
}
CFont* font = NULL;
CStrW LastFontName;
for (std::vector::const_iterator it = Text.m_TextCalls.begin();
it != Text.m_TextCalls.end();
++it)
{
// If this is just a placeholder for a sprite call, continue
if (it->m_pSpriteCall)
continue;
// Switch fonts when necessary, but remember the last one used
if (it->m_Font != LastFontName)
{
delete font;
font = new CFont(it->m_Font);
font->Bind();
LastFontName = it->m_Font;
}
CColor color = it->m_UseCustomColor ? it->m_Color : DefaultColor;
glPushMatrix();
// TODO Gee: (2004-09-04) Why are font corrupted if inputted float value?
glTranslatef((GLfloat)int(pos.x+it->m_Pos.x), (GLfloat)int(pos.y+it->m_Pos.y), z);
glColor4fv(color.FloatArray());
glwprintf(L"%ls", it->m_String.c_str()); // "%ls" is necessary in case m_String contains % symbols
glPopMatrix();
}
if (font)
delete font;
for (std::list::iterator it=Text.m_SpriteCalls.begin();
it!=Text.m_SpriteCalls.end();
++it)
{
DrawSprite(it->m_Sprite, it->m_CellID, z, it->m_Area + pos);
}
// TODO To whom it may concern: Thing were not reset, so
// I added this line, modify if incorrect --
if (clipping != CRect())
{
for (int i=0; i<4; ++i)
glDisable(GL_CLIP_PLANE0+i);
}
glDisable(GL_TEXTURE_2D);
// -- GL
}
bool CGUI::GetPreDefinedColor(const CStr& name, CColor &Output)
{
if (m_PreDefinedColors.count(name) == 0)
{
return false;
}
else
{
Output = m_PreDefinedColors[name];
return true;
}
}
/**
* @callgraph
*/
void CGUI::LoadXmlFile(const VfsPath& Filename, std::set& Paths)
{
Paths.insert(Filename);
CXeromyces XeroFile;
if (XeroFile.Load(g_VFS, Filename) != PSRETURN_OK)
// Fail silently
return;
XMBElement node = XeroFile.GetRoot();
// Check root element's (node) name so we know what kind of
// data we'll be expecting
CStr root_name (XeroFile.GetElementString(node.GetNodeName()));
try
{
if (root_name == "objects")
{
Xeromyces_ReadRootObjects(node, &XeroFile, Paths);
// Re-cache all values so these gets cached too.
//UpdateResolution();
}
else
if (root_name == "sprites")
{
Xeromyces_ReadRootSprites(node, &XeroFile);
}
else
if (root_name == "styles")
{
Xeromyces_ReadRootStyles(node, &XeroFile);
}
else
if (root_name == "setup")
{
Xeromyces_ReadRootSetup(node, &XeroFile);
}
else
{
debug_warn(L"CGUI::LoadXmlFile error");
// TODO Gee: Output in log
}
}
catch (PSERROR_GUI& e)
{
LOG(CLogger::Error, LOG_CATEGORY, L"Errors loading GUI file %ls (%d)", Filename.string().c_str(), e.getCode());
return;
}
}
//===================================================================
// XML Reading Xeromyces Specific Sub-Routines
//===================================================================
void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, std::set& Paths)
{
int el_script = pFile->GetElementID("script");
std::vector > subst;
// Iterate main children
// they should all be