void CCinemaManager::Update(const float deltaRealTime) { if (g_Game->m_Paused != m_CinematicSimulationData.m_Paused) { m_CinematicSimulationData.m_Paused = g_Game->m_Paused; // sn - session gui object IGUIObject *sn = g_GUI->FindObjectByName("sn"); // GUI visibility if (sn) { if (m_CinematicSimulationData.m_Paused) sn->SetSetting("hidden", L"false"); else sn->SetSetting("hidden", L"true"); } } if (m_CinematicSimulationData.m_PathQueue.empty() || !m_CinematicSimulationData.m_Enabled || m_CinematicSimulationData.m_Paused) return; if (HotkeyIsPressed("leave")) { // TODO: implement skip } m_CinematicSimulationData.m_PathQueue.front().Play(deltaRealTime); }
JSBool JSI_IGUIObject::getComputedSize(JSContext* cx, uintN argc, jsval* vp) { UNUSED2(argc); IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; e->UpdateCachedSize(); CRect size = e->m_CachedActualSize; JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL); try { g_ScriptingHost.SetObjectProperty_Double(obj, "left", size.left); g_ScriptingHost.SetObjectProperty_Double(obj, "right", size.right); g_ScriptingHost.SetObjectProperty_Double(obj, "top", size.top); g_ScriptingHost.SetObjectProperty_Double(obj, "bottom", size.bottom); } catch (PSERROR_Scripting_ConversionFailed) { debug_warn(L"Error creating size object!"); return JS_FALSE; } JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(obj)); return JS_TRUE; }
void CGUI::Xeromyces_ReadTooltip(XMBElement Element, CXeromyces* pFile) { // Read the tooltip, and store it as a specially-named object IGUIObject* object = new CTooltip; XMBAttributeList attributes = Element.GetAttributes(); for (int i=0; i<attributes.Count; ++i) { XMBAttribute attr = attributes.Item(i); CStr attr_name (pFile->GetAttributeString(attr.Name)); CStr attr_value (attr.Value); if (attr_name == "name") { object->SetName("__tooltip_" + attr_value); } else { object->SetSetting(attr_name, attr_value.FromUTF8()); } } AddObject(object); }
JSBool JSI_IGUIObject::blur(JSContext* cx, uintN argc, jsval* vp) { UNUSED2(argc); IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; e->GetGUI()->SetFocusedObject(NULL); JS_SET_RVAL(cx, vp, JSVAL_VOID); return JS_TRUE; }
JSBool JSI_IGUIObject::toString(JSContext* cx, uintN argc, jsval* vp) { UNUSED2(argc); IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, JS_THIS_OBJECT(cx, vp), &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; char buffer[256]; snprintf(buffer, 256, "[GUIObject: %s]", e->GetName().c_str()); buffer[255] = 0; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer))); return JS_TRUE; }
void CCinemaManager::SetEnabled(bool enabled) { // TODO: maybe assert? if (m_CinematicSimulationData.m_PathQueue.empty() && enabled) { enabled = false; m_CinematicSimulationData.m_Paused = true; } if (m_CinematicSimulationData.m_Enabled == enabled) return; // TODO: Enabling/Disabling does not work if the session GUI page is not the top page. // This can happen in various situations, for example when the player wins/looses the game // while the cinematic is running (a message box is the top page in this case). // It might be better to disable the whole GUI during the cinematic instead of a specific // GUI object. // sn - session gui object IGUIObject *sn = g_GUI->FindObjectByName("sn"); CmpPtr<ICmpRangeManager> cmpRangeManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity()); CmpPtr<ICmpTerritoryManager> cmpTerritoryManager(g_Game->GetSimulation2()->GetSimContext().GetSystemEntity()); // GUI visibility if (sn) { if (enabled) sn->SetSetting("hidden", L"true"); else sn->SetSetting("hidden", L"false"); } // Overlay visibility g_Renderer.SetOptionBool(CRenderer::Option::OPT_SILHOUETTES, !enabled); if (cmpRangeManager) { if (enabled) m_CinematicSimulationData.m_MapRevealed = cmpRangeManager->GetLosRevealAll(-1); // TODO: improve m_MapRevealed state and without fade in cmpRangeManager->SetLosRevealAll(-1, enabled); } if (cmpTerritoryManager) cmpTerritoryManager->SetVisibility(!enabled); ICmpSelectable::SetOverrideVisibility(!enabled); ICmpOverlayRenderer::SetOverrideVisibility(!enabled); m_CinematicSimulationData.m_Enabled = enabled; }
void CGUI::Xeromyces_ReadTooltip(XMBElement Element, CXeromyces* pFile) { IGUIObject* object = new CTooltip; for (XMBAttribute attr : Element.GetAttributes()) { CStr attr_name(pFile->GetAttributeString(attr.Name)); CStr attr_value(attr.Value); if (attr_name == "name") object->SetName("__tooltip_" + attr_value); else object->SetSetting(attr_name, attr_value.FromUTF8()); } AddObject(object); }
JSBool GetGUIObjectByName(JSContext* cx, uintN argc, jsval* vp) { JSU_REQUIRE_PARAMS(1); try { CStr name = ToPrimitive<CStr>(cx, JS_ARGV(cx, vp)[0]); IGUIObject* guiObj = g_GUI->FindObjectByName(name); if (guiObj) JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(guiObj->GetJSObject())); else JS_SET_RVAL(cx, vp, JSVAL_NULL); return JS_TRUE; } catch (PSERROR_Scripting&) { return JS_FALSE; } }
void GUITooltip::HideTooltip(const CStr& style, CGUI* gui) { // Ignore attempts to use tooltip "" if (style.empty()) return; IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style); if (! tooltipobj) { LOGERROR(L"Cannot find tooltip named '%hs'", style.c_str()); return; } CStr usedObjectName; if (GUI<CStr>::GetSetting(tooltipobj, "use_object", usedObjectName) == PSRETURN_OK && !usedObjectName.empty()) { IGUIObject* usedobj = gui->FindObjectByName(usedObjectName); if (! usedobj) { LOGERROR(L"Cannot find object named '%hs' used by tooltip '%hs'", usedObjectName.c_str(), style.c_str()); return; } // Clear the caption usedobj->SetSetting("caption", L""); SGUIMessage msg(GUIM_SETTINGS_UPDATED, "caption"); usedobj->HandleMessage(msg); bool hideobject = true; GUI<bool>::GetSetting(tooltipobj, "hide_object", hideobject); // If hide_object was enabled, hide it if (hideobject) GUI<bool>::SetSetting(usedobj, "hidden", true); } else { GUI<bool>::SetSetting(tooltipobj, "hidden", true); } }
InReaction CGUI::HandleEvent(const SDL_Event_* ev) { InReaction ret = IN_PASS; if (ev->ev.type == SDL_HOTKEYDOWN) { const char* hotkey = static_cast<const char*>(ev->ev.user.data1); std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey); if (it != m_HotkeyObjects.end()) { for (size_t i = 0; i < it->second.size(); ++i) { it->second[i]->SendEvent(GUIM_PRESSED, "press"); } } } else if (ev->ev.type == SDL_MOUSEMOTION) { // Yes the mouse position is stored as float to avoid // constant conversions when operating in a // float-based environment. m_MousePos = CPos((float)ev->ev.motion.x, (float)ev->ev.motion.y); SGUIMessage msg(GUIM_MOUSE_MOTION); GUI<SGUIMessage>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::HandleMessage, msg); } // Update m_MouseButtons. (BUTTONUP is handled later.) else if (ev->ev.type == SDL_MOUSEBUTTONDOWN) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: case SDL_BUTTON_RIGHT: case SDL_BUTTON_MIDDLE: m_MouseButtons |= Bit<unsigned int>(ev->ev.button.button); break; default: break; } } // Update m_MousePos (for delayed mouse button events) CPos oldMousePos = m_MousePos; if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP) { m_MousePos = CPos((float)ev->ev.button.x, (float)ev->ev.button.y); } // Only one object can be hovered IGUIObject *pNearest = NULL; // TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress! try { PROFILE( "mouse events" ); // TODO Gee: Optimizations needed! // these two recursive function are quite overhead heavy. // pNearest will after this point at the hovered object, possibly NULL pNearest = FindObjectUnderMouse(); // Is placed in the UpdateMouseOver function //if (ev->ev.type == SDL_MOUSEMOTION && pNearest) // pNearest->ScriptEvent("mousemove"); // Now we'll call UpdateMouseOver on *all* objects, // we'll input the one hovered, and they will each // update their own data and send messages accordingly GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest); if (ev->ev.type == SDL_MOUSEBUTTONDOWN) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: // Focus the clicked object (or focus none if nothing clicked on) SetFocusedObject(pNearest); if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_LEFT, "mouseleftpress"); break; case SDL_BUTTON_RIGHT: if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress"); break; #if !SDL_VERSION_ATLEAST(2, 0, 0) case SDL_BUTTON_WHEELDOWN: // wheel down if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_DOWN, "mousewheeldown"); break; case SDL_BUTTON_WHEELUP: // wheel up if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_UP, "mousewheelup"); break; #endif default: break; } } #if SDL_VERSION_ATLEAST(2, 0, 0) else if (ev->ev.type == SDL_MOUSEWHEEL) { if (ev->ev.wheel.y < 0) { if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_DOWN, "mousewheeldown"); } else if (ev->ev.wheel.y > 0) { if (pNearest) ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_UP, "mousewheelup"); } } #endif else if (ev->ev.type == SDL_MOUSEBUTTONUP) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: if (pNearest) { double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time(); //Double click? if (timeElapsed < SELECT_DBLCLICK_RATE) { ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT, "mouseleftdoubleclick"); } else { ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_LEFT, "mouseleftrelease"); } } break; case SDL_BUTTON_RIGHT: if (pNearest) { double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time(); //Double click? if (timeElapsed < SELECT_DBLCLICK_RATE) { ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_RIGHT, "mouserightdoubleclick"); } else { ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease"); } } break; } // Reset all states on all visible objects GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ResetStates); // It will have reset the mouse over of the current hovered, so we'll // have to restore that if (pNearest) pNearest->m_MouseHovering = true; } } catch (PSERROR_GUI& e) { UNUSED2(e); debug_warn(L"CGUI::HandleEvent error"); // TODO Gee: Handle } // BUTTONUP's effect on m_MouseButtons is handled after // everything else, so that e.g. 'press' handlers (activated // on button up) see which mouse button had been pressed. if (ev->ev.type == SDL_MOUSEBUTTONUP) { switch (ev->ev.button.button) { case SDL_BUTTON_LEFT: case SDL_BUTTON_RIGHT: case SDL_BUTTON_MIDDLE: m_MouseButtons &= ~Bit<unsigned int>(ev->ev.button.button); break; default: break; } } // Restore m_MousePos (for delayed mouse button events) if (ev->ev.type == SDL_MOUSEBUTTONDOWN || ev->ev.type == SDL_MOUSEBUTTONUP) { m_MousePos = oldMousePos; } // Handle keys for input boxes if (GetFocusedObject()) { if ( (ev->ev.type == SDL_KEYDOWN && ev->ev.key.keysym.sym != SDLK_ESCAPE && !g_keys[SDLK_LCTRL] && !g_keys[SDLK_RCTRL] && !g_keys[SDLK_LALT] && !g_keys[SDLK_RALT]) || ev->ev.type == SDL_HOTKEYDOWN ) { ret = GetFocusedObject()->ManuallyHandleEvent(ev); } // else will return IN_PASS because we never used the button. } return ret; }
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject *pParent, const std::vector<std::pair<CStr, CStr> >& NameSubst, boost::unordered_set<VfsPath>& Paths) { ENSURE(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 (attributes.GetNamedItem(pFile->GetAttributeID("type"))); if (type.empty()) type = "empty"; // Construct object from specified type // henceforth, we need to do a rollback before aborting. // i.e. releasing this object object = ConstructObject(type); if (!object) { // Report error that object was unsuccessfully loaded LOGERROR(L"GUI: Unrecognized object type \"%hs\"", type.c_str()); return; } // Cache some IDs for element attribute names, to avoid string comparisons #define ELMT(x) int elmt_##x = pFile->GetElementID(#x) #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) ELMT(object); ELMT(action); ELMT(repeat); ATTR(style); ATTR(type); ATTR(name); ATTR(hotkey); ATTR(z); ATTR(on); ATTR(file); // // 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 (attributes.GetNamedItem(attr_style)); if (m_Styles.count("default") == 1) object->LoadStyle(*this, "default"); if (! argStyle.empty()) { // additional check if (m_Styles.count(argStyle) == 0) { LOGERROR(L"GUI: Trying to use style '%hs' that doesn't exist.", argStyle.c_str()); } else object->LoadStyle(*this, argStyle); } // // Read Attributes // bool NameSet = false; bool ManuallySetZ = false; // if z has been manually set, this turn true CStr hotkeyTag; // Now we can iterate all attributes and store for (i=0; i<attributes.Count; ++i) { XMBAttribute attr = attributes.Item(i); // If value is "null", then it is equivalent as never being entered if (CStr(attr.Value) == "null") continue; // Ignore "type" and "style", we've already checked it if (attr.Name == attr_type || attr.Name == attr_style) continue; // Also the name needs some special attention if (attr.Name == attr_name) { CStr name (attr.Value); // Apply the requested substitutions for (size_t j = 0; j < NameSubst.size(); ++j) name.Replace(NameSubst[j].first, NameSubst[j].second); object->SetName(name); NameSet = true; continue; } // Wire up the hotkey tag, if it has one if (attr.Name == attr_hotkey) hotkeyTag = attr.Value; if (attr.Name == attr_z) ManuallySetZ = true; // Try setting the value if (object->SetSetting(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), true) != PSRETURN_OK) { LOGERROR(L"GUI: (object: %hs) Can't set \"%hs\" to \"%ls\"", object->GetPresentableName().c_str(), pFile->GetAttributeString(attr.Name).c_str(), attr.Value.FromUTF8().c_str()); // This is not a fatal error } } // Check if name isn't set, generate an internal name in that case. if (!NameSet) { object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); ++m_InternalNameNumber; } // Attempt to register the hotkey tag, if one was provided if (! hotkeyTag.empty()) m_HotkeyObjects[hotkeyTag].push_back(object); CStrW caption (Element.GetText().FromUTF8()); if (! caption.empty()) { // Set the setting caption to this object->SetSetting("caption", caption, true); // There is no harm if the object didn't have a "caption" } // // Read Children // // Iterate children XMBElementList children = Element.GetChildNodes(); for (i=0; i<children.Count; ++i) { // Get node XMBElement child = children.Item(i); // Check what name the elements got int element_name = child.GetNodeName(); if (element_name == elmt_object) { // Call this function on the child Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths); } else if (element_name == elmt_action) { // Scripted <action> element // Check for a 'file' parameter CStrW filename (child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStr code; // If there is a file, open it and use it as the code if (! filename.empty()) { Paths.insert(filename); CVFSFile scriptfile; if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) { LOGERROR(L"Error opening GUI script action file '%ls'", filename.c_str()); throw PSERROR_GUI_JSOpenFailed(); } code = scriptfile.DecodeUTF8(); // assume it's UTF-8 } // Read the inline code (concatenating to the file code, if both are specified) code += CStr(child.GetText()); CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on)); // We need to set the GUI this object belongs to because RegisterScriptHandler requires an associated GUI. object->SetGUI(this); object->RegisterScriptHandler(action.LowerCase(), code, this); } else if (element_name == elmt_repeat) { Xeromyces_ReadRepeat(child, pFile, object, Paths); } else { // Try making the object read the tag. if (!object->HandleAdditionalChildren(child, pFile)) { LOGERROR(L"GUI: (object: %hs) Reading unknown children for its type", object->GetPresentableName().c_str()); } } } // // Check if Z wasn't manually set // if (!ManuallySetZ) { // Set it automatically to 10 plus its parents bool absolute; GUI<bool>::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<float>::SetSetting(object, "z", pParent->GetBufferedZ() + 10.f, true); } else // If the object is relative, then we'll just store Z as "10" { GUI<float>::SetSetting(object, "z", 10.f, true); } } // // Input Child // try { if (pParent == m_BaseObject) AddObject(object); else pParent->AddChild(object); } catch (PSERROR_GUI& e) { LOGERROR(L"GUI error: %hs", e.what()); } }
JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp) { IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; jsval idval; if (!JS_IdToValue(cx, id, &idval)) return JS_FALSE; std::string propName; if (!ScriptInterface::FromJSVal(cx, idval, propName)) return JS_FALSE; // Skip some things which are known to be functions rather than properties. // ("constructor" *must* be here, else it'll try to GetSettingType before // the private IGUIObject* has been set (and thus crash). The others are // partly for efficiency, and also to allow correct reporting of attempts to // access nonexistent properties.) if (propName == "constructor" || propName == "prototype" || propName == "toString" || propName == "focus" || propName == "blur" || propName == "getComputedSize" ) return JS_TRUE; // Use onWhatever to access event handlers if (propName.substr(0, 2) == "on") { CStr eventName (CStr(propName.substr(2)).LowerCase()); std::map<CStr, JSObject**>::iterator it = e->m_ScriptHandlers.find(eventName); if (it == e->m_ScriptHandlers.end()) *vp = JSVAL_NULL; else *vp = OBJECT_TO_JSVAL(*(it->second)); return JS_TRUE; } // Handle the "parent" property specially if (propName == "parent") { IGUIObject* parent = e->GetParent(); if (parent) { // If the object isn't parentless, return a new object *vp = OBJECT_TO_JSVAL(parent->GetJSObject()); } else { // Return null if there's no parent *vp = JSVAL_NULL; } return JS_TRUE; } // Also handle "name" specially else if (propName == "name") { *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, e->GetName().c_str())); return JS_TRUE; } // Handle all other properties else { // Retrieve the setting's type (and make sure it actually exists) EGUISettingType Type; if (e->GetSettingType(propName, Type) != PSRETURN_OK) { JS_ReportError(cx, "Invalid GUIObject property '%s'", propName.c_str()); return JS_FALSE; } // (All the cases are in {...} to avoid scoping problems) switch (Type) { case GUIST_bool: { bool value; GUI<bool>::GetSetting(e, propName, value); *vp = value ? JSVAL_TRUE : JSVAL_FALSE; break; } case GUIST_int: { int value; GUI<int>::GetSetting(e, propName, value); *vp = INT_TO_JSVAL(value); break; } case GUIST_float: { float value; GUI<float>::GetSetting(e, propName, value); // Create a garbage-collectable double return JS_NewNumberValue(cx, value, vp); } case GUIST_CColor: { CColor colour; GUI<CColor>::GetSetting(e, propName, colour); JSObject* obj = JS_NewObject(cx, &JSI_GUIColor::JSI_class, NULL, NULL); *vp = OBJECT_TO_JSVAL(obj); // root it jsval c; // Attempt to minimise ugliness through macrosity #define P(x) if (!JS_NewNumberValue(cx, colour.x, &c)) return JS_FALSE; JS_SetProperty(cx, obj, #x, &c) P(r); P(g); P(b); P(a); #undef P break; } case GUIST_CClientArea: { CClientArea area; GUI<CClientArea>::GetSetting(e, propName, area); JSObject* obj = JS_NewObject(cx, &JSI_GUISize::JSI_class, NULL, NULL); *vp = OBJECT_TO_JSVAL(obj); // root it try { #define P(x, y, z) g_ScriptingHost.SetObjectProperty_Double(obj, #z, area.x.y) P(pixel, left, left); P(pixel, top, top); P(pixel, right, right); P(pixel, bottom, bottom); P(percent, left, rleft); P(percent, top, rtop); P(percent, right, rright); P(percent, bottom, rbottom); #undef P } catch (PSERROR_Scripting_ConversionFailed) { debug_warn(L"Error creating size object!"); break; } break; } case GUIST_CGUIString: { CGUIString value; GUI<CGUIString>::GetSetting(e, propName, value); *vp = ScriptInterface::ToJSVal(cx, value.GetOriginalString()); break; } case GUIST_CStr: { CStr value; GUI<CStr>::GetSetting(e, propName, value); *vp = ScriptInterface::ToJSVal(cx, value); break; } case GUIST_CStrW: { CStrW value; GUI<CStrW>::GetSetting(e, propName, value); *vp = ScriptInterface::ToJSVal(cx, value); break; } case GUIST_CGUISpriteInstance: { CGUISpriteInstance *value; GUI<CGUISpriteInstance>::GetSettingPointer(e, propName, value); *vp = ScriptInterface::ToJSVal(cx, value->GetName()); break; } case GUIST_EAlign: { EAlign value; GUI<EAlign>::GetSetting(e, propName, value); CStr word; switch (value) { case EAlign_Left: word = "left"; break; case EAlign_Right: word = "right"; break; case EAlign_Center: word = "center"; break; default: debug_warn(L"Invalid EAlign!"); word = "error"; break; } *vp = ScriptInterface::ToJSVal(cx, word); break; } case GUIST_EVAlign: { EVAlign value; GUI<EVAlign>::GetSetting(e, propName, value); CStr word; switch (value) { case EVAlign_Top: word = "top"; break; case EVAlign_Bottom: word = "bottom"; break; case EVAlign_Center: word = "center"; break; default: debug_warn(L"Invalid EVAlign!"); word = "error"; break; } *vp = ScriptInterface::ToJSVal(cx, word); break; } case GUIST_CGUIList: { CGUIList value; GUI<CGUIList>::GetSetting(e, propName, value); JSObject *obj = JS_NewArrayObject(cx, 0, NULL); *vp = OBJECT_TO_JSVAL(obj); // root it for (size_t i = 0; i < value.m_Items.size(); ++i) { jsval val = ScriptInterface::ToJSVal(cx, value.m_Items[i].GetOriginalString()); JS_SetElement(cx, obj, (jsint)i, &val); } break; } default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); DEBUG_WARN_ERR(ERR::LOGIC); return JS_FALSE; } return JS_TRUE; } }
JSBool JSI_IGUIObject::setProperty(JSContext* cx, JSObject* obj, jsid id, JSBool UNUSED(strict), jsval* vp) { IGUIObject* e = (IGUIObject*)JS_GetInstancePrivate(cx, obj, &JSI_IGUIObject::JSI_class, NULL); if (!e) return JS_FALSE; jsval idval; if (!JS_IdToValue(cx, id, &idval)) return JS_FALSE; std::string propName; if (!ScriptInterface::FromJSVal(cx, idval, propName)) return JS_FALSE; if (propName == "name") { std::string value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; e->SetName(value); return JS_TRUE; } // Use onWhatever to set event handlers if (propName.substr(0, 2) == "on") { if (!JSVAL_IS_OBJECT(*vp) || !JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(*vp))) { JS_ReportError(cx, "on- event-handlers must be functions"); return JS_FALSE; } CStr eventName (CStr(propName.substr(2)).LowerCase()); e->SetScriptHandler(eventName, JSVAL_TO_OBJECT(*vp)); return JS_TRUE; } // Retrieve the setting's type (and make sure it actually exists) EGUISettingType Type; if (e->GetSettingType(propName, Type) != PSRETURN_OK) { JS_ReportError(cx, "Invalid setting '%s'", propName.c_str()); return JS_TRUE; } switch (Type) { case GUIST_CStr: { std::string value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; GUI<CStr>::SetSetting(e, propName, value); break; } case GUIST_CStrW: { std::wstring value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; GUI<CStrW>::SetSetting(e, propName, value); break; } case GUIST_CGUISpriteInstance: { std::string value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; GUI<CGUISpriteInstance>::SetSetting(e, propName, CGUISpriteInstance(value)); break; } case GUIST_CGUIString: { std::wstring value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; CGUIString str; str.SetValue(value); GUI<CGUIString>::SetSetting(e, propName, str); break; } case GUIST_EAlign: { std::string value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; EAlign a; if (value == "left") a = EAlign_Left; else if (value == "right") a = EAlign_Right; else if (value == "center" || value == "centre") a = EAlign_Center; else { JS_ReportError(cx, "Invalid alignment (should be 'left', 'right' or 'center')"); return JS_FALSE; } GUI<EAlign>::SetSetting(e, propName, a); break; } case GUIST_EVAlign: { std::string value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; EVAlign a; if (value == "top") a = EVAlign_Top; else if (value == "bottom") a = EVAlign_Bottom; else if (value == "center" || value == "centre") a = EVAlign_Center; else { JS_ReportError(cx, "Invalid alignment (should be 'top', 'bottom' or 'center')"); return JS_FALSE; } GUI<EVAlign>::SetSetting(e, propName, a); break; } case GUIST_int: { int32 value; if (JS_ValueToInt32(cx, *vp, &value) == JS_TRUE) GUI<int>::SetSetting(e, propName, value); else { JS_ReportError(cx, "Cannot convert value to int"); return JS_FALSE; } break; } case GUIST_float: { jsdouble value; if (JS_ValueToNumber(cx, *vp, &value) == JS_TRUE) GUI<float>::SetSetting(e, propName, (float)value); else { JS_ReportError(cx, "Cannot convert value to float"); return JS_FALSE; } break; } case GUIST_bool: { JSBool value; if (JS_ValueToBoolean(cx, *vp, &value) == JS_TRUE) GUI<bool>::SetSetting(e, propName, value||0); // ||0 to avoid int-to-bool compiler warnings else { JS_ReportError(cx, "Cannot convert value to bool"); return JS_FALSE; } break; } case GUIST_CClientArea: { if (JSVAL_IS_STRING(*vp)) { std::wstring value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; if (e->SetSetting(propName, value) != PSRETURN_OK) { JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); return JS_FALSE; } } else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUISize::JSI_class, NULL)) { CClientArea area; GUI<CClientArea>::GetSetting(e, propName, area); JSObject* obj = JSVAL_TO_OBJECT(*vp); #define P(x, y, z) area.x.y = (float)g_ScriptingHost.GetObjectProperty_Double(obj, #z) P(pixel, left, left); P(pixel, top, top); P(pixel, right, right); P(pixel, bottom, bottom); P(percent, left, rleft); P(percent, top, rtop); P(percent, right, rright); P(percent, bottom, rbottom); #undef P GUI<CClientArea>::SetSetting(e, propName, area); } else { JS_ReportError(cx, "Size only accepts strings or GUISize objects"); return JS_FALSE; } break; } case GUIST_CColor: { if (JSVAL_IS_STRING(*vp)) { std::wstring value; if (!ScriptInterface::FromJSVal(cx, *vp, value)) return JS_FALSE; if (e->SetSetting(propName, value) != PSRETURN_OK) { JS_ReportError(cx, "Invalid value for setting '%s'", propName.c_str()); return JS_FALSE; } } else if (JSVAL_IS_OBJECT(*vp) && JS_InstanceOf(cx, JSVAL_TO_OBJECT(*vp), &JSI_GUIColor::JSI_class, NULL)) { CColor colour; JSObject* obj = JSVAL_TO_OBJECT(*vp); jsval t; double s; #define PROP(x) JS_GetProperty(cx, obj, #x, &t); \ JS_ValueToNumber(cx, t, &s); \ colour.x = (float)s PROP(r); PROP(g); PROP(b); PROP(a); #undef PROP GUI<CColor>::SetSetting(e, propName, colour); } else { JS_ReportError(cx, "Color only accepts strings or GUIColor objects"); return JS_FALSE; } break; } case GUIST_CGUIList: { JSObject* obj = JSVAL_TO_OBJECT(*vp); jsuint length; if (JSVAL_IS_OBJECT(*vp) && JS_GetArrayLength(cx, obj, &length) == JS_TRUE) { CGUIList list; for (int i=0; i<(int)length; ++i) { jsval element; if (! JS_GetElement(cx, obj, i, &element)) { JS_ReportError(cx, "Failed to get list element"); return JS_FALSE; } std::wstring value; if (!ScriptInterface::FromJSVal(cx, element, value)) return JS_FALSE; CGUIString str; str.SetValue(value); list.m_Items.push_back(str); } GUI<CGUIList>::SetSetting(e, propName, list); } else { JS_ReportError(cx, "List only accepts a GUIList object"); return JS_FALSE; } break; } // TODO Gee: (2004-09-01) EAlign and EVAlign too. default: JS_ReportError(cx, "Setting '%s' uses an unimplemented type", propName.c_str()); break; } return !JS_IsExceptionPending(cx); }
void GUITooltip::ShowTooltip(IGUIObject* obj, CPos pos, const CStr& style, CGUI* gui) { ENSURE(obj); // Ignore attempts to use tooltip "" if (style.empty()) return; // Get the object referenced by 'tooltip_style' IGUIObject* tooltipobj = gui->FindObjectByName("__tooltip_"+style); if (! tooltipobj) { LOGERROR(L"Cannot find tooltip named '%hs'", style.c_str()); return; } IGUIObject* usedobj = tooltipobj; // object actually used to display the tooltip in CStr usedObjectName; if (GUI<CStr>::GetSetting(tooltipobj, "use_object", usedObjectName) == PSRETURN_OK && !usedObjectName.empty()) { usedobj = gui->FindObjectByName(usedObjectName); if (! usedobj) { LOGERROR(L"Cannot find object named '%hs' used by tooltip '%hs'", usedObjectName.c_str(), style.c_str()); return; } // Unhide the object. (If it had use_object and hide_object="true", // still unhide it, because the used object might be hidden by default) GUI<bool>::SetSetting(usedobj, "hidden", false); } else { // Unhide the object GUI<bool>::SetSetting(usedobj, "hidden", false); // Store mouse position inside the CTooltip if (GUI<CPos>::SetSetting(usedobj, "_mousepos", pos) != PSRETURN_OK) debug_warn(L"Failed to set tooltip mouse position"); } // Retrieve object's 'tooltip' setting CStrW text; if (m_IsIconTooltip) { // Use icon tooltip property if (GUI<CStrW>::GetSetting(obj, "_icon_tooltip", text) != PSRETURN_OK) debug_warn(L"Failed to retrieve icon tooltip text"); // shouldn't fail } else { // Use normal tooltip property if (GUI<CStrW>::GetSetting(obj, "tooltip", text) != PSRETURN_OK) debug_warn(L"Failed to retrieve tooltip text"); // shouldn't fail } // Do some minimal processing ("\n" -> newline, etc) text = text.UnescapeBackslashes(); // Set tooltip's caption if (usedobj->SetSetting("caption", text) != PSRETURN_OK) debug_warn(L"Failed to set tooltip caption"); // shouldn't fail // Make the tooltip object regenerate its text SGUIMessage msg(GUIM_SETTINGS_UPDATED, "caption"); usedobj->HandleMessage(msg); }
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, boost::unordered_set<VfsPath>& Paths, u32 nesting_depth) { ENSURE(pParent); XMBAttributeList attributes = Element.GetAttributes(); CStr type(attributes.GetNamedItem(pFile->GetAttributeID("type"))); if (type.empty()) type = "empty"; // Construct object from specified type // henceforth, we need to do a rollback before aborting. // i.e. releasing this object IGUIObject* object = ConstructObject(type); if (!object) { LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); return; } // Cache some IDs for element attribute names, to avoid string comparisons #define ELMT(x) int elmt_##x = pFile->GetElementID(#x) #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) ELMT(object); ELMT(action); ELMT(repeat); ELMT(translatableAttribute); ELMT(translate); ELMT(attribute); ELMT(keep); ELMT(include); ATTR(style); ATTR(type); ATTR(name); ATTR(hotkey); ATTR(z); ATTR(on); ATTR(file); ATTR(directory); ATTR(id); ATTR(context); // // 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(attributes.GetNamedItem(attr_style)); if (m_Styles.count("default") == 1) object->LoadStyle(*this, "default"); if (!argStyle.empty()) { if (m_Styles.count(argStyle) == 0) LOGERROR("GUI: Trying to use style '%s' that doesn't exist.", argStyle.c_str()); else object->LoadStyle(*this, argStyle); } bool NameSet = false; bool ManuallySetZ = false; CStrW inclusionPath; CStr hotkeyTag; for (XMBAttribute attr : attributes) { // If value is "null", then it is equivalent as never being entered if (CStr(attr.Value) == "null") continue; // Ignore "type" and "style", we've already checked it if (attr.Name == attr_type || attr.Name == attr_style) continue; if (attr.Name == attr_name) { CStr name(attr.Value); for (const std::pair<CStr, CStr>& sub : NameSubst) name.Replace(sub.first, sub.second); object->SetName(name); NameSet = true; continue; } if (attr.Name == attr_hotkey) hotkeyTag = attr.Value; if (attr.Name == attr_z) ManuallySetZ = true; if (object->SetSetting(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), true) != PSRETURN_OK) LOGERROR("GUI: (object: %s) Can't set \"%s\" to \"%s\"", object->GetPresentableName(), pFile->GetAttributeString(attr.Name), attr.Value); } // Check if name isn't set, generate an internal name in that case. if (!NameSet) { object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); ++m_InternalNameNumber; } if (!hotkeyTag.empty()) m_HotkeyObjects[hotkeyTag].push_back(object); CStrW caption(Element.GetText().FromUTF8()); if (!caption.empty()) object->SetSetting("caption", caption, true); for (XMBElement child : Element.GetChildNodes()) { // Check what name the elements got int element_name = child.GetNodeName(); if (element_name == elmt_object) { // Call this function on the child Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths, nesting_depth); } else if (element_name == elmt_action) { // Scripted <action> element // Check for a 'file' parameter CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStr code; // If there is a file, open it and use it as the code if (!filename.empty()) { Paths.insert(filename); CVFSFile scriptfile; if (scriptfile.Load(g_VFS, filename) != PSRETURN_OK) { LOGERROR("Error opening GUI script action file '%s'", utf8_from_wstring(filename)); throw PSERROR_GUI_JSOpenFailed(); } code = scriptfile.DecodeUTF8(); // assume it's UTF-8 } XMBElementList grandchildren = child.GetChildNodes(); if (!grandchildren.empty()) // The <action> element contains <keep> and <translate> tags. for (XMBElement grandchild : grandchildren) { if (grandchild.GetNodeName() == elmt_translate) code += g_L10n.Translate(grandchild.GetText()); else if (grandchild.GetNodeName() == elmt_keep) code += grandchild.GetText(); } else // It’s pure JavaScript code. // Read the inline code (concatenating to the file code, if both are specified) code += CStr(child.GetText()); CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on)); // We need to set the GUI this object belongs to because RegisterScriptHandler requires an associated GUI. object->SetGUI(this); object->RegisterScriptHandler(action.LowerCase(), code, this); } else if (element_name == elmt_repeat) { Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); } else if (element_name == elmt_translatableAttribute) { // This is an element in the form “<translatableAttribute id="attributeName">attributeValue</translatableAttribute>”. CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. if (attributeName.empty()) { LOGERROR("GUI: ‘translatableAttribute’ XML element with empty ‘id’ XML attribute found. (object: %s)", object->GetPresentableName().c_str()); continue; } CStr value(child.GetText()); if (value.empty()) continue; CStr context(child.GetAttributes().GetNamedItem(attr_context)); // Read the context if any. if (!context.empty()) { CStr translatedValue(g_L10n.TranslateWithContext(context, value)); object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } else { CStr translatedValue(g_L10n.Translate(value)); object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } } else if (element_name == elmt_attribute) { // This is an element in the form “<attribute id="attributeName"><keep>Don’t translate this part // </keep><translate>but translate this one.</translate></attribute>”. CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. if (attributeName.empty()) { LOGERROR("GUI: ‘attribute’ XML element with empty ‘id’ XML attribute found. (object: %s)", object->GetPresentableName().c_str()); continue; } CStr translatedValue; for (XMBElement grandchild : child.GetChildNodes()) { if (grandchild.GetNodeName() == elmt_translate) translatedValue += g_L10n.Translate(grandchild.GetText()); else if (grandchild.GetNodeName() == elmt_keep) translatedValue += grandchild.GetText(); } object->SetSetting(attributeName, translatedValue.FromUTF8(), true); } else if (element_name == elmt_include) { CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); CStrW directory(child.GetAttributes().GetNamedItem(attr_directory).FromUTF8()); if (!filename.empty()) { if (!directory.empty()) LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); Paths.insert(filename); CXeromyces XeroIncluded; if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) { LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); continue; } XMBElement node = XeroIncluded.GetRoot(); if (node.GetNodeName() != XeroIncluded.GetElementID("object")) { LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); continue; } if (nesting_depth+1 >= MAX_OBJECT_DEPTH) { LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); continue; } Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); } else if (!directory.empty()) { if (nesting_depth+1 >= MAX_OBJECT_DEPTH) { LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); continue; } VfsPaths pathnames; vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); for (const VfsPath& path : pathnames) { // as opposed to loading scripts, don't care if it's loaded before // one might use the same parts of the GUI in different situations Paths.insert(path); CXeromyces XeroIncluded; if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) { LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); continue; } XMBElement node = XeroIncluded.GetRoot(); if (node.GetNodeName() != XeroIncluded.GetElementID("object")) { LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); continue; } Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); } } else LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); } else { // Try making the object read the tag. if (!object->HandleAdditionalChildren(child, pFile)) LOGERROR("GUI: (object: %s) Reading unknown children for its type", object->GetPresentableName().c_str()); } } if (!ManuallySetZ) { // Set it automatically to 10 plus its parents bool absolute; GUI<bool>::GetSetting(object, "absolute", absolute); if (absolute) // If the object is absolute, we'll have to get the parent's Z buffered, // and add to that! GUI<float>::SetSetting(object, "z", pParent->GetBufferedZ() + 10.f, true); else // If the object is relative, then we'll just store Z as "10" GUI<float>::SetSetting(object, "z", 10.f, true); } try { if (pParent == m_BaseObject) AddObject(object); else pParent->AddChild(object); } catch (PSERROR_GUI& e) { LOGERROR("GUI error: %s", e.what()); } }