Index: ps/trunk/source/workspaces/premake/premake.lua =================================================================== --- ps/trunk/source/workspaces/premake/premake.lua (revision 665) +++ ps/trunk/source/workspaces/premake/premake.lua (revision 666) @@ -1,120 +1,121 @@ dofile("functions.lua") -- Set up the Project project.name = "prometheus" project.bindir = "../../../binaries/system" project.libdir = "../../../binaries/system" -- Start the package part package = newpackage() package.name = "prometheus" -- Windowed executable on windows, "exe" on all other platforms package.kind = "winexe" package.language = "c++" -- Package target for debug and release build -- On Windows, ".exe" is added on the end, on unices the name is used directly package.config["Debug"].target = "ps_dbg" package.config["Release"].target = "ps" -- Files package.files = { -- ps/ { sourcesfromdirs("../../ps") }, -- ps/Network/ { sourcesfromdirs("../../ps/Network") }, -- simulation/ { sourcesfromdirs("../../simulation") }, { sourcesfromdirs("../../simulation/scripting") }, -- lib/ { sourcesfromdirs( "../../lib", "../../lib/sysdep", "../../lib/res") }, -- graphics/ { sourcesfromdirs( "../../graphics") }, -- maths/ { sourcesfromdirs( "../../maths") }, { sourcesfromdirs( "../../maths/scripting" ) }, -- renderer/ { sourcesfromdirs( "../../renderer") }, -- gui/ { sourcesfromdirs( "../../gui") }, + { sourcesfromdirs( "../../gui/scripting" ) }, -- terrain/ { sourcesfromdirs( "../../terrain") }, -- main { "../../main.cpp" }, -- scripting { sourcesfromdirs("../../scripting") } } package.includepaths = { "../../ps", "../../simulation", "../../lib", "../../graphics", "../../maths", "../../renderer", "../../terrain", "../.." } package.libpaths = {} -- Platform Specifics if (OS == "windows") then -- Libraries package.links = { "opengl32" } -- package.defines = { "XERCES_STATIC_LIB" } package.config["Release"].defines = { "NDEBUG" } tinsert(package.files, sourcesfromdirs("../../lib/sysdep/win")) package.linkoptions = { "/ENTRY:entry", "/DELAYLOAD:opengl32.dll", "/DELAYLOAD:advapi32.dll", "/DELAYLOAD:gdi32.dll", "/DELAYLOAD:user32.dll", "/DELAYLOAD:ws2_32.dll", "/DELAYLOAD:fmod.dll", "/DELAYLOAD:version.dll", "/DELAYLOAD:ddraw.dll", "/DELAYLOAD:libpng10.dll", "/DELAYLOAD:zlib1.dll", "/DELAYLOAD:glu32.dll", "/DELAY:UNLOAD" -- allow manual unload of delay-loaded DLLs } package.config["Debug"].linkoptions = { "/DELAYLOAD:js32d.dll", } package.config["Release"].linkoptions = { "/DELAYLOAD:js32.dll", } package.buildflags = { "no-main" } package.pchHeader = "precompiled.h" package.pchSource = "precompiled.cpp" else -- Non-Windows, = Unix -- Libraries package.links = { -- OpenGL and X-Windows "GL", "GLU", "X11", "SDL", "png", "fmod-3.70", "fam", -- Utilities "xerces-c", "z", "rt" } tinsert(package.libpaths, { "/usr/X11R6/lib" } ) -- Defines package.defines = { "__STDC_VERSION__=199901L" } -- Includes tinsert(package.includepaths, { "/usr/X11R6/include/X11" } ) -- Build Flags package.buildoptions = { "`pkg-config mozilla-js --cflags`" } package.linkoptions = { "`pkg-config mozilla-js --libs`" } package.config["Debug"].buildoptions = { "-ggdb" } end Index: ps/trunk/source/gui/CButton.cpp =================================================================== --- ps/trunk/source/gui/CButton.cpp (revision 665) +++ ps/trunk/source/gui/CButton.cpp (revision 666) @@ -1,123 +1,149 @@ /* CButton by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CButton.h" // TODO Gee: font.h is temporary. #include "res/font.h" #include "res/res.h" #include "ogl.h" using namespace std; //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CButton::CButton() { AddSetting(GUIST_CGUIString, "caption"); AddSetting(GUIST_CStr, "font"); AddSetting(GUIST_CStr, "sprite"); AddSetting(GUIST_CStr, "sprite-over"); AddSetting(GUIST_CStr, "sprite-pressed"); AddSetting(GUIST_CStr, "sprite-disabled"); AddSetting(GUIST_CColor, "textcolor"); AddSetting(GUIST_CColor, "textcolor-over"); AddSetting(GUIST_CColor, "textcolor-pressed"); AddSetting(GUIST_CColor, "textcolor-disabled"); // Add text AddText(new SGUIText()); } CButton::~CButton() { } void CButton::SetupText() { if (!GetGUI()) return; assert(m_GeneratedTexts.size()>=1); CStr font; CGUIString caption; GUI::GetSetting(this, "caption", caption); *m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, CStr("verdana12.fnt"), 0, 0); // Set position of text m_TextPos = m_CachedActualSize.CenterPoint() - m_GeneratedTexts[0]->m_Size/2; } void CButton::HandleMessage(const SGUIMessage &Message) { // Important IGUIButtonBehavior::HandleMessage(Message); IGUITextOwner::HandleMessage(Message); switch (Message.type) { case GUIM_PREPROCESS: break; case GUIM_POSTPROCESS: break; case GUIM_MOUSE_OVER: + ScriptEvent("MouseOver"); break; case GUIM_MOUSE_ENTER: + ScriptEvent("MouseEnter"); break; case GUIM_MOUSE_LEAVE: + ScriptEvent("MouseLeave"); break; case GUIM_MOUSE_PRESS_LEFT: + ScriptEvent("MouseLeftPress"); break; case GUIM_MOUSE_RELEASE_LEFT: + ScriptEvent("MouseLeftRelease"); + break; + + case GUIM_MOUSE_DOWN_LEFT: + ScriptEvent("MouseLeftDown"); + break; + + case GUIM_MOUSE_PRESS_RIGHT: + ScriptEvent("MouseRightPress"); + break; + + case GUIM_MOUSE_RELEASE_RIGHT: + ScriptEvent("MouseRightRelease"); + break; + + case GUIM_MOUSE_DOWN_RIGHT: + ScriptEvent("MouseRightDown"); break; case GUIM_PRESSED: GetGUI()->TEMPmessage = "Button " + string((const TCHAR*)m_Name) + " was pressed!"; + ScriptEvent("Press"); + break; + + case GUIM_LOAD: + ScriptEvent("Load"); break; default: break; } } void CButton::Draw() { ////////// Gee: janwas, this is just temp to see it glDisable(GL_TEXTURE_2D); ////////// float bz = GetBufferedZ(); CStr sprite, sprite_over, sprite_pressed, sprite_disabled; GUI::GetSetting(this, "sprite", sprite); GUI::GetSetting(this, "sprite-over", sprite_over); GUI::GetSetting(this, "sprite-pressed", sprite_pressed); GUI::GetSetting(this, "sprite-disabled", sprite_disabled); DrawButton(m_CachedActualSize, bz, sprite, sprite_over, sprite_pressed, sprite_disabled); CColor color = ChooseColor(); IGUITextOwner::Draw(0, color, m_TextPos, bz+0.1f); } Index: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (nonexistent) +++ ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp (revision 666) @@ -0,0 +1,355 @@ +// $Id: JSInterface_IGUIObject.cpp,v 1.1 2004/07/08 15:19:45 philip Exp $ + +#include "precompiled.h" + +#include "JSInterface_IGUIObject.h" +#include "JSInterface_GUITypes.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, NULL +}; + +JSPropertySpec JSI_IGUIObject::JSI_props[] = +{ + { 0 } +}; + +JSFunctionSpec JSI_IGUIObject::JSI_methods[] = +{ + { "toString", JSI_IGUIObject::toString, 0, 0, 0 }, + { "getByName", JSI_IGUIObject::getByName, 1, 0, 0 }, + { 0 } +}; + +JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) +{ + CStr propName = JS_GetStringBytes(JS_ValueToString(cx, id)); + + // 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 + // just for efficiency.) + if (propName == (CStr)"constructor" || + propName == (CStr)"toString" || + propName == (CStr)"getByName" + ) + return JS_TRUE; + + IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); + + // Handle the "parent" property specially + if (propName == (CStr)"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); + } + else + { + // Return null if there's no parent + *vp = JSVAL_VOID; + } + return JS_TRUE; + } + // Also handle "name" specially + else if (propName == (CStr)"name") + { + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName())); + return JS_TRUE; + } + else + { + EGUISettingType Type; + if (e->GetSettingType(propName, Type) != PS_OK) + { + // Possibly a function, but they should have been individually + // handled above, so complain about it + JS_ReportError(cx, "Invalid setting '%s'", propName.c_str()); + return JS_TRUE; + } + + // (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 + *vp = DOUBLE_TO_JSVAL(JS_NewDouble(cx, value) ); + break; + } + + case GUIST_CColor: + { + CColor colour; + GUI::GetSetting(e, propName, colour); + JSObject* obj = JS_NewObject(cx, &JSI_GUIColour::JSI_class, NULL, NULL); + jsval r = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.r)); + jsval g = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.g)); + jsval b = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.b)); + jsval a = DOUBLE_TO_JSVAL(JS_NewDouble(cx, colour.a)); + JS_SetProperty(cx, obj, "r", &r); + JS_SetProperty(cx, obj, "g", &g); + JS_SetProperty(cx, obj, "b", &b); + JS_SetProperty(cx, obj, "a", &a); + *vp = OBJECT_TO_JSVAL(obj); + break; + } + + case GUIST_CClientArea: + { + CClientArea area; + GUI::GetSetting(e, propName, area); + CRect size = area.pixel; + JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL); + jsval left = INT_TO_JSVAL(size.left); + jsval top = INT_TO_JSVAL(size.top); + jsval right = INT_TO_JSVAL(size.right); + jsval bottom = INT_TO_JSVAL(size.bottom); + JS_SetProperty(cx, obj, "left", &left); + JS_SetProperty(cx, obj, "top", &top); + JS_SetProperty(cx, obj, "right", &right); + JS_SetProperty(cx, obj, "bottom", &bottom); + *vp = OBJECT_TO_JSVAL(obj); + break; + } + + case GUIST_CGUIString: + { + CGUIString value; + GUI::GetSetting(e, propName, value); + // Create a garbage-collectable copy of the string + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value.GetRawString().c_str() )); + break; + } + + case GUIST_CStr: + { + CStr value; + GUI::GetSetting(e, propName, value); + // Create a garbage-collectable copy of the string + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, value.c_str() )); + break; + } + + default: + JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); + *vp = JSVAL_NULL; + break; + } + + return JS_TRUE; + } + + // Automatically falls through to methods + return JS_TRUE; +} + +JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp) +{ + IGUIObject* e = (IGUIObject*)JS_GetPrivate(cx, obj); + CStr propName = g_ScriptingHost.ValueToString(id); + if (propName == (CStr)"name") + { + CStr propValue = JS_GetStringBytes(JS_ValueToString(cx, *vp)); + e->SetName(propValue); + } + else + { + EGUISettingType Type; + if (e->GetSettingType(propName, Type) != PS_OK) + { + JS_ReportError(cx, "Invalid setting '%s'", propName.c_str()); + return JS_TRUE; + } + + try + { + switch (Type) + { + + case GUIST_CStr: + case GUIST_CGUIString: + e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); + 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"); + 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"); + 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"); + break; + } + + case GUIST_CClientArea: + { + if (JSVAL_IS_STRING(*vp)) + { + e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); + } + else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUISize::JSI_class) + { + CClientArea area; + GUI::GetSetting(e, propName, area); + + JSObject* obj = JSVAL_TO_OBJECT(*vp); + jsval t; int32 s; + #define PROP(x) JS_GetProperty(cx, obj, #x, &t); \ + JS_ValueToInt32(cx, t, &s); \ + area.pixel.##x = s + PROP(left); PROP(top); PROP(right); PROP(bottom); + #undef PROP + + GUI::SetSetting(e, propName, area); + } + else + { + JS_ReportError(cx, "Size only accepts strings or GUISize objects"); + } + break; + } + + case GUIST_CColor: + { + if (JSVAL_IS_STRING(*vp)) + { + e->SetSetting(propName, JS_GetStringBytes(JS_ValueToString(cx, *vp)) ); + } + else if (JSVAL_IS_OBJECT(*vp) && JS_GetClass(JSVAL_TO_OBJECT(*vp)) == &JSI_GUIColour::JSI_class) + { + 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 GUIColour objects"); + } + break; + } + + default: + JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); + break; + } + } + catch (PS_RESULT) + { + JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); + return JS_TRUE; + } + } + return JS_TRUE; +} + + +JSBool JSI_IGUIObject::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) +{ + if (argc == 0) + { + JS_ReportError(cx, "GUIObject has no default constructor"); + return JS_FALSE; + } + + assert(argc == 1); + + // Store the IGUIObject in the JS object's 'private' area + IGUIObject* guiObject = (IGUIObject*)JSVAL_TO_PRIVATE(argv[0]); + JS_SetPrivate(cx, obj, guiObject); + + return JS_TRUE; +} + + +JSBool JSI_IGUIObject::getByName(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) +{ + assert(argc == 1); + + CStr objectName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + + IGUIObject* guiObject = g_GUI.FindObjectByName(objectName); + + if (!guiObject) + { + // Not found - return null + *rval = JSVAL_NULL; + return JS_TRUE; + } + + JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); + JS_SetPrivate(cx, entity, guiObject); + *rval = OBJECT_TO_JSVAL(entity); + 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, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +{ + IGUIObject* e = (IGUIObject*)JS_GetPrivate( cx, obj ); + + char buffer[256]; + snprintf(buffer, 256, "[GUIObject: %s]", (const TCHAR*)e->GetName()); + buffer[255] = 0; + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); + return JS_TRUE; +} Property changes on: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +author date id revision \ No newline at end of property Index: ps/trunk/source/gui/scripting/JSInterface_GUITypes.cpp =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_GUITypes.cpp (nonexistent) +++ ps/trunk/source/gui/scripting/JSInterface_GUITypes.cpp (revision 666) @@ -0,0 +1,194 @@ +// $Id: JSInterface_GUITypes.cpp,v 1.1 2004/07/08 15:19:45 philip Exp $ + +#include "precompiled.h" + +#include "JSInterface_GUITypes.h" + +/**** GUISize ****/ + +JSClass JSI_GUISize::JSI_class = { + "GUISize", 0, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub, + NULL, NULL, NULL, NULL +}; + +JSPropertySpec JSI_GUISize::JSI_props[] = +{ + { "left", 0, JSPROP_ENUMERATE}, + { "top", 1, JSPROP_ENUMERATE}, + { "right", 2, JSPROP_ENUMERATE}, + { "bottom", 3, JSPROP_ENUMERATE}, + { 0 } +}; + +JSFunctionSpec JSI_GUISize::JSI_methods[] = +{ + { "toString", JSI_GUISize::toString, 0, 0, 0 }, + { 0 } +}; + +JSBool JSI_GUISize::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) +{ + if (argc == 4) + { + JS_SetProperty(cx, obj, "left", &argv[0]); + JS_SetProperty(cx, obj, "top", &argv[1]); + JS_SetProperty(cx, obj, "right", &argv[2]); + JS_SetProperty(cx, obj, "bottom", &argv[3]); + } + else + { + jsval zero = JSVAL_ZERO; + JS_SetProperty(cx, obj, "left", &zero); + JS_SetProperty(cx, obj, "top", &zero); + JS_SetProperty(cx, obj, "right", &zero); + JS_SetProperty(cx, obj, "bottom", &zero); + } + + return JS_TRUE; +} + +JSBool JSI_GUISize::toString(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +{ + char buffer[256]; + snprintf(buffer, 256, "%i %i %i %i", + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "left" )), + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "top" )), + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "right" )), + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "bottom")) ); + + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); + return JS_TRUE; +} + + +/**** GUIColour ****/ + + +JSClass JSI_GUIColour::JSI_class = { + "GUIColour", 0, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub, + NULL, NULL, NULL, NULL +}; + +JSPropertySpec JSI_GUIColour::JSI_props[] = +{ + { "r", 0, JSPROP_ENUMERATE}, + { "g", 1, JSPROP_ENUMERATE}, + { "b", 2, JSPROP_ENUMERATE}, + { "a", 3, JSPROP_ENUMERATE}, + { 0 } +}; + +JSFunctionSpec JSI_GUIColour::JSI_methods[] = +{ + { "toString", JSI_GUIColour::toString, 0, 0, 0 }, + { 0 } +}; + +JSBool JSI_GUIColour::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) +{ + if (argc == 4) + { + JS_SetProperty(cx, obj, "r", &argv[0]); + JS_SetProperty(cx, obj, "g", &argv[1]); + JS_SetProperty(cx, obj, "b", &argv[2]); + JS_SetProperty(cx, obj, "a", &argv[3]); + } + else + { + // Nice magenta: + jsval r = DOUBLE_TO_JSVAL(JS_NewDouble(cx, 1.0)); + jsval g = DOUBLE_TO_JSVAL(JS_NewDouble(cx, 0.0)); + jsval b = DOUBLE_TO_JSVAL(JS_NewDouble(cx, 1.0)); + jsval a = DOUBLE_TO_JSVAL(JS_NewDouble(cx, 1.0)); + JS_SetProperty(cx, obj, "r", &r); + JS_SetProperty(cx, obj, "g", &g); + JS_SetProperty(cx, obj, "b", &b); + JS_SetProperty(cx, obj, "a", &a); + } + return JS_TRUE; +} + +JSBool JSI_GUIColour::toString(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +{ + char buffer[256]; + // Convert to integers, to be compatible with the GUI's string SetSetting + snprintf(buffer, 256, "%i %i %i %i", + (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "r")) ), + (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "g")) ), + (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "b")) ), + (int)( 255.0 * *JSVAL_TO_DOUBLE(g_ScriptingHost.GetObjectProperty(obj, "a")) )); + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); + return JS_TRUE; +} + +/**** GUIMouse ****/ + + +JSClass JSI_GUIMouse::JSI_class = { + "GUIMouse", 0, + JS_PropertyStub, JS_PropertyStub, + JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub, + NULL, NULL, NULL, NULL +}; + +JSPropertySpec JSI_GUIMouse::JSI_props[] = +{ + { "x", 0, JSPROP_ENUMERATE}, + { "y", 1, JSPROP_ENUMERATE}, + { "buttons", 2, JSPROP_ENUMERATE}, + { 0 } +}; + +JSFunctionSpec JSI_GUIMouse::JSI_methods[] = +{ + { "toString", JSI_GUIMouse::toString, 0, 0, 0 }, + { 0 } +}; + +JSBool JSI_GUIMouse::construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval) +{ + if (argc == 3) + { + JS_SetProperty(cx, obj, "x", &argv[0]); + JS_SetProperty(cx, obj, "y", &argv[1]); + JS_SetProperty(cx, obj, "buttons", &argv[2]); + } + else + { + jsval zero = JSVAL_ZERO; + JS_SetProperty(cx, obj, "x", &zero); + JS_SetProperty(cx, obj, "y", &zero); + JS_SetProperty(cx, obj, "buttons", &zero); + } + return JS_TRUE; +} + +JSBool JSI_GUIMouse::toString(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) +{ + char buffer[256]; + snprintf(buffer, 256, "%i %i %i", + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "x")), + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "y")), + JSVAL_TO_INT(g_ScriptingHost.GetObjectProperty(obj, "buttons")) ); + *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer)); + return JS_TRUE; +} + + +// Initialise all the types at once: +void JSI_GUITypes::init() +{ + g_ScriptingHost.DefineCustomObjectType(&JSI_GUISize::JSI_class, JSI_GUISize::construct, 1, JSI_GUISize::JSI_props, JSI_GUISize::JSI_methods, NULL, NULL); + g_ScriptingHost.DefineCustomObjectType(&JSI_GUIColour::JSI_class, JSI_GUIColour::construct, 1, JSI_GUIColour::JSI_props, JSI_GUIColour::JSI_methods, NULL, NULL); + g_ScriptingHost.DefineCustomObjectType(&JSI_GUIMouse::JSI_class, JSI_GUIMouse::construct, 1, JSI_GUIMouse::JSI_props, JSI_GUIMouse::JSI_methods, NULL, NULL); +} Property changes on: ps/trunk/source/gui/scripting/JSInterface_GUITypes.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +author date id revision \ No newline at end of property Index: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.h =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUIObject.h (nonexistent) +++ ps/trunk/source/gui/scripting/JSInterface_IGUIObject.h (revision 666) @@ -0,0 +1,25 @@ +// $Id: JSInterface_IGUIObject.h,v 1.1 2004/07/08 15:19:45 philip Exp $ + +#include "scripting/ScriptingHost.h" +#include "gui/GUI.h" + +#ifndef JSI_IGUIOBJECT_INCLUDED +#define JSI_IGUIOBJECT_INCLUDED + +namespace JSI_IGUIObject +{ + extern JSClass JSI_class; + extern JSPropertySpec JSI_props[]; + extern JSFunctionSpec JSI_methods[]; + JSBool addProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp); + JSBool delProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp); + JSBool getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp); + JSBool setProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp); + JSBool construct(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval); + JSBool getByName(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval); + JSBool toString(JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval); + void init(); + void x(); +} + +#endif \ No newline at end of file Property changes on: ps/trunk/source/gui/scripting/JSInterface_IGUIObject.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +author date id revision \ No newline at end of property Index: ps/trunk/source/gui/scripting/JSInterface_GUITypes.h =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_GUITypes.h (nonexistent) +++ ps/trunk/source/gui/scripting/JSInterface_GUITypes.h (revision 666) @@ -0,0 +1,30 @@ +// $Id: JSInterface_GUITypes.h,v 1.1 2004/07/08 15:19:45 philip Exp $ + +#include "scripting/ScriptingHost.h" + +#ifndef JSI_GUITYPES_INCLUDED +#define JSI_GUITYPES_INCLUDED + +#define GUISTDTYPE(x) \ + namespace JSI_GUI##x \ + { \ + extern JSClass JSI_class; \ + extern JSPropertySpec JSI_props[]; \ + extern JSFunctionSpec JSI_methods[]; \ + JSBool construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ); \ + JSBool getByName( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ); \ + JSBool toString( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ); \ + } + +GUISTDTYPE(Size) +GUISTDTYPE(Colour) +GUISTDTYPE(Mouse) + +#undef GUISTDTYPE // avoid unnecessary pollution + +namespace JSI_GUITypes +{ + void init(); +} + +#endif Property changes on: ps/trunk/source/gui/scripting/JSInterface_GUITypes.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +author date id revision \ No newline at end of property Index: ps/trunk/source/gui/CGUI.cpp =================================================================== --- ps/trunk/source/gui/CGUI.cpp (revision 665) +++ ps/trunk/source/gui/CGUI.cpp (revision 666) @@ -1,1457 +1,1426 @@ /* CGUI by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" -#undef new // if it was redefined for leak detection, since xerces doesn't like it #include "GUI.h" // Types - when including them into the engine. #include "CButton.h" #include "CText.h" #include "CCheckBox.h" #include "CRadioButton.h" -#include "XML.h" -//#include -//#include -//#include -//#include -//#include +#include "ps/Xeromyces.h" -#include "XercesErrorHandler.h" #include "Prometheus.h" #include "input.h" #include "OverlayText.h" // TODO Gee: Whatever include CRect/CPos/CSize #include "Overlay.h" +#include "scripting/ScriptingHost.h" + #include #include #include // namespaces used -XERCES_CPP_NAMESPACE_USE +//XERCES_CPP_NAMESPACE_USE using namespace std; +#include "sysdep/ia32.h" +#include "ps/CLogger.h" +#define XERO_TIME + +// Class for global JavaScript object +JSClass GUIClass = { + "GUIClass", 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, +}; + extern int g_xres, g_yres; // TODO Gee: how to draw overlays? void render(COverlayText* overlaytext) { } //------------------------------------------------------------------- // called from main loop when (input) events are received. // event is passed to other handlers if false is returned. // trampoline: we don't want to make the implementation (in CGUI) static //------------------------------------------------------------------- int gui_handler(const SDL_Event* ev) { return g_GUI.HandleEvent(ev); } int CGUI::HandleEvent(const SDL_Event* ev) { if(ev->type == SDL_MOUSEMOTION) { m_MousePos = CPos(ev->motion.x, ev->motion.y); // pNearest will after this point at the hovered object, possibly NULL GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_MOUSE_MOTION)); } // TODO Gee: temp-stuff // char buf[30]; // sprintf(buf, "type = %d", ev->type); //TEMPmessage = buf; + // Update m_MouseButtons. (BUTTONUP is handled later.) if (ev->type == SDL_MOUSEBUTTONDOWN) { - // sprintf(buf, "button = %d", ev->button.button); - //TEMPmessage = buf; + // (0,1,2) = (LMB,RMB,MMB) + if (ev->button.button < 3) + m_MouseButtons |= (1 << ev->button.button); } // JW: (pre|post)process omitted; what're they for? why would we need any special button_released handling? // Only one object can be hovered IGUIObject *pNearest = NULL; try { // TODO Gee: Optimizations needed! // these two recursive function are quite overhead heavy. // pNearest will after this point at the hovered object, possibly NULL GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest); // 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->type == SDL_MOUSEBUTTONDOWN) { switch (ev->button.button) { case SDL_BUTTON_LEFT: if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_PRESS_LEFT)); } { // some temp /* CClientArea ca; bool hidden; GUI::GetSetting(*this, CStr("backdrop43"), CStr("size"), ca); //hidden = !hidden; ca.pixel.right -= 3; GUI::SetSetting(*this, CStr("backdrop43"), CStr("size"), ca); */ } break; case SDL_BUTTON_WHEELDOWN: // wheel down if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_DOWN)); } break; case SDL_BUTTON_WHEELUP: // wheel up if (pNearest) { pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_UP)); } break; // TODO Gee: Just temp case SDL_BUTTON_RIGHT: { CClientArea ca; GUI::GetSetting(*this, CStr("backdrop43"), CStr("size"), ca); //hidden = !hidden; ca.pixel.right -= 3; GUI::SetSetting(*this, CStr("backdrop43"), CStr("size"), ca); } break; default: break; } } else if (ev->type == SDL_MOUSEBUTTONUP) { if (ev->button.button == SDL_BUTTON_LEFT) { if (pNearest) pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_RELEASE_LEFT)); } // 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 (PS_RESULT e) { UNUSED(e); // 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->type == SDL_MOUSEBUTTONUP) + { + // (0,1,2) = (LMB,RMB,MMB) + if (ev->button.button < 3) + m_MouseButtons &= ~(1 << ev->button.button); + } + return EV_PASS; } //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- -CGUI::CGUI() : m_InternalNameNumber(0) +CGUI::CGUI() : m_InternalNameNumber(0), m_MouseButtons(0) { m_BaseObject = new CGUIDummyObject; m_BaseObject->SetGUI(this); + // Construct the parent object for all GUI JavaScript things + m_ScriptObject = (void*)JS_NewObject(g_ScriptingHost.getContext(), &GUIClass, NULL, NULL); + assert(m_ScriptObject != NULL); // How should it handle errors? + JS_AddRoot(g_ScriptingHost.getContext(), &m_ScriptObject); + // This will make this invisible, not add //m_BaseObject->SetName(BASE_OBJECT_NAME); } CGUI::~CGUI() { if (m_BaseObject) delete m_BaseObject; + + if (m_ScriptObject) + // Let it be garbage-collected + JS_RemoveRoot(g_ScriptingHost.getContext(), &m_ScriptObject); + } //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- IGUIObject *CGUI::ConstructObject(const CStr& str) { if (m_ObjectTypes.count(str) > 0) return (*m_ObjectTypes[str])(); else { // TODO Gee: Report in log return NULL; } } void CGUI::Initialize() { // Add base types! // You can also add types outside the GUI to extend the flexibility of the GUI. // Prometheus though will have all the object types inserted from here. AddObjectType("empty", &CGUIDummyObject::ConstructObject); AddObjectType("button", &CButton::ConstructObject); AddObjectType("text", &CText::ConstructObject); AddObjectType("checkbox", &CCheckBox::ConstructObject); AddObjectType("radiobutton", &CRadioButton::ConstructObject); } void CGUI::Process() { /* // TODO Gee: check if m_pInput is valid, otherwise return /// assert(m_pInput); // Pre-process all objects try { GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_PREPROCESS); } catch (PS_RESULT e) { return; } // Check mouse over try { // Only one object can be hovered // check which one it is, if any ! IGUIObject *pNearest = NULL; GUI::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest); // 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, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); // If pressed if (m_pInput->mPress(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_PRESS_LEFT); } else // If released if (m_pInput->mRelease(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_RELEASE_LEFT); } // Generally if just mouse is clicked if (m_pInput->mDown(NEMM_BUTTON1) && pNearest) { pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT); } } catch (PS_RESULT e) { return; } // Post-process all objects try { GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_POSTPROCESS); } catch (PS_RESULT e) { return; } */ } void CGUI::Draw() { + // Clear the depth buffer, so the GUI is + // drawn on top of everything else + glClear(GL_DEPTH_BUFFER_BIT); + glPushMatrix(); glLoadIdentity(); // Adapt (origio) to being in top left corner and down // just like the mouse position glTranslatef(0.0f, (GLfloat)g_yres, -1000.0f); glScalef(1.0f, -1.f, 1.0f); 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 (PS_RESULT e) { UNUSED(e); glPopMatrix(); // TODO Gee: Report error. return; } glPopMatrix(); } void CGUI::DrawSprite(const CStr& SpriteName, const float &Z, const CRect &Rect, const CRect &Clipping) { // This is not an error, it's just a choice not to draw any sprite. if (SpriteName == CStr()) return; const char * buf = SpriteName; bool DoClipping = (Clipping != CRect()); CGUISprite Sprite; // Fetch real sprite from name if (m_Sprites.count(SpriteName) == 0) { // TODO Gee: Report error return; } else Sprite = m_Sprites[SpriteName]; glPushMatrix(); glTranslatef(0.0f, 0.0f, Z); // Iterate all images and request them being drawn be the // CRenderer std::vector::const_iterator cit; for (cit=Sprite.m_Images.begin(); cit!=Sprite.m_Images.end(); ++cit) { CRect real = cit->m_Size.GetClientArea(Rect); glPushMatrix(); glTranslatef(0.f, 0.f, cit->m_DeltaZ); glColor3f(cit->m_BackColor.r , cit->m_BackColor.g, cit->m_BackColor.b); //glColor3f((float)real.right/1000.f, 0.5f, 0.5f); // Do this glBegin(GL_QUADS); glVertex2i(real.right, real.bottom); glVertex2i(real.left, real.bottom); glVertex2i(real.left, real.top); glVertex2i(real.right, real.top); glEnd(); glPopMatrix(); } 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 (PS_RESULT e) { UNUSED(e); // TODO Gee: Handle } delete it->second; } // Clear all m_pAllObjects.clear(); m_Sprites.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); // Cache tree GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize); // Loaded GUI::RecurseObject(0, pObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_LOAD)); } catch (PS_RESULT e) { throw e; } } 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 (PS_RESULT e) { // Throw the same error throw e; } // Else actually update the real one m_pAllObjects = 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; +} + + // private struct used only in GenerateText(...) struct SGenerateTextImage { int m_YFrom, // The images starting location in Y m_YTo, // The images 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 int &width, const int &y, const CSize &Size, const CStr& TextureName, const int &BufferZone) { // 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_TextureName = 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 CColor &Color, */ const CStr& Font, const int &Width, const int &BufferZone) { SGUIText Text; // object we're generating if (string.m_Words.size() == 0) return Text; int x=BufferZone, y=BufferZone; // drawing pointer int from=0; bool done=false; // Images on the left or the right side. 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. int prelim_line_height=0; // Width and height of all text calls generated. string.GenerateTextCall(Feedback, Font, /*CColor(),*/ string.m_Words[i], string.m_Words[i+1]); // 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 (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. int _y; if (Images[j].size() > 0) _y = max(y, Images[j].back().m_YTo); else _y = y; // TODO Gee: CSize temp CSize size; size.cx = 100; size.cy = 100; Image.SetupSpriteCall((j==CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, CStr("white-border"), BufferZone); // Check if image is the lowest thing. Text.m_Size.cy = max(Text.m_Size.cy, Image.m_YTo); Images[j].push_back(Image); Text.m_SpriteCalls.push_back(SpriteCall); } } } pos_last_img = max(pos_last_img, i); x += Feedback.m_Size.cx; prelim_line_height = 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 == 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; int width_range[2]; width_range[From] = BufferZone; width_range[To] = Width - BufferZone; // Floating images are only appicable 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 noticably if one // stuctures 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 (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. int union_from, union_to; union_from = max(y, it->m_YFrom); union_to = min(y+prelim_line_height, it->m_YTo); // The union is not ø if (union_to > union_from) { if (j == From) width_range[From] = max(width_range[From], it->m_Indentation); else width_range[To] = 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. int line_height=0; 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; string.GenerateTextCall(Feedback2, Font, /*CColor(),*/ string.m_Words[j], string.m_Words[j+1]); // 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 = max(line_height, Feedback2.m_Size.cy); if (WordWrapping && Feedback2.m_NewLine) break; } // Reset x once more x = width_range[From]; // 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. CGUIString::SFeedback Feedback2; // Defaults string.GenerateTextCall(Feedback2, Font, /*Color, */ string.m_Words[j], string.m_Words[j+1]); // 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. int x_pointer=0; vector::iterator it; for (it = Feedback2.m_TextCalls.begin(); it != Feedback2.m_TextCalls.end(); ++it) { it->m_Pos = CPos(x + x_pointer, y + line_height - it->m_Size.cy); x_pointer += it->m_Size.cx; if (it->m_pSpriteCall) { it->m_pSpriteCall->m_Area = it->m_pSpriteCall->m_Area + it->m_Pos; } } // Append X value. x += Feedback2.m_Size.cx; Text.m_Size.cx = max(Text.m_Size.cx, x+BufferZone); // The first word overrides the width limit, that we // do in those cases, are just draw that word even // though it'll extend the object. if (WordWrapping) // only if word-wrapping is applicable { if (Feedback2.m_NewLine) { from = j+1; 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 == string.m_Words.size()-2) done = true; } // Reset X, and append Y. x = 0; y += line_height; // Update height of all Text.m_Size.cy = max(Text.m_Size.cy, y+BufferZone); // 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(const SGUIText &Text, const CColor &DefaultColor, const CPos &pos, const float &z) { for (vector::const_iterator it=Text.m_TextCalls.begin(); it!=Text.m_TextCalls.end(); ++it) { if (it->m_pSpriteCall) continue; COverlayText txt((float)pos.x+it->m_Pos.x, (float)pos.y+it->m_Pos.y, (int)z, it->m_Font, it->m_String, (it->m_UseCustomColor?it->m_Color:DefaultColor)); render(&txt); } for (vector::const_iterator it=Text.m_SpriteCalls.begin(); it!=Text.m_SpriteCalls.end(); ++it) { DrawSprite(it->m_TextureName, z, it->m_Area + pos); } } void CGUI::ReportParseError(const CStr& str, ...) { // Print header if (m_Errors==0) { /// g_nemLog("*** GUI Tree Creation Errors"); } // Important, set ParseError to true ++m_Errors; /* TODO Gee: (MEGA) char buffer[512]; va_list args; // get arguments va_start(args, str); vsprintf(buffer, str.c_str(), args); va_end(args); */ /// g_nemLog(" %s", buffer); } /** * @callgraph */ void CGUI::LoadXMLFile(const string &Filename) { // Reset parse error // we can later check if this has increased m_Errors = 0; - // Initialize XML library - XMLPlatformUtils::Initialize(); - { - // Create parser instance - XercesDOMParser *parser = new XercesDOMParser(); + CXeromyces XeroFile; + XeroFile.Load(Filename.c_str()); - bool ParseFailed = false; + bool ParseFailed = false; - if (parser) - { - // Setup parser - parser->setValidationScheme(XercesDOMParser::Val_Auto); - parser->setDoNamespaces(false); - parser->setDoSchema(false); - parser->setCreateEntityReferenceNodes(false); + XMBElement node = XeroFile.getRoot(); - // Set cosutomized error handler - CXercesErrorHandler *errorHandler = new CXercesErrorHandler(); - parser->setErrorHandler(errorHandler); - - /// g_nemLog("*** Xerces XML Parsing Errors"); + // Check root element's (node) name so we know what kind of + // data we'll be expecting + std::wstring root_name = XeroFile.getElementString(node.getNodeName()); - // Get main node - XMLCh *fname=XMLString::transcode(Filename.c_str()); - LocalFileInputSource source(fname); - XMLString::release(&fname); - - // parse - parser->parse(source); - - // Check how many errors - ParseFailed = parser->getErrorCount() != 0; - - // Parse Failed? - if (!ParseFailed) - { - DOMDocument *doc = parser->getDocument(); - DOMElement *node = doc->getDocumentElement(); - - // Check root element's (node) name so we know what kind of - // data we'll be expecting - CStr root_name = XMLTranscode( node->getNodeName() ); - - if (root_name == CStr("objects")) - { - Xerces_ReadRootObjects(node); - - // Re-cache all values so these gets cached too. - //UpdateResolution(); - } - else - if (root_name == CStr("sprites")) - { - Xerces_ReadRootSprites(node); - } - else - if (root_name == CStr("styles")) - { - Xerces_ReadRootStyles(node); - } - else - if (root_name == CStr("setup")) - { - Xerces_ReadRootSetup(node); - } - else - { - // TODO Gee: Output in log - } - - } - } + if (root_name == L"objects") + { + Xeromyces_ReadRootObjects(node, &XeroFile); - // Now report if any other errors occured - if (m_Errors > 0) - { - /// g_console.submit("echo GUI Tree Creation Reports %d errors", m_Errors); - } + // Re-cache all values so these gets cached too. + //UpdateResolution(); + } + else + if (root_name == L"sprites") + { + Xeromyces_ReadRootSprites(node, &XeroFile); + } + else + if (root_name == L"styles") + { + Xeromyces_ReadRootStyles(node, &XeroFile); + } + else + if (root_name == L"setup") + { + Xeromyces_ReadRootSetup(node, &XeroFile); + } + else + { + // TODO Gee: Output in log + } - delete parser->getErrorHandler(); - delete parser; + // Now report if any other errors occured + if (m_Errors > 0) + { +/// g_console.submit("echo GUI Tree Creation Reports %d errors", m_Errors); } - XMLPlatformUtils::Terminate(); + } //=================================================================== -// XML Reading Xerces Specific Sub-Routines +// XML Reading Xeromyces Specific Sub-Routines //=================================================================== -void CGUI::Xerces_ReadRootObjects(DOMElement *pElement) +void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile) { // Iterate main children // they should all be elements - DOMNodeList *children = pElement->getChildNodes(); - for (u16 i=0; igetLength(); ++i) + XMBElementList children = Element.getChildNodes(); + for (int i=0; iitem(i); + //debug_out("Object %d\n", i); + XMBElement child = children.item(i); - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // Read in this whole object into the GUI - DOMElement *element = (DOMElement*)child; - Xerces_ReadObject(element, m_BaseObject); - } + // Read in this whole object into the GUI + Xeromyces_ReadObject(child, pFile, m_BaseObject); } } -void CGUI::Xerces_ReadRootSprites(DOMElement *pElement) +void CGUI::Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile) { // Iterate main children // they should all be elements - DOMNodeList *children = pElement->getChildNodes(); - for (u16 i=0; igetLength(); ++i) + XMBElementList children = Element.getChildNodes(); + for (int i=0; iitem(i); + XMBElement child = children.item(i); - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // Read in this whole object into the GUI - DOMElement *element = (DOMElement*)child; - Xerces_ReadSprite(element); - } + // Read in this whole object into the GUI + Xeromyces_ReadSprite(child, pFile); } } -void CGUI::Xerces_ReadRootStyles(DOMElement *pElement) +void CGUI::Xeromyces_ReadRootStyles(XMBElement Element, CXeromyces* pFile) { // Iterate main children // they should all be elements - DOMNodeList *children = pElement->getChildNodes(); - for (u16 i=0; igetLength(); ++i) + XMBElementList children = Element.getChildNodes(); + for (int i=0; iitem(i); + XMBElement child = children.item(i); - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // Read in this whole object into the GUI - DOMElement *element = (DOMElement*)child; - Xerces_ReadStyle(element); - } + // Read in this whole object into the GUI + Xeromyces_ReadStyle(child, pFile); } } -void CGUI::Xerces_ReadRootSetup(DOMElement *pElement) +void CGUI::Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile) { // Iterate main children // they should all be , or . - DOMNodeList *children = pElement->getChildNodes(); - for (u16 i=0; igetLength(); ++i) + XMBElementList children = Element.getChildNodes(); + for (int i=0; iitem(i); + XMBElement child = children.item(i); - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // Read in this whole object into the GUI - DOMElement *element = (DOMElement*)child; + // Read in this whole object into the GUI - CStr name = XMLTranscode( element->getNodeName() ); + std::wstring name = pFile->getElementString(child.getNodeName()); - if (name == CStr("scrollbar")) - { - Xerces_ReadScrollBarStyle(element); - } - // No need for else, we're using DTD. + if (name == L"scrollbar") + { + Xeromyces_ReadScrollBarStyle(child, pFile); } + // No need for else, we're using DTD. } } -void CGUI::Xerces_ReadObject(DOMElement *pElement, IGUIObject *pParent) +void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent) { - assert(pParent && pElement); - u16 i; + assert(pParent); + int i; // Our object we are going to create IGUIObject *object = NULL; + XMBAttributeList attributes = Element.getAttributes(); + // Well first of all we need to determine the type - CStr type = XMLTranscode( pElement->getAttribute( (XMLCh*)L"type" ) ); + std::wstring type = attributes.getNamedItem( pFile->getAttributeID(L"type") ); // Construct object from specified type // henceforth, we need to do a rollback before aborting. // i.e. releasing this object - object = ConstructObject(type); + object = ConstructObject(tocstr(type)); if (!object) { // Report error that object was unsuccessfully loaded - ReportParseError(CStr("Unrecognized type: ") + type); + ReportParseError(CStr("Unrecognized type: ") + tocstr(type)); delete object; return; } + // Cache some IDs for element attribute names, to avoid string comparisons + #define ELMT(x) int elmt_##x = pFile->getElementID(L#x) + #define ATTR(x) int attr_##x = pFile->getAttributeID(L#x) + ELMT(object); + ELMT(action); + ATTR(style); + ATTR(type); + ATTR(name); + ATTR(z); + ATTR(on); + // // Read Style and set defaults // // If the setting "style" is set, try loading that setting. // // Always load default (if it's available) first! // - CStr argStyle = XMLTranscode( pElement->getAttribute( (XMLCh*)L"style" ) ); + CStr argStyle = tocstr(attributes.getNamedItem(attr_style)); if (m_Styles.count(CStr("default")) == 1) object->LoadStyle(*this, CStr("default")); if (argStyle != CStr()) { // additional check if (m_Styles.count(argStyle) == 0) { // TODO Gee: Error } else object->LoadStyle(*this, argStyle); } // // Read Attributes // bool NameSet = false; bool ManuallySetZ = false; // if z has been manually set, this turn true // Now we can iterate all attributes and store - DOMNamedNodeMap *attributes = pElement->getAttributes(); - for (i=0; igetLength(); ++i) + for (i=0; iitem(i); - CStr attr_name = XMLTranscode( attr->getName() ); - CStr attr_value = XMLTranscode( attr->getValue() ); + XMBAttribute attr = attributes.item(i); // If value is "null", then it is equivalent as never being entered - if (attr_value == CStr("null")) + if (attr.Value == L"null") continue; // Ignore "type" and "style", we've already checked it - if (attr_name == CStr("type") || attr_name == CStr("style") ) + if (attr.Name == attr_type || attr.Name == attr_style ) continue; // Also the name needs some special attention - if (attr_name == CStr("name")) + if (attr.Name == attr_name) { - object->SetName(attr_value); + object->SetName(tocstr(attr.Value)); NameSet = true; continue; } - if (attr_name == CStr("z")) + if (attr.Name == attr_z) ManuallySetZ = true; // Try setting the value try { - object->SetSetting(attr_name, attr_value); + object->SetSetting(tocstr(pFile->getAttributeString(attr.Name)), tocstr(attr.Value)); } catch (PS_RESULT e) { UNUSED(e); - ReportParseError(CStr("Can't set \"") + attr_name + CStr("\" to \"") + attr_value + CStr("\"")); + ReportParseError(CStr("Can't set \"") + tocstr(pFile->getAttributeString(attr.Name)) + CStr("\" to \"") + tocstr(attr.Value) + CStr("\"")); // This is not a fatal error } } // Check if name isn't set, generate an internal name in that case. if (!NameSet) { object->SetName(CStr("__internal(") + CStr(m_InternalNameNumber) + CStr(")")); ++m_InternalNameNumber; } + + CStr caption = tocstr(Element.getText()); + caption.Trim(PS_TRIM_BOTH); + if (caption.Length()) + { + try + { + // Set the setting caption to this + object->SetSetting("caption", caption); + } + catch (...) + { + // There is no harm if the object didn't have a "caption" + } + } + + // // Read Children // // Iterate children - DOMNodeList *children = pElement->getChildNodes(); + XMBElementList children = Element.getChildNodes(); - for (i=0; igetLength(); ++i) + for (i=0; iitem(i); + XMBElement child = children.item(i); - // Check type (it's probably text or element) - - // A child element - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // Check what name the elements got - CStr element_name = XMLTranscode( child->getNodeName() ); - - if (element_name == CStr("object")) - { - // First get element and not node - DOMElement *element = (DOMElement*)child; + // Check what name the elements got + int element_name = child.getNodeName(); - // TODO Gee: REPORT ERROR + if (element_name == elmt_object) + { + // TODO Gee: REPORT ERROR - // Call this function on the child - Xerces_ReadObject(element, object); - } + // Call this function on the child + Xeromyces_ReadObject(child, pFile, object); } - else - if (child->getNodeType() == DOMNode::TEXT_NODE) + else if (element_name == elmt_action) { - CStr caption = XMLTranscode( child->getNodeValue() ); - - caption = caption.Trim(PS_TRIM_BOTH); - - if (caption.Length()) - { - // Text is only okay if it's the first element i.e. caption ... - if (i==0) - { - - try - { - // Set the setting caption to this - object->SetSetting("caption", caption); - } - catch (...) - { - // There is no harm if the object didn't have a "caption" - } - } - else - debug_warn("Text is only okay if it's the first element i.e. caption ... "); - } + CStr action = tocstr(child.getAttributes().getNamedItem(attr_on)); + CStr code = tocstr(child.getText()); + object->RegisterScriptHandler(action, code, this); } } // // Check if Z wasn't manually set // if (!ManuallySetZ) { // Set it automatically to 10 plus its parents if (pParent==NULL) { // TODO Gee: Report error } else { bool absolute; GUI::GetSetting(object, "absolute", absolute); // If the object is absolute, we'll have to get the parent's Z buffered, // and add to that! if (absolute) { GUI::SetSetting(object, "z", pParent->GetBufferedZ() + 10.f); } else // If the object is relative, then we'll just store Z as "10" { GUI::SetSetting(object, "z", 10.f); } } } // // Input Child // try { if (pParent == m_BaseObject) AddObject(object); else pParent->AddChild(object); } catch (PS_RESULT e) { ReportParseError(e); } } -void CGUI::Xerces_ReadSprite(DOMElement *pElement) +void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile) { - assert(pElement); - // Sprite object we're adding CGUISprite sprite; // and what will be its reference name CStr name; // // Read Attributes // // Get name, we know it exists because of DTD requirements - name = XMLTranscode( pElement->getAttribute( (XMLCh*)L"name" ) ); + name = tocstr( Element.getAttributes().getNamedItem( pFile->getAttributeID(L"name") ) ); // // Read Children (the images) // // Iterate children - DOMNodeList *children = pElement->getChildNodes(); + XMBElementList children = Element.getChildNodes(); - for (u16 i=0; igetLength(); ++i) + for (int i=0; iitem(i); + XMBElement child = children.item(i); - // Check type (it's probably text or element) - - // A child element - if (child->getNodeType() == DOMNode::ELEMENT_NODE) - { - // All Elements will be of type "image" by DTD law - - // First get element and not node - DOMElement *element = (DOMElement*)child; + // All Elements will be of type "image" by DTD law - // Call this function on the child - Xerces_ReadImage(element, sprite); - } + // Call this function on the child + Xeromyces_ReadImage(child, pFile, sprite); } // // Add Sprite // m_Sprites[name] = sprite; } -void CGUI::Xerces_ReadImage(DOMElement *pElement, CGUISprite &parent) +void CGUI::Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite &parent) { - assert(pElement); // Image object we're adding SGUIImage image; // TODO Gee: Setup defaults here (or maybe they are in the SGUIImage ctor) // // Read Attributes // // Now we can iterate all attributes and store - DOMNamedNodeMap *attributes = pElement->getAttributes(); - for (u16 i=0; igetLength(); ++i) + XMBAttributeList attributes = Element.getAttributes(); + for (int i=0; iitem(i); - CStr attr_name = XMLTranscode( attr->getName() ); - CStr attr_value(XMLTranscode( attr->getValue() )); + XMBAttribute attr = attributes.item(i); + std::wstring attr_name = pFile->getAttributeString(attr.Name); + CStr attr_value = tocstr( attr.Value ); // This is the only attribute we want - if (attr_name == CStr("texture")) + if (attr_name == L"texture") { image.m_Texture = attr_value; } else - if (attr_name == CStr("size")) + if (attr_name == L"size") { CClientArea ca; if (!GUI::ParseString(attr_value, ca)) { // TODO Gee: Error } else image.m_Size = ca; } else - if (attr_name == CStr("z-level")) + if (attr_name == L"z-level") { int z_level; if (!GUI::ParseString(attr_value, z_level)) { // TODO Gee: Error } else image.m_DeltaZ = (float)z_level/100.f; } else - if (attr_name == CStr("backcolor")) + if (attr_name == L"backcolor") { CColor color; if (!GUI::ParseString(attr_value, color)) { // TODO Gee: Error } else image.m_BackColor = color; } else { // TODO Gee: Log //g_console.submit("echo Error attribute " + attr_name + " is not expected in "); return; } } // // Input // parent.AddImage(image); } -void CGUI::Xerces_ReadStyle(DOMElement *pElement) +void CGUI::Xeromyces_ReadStyle(XMBElement Element, CXeromyces* pFile) { - assert(pElement); - // style object we're adding SGUIStyle style; CStr name; // // Read Attributes // // Now we can iterate all attributes and store - DOMNamedNodeMap *attributes = pElement->getAttributes(); - for (u16 i=0; igetLength(); ++i) + XMBAttributeList attributes = Element.getAttributes(); + for (int i=0; iitem(i); - CStr attr_name = XMLTranscode( attr->getName() ); - CStr attr_value = XMLTranscode( attr->getValue() ); + XMBAttribute attr = attributes.item(i); + std::wstring attr_name = pFile->getAttributeString(attr.Name); + CStr attr_value = tocstr( attr.Value ); // The "name" setting is actually the name of the style // and not a new default - if (attr_name == CStr("name")) + if (attr_name == L"name") name = attr_value; else - style.m_SettingsDefaults[attr_name] = attr_value; + style.m_SettingsDefaults[tocstr(attr_name)] = attr_value; } // // Add to CGUI // m_Styles[name] = style; } -void CGUI::Xerces_ReadScrollBarStyle(DOMElement *pElement) +void CGUI::Xeromyces_ReadScrollBarStyle(XMBElement Element, CXeromyces* pFile) { - assert(pElement); - // style object we're adding SGUIScrollBarStyle scrollbar; CStr name; // // Read Attributes // // Now we can iterate all attributes and store - DOMNamedNodeMap *attributes = pElement->getAttributes(); - for (u16 i=0; igetLength(); ++i) + XMBAttributeList attributes = Element.getAttributes(); + for (int i=0; iitem(i); - CStr attr_name = XMLTranscode( attr->getName() ); - CStr attr_value = XMLTranscode( attr->getValue() ); + XMBAttribute attr = attributes.item(i); + std::wstring attr_name = pFile->getAttributeString(attr.Name); + CStr attr_value = tocstr( attr.Value ); if (attr_value == CStr("null")) continue; - if (attr_name == CStr("name")) + if (attr_name == L"name") name = attr_value; else - if (attr_name == CStr("width")) + if (attr_name == L"width") { int i; if (!GUI::ParseString(attr_value, i)) { // TODO Gee: Report in log file } scrollbar.m_Width = i; } else - if (attr_name == CStr("minimum-bar-size")) + if (attr_name == L"minimum-bar-size") { int i; if (!GUI::ParseString(attr_value, i)) { // TODO Gee: Report in log file } scrollbar.m_MinimumBarSize = i; } else - if (attr_name == CStr("sprite-button-top")) + if (attr_name == L"sprite-button-top") scrollbar.m_SpriteButtonTop = attr_value; else - if (attr_name == CStr("sprite-button-top-pressed")) + if (attr_name == L"sprite-button-top-pressed") scrollbar.m_SpriteButtonTopPressed = attr_value; else - if (attr_name == CStr("sprite-button-top-disabled")) + if (attr_name == L"sprite-button-top-disabled") scrollbar.m_SpriteButtonTopDisabled = attr_value; else - if (attr_name == CStr("sprite-button-top-over")) + if (attr_name == L"sprite-button-top-over") scrollbar.m_SpriteButtonTopOver = attr_value; else - if (attr_name == CStr("sprite-button-bottom")) + if (attr_name == L"sprite-button-bottom") scrollbar.m_SpriteButtonBottom = attr_value; else - if (attr_name == CStr("sprite-button-bottom-pressed")) + if (attr_name == L"sprite-button-bottom-pressed") scrollbar.m_SpriteButtonBottomPressed = attr_value; else - if (attr_name == CStr("sprite-button-bottom-disabled")) + if (attr_name == L"sprite-button-bottom-disabled") scrollbar.m_SpriteButtonBottomDisabled = attr_value; else - if (attr_name == CStr("sprite-button-bottom-over")) + if (attr_name == L"sprite-button-bottom-over") scrollbar.m_SpriteButtonBottomOver = attr_value; else - if (attr_name == CStr("sprite-back-vertical")) + if (attr_name == L"sprite-back-vertical") scrollbar.m_SpriteBackVertical = attr_value; else - if (attr_name == CStr("sprite-bar-vertical")) + if (attr_name == L"sprite-bar-vertical") scrollbar.m_SpriteBarVertical = attr_value; else - if (attr_name == CStr("sprite-bar-vertical-over")) + if (attr_name == L"sprite-bar-vertical-over") scrollbar.m_SpriteBarVerticalOver = attr_value; else - if (attr_name == CStr("sprite-bar-vertical-pressed")) + if (attr_name == L"sprite-bar-vertical-pressed") scrollbar.m_SpriteBarVerticalPressed = attr_value; /* CStr m_SpriteButtonTop; CStr m_SpriteButtonTopPressed; CStr m_SpriteButtonTopDisabled; CStr m_SpriteButtonBottom; CStr m_SpriteButtonBottomPressed; CStr m_SpriteButtonBottomDisabled; CStr m_SpriteScrollBackHorizontal; CStr m_SpriteScrollBarHorizontal; CStr m_SpriteButtonLeft; CStr m_SpriteButtonLeftPressed; CStr m_SpriteButtonLeftDisabled; CStr m_SpriteButtonRight; CStr m_SpriteButtonRightPressed; CStr m_SpriteButtonRightDisabled; CStr m_SpriteScrollBackVertical; CStr m_SpriteScrollBarVertical; */ } // // Add to CGUI // m_ScrollBarStyles[name] = scrollbar; } Index: ps/trunk/source/gui/IGUIObject.h =================================================================== --- ps/trunk/source/gui/IGUIObject.h (revision 665) +++ ps/trunk/source/gui/IGUIObject.h (revision 666) @@ -1,485 +1,521 @@ /* The base class of an object by Gustav Larsson gee@pyro.nu --Overview-- All objects are derived from this class, it's an ADT so it can't be used per se Also contains a Dummy object which is used for completely blank objects. --Usage-- Write about how to use it here --Examples-- Provide examples of how to use this code, if necessary --More info-- Check GUI.h */ #ifndef IGUIObject_H #define IGUIObject_H //-------------------------------------------------------- // Includes / Compiler directives //-------------------------------------------------------- #include "GUI.h" #include #include +#include "gui/scripting/JSInterface_IGUIObject.h" + struct SGUISetting; struct SGUIStyle; class CGUI; //-------------------------------------------------------- // Macros //-------------------------------------------------------- //-------------------------------------------------------- // Types //-------------------------------------------------------- // Map with pointers typedef std::map map_Settings; //-------------------------------------------------------- // Error declarations //-------------------------------------------------------- //-------------------------------------------------------- // Declarations //-------------------------------------------------------- // Text alignments enum EAlign { EAlign_Left, EAlign_Right, EAlign_Center }; enum EValign { EValign_Top, EValign_Bottom, EValign_Center }; /** * Setting Type * @see SGUISetting * * For use of later macros, all names should be GUIST_ followed * by the code name (case sensitive!). */ enum EGUISettingType { GUIST_bool, GUIST_int, GUIST_float, GUIST_CColor, GUIST_CClientArea, GUIST_CGUIString, GUIST_CStr }; /** * @author Gustav Larsson * * A GUI Setting is anything that can be inputted from XML as * -attributes (with exceptions). For instance: * * * "style" will be a SGUISetting. */ struct SGUISetting { SGUISetting() : m_pSetting(NULL) {} void *m_pSetting; EGUISettingType m_Type; }; /** * @author Gustav Larsson * * Base settings, all objects possess these settings * in their m_BaseSettings * Instructions can be found in the documentations. */ /*struct SGUIBaseSettings { //int banan; bool m_Absolute; CStr m_Caption; // Is usually set within an XML element and not in the attributes bool m_Enabled; bool m_Ghost; bool m_Hidden; CClientArea m_Size; CStr m_Style; float m_Z; };*/ ////////////////////////////////////////////////////////// /** * @author Gustav Larsson * * GUI object such as a button or an input-box. * Abstract data type ! */ class IGUIObject { friend class CGUI; friend class CInternalCGUIAccessorBase; friend class IGUIScrollBar; + // Allow getProperty to access things like GetParent() + friend JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsval id, jsval* vp); + public: IGUIObject(); virtual ~IGUIObject(); - + /** * Checks if mouse is hovering this object. * The mouse position is cached in CGUI. * * This function checks if the mouse is hovering the * rectangle that the base setting "size" makes. * Although it is virtual, so one could derive * an object from CButton, which changes only this * to checking the circle that "size" makes. * * @return true if mouse is hovering */ virtual bool MouseOver(); //-------------------------------------------------------- /** @name Leaf Functions */ //-------------------------------------------------------- //@{ /// Get object name, name is unique CStr GetName() const { return m_Name; } /// Get object name void SetName(const CStr& Name) { m_Name = Name; } /** * Adds object and its children to the map, it's name being the * first part, and the second being itself. * * @param ObjectMap Adds this to the map_pObjects. * * @throws PS_NEEDS_NAME Name is missing * @throws PS_NAME_AMBIGUITY Name is already taken */ void AddToPointersMap(map_pObjects &ObjectMap); /** * Notice nothing will be returned or thrown if the child hasn't * been inputted into the GUI yet. This is because that's were * all is checked. Now we're just linking two objects, but * it's when we're inputting them into the GUI we'll check * validity! Notice also when adding it to the GUI this function * will inevitably have been called by CGUI::AddObject which * will catch the throw and return the error code. * i.e. The user will never put in the situation wherein a throw * must be caught, the GUI's internal error handling will be * completely transparent to the interfacially sequential model. * * @param pChild Child to add * * @throws PS_RESULT from CGUI::UpdateObjects(). */ void AddChild(IGUIObject *pChild); //@} //-------------------------------------------------------- /** @name Iterate */ //-------------------------------------------------------- //@{ vector_pObjects::iterator ChildrenItBegin() { return m_Children.begin(); } vector_pObjects::iterator ChildrenItEnd() { return m_Children.end(); } //@} //-------------------------------------------------------- /** @name Settings Management */ //-------------------------------------------------------- //@{ /** * Checks if settings exists, only available for derived * classes that has this set up, that's why the base * class just returns false * * @param Setting setting name * @return True if settings exist. */ bool SettingExists(const CStr& Setting) const; /** * All sizes are relative to resolution, and the calculation * is not wanted in real time, therefore it is cached, update * the cached size with this function. */ void UpdateCachedSize(); /** * Should be called every time the settings has been updated * will also send a message GUIM_SETTINGS_UPDATED, so that * if a derived object wants to add things to be updated, * they add it in that message part, this is a better solution * than making this virtual, since the updates that the base * class does, are the most essential. * This is not private since there should be no harm in * checking validity. * * @throws TODO not quite settled yet. */ void CheckSettingsValidity(); /** * Sets up a map_size_t to include the variables in m_BaseSettings * * @param SettingsInfo Pointers that should be filled with base variables */ //void SetupBaseSettingsInfo(map_Settings &SettingsInfo); /** * Set a setting by string, regardless of what type it is. * * example a CRect(10,10,20,20) would be "10 10 20 20" * * @param Setting Setting by name * @param Value Value to set to * * @throws PS_RESULT */ void SetSetting(const CStr& Setting, const CStr& Value); + /** + * Retrieves the type of a named setting. + * + * @param Setting Setting by name + * @param Type Stores an EGUISettingType + * @return PS_RESULT (PS_OK if successful) + */ + PS_RESULT GetSettingType(const CStr& Setting, EGUISettingType &Type) const; + + /** + * Set the script handler for a particular object-specific action + * + * @param Action Name of action + * @param Code Javascript code to execute when the action occurs + * @param pGUI GUI instance to associate the script with + */ + void RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI* pGUI); + //@} protected: //-------------------------------------------------------- /** @name Called by CGUI and friends * * Methods that the CGUI will call using * its friendship, these should not * be called by user. * These functions' security are a lot * what constitutes the GUI's */ //-------------------------------------------------------- //@{ /** * Add a setting to m_Settings * * @param Type Setting type * @param Name Setting reference name */ void AddSetting(const EGUISettingType &Type, const CStr& Name); /** * Calls Destroy on all children, and deallocates all memory. * MEGA TODO Should it destroy it's children? */ virtual void Destroy(); /** * This function is called with different messages * for instance when the mouse enters the object. * * @param Message GUI Message */ virtual void HandleMessage(const SGUIMessage &Message) {}; /** * Draws the object. * * @throws PS_RESULT if any. But this will mostlikely be * very rare since if an object is drawn unsuccessfully * it'll probably only output in the Error log, and not * disrupt the whole GUI drawing. */ virtual void Draw()=0; /** * Loads a style. * * @param GUIinstance Reference to the GUI * @param StyleName Style by name */ void LoadStyle(CGUI &GUIinstance, const CStr& StyleName); /** * Loads a style. * * @param Style The style object. */ void LoadStyle(const SGUIStyle &Style); /** * Returns not the Z value, but the actual buffered Z value, i.e. if it's * defined relative, then it will check its parent's Z value and add * the relativity. * * @return Actual Z value on the screen. */ virtual float GetBufferedZ() const; // This is done internally CGUI *GetGUI() { return m_pGUI; } const CGUI *GetGUI() const { return m_pGUI; } void SetGUI(CGUI * const &pGUI) { m_pGUI = pGUI; } // Set parent void SetParent(IGUIObject *pParent) { m_pParent = pParent; } virtual void ResetStates() { m_MouseHovering = false; } /** * NOTE! This will not just return m_pParent, when that is * need use it! There is one exception to it, when the parent is * the top-node (the object that isn't a real object), this * will return NULL, so that the top-node's children are * seemingly parentless. * * @return Pointer to parent */ IGUIObject *GetParent() const; /** * Same as reference, but returns a const */ // IGUIObject const *GetParent() const; /** * You input the setting struct you want, and it will return a pointer to * the struct. * * @param SettingsStruct tells us which pointer ot return */ //virtual void *GetStructPointer(const EGUISettingsStruct &SettingsStruct) const; /** * Get Mouse from CGUI. */ CPos GetMousePos() const; /** * Cached size, real size m_Size is actually dependent on resolution * and can have different *real* outcomes, this is the real outcome * cached to avoid slow calculations in real time. */ CRect m_CachedActualSize; + /** + * Execute the script for a particular action. + * Does nothing if no script has been registered for that action. + * + * @param Action Name of action + */ + void ScriptEvent(const CStr& Action); + + /** + * Internal storage for registered script handlers. + */ + std::map m_ScriptHandlers; + //@} private: //-------------------------------------------------------- /** @name Internal functions */ //-------------------------------------------------------- //@{ /** * Inputs a reference pointer, checks if the new inputted object * if hovered, if so, then check if this's Z value is greater * than the inputted object... If so then the object is closer * and we'll replace the pointer with this. * Also Notice input can be NULL, which means the Z value demand * is out. NOTICE you can't input NULL as const so you'll have * to set an object to NULL. * * @param pObject Object pointer, can be either the old one, or * the new one. */ void ChooseMouseOverAndClosest(IGUIObject* &pObject); /** * Inputes the object that is currently hovered, this function * updates this object accordingly (i.e. if it's the object * being inputted one thing happens, and not, another). * * @param pMouseOver Object that is currently hovered, * can OF COURSE be NULL too! */ void UpdateMouseOver(IGUIObject * const &pMouseOver); //@} // Variables protected: /// Name of object CStr m_Name; /// Constructed on the heap, will be destroyed along with the the object // TODO Gee: really the above? vector_pObjects m_Children; /// Pointer to parent IGUIObject *m_pParent; /// Base settings //SGUIBaseSettings m_BaseSettings; /** * This is an array of true or false, each element is associated with * a string representing a setting. Number of elements is equal to * number of settings. * * A true means the setting has been manually set in the file when * read. This is important to know because I don't want to force * the user to include its -XML-files first, so somehow * the GUI needs to know which settings were set, and which is meant * to */ // More variables /// Is mouse hovering the object? used with the function MouseOver() bool m_MouseHovering; /** * Tells us where a variable by a string name is * located hardcoded, in order to acquire a pointer * for that variable... Say "frozen" gives * the offset from IGUIObject to m_Frozen. * note! @uNOT from SGUIBaseSettings to * m_Frozen! */ //static map_Settings ms_SettingsInfo; /** * Settings pool, all an object's settings are located here * If a derived object has got more settings that the base * settings, it's becasue they have a new version of the * function SetupSettings(). * * @see SetupSettings() */ public: std::map m_Settings; private: /// An object can't function stand alone CGUI *m_pGUI; }; /** * @author Gustav Larsson * * Dummy object used primarily for the root object * or objects of type 'empty' */ class CGUIDummyObject : public IGUIObject { GUI_OBJECT(CGUIDummyObject) public: virtual void HandleMessage(const SGUIMessage &Message) {} virtual void Draw() {} }; #endif Index: ps/trunk/source/gui/CRadioButton.cpp =================================================================== --- ps/trunk/source/gui/CRadioButton.cpp (revision 665) +++ ps/trunk/source/gui/CRadioButton.cpp (revision 666) @@ -1,39 +1,41 @@ /* CCheckBox by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CRadioButton.h" using namespace std; void CRadioButton::HandleMessage(const SGUIMessage &Message) { // Important IGUIButtonBehavior::HandleMessage(Message); switch (Message.type) { case GUIM_PRESSED: { // janwas added scoping to squelch ICC var decl warning for (vector_pObjects::iterator it = GetParent()->ChildrenItBegin(); it != GetParent()->ChildrenItEnd(); ++it) { // Notice, if you use other objects within the parent object that has got // this the "checked", it too will change. Hence NO OTHER OBJECTS THAN // RADIO BUTTONS SHOULD BE WITHIN IT! GUI::SetSetting((*it), "checked", false); } GUI::SetSetting(this, "checked", true); //GetGUI()->TEMPmessage = "Check box " + string((const TCHAR*)m_Name) + " was " + (m_Settings.m_Checked?"checked":"unchecked"); + + ScriptEvent("Press"); break; } default: break; } } Index: ps/trunk/source/gui/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/IGUIObject.cpp (revision 665) +++ ps/trunk/source/gui/IGUIObject.cpp (revision 666) @@ -1,377 +1,447 @@ /* IGUIObject by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" ///// janwas: again, including etiquette? #include "Parser.h" #include ///// +#include "gui/scripting/JSInterface_IGUIObject.h" +#include "gui/scripting/JSInterface_GUITypes.h" + extern int g_xres, g_yres; using namespace std; //------------------------------------------------------------------- // Implementation Macros //------------------------------------------------------------------- //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- IGUIObject::IGUIObject() : m_pGUI(NULL), m_pParent(NULL), m_MouseHovering(false) { AddSetting(GUIST_bool, "enabled"); AddSetting(GUIST_bool, "hidden"); AddSetting(GUIST_CClientArea, "size"); AddSetting(GUIST_CStr, "style"); AddSetting(GUIST_float, "z"); // AddSetting(GUIST_CGUIString, "caption"); AddSetting(GUIST_bool, "absolute"); AddSetting(GUIST_bool, "ghost"); // Setup important defaults GUI::SetSetting(this, "hidden", false); GUI::SetSetting(this, "ghost", false); GUI::SetSetting(this, "enabled", true); GUI::SetSetting(this, "absolute", true); bool hidden=true; GUI::GetSetting(this, "hidden", hidden); int hej=23; } IGUIObject::~IGUIObject() { map::iterator it; for (it = m_Settings.begin(); it != m_Settings.end(); ++it) delete it->second.m_pSetting; } //------------------------------------------------------------------- // Functions //------------------------------------------------------------------- void IGUIObject::AddChild(IGUIObject *pChild) { // // assert(pChild); pChild->SetParent(this); m_Children.push_back(pChild); // If this (not the child) object is already attached // to a CGUI, it pGUI pointer will be non-null. // This will mean we'll have to check if we're using // names already used. if (pChild->GetGUI()) { try { // Atomic function, if it fails it won't // have changed anything //UpdateObjects(); pChild->GetGUI()->UpdateObjects(); } catch (PS_RESULT e) { // If anything went wrong, reverse what we did and throw // an exception telling it never added a child m_Children.erase( m_Children.end()-1 ); // We'll throw the same exception for easier // error handling throw e; } } // else do nothing } void IGUIObject::AddToPointersMap(map_pObjects &ObjectMap) { // Just don't do anything about the top node if (m_pParent == NULL) return; // Now actually add this one // notice we won't add it if it's doesn't have any parent // (i.e. being the base object) if (m_Name == string()) { throw PS_NEEDS_NAME; } if (ObjectMap.count(m_Name) > 0) { throw PS_NAME_AMBIGUITY; } else { ObjectMap[m_Name] = this; } } void IGUIObject::Destroy() { // Is there anything besides the children to destroy? } // Notice if using this, the naming convention of GUIST_ should be strict. #define CASE_TYPE(type) \ case GUIST_##type: \ m_Settings[Name].m_pSetting = new type(); \ break; void IGUIObject::AddSetting(const EGUISettingType &Type, const CStr& Name) { // Is name already taken? if (m_Settings.count(Name) >= 1) return; // Construct, and set type m_Settings[Name].m_Type = Type; switch (Type) { // Construct the setting. CASE_TYPE(bool) CASE_TYPE(int) CASE_TYPE(float) CASE_TYPE(CClientArea) CASE_TYPE(CStr) CASE_TYPE(CColor) CASE_TYPE(CGUIString) default: // TODO Gee: Report in log, type is not recognized. break; } } bool IGUIObject::MouseOver() { if(!GetGUI()) throw PS_NEEDS_PGUI; return m_CachedActualSize.PointInside(GetMousePos()); } CPos IGUIObject::GetMousePos() const { return ((GetGUI())?(GetGUI()->m_MousePos):CPos()); } void IGUIObject::UpdateMouseOver(IGUIObject * const &pMouseOver) { // Check if this is the object being hovered. if (pMouseOver == this) { if (!m_MouseHovering) { // It wasn't hovering, so that must mean it just entered HandleMessage(GUIM_MOUSE_ENTER); } // Either way, set to true m_MouseHovering = true; // call mouse over HandleMessage(GUIM_MOUSE_OVER); } else // Some other object (or none) is hovered { if (m_MouseHovering) { m_MouseHovering = false; HandleMessage(GUIM_MOUSE_LEAVE); } } } bool IGUIObject::SettingExists(const CStr& Setting) const { // Because GetOffsets will direct dynamically defined // classes with polymorifsm to respective ms_SettingsInfo // we need to make no further updates on this function // in derived classes. //return (GetSettingsInfo().count(Setting) >= 1); return (m_Settings.count(Setting) >= 1); } #define ADD_TYPE(type, type_name) \ else \ if (set.m_Type == GUIST_##type) \ { \ type _Value; \ if (!GUI::ParseString(Value, _Value)) \ throw PS_FAIL; \ \ GUI::SetSetting(this, Setting, _Value); \ } void IGUIObject::SetSetting(const CStr& Setting, const CStr& Value) { if (!SettingExists(Setting)) { throw PS_FAIL; } // Get setting SGUISetting set = m_Settings[Setting]; if (set.m_Type == GUIST_CStr) { GUI::SetSetting(this, Setting, Value); } ADD_TYPE(bool, "bool") ADD_TYPE(float, "float") ADD_TYPE(int, "int") ADD_TYPE(CColor, "color") ADD_TYPE(CClientArea, "client area") ADD_TYPE(CGUIString, "text") else { throw PS_FAIL; } } +PS_RESULT IGUIObject::GetSettingType(const CStr& Setting, EGUISettingType &Type) const +{ + if (!SettingExists(Setting)) + return PS_SETTING_FAIL; + + if (m_Settings.find(Setting) == m_Settings.end()) + return PS_FAIL; + + Type = m_Settings.find(Setting)->second.m_Type; + + return PS_OK; +} + + void IGUIObject::ChooseMouseOverAndClosest(IGUIObject* &pObject) { if (MouseOver()) { // Check if we've got competition at all if (pObject == NULL) { pObject = this; return; } // Or if it's closer if (GetBufferedZ() >= pObject->GetBufferedZ()) { pObject = this; return; } } } IGUIObject *IGUIObject::GetParent() const { // Important, we're not using GetParent() for these // checks, that could screw it up if (m_pParent) { if (m_pParent->m_pParent == NULL) return NULL; } return m_pParent; } void IGUIObject::UpdateCachedSize() { bool absolute; GUI::GetSetting(this, "absolute", absolute); CClientArea ca; GUI::GetSetting(this, "size", ca); // If absolute="false" and the object has got a parent, // use its cached size instead of the screen. Notice // it must have just been cached for it to work. if (absolute == false && m_pParent) m_CachedActualSize = ca.GetClientArea(m_pParent->m_CachedActualSize); else m_CachedActualSize = ca.GetClientArea(CRect(0, 0, g_xres, g_yres)); } void IGUIObject::LoadStyle(CGUI &GUIinstance, const CStr& StyleName) { // Fetch style if (GUIinstance.m_Styles.count(StyleName)==1) { LoadStyle(GUIinstance.m_Styles[StyleName]); } else ;// TODO Gee: report error } void IGUIObject::LoadStyle(const SGUIStyle &Style) { // Iterate settings, it won't be able to set them all probably, but that doesn't matter std::map::const_iterator cit; for (cit = Style.m_SettingsDefaults.begin(); cit != Style.m_SettingsDefaults.end(); ++cit) { // Try set setting in object try { SetSetting(cit->first, cit->second); } // It doesn't matter if it fail, it's not suppose to be able to set every setting. // since it's generic. catch (PS_RESULT e) { UNUSED(e); // TODO Gee: was ist das? } } } float IGUIObject::GetBufferedZ() const { bool absolute; GUI::GetSetting(this, "absolute", absolute); float Z; GUI::GetSetting(this, "z", Z); if (absolute) return Z; else { if (GetParent()) return GetParent()->GetBufferedZ() + Z; else // TODO Gee: Error, no object should be relative without a parent! return Z; } } // TODO Gee: keep this function and all??? void IGUIObject::CheckSettingsValidity() { bool hidden; GUI::GetSetting(this, "hidden", hidden); // If we hide an object, reset many of its parts if (hidden) { // Simulate that no object is hovered for this object and all its children // why? because it's try { GUI::RecurseObject(0, this, &IGUIObject::UpdateMouseOver, NULL); } catch (...) {} } try { // Send message to itself HandleMessage(GUIM_SETTINGS_UPDATED); } catch (...) { } } + +void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI* pGUI) +{ + const int paramCount = 1; + const char* paramNames[paramCount] = { "mouse" }; + + JSFunction* func = JS_CompileFunction(g_ScriptingHost.getContext(), (JSObject*)pGUI->m_ScriptObject, "", paramCount, paramNames, (const char*)Code, Code.Length(), "GUI script", 0); + m_ScriptHandlers[Action] = func; +} + +void IGUIObject::ScriptEvent(const CStr& Action) +{ + map::iterator it = m_ScriptHandlers.find(Action); + if (it == m_ScriptHandlers.end()) + return; + + // PRIVATE_TO_JSVAL assumes two-byte alignment, + // so make sure that's always true + assert(! ((jsval)this & JSVAL_INT)); + + // The IGUIObject needs to be stored inside the script's object + jsval guiObject = PRIVATE_TO_JSVAL(this); + + // Make a 'this', allowing access to the IGUIObject + JSObject* jsGuiObject = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_IGUIObject::JSI_class, NULL, (JSObject*)m_pGUI->m_ScriptObject, 1, &guiObject); + + // Prevent it from being garbage-collected before + // it's passed into the function + JS_AddRoot(g_ScriptingHost.getContext(), &jsGuiObject); + + + // Set up the 'mouse' parameter + jsval mouseParams[3]; + mouseParams[0] = INT_TO_JSVAL(m_pGUI->m_MousePos.x); + mouseParams[1] = INT_TO_JSVAL(m_pGUI->m_MousePos.y); + mouseParams[2] = INT_TO_JSVAL(m_pGUI->m_MouseButtons); + JSObject* mouseObj = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_GUIMouse::JSI_class, NULL, (JSObject*)m_pGUI->m_ScriptObject, 3, mouseParams); + assert(mouseObj); // need better error handling + + // Don't garbage collect the mouse + JS_AddRoot(g_ScriptingHost.getContext(), &mouseObj); + + const int paramCount = 1; + jsval paramData[paramCount]; + paramData[0] = OBJECT_TO_JSVAL(mouseObj); + + jsval result; + JSBool ok = JS_CallFunction(g_ScriptingHost.getContext(), jsGuiObject, (JSFunction*)((*it).second), 1, paramData, &result); + + // Allow the temporary parameters to be collected + JS_RemoveRoot(g_ScriptingHost.getContext(), &mouseObj); + JS_RemoveRoot(g_ScriptingHost.getContext(), &jsGuiObject); +} Index: ps/trunk/source/gui/CCheckBox.cpp =================================================================== --- ps/trunk/source/gui/CCheckBox.cpp (revision 665) +++ ps/trunk/source/gui/CCheckBox.cpp (revision 666) @@ -1,153 +1,155 @@ /* CCheckBox by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" #include "CCheckBox.h" using namespace std; //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- CCheckBox::CCheckBox() { /* bool m_Checked; CStr m_Font; CStr m_Sprite; CStr m_SpriteDisabled; CStr m_SpriteOver; CStr m_SpritePressed; CStr m_Sprite2; CStr m_Sprite2Disabled; CStr m_Sprite2Over; CStr m_Sprite2Pressed; int m_SquareSide; EAlign m_TextAlign; CColor m_TextColor; CColor m_TextColorDisabled; CColor m_TextColorOver; CColor m_TextColorPressed; EValign m_TextValign; CStr m_ToolTip; CStr m_ToolTipStyle; */ AddSetting(GUIST_CGUIString, "caption"); AddSetting(GUIST_bool, "checked"); AddSetting(GUIST_CStr, "sprite"); AddSetting(GUIST_CStr, "sprite-over"); AddSetting(GUIST_CStr, "sprite-pressed"); AddSetting(GUIST_CStr, "sprite-disabled"); AddSetting(GUIST_CStr, "sprite2"); AddSetting(GUIST_CStr, "sprite2-over"); AddSetting(GUIST_CStr, "sprite2-pressed"); AddSetting(GUIST_CStr, "sprite2-disabled"); AddSetting(GUIST_int, "square-side"); // Add text AddText(new SGUIText()); } CCheckBox::~CCheckBox() { } void CCheckBox::SetupText() { if (!GetGUI()) return; assert(m_GeneratedTexts.size()>=1); CStr font; CGUIString caption; //int square_side; GUI::GetSetting(this, "caption", caption); //GUI::GetSetting(this, "square-side", square_side); // TODO Gee: Establish buffer zones *m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, CStr("verdana12.fnt"), m_CachedActualSize.GetWidth()-20, 0); // Set position of text // TODO Gee: Big TODO // m_TextPos.x = m_CachedActualSize.left + 20; // m_TextPos.y = m_CachedActualSize.top; } void CCheckBox::HandleMessage(const SGUIMessage &Message) { // Important IGUIButtonBehavior::HandleMessage(Message); switch (Message.type) { case GUIM_PRESSED: { bool checked; GUI::GetSetting(this, "checked", checked); checked = !checked; GUI::SetSetting(this, "checked", checked); //GetGUI()->TEMPmessage = "Check box " + string((const TCHAR*)m_Name) + " was " + (m_Settings.m_Checked?"checked":"unchecked"); + + ScriptEvent("Press"); } break; default: break; } } void CCheckBox::Draw() { ////////// Gee: janwas, this is just temp to see it glDisable(GL_TEXTURE_2D); ////////// int square_side; GUI::GetSetting(this, "square-side", square_side); float bz = GetBufferedZ(); // Get square // TODO Gee: edit below when CRect has got "height()" int middle = (m_CachedActualSize.bottom - m_CachedActualSize.top)/2; CRect rect; rect.left = m_CachedActualSize.left + middle - square_side/2; rect.right = rect.left + square_side; rect.top = m_CachedActualSize.top + middle - square_side/2; rect.bottom = rect.top + square_side; bool checked; GUI::GetSetting(this, "checked", checked); CStr sprite, sprite_over, sprite_pressed, sprite_disabled; if (checked) { GUI::GetSetting(this, "sprite2", sprite); GUI::GetSetting(this, "sprite2-over", sprite_over); GUI::GetSetting(this, "sprite2-pressed", sprite_pressed); GUI::GetSetting(this, "sprite2-disabled", sprite_disabled); } else { GUI::GetSetting(this, "sprite", sprite); GUI::GetSetting(this, "sprite-over", sprite_over); GUI::GetSetting(this, "sprite-pressed", sprite_pressed); GUI::GetSetting(this, "sprite-disabled", sprite_disabled); } DrawButton( rect, bz, sprite, sprite_over, sprite_pressed, sprite_disabled); CColor color = ChooseColor(); // IGUITextOwner::Draw(0, color, m_TextPos, bz+0.1f); } Index: ps/trunk/source/gui/CGUI.h =================================================================== --- ps/trunk/source/gui/CGUI.h (revision 665) +++ ps/trunk/source/gui/CGUI.h (revision 666) @@ -1,478 +1,508 @@ /* CGUI by Gustav Larsson gee@pyro.nu --Overview-- This is the top class of the whole GUI, all objects and settings are stored within this class. --More info-- Check GUI.h */ #ifndef CGUI_H #define CGUI_H //-------------------------------------------------------- // Includes / Compiler directives //-------------------------------------------------------- #include "GUI.h" -#include "XML.h" -//#include -//#include -//#include #include "Singleton.h" #include "input.h" // JW: grr, classes suck in this case :P -class XERCES_CPP_NAMESPACE::DOMElement; +#include "Xeromyces.h" extern int gui_handler(const SDL_Event* ev); //-------------------------------------------------------- // Macros //-------------------------------------------------------- //-------------------------------------------------------- // Types //-------------------------------------------------------- //-------------------------------------------------------- // Error declarations //-------------------------------------------------------- //-------------------------------------------------------- // Declarations //-------------------------------------------------------- /** * @author Gustav Larsson * * Contains a list of values for new defaults to objects. */ struct SGUIStyle { // A list of defualts for std::map m_SettingsDefaults; }; /** * @author Gustav Larsson * * The main object that includes the whole GUI. Is singleton * and accessed by g_GUI. * * No interfacial functions throws. */ class CGUI : public Singleton { friend class IGUIObject; friend class IGUIScrollBarOwner; friend class CInternalCGUIAccessorBase; private: // Private typedefs typedef IGUIObject *(*ConstructObjectFunction)(); public: CGUI(); ~CGUI(); // TODO Gee: (MEGA) Extremely temporary. std::string TEMPmessage; /** * Initializes the GUI, needs to be called before the GUI is used */ void Initialize(); /** * @deprecated Will be removed */ void Process(); /** * Displays the whole GUI */ void Draw(); /** * Draw GUI Sprite, cooperates with CRenderer. * * @param SpriteName By name! The GUI will fetch the real object itself. * @param Z Drawing order, depth value * @param Rect Position and Size * @param Clipping The sprite shouldn't be drawn outside this rectangle */ void DrawSprite(const CStr& SpriteName, const float &Z, const CRect &Rect, const CRect &Clipping=CRect()); /** * Draw a SGUIText object * * @param Text Text object. * @param DefaultColor Color used if no tag applied. * @param pos position * @param z z value. */ void DrawText(const SGUIText &Text, const CColor &DefaultColor, const CPos &pos, const float &z); /** * Clean up, call this to clean up all memory allocated * within the GUI. */ void Destroy(); /** * The replacement of Process(), handles an SDL_Event * * @param ev SDL Event, like mouse/keyboard input */ int HandleEvent(const SDL_Event* ev); /** * Load a GUI XML file into the GUI. * * VERY IMPORTANT! All \-files must be read before * everything else! * * @param Filename Name of file */ void LoadXMLFile(const std::string &Filename); /** * Checks if object exists and return true or false accordingly * * @param Name String name of object * @return true if object exists */ bool ObjectExists(const CStr& Name) const; + + /** + * Returns the GUI object with the desired name, or NULL + * if no match is found, + * + * @param Name String name of object + * @return Matching object, or NULL + */ + IGUIObject* FindObjectByName(const CStr& Name) const; + /** * The GUI needs to have all object types inputted and * their constructors. Also it needs to associate a type * by a string name of the type. * * To add a type: * @code * AddObjectType("button", &CButton::ConstructObject); * @endcode * * @param str Reference name of object type * @param pFunc Pointer of function ConstuctObject() in the object * * @see CGUI#ConstructObject() */ void AddObjectType(const CStr& str, ConstructObjectFunction pFunc) { m_ObjectTypes[str] = pFunc; } /** * Update Resolution, should be called every time the resolution * of the opengl screen has been changed, this is becuase it needs * to re-cache all its actual sizes * * Needs no input since screen resolution is global. * * @see IGUIObject#UpdateCachedSize() */ void UpdateResolution(); /** * Generate a SGUIText object from the inputted string. * The function will break down the string and its * tags to calculate exactly which rendering queries * will be sent to the Renderer. * * Done through the CGUI since it can communicate with * * @param Text Text to generate SGUIText object from * @param Color Default color * @param Font Default font, notice both Default color and defult font * can be changed by tags. * @param Width Width, 0 if no word-wrapping. * @param BufferZone space between text and edge, and space between text and images. */ SGUIText GenerateText(const CGUIString &Text, /*const CColor &Color, */ const CStr& Font, const int &Width, const int &BufferZone); private: /** * Updates the object pointers, needs to be called each * time an object has been added or removed. * * This function is atomic, meaning if it throws anything, it will * have seen it through that nothing was ultimately changed. * * @throws PS_RESULT that is thrown from IGUIObject::AddToPointersMap(). */ void UpdateObjects(); /** * Adds an object to the GUI's object database * Private, since you can only add objects through * XML files. Why? Becasue it enables the GUI to * be much more encapsulated and safe. * * @throws Rethrows PS_RESULT from IGUIObject::SetGUI() and * IGUIObject::AddChild(). */ void AddObject(IGUIObject* pObject); /** * Report XML Reading Error, should be called from within the * Xerces_* functions. * * @param str Error message */ void ReportParseError(const CStr& str, ...); /** * You input the name of the object type, and let's * say you input "button", then it will construct a * CGUIObjet* as a CButton. * * @param str Name of object type * @return Newly constructed IGUIObject (but constructed as a subclass) */ IGUIObject *ConstructObject(const CStr& str); //-------------------------------------------------------- - /** @name XML Reading Xerces C++ specific subroutines + /** @name XML Reading Xeromyces specific subroutines * * These does not throw! * Because when reading in XML files, it won't be fatal * if an error occurs, perhaps one particular object * fails, but it'll still continue reading in the next. * All Error are reported with ReportParseError */ //-------------------------------------------------------- /** - Xerces_* functions tree + Xeromyces_* functions tree \ (ReadRootObjects) | +-\ (ReadObject) | +-\ | +-Optional Type Extensions (IGUIObject::ReadExtendedElement) TODO | +-«object» *recursive* \ (ReadRootStyles) | +-\ (ReadStyle) \ (ReadRootSprites) | +-\ (ReadSprite) | +-\ (ReadImage) \\ (ReadRootSetup) | +-\\ (ReadToolTip) | +-\\ (ReadScrollBar) | +-\\ (ReadIcon) */ //@{ // Read Roots /** * Reads in the root element \ (the DOMElement). * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the objects-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadRootObjects(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile); /** * Reads in the root element \ (the DOMElement). * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the sprites-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadRootSprites(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile); /** * Reads in the root element \ (the DOMElement). * - * @param pElement The Xerces C++ Parser object that represents - * the sprites-tag. + * @param Element The Xeromyces object that represents + * the styles-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadRootStyles(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadRootStyles(XMBElement Element, CXeromyces* pFile); /** * Reads in the root element \ (the DOMElement). * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the setup-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadRootSetup(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile); // Read Subs /** * Notice! Recursive function! * * Read in an \ (the DOMElement) and stores it * as a child in the pParent. * * It will also check the object's children and call this function * on them too. Also it will call all other functions that reads * in other stuff that can be found within an object. Such as a * \ will call Xerces_ReadAction (TODO, real funcion?). * * Reads in the root element \ (the DOMElement). * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the object-tag. + * @param pFile The Xeromyces object for the file being read * @param pParent Parent to add this object as child in. * * @see LoadXMLFile() */ - void Xerces_ReadObject(XERCES_CPP_NAMESPACE::DOMElement *, IGUIObject *pParent); + void Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent); /** * Reads in the element \ (the DOMElement) and stores the * result in a new CGUISprite. * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the sprite-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadSprite(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile); /** * Reads in the element \ (the DOMElement) and stores the * result within the CGUISprite. * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the image-tag. + * @param pFile The Xeromyces object for the file being read * @param parent Parent sprite. * * @see LoadXMLFile() */ - void Xerces_ReadImage(XERCES_CPP_NAMESPACE::DOMElement *pElement, CGUISprite &parent); + void Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite &parent); /** * Reads in the element \ (the DOMElement) and stores the * result in m_Styles. * - * @param pElement The Xerces C++ Parser object that represents - * the sprite-tag. + * @param Element The Xeromyces object that represents + * the style-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadStyle(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadStyle(XMBElement Element, CXeromyces* pFile); /** * Reads in the element \ (the DOMElement) and stores the * result in m_ScrollBarStyles. * - * @param pElement The Xerces C++ Parser object that represents + * @param Element The Xeromyces object that represents * the scrollbar-tag. + * @param pFile The Xeromyces object for the file being read * * @see LoadXMLFile() */ - void Xerces_ReadScrollBarStyle(XERCES_CPP_NAMESPACE::DOMElement *pElement); + void Xeromyces_ReadScrollBarStyle(XMBElement Element, CXeromyces* pFile); //@} + private: // Variables //-------------------------------------------------------- /** @name Miscellaneous */ //-------------------------------------------------------- //@{ /** + * An JSObject* under which all GUI JavaScript things will + * be created, so that they can be garbage-collected + * when the GUI shuts down. (Stored as void* to avoid + * to avoid pulling in all the JS headers) + */ + void* m_ScriptObject; + + /** * don't want to pass this around with the * ChooseMouseOverAndClosest broadcast - * we'd need to pack this and pNearest in a struct */ CPos m_MousePos; + /** + * Indicates which buttons are pressed (bit 0 = LMB, + * bit 1 = RMB, bit 2 = MMB) + */ + unsigned int m_MouseButtons; + /// Used when reading in XML files // TODO Gee: Used? int16_t m_Errors; //@} //-------------------------------------------------------- /** @name Objects */ //-------------------------------------------------------- //@{ /** * Base Object, all its children are considered parentless * because this is not a real object per se. */ IGUIObject* m_BaseObject; /** * Just pointers for fast name access, each object * is really constructed within its parent for easy * recursive management. * Notice m_BaseObject won't belong here since it's * not considered a real object. */ map_pObjects m_pAllObjects; /** * Number of object that has been given name automatically. * the name given will be '__internal(#)', the number (#) * being this variable. When an object's name has been set * as followed, the value will increment. */ int m_InternalNameNumber; /** * Function pointers to functions that constructs * IGUIObjects by name... For instance m_ObjectTypes["button"] * is filled with a function that will "return new CButton();" */ std::map m_ObjectTypes; //@} //-------------------------------------------------------- /** @name Databases */ //-------------------------------------------------------- //@{ /// Sprites std::map m_Sprites; /// Styles std::map m_Styles; /// Scroll-bar styles std::map m_ScrollBarStyles; //@} }; #endif Index: ps/trunk/source/scripting/ScriptGlue.cpp =================================================================== --- ps/trunk/source/scripting/ScriptGlue.cpp (revision 665) +++ ps/trunk/source/scripting/ScriptGlue.cpp (revision 666) @@ -1,131 +1,131 @@ #include "precompiled.h" #include "ScriptGlue.h" #include "CConsole.h" #include "CStr.h" #include "EntityHandles.h" #include "Entity.h" #include "EntityManager.h" #include "BaseEntityCollection.h" #include "scripting/JSInterface_Entity.h" #include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_Vector3D.h" -//#include "gui/scripting/JSInterface_IGUIObject.h" +#include "gui/scripting/JSInterface_IGUIObject.h" extern CConsole* g_Console; // Parameters for the table are: // 0: The name the function will be called as from script // 1: The function which will be called // 2: The number of arguments this function expects // 3: Flags (deprecated, always zero) // 4: Extra (reserved for future use, always zero) JSFunctionSpec ScriptFunctionTable[] = { {"WriteLog", WriteLog, 1, 0, 0}, {"writeConsole", writeConsole, 1, 0, 0 }, {"getEntityByHandle", getEntityByHandle, 1, 0, 0 }, {"getEntityTemplate", getEntityTemplate, 1, 0, 0 }, -// {"getGUIObjectByName", JSI_IGUIObject::getByName, 1, 0, 0 }, + {"getGUIObjectByName", JSI_IGUIObject::getByName, 1, 0, 0 }, {"getGlobal", getGlobal, 0, 0, 0 }, {0, 0, 0, 0, 0}, }; // Allow scripts to output to the global log file JSBool WriteLog(JSContext * context, JSObject * globalObject, unsigned int argc, jsval * argv, jsval * rval) { if (argc < 1) return JS_FALSE; for (int i = 0; i < (int)argc; i++) { if (JSVAL_IS_INT(argv[i])) { printf("%d", JSVAL_TO_INT(argv[i])); } if (JSVAL_IS_DOUBLE(argv[i])) { double d = g_ScriptingHost.ValueToDouble(argv[i]); printf("%e", d); } if (JSVAL_IS_STRING(argv[i])) { JSString * str = JS_ValueToString(context, argv[i]); char * chars = JS_GetStringBytes(str); printf(chars); } } printf("\n"); return JS_TRUE; } JSBool writeConsole( JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval ) { assert( argc >= 1 ); CStr output = g_ScriptingHost.ValueToString( argv[0] ); g_Console->InsertMessage( L"%S", (const char*)output ); return( JS_TRUE ); } JSBool getEntityByHandle( JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval ) { assert( argc >= 1 ); i32 handle; try { handle = g_ScriptingHost.ValueToInt( argv[0] ); } catch( ... ) { *rval = JSVAL_NULL; return( JS_TRUE ); } HEntity* v = g_EntityManager.getByHandle( (u16)handle ); if( !v ) { *rval = JSVAL_NULL; return( JS_TRUE ); } JSObject* entity = JS_NewObject( context, &JSI_Entity::JSI_class, NULL, NULL ); JS_SetPrivate( context, entity, v ); *rval = OBJECT_TO_JSVAL( entity ); return( JS_TRUE ); } JSBool getEntityTemplate( JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval ) { assert( argc >= 1 ); CStr templateName; try { templateName = g_ScriptingHost.ValueToString( argv[0] ); } catch( ... ) { *rval = JSVAL_NULL; return( JS_TRUE ); } CBaseEntity* v = g_EntityTemplateCollection.getTemplate( templateName ); if( !v ) { *rval = JSVAL_NULL; return( JS_TRUE ); } JSObject* baseEntity = JS_NewObject( context, &JSI_BaseEntity::JSI_class, NULL, NULL ); JS_SetPrivate( context, baseEntity, v ); *rval = OBJECT_TO_JSVAL( baseEntity ); return( JS_TRUE ); } JSBool getGlobal( JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval ) { *rval = OBJECT_TO_JSVAL( globalObject ); return( JS_TRUE ); }