diff options
Diffstat (limited to 'src/player/Node.cpp')
-rw-r--r-- | src/player/Node.cpp | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/src/player/Node.cpp b/src/player/Node.cpp new file mode 100644 index 0000000..4f1f6c6 --- /dev/null +++ b/src/player/Node.cpp @@ -0,0 +1,624 @@ +// +// libavg - Media Playback Engine. +// Copyright (C) 2003-2014 Ulrich von Zadow +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// Current versions can be found at www.libavg.de +// + +#include "Node.h" + +#include "TypeDefinition.h" +#include "Arg.h" +#include "Canvas.h" +#include "DivNode.h" +#include "Player.h" +#include "CursorEvent.h" +#include "PublisherDefinition.h" + +#include "../base/Exception.h" +#include "../base/Logger.h" +#include "../base/ObjectCounter.h" +#include "../base/StringHelper.h" +#include "../base/OSHelper.h" + +#include <string> + +using namespace std; +using namespace boost; + +namespace avg { + +void Node::registerType() +{ + PublisherDefinitionPtr pPubDef = PublisherDefinition::create("Node"); + pPubDef->addMessage("CURSOR_DOWN"); + pPubDef->addMessage("CURSOR_MOTION"); + pPubDef->addMessage("CURSOR_UP"); + pPubDef->addMessage("CURSOR_OVER"); + pPubDef->addMessage("CURSOR_OUT"); + pPubDef->addMessage("HOVER_DOWN"); + pPubDef->addMessage("HOVER_MOTION"); + pPubDef->addMessage("HOVER_UP"); + pPubDef->addMessage("HOVER_OVER"); + pPubDef->addMessage("HOVER_OUT"); + pPubDef->addMessage("END_OF_FILE"); + pPubDef->addMessage("SIZE_CHANGED"); + + TypeDefinition def = TypeDefinition("node") + .addArg(Arg<string>("id", "", false, offsetof(Node, m_ID))) + .addArg(Arg<bool>("active", true, false, offsetof(Node, m_bActive))) + .addArg(Arg<bool>("sensitive", true, false, offsetof(Node, m_bSensitive))) + .addArg(Arg<float>("opacity", 1.0, false, offsetof(Node, m_Opacity))); + TypeRegistry::get()->registerType(def); +} + +Node::Node(const std::string& sPublisherName) + : Publisher(sPublisherName), + m_pParent(0), + m_pCanvas(), + m_State(NS_UNCONNECTED) +{ + ObjectCounter::get()->incRef(&typeid(*this)); +} + +Node::~Node() +{ + m_EventHandlerMap.clear(); + ObjectCounter::get()->decRef(&typeid(*this)); +} + +void Node::registerInstance(PyObject* pSelf, const DivNodePtr& pParent) +{ + ExportedObject::registerInstance(pSelf); + if (pParent) { + pParent->appendChild(getSharedThis()); + } +} + +void Node::checkSetParentError(DivNode* pParent) +{ + if (getParent() && pParent != 0) { + throw(Exception(AVG_ERR_UNSUPPORTED, + string("Can't change parent of node (") + getID() + ").")); + } + if (getSharedThis() == NodePtr()) { + throw(Exception(AVG_ERR_UNSUPPORTED, + "Node not registered. Please use Node.registerInstance() when deriving from libavg Nodes in python.")); + } +} + +void Node::setParent(DivNode* pParent, NodeState parentState, CanvasPtr pCanvas) +{ + AVG_ASSERT(getState() == NS_UNCONNECTED); + checkSetParentError(pParent); + m_pParent = pParent; + if (parentState != NS_UNCONNECTED) { + connect(pCanvas); + } +} + +void Node::removeParent() +{ + m_pParent = 0; +} + +DivNodePtr Node::getParent() const +{ + if (m_pParent == 0) { + return DivNodePtr(); + } else { + NodePtr pParent = m_pParent->getSharedThis(); + return boost::dynamic_pointer_cast<DivNode>(pParent); + } +} + +vector<NodePtr> Node::getParentChain() +{ + vector<NodePtr> pNodes; + NodePtr pCurNode = getSharedThis(); + while (pCurNode) { + pNodes.push_back(pCurNode); + pCurNode = pCurNode->getParent(); + } + return pNodes; +} + +void Node::connectDisplay() +{ + AVG_ASSERT(getState() == NS_CONNECTED); + setState(NS_CANRENDER); +} + +void Node::connect(CanvasPtr pCanvas) +{ + m_pCanvas = pCanvas; + setState(NS_CONNECTED); +} + +void Node::disconnect(bool bKill) +{ + AVG_ASSERT(getState() != NS_UNCONNECTED); + m_pCanvas.lock()->removeNodeID(getID()); + setState(NS_UNCONNECTED); + if (bKill) { + m_EventHandlerMap.clear(); + } +} + +void Node::unlink(bool bKill) +{ + DivNodePtr pParent = getParent(); + if (pParent != DivNodePtr()) { + pParent->removeChild(getSharedThis(), bKill); + } +} + +const string& Node::getID() const +{ + return m_ID; +} + +void Node::setID(const std::string& id) +{ + if (getState() != NS_UNCONNECTED) { + throw Exception(AVG_ERR_UNSUPPORTED, "Node with ID "+getID() + +" is connected. setID invalid."); + } + m_ID = id; +} + +float Node::getOpacity() const +{ + return m_Opacity; +} + +void Node::setOpacity(float opacity) +{ + m_Opacity = opacity; + if (m_Opacity < 0.0) { + m_Opacity = 0.0; + } else if (m_Opacity > 1.0) { + m_Opacity = 1.0; + } +} + +bool Node::getActive() const +{ + return m_bActive; +} + +void Node::setActive(bool bActive) +{ + if (bActive != m_bActive) { + m_bActive = bActive; + } +} + +bool Node::getSensitive() const +{ + return m_bSensitive; +} + +void Node::setSensitive(bool bSensitive) +{ + m_bSensitive = bSensitive; +} + +void Node::setMouseEventCapture() +{ + setEventCapture(MOUSECURSORID); +} + +void Node::releaseMouseEventCapture() +{ + releaseEventCapture(MOUSECURSORID); +} + +void Node::setEventCapture(int cursorID) +{ + Player::get()->setEventCapture(getSharedThis(), cursorID); +} + +void Node::releaseEventCapture(int cursorID) +{ + Player::get()->releaseEventCapture(cursorID); +} + +void Node::setEventHandler(Event::Type type, int sources, PyObject * pFunc) +{ + avgDeprecationWarning("1.7", "Node.setEventHandler()", "Node.subscribe()"); + for (int source = 1; source <= Event::NONE; source *= 2) { + if (source & sources) { + EventID id(type, (Event::Source)source); + EventHandlerMap::iterator it = m_EventHandlerMap.find(id); + if (it != m_EventHandlerMap.end()) { + m_EventHandlerMap.erase(it); + } + if (pFunc != Py_None) { + connectOneEventHandler(id, Py_None, pFunc); + } + } + } +} + +void Node::connectEventHandler(Event::Type type, int sources, + PyObject * pObj, PyObject * pFunc) +{ + avgDeprecationWarning("1.8", "Node.connectEventHandler()", "Node.subscribe()"); + for (int source = 1; source <= Event::NONE; source *= 2) { + if (source & sources) { + EventID id(type, (Event::Source)source); + connectOneEventHandler(id, pObj, pFunc); + } + } +// cerr << "connectEventHandler" << endl; +// dumpEventHandlers(); +} + +void Node::disconnectEventHandler(PyObject * pObj, PyObject * pFunc) +{ + avgDeprecationWarning("1.8", "Node.disconnectEventHandler()", "Node.unsubscribe()"); + int numDisconnected = 0; + EventHandlerMap::iterator it; + for (it = m_EventHandlerMap.begin(); it != m_EventHandlerMap.end();) { + EventHandlerArrayPtr pEventHandlers = it->second; + EventHandlerArray::iterator listIt; + for (listIt = pEventHandlers->begin(); listIt != pEventHandlers->end();) + { + EventHandler& eventHandler = *listIt; + if (eventHandler.m_pObj == pObj && + (pFunc == 0 || + PyObject_RichCompareBool(eventHandler.m_pMethod, pFunc, Py_EQ))) + { + listIt = pEventHandlers->erase(listIt); + numDisconnected++; + } else { + ++listIt; + } + } + + if (pEventHandlers->empty()) { + EventHandlerMap::iterator itErase = it; + ++it; + m_EventHandlerMap.erase(itErase); + } else { + ++it; + } + } +// cerr << "disconnectEventHandler" << endl; +// dumpEventHandlers(); +} + +bool Node::reactsToMouseEvents() +{ + return m_bActive && m_bSensitive; +} + +glm::vec2 Node::getRelPos(const glm::vec2& absPos) const +{ + glm::vec2 parentPos; + if (m_pParent == 0) { + parentPos = absPos; + } else { + parentPos = m_pParent->getSharedThis()->getRelPos(absPos); + } + return toLocal(parentPos); +} + +glm::vec2 Node::getAbsPos(const glm::vec2& relPos) const +{ + glm::vec2 thisPos = toGlobal(relPos); + glm::vec2 parentPos; + if (m_pParent == 0) { + parentPos = thisPos; + } else { + parentPos = m_pParent->getSharedThis()->getAbsPos(thisPos); + } + return parentPos; +} + +glm::vec2 Node::toLocal(const glm::vec2& globalPos) const +{ + return globalPos; +} + +glm::vec2 Node::toGlobal(const glm::vec2& localPos) const +{ + return localPos; +} + +NodePtr Node::getElementByPos(const glm::vec2& pos) +{ + vector<NodePtr> elements; + getElementsByPos(pos, elements); + if (elements.empty()) { + return NodePtr(); + } else { + return elements[0]; + } +} + +void Node::getElementsByPos(const glm::vec2& pos, vector<NodePtr>& pElements) +{ +} + +void Node::preRender(const VertexArrayPtr& pVA, bool bIsParentActive, + float parentEffectiveOpacity) +{ + m_EffectiveOpacity = m_Opacity*parentEffectiveOpacity; + m_bEffectiveActive = bIsParentActive && m_bActive; +} + +Node::NodeState Node::getState() const +{ + return m_State; +} + +CanvasPtr Node::getCanvas() const +{ + return m_pCanvas.lock(); +} + +bool Node::handleEvent(EventPtr pEvent) +{ + if (pEvent->getSource() != Event::NONE && pEvent->getSource() != Event::CUSTOM) { + string messageID = getEventMessageID(pEvent); + notifySubscribers(messageID, pEvent); + } + + EventID id(pEvent->getType(), pEvent->getSource()); + EventHandlerMap::iterator it = m_EventHandlerMap.find(id); + if (it != m_EventHandlerMap.end()) { + bool bHandled = false; + // We need to copy the array because python code in callbacks can + /// connect and disconnect event handlers. + EventHandlerArray eventHandlers = *(it->second); + EventHandlerArray::iterator listIt; + for (listIt = eventHandlers.begin(); listIt != eventHandlers.end(); ++listIt) { + bHandled = callPython(listIt->m_pMethod, pEvent); + } + return bHandled; + } else { + return false; + } +} + +float Node::getEffectiveOpacity() const +{ + return m_EffectiveOpacity; +} + +string Node::dump(int indent) +{ + string dumpStr = string(indent, ' ') + getTypeStr() + ": m_ID=" + getID() + + "m_Opacity=" + toString(m_Opacity); + return dumpStr; +} + +void Node::setState(Node::NodeState state) +{ +/* + cerr << m_ID << " state: "; + switch(state) { + case NS_UNCONNECTED: + cerr << "unconnected" << endl; + break; + case NS_CONNECTED: + cerr << "connected" << endl; + break; + case NS_CANRENDER: + cerr << "canrender" << endl; + break; + } +*/ + if (m_State == NS_UNCONNECTED) { + AVG_ASSERT(state != NS_CANRENDER); + } + if (m_State == NS_CANRENDER) { + AVG_ASSERT(state != NS_CONNECTED); + } + + m_State = state; +} + +void Node::initFilename(string& sFilename) +{ + if (sFilename != "") { + bool bAbsDir = sFilename[0] == '/'; +#ifdef _WIN32 + if (!bAbsDir) { + bAbsDir = (sFilename[0] == '\\' || sFilename[1] == ':'); + } +#endif + if (!bAbsDir) { + DivNodePtr pParent = getParent(); + if (!pParent) { + sFilename = Player::get()->getRootMediaDir()+sFilename; + } else { + sFilename = pParent->getEffectiveMediaDir()+sFilename; + } + } + } +} + +bool Node::checkReload(const std::string& sHRef, const ImagePtr& pImage, + Image::TextureCompression comp) +{ + string sLastFilename = pImage->getFilename(); + string sFilename = sHRef; + initFilename(sFilename); + if (sLastFilename != sFilename) { + try { + sFilename = convertUTF8ToFilename(sFilename); + if (sHRef == "") { + pImage->setEmpty(); + } else { + pImage->setFilename(sFilename, comp); + } + } catch (Exception& ex) { + pImage->setEmpty(); + logFileNotFoundWarning(ex.getStr()); + } + return true; + } else { + return false; + } +} + +bool Node::isVisible() const +{ + return getEffectiveActive() && getEffectiveOpacity() > 0.01; +} + +bool Node::getEffectiveActive() const +{ + return m_bEffectiveActive; +} + +NodePtr Node::getSharedThis() +{ + return dynamic_pointer_cast<Node>(ExportedObject::getSharedThis()); +} + +void Node::logFileNotFoundWarning(const string& sWarn) const +{ + unsigned int sev; + if (getState() != Node::NS_UNCONNECTED) { + sev = Logger::severity::WARNING; + } else { + sev = Logger::severity::INFO; + } + AVG_TRACE(Logger::category::MEMORY, sev, sWarn); +} + +void Node::connectOneEventHandler(const EventID& id, PyObject * pObj, + PyObject * pFunc) +{ + EventHandlerMap::iterator it = m_EventHandlerMap.find(id); + EventHandlerArrayPtr pEventHandlers; + if (it == m_EventHandlerMap.end()) { + pEventHandlers = EventHandlerArrayPtr(new EventHandlerArray); + m_EventHandlerMap[id] = pEventHandlers; + } else { + pEventHandlers = it->second; + } + pEventHandlers->push_back(EventHandler(pObj, pFunc)); +} + +void Node::dumpEventHandlers() +{ + EventHandlerMap::iterator it; + cerr << "-----" << endl; + for (it = m_EventHandlerMap.begin(); it != m_EventHandlerMap.end(); ++it) { + EventID id = it->first; + EventHandlerArrayPtr pEventHandlers = it->second; + cerr << "type: " << id.m_Type << ", source: " << id.m_Source << endl; + EventHandlerArray::iterator listIt; + for (listIt = pEventHandlers->begin(); listIt != pEventHandlers->end(); ++listIt) + { + EventHandler& handler = *listIt; + cerr << " " << handler.m_pObj << ", " << handler.m_pMethod + << endl; + } + } + cerr << "-----" << endl; +} + +string Node::getEventMessageID(const EventPtr& pEvent) +{ + Event::Source source = pEvent->getSource(); + if (source == Event::MOUSE || source == Event::TOUCH) { + switch (pEvent->getType()) { + case Event::CURSOR_DOWN: + return "CURSOR_DOWN"; + case Event::CURSOR_MOTION: + return "CURSOR_MOTION"; + case Event::CURSOR_UP: + return "CURSOR_UP"; + case Event::CURSOR_OVER: + return "CURSOR_OVER"; + case Event::CURSOR_OUT: + return "CURSOR_OUT"; + default: + AVG_ASSERT_MSG(false, + (string("Unknown message type ")+pEvent->typeStr()).c_str()); + return ""; + } + } else { + switch (pEvent->getType()) { + case Event::CURSOR_DOWN: + return "HOVER_DOWN"; + case Event::CURSOR_MOTION: + return "HOVER_MOTION"; + case Event::CURSOR_UP: + return "HOVER_UP"; + case Event::CURSOR_OVER: + return "HOVER_OVER"; + case Event::CURSOR_OUT: + return "HOVER_OUT"; + default: + AVG_ASSERT_MSG(false, + (string("Unknown message type ")+pEvent->typeStr()).c_str()); + return ""; + } + } +} + +bool Node::callPython(PyObject * pFunc, EventPtr pEvent) +{ + bool bOk = py::call<bool>(pFunc, pEvent); + return bOk; +} + +Node::EventID::EventID(Event::Type eventType, Event::Source source) + : m_Type(eventType), + m_Source(source) +{ +} + +bool Node::EventID::operator < (const EventID& other) const +{ + if (m_Type == other.m_Type) { + return m_Source < other.m_Source; + } else { + return m_Type < other.m_Type; + } +} + +Node::EventHandler::EventHandler(PyObject * pObj, PyObject * pMethod) +{ + Py_INCREF(pObj); + m_pObj = pObj; + Py_INCREF(pMethod); + m_pMethod = pMethod; +} + +Node::EventHandler::EventHandler(const EventHandler& other) +{ + Py_INCREF(other.m_pObj); + m_pObj = other.m_pObj; + Py_INCREF(other.m_pMethod); + m_pMethod = other.m_pMethod; +} + +Node::EventHandler::~EventHandler() +{ + Py_DECREF(m_pObj); + Py_DECREF(m_pMethod); +} + +} |