/* IGUIObject by Gustav Larsson gee@pyro.nu */ #include "precompiled.h" #include "GUI.h" ///// janwas: again, including etiquette? #include "Parser.h" #include ///// 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) { if (!it->second.m_pSetting) 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; } } 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 (...) { } }