summaryrefslogtreecommitdiff
path: root/src/core/dom
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/dom')
-rw-r--r--src/core/dom/Node.cpp58
-rw-r--r--src/core/dom/Node.hpp233
2 files changed, 286 insertions, 5 deletions
diff --git a/src/core/dom/Node.cpp b/src/core/dom/Node.cpp
index a65fc7f..c9651fb 100644
--- a/src/core/dom/Node.cpp
+++ b/src/core/dom/Node.cpp
@@ -23,6 +23,16 @@ namespace dom {
/* Class Node */
+void Node::setName(std::string name)
+{
+ // Call the name change event
+ NameChangeEvent ev{this->name, name};
+ triggerEvent(ev);
+
+ // Set the new name
+ this->name = std::move(name);
+}
+
void Node::path(std::vector<std::string> &p) const
{
if (!isRoot()) {
@@ -46,9 +56,9 @@ void Node::doResolve(std::vector<Rooted<Node>> &res,
}
int Node::resolve(std::vector<Rooted<Node>> &res,
- const std::vector<std::string> &path, Filter filter,
- void *filterData, unsigned idx, VisitorSet &visited,
- const std::string *alias)
+ const std::vector<std::string> &path, Filter filter,
+ void *filterData, unsigned idx, VisitorSet &visited,
+ const std::string *alias)
{
// Abort if this node was already visited for this path index
std::pair<const Node *, int> recKey = std::make_pair(this, idx);
@@ -88,6 +98,48 @@ std::vector<Rooted<Node>> Node::resolve(const std::vector<std::string> &path,
return res;
}
+int Node::registerEventHandler(EventType type, EventHandler handler,
+ Handle<Managed> owner,
+ bool includeChildren)
+{
+ const int id = handlerIdCounter++;
+ handlers.insert(std::make_pair(
+ type,
+ EventHandlerDescriptor{id, handler, owner, this, includeChildren}));
+ return id;
+}
+
+bool Node::unregisterEventHandler(int id) {
+ for (auto it = handlers.begin(); it != handlers.end(); it++) {
+ if (it->second.id == id) {
+ handlers.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Node::triggerEvent(Event &event, bool fromChild) {
+ bool res = false;
+ // Iterate over all event handlers
+ const auto range = handlers.equal_range(event.type);
+ for (auto it = range.first; it != range.second; it++) {
+ // Fetch a reference to the descriptor, check whether it should be
+ // called for bubbled events
+ EventHandlerDescriptor descr = it->second;
+ if (!fromChild || descr.includeChildren) {
+ descr.handler(event, descr.owner);
+ res = true;
+ }
+ }
+
+ // If possible, let the event bubble up to the parent node
+ if (event.canBubble() && !parent.isNull()) {
+ res = parent->triggerEvent(event, true) | res;
+ }
+ return res;
+}
+
}
}
diff --git a/src/core/dom/Node.hpp b/src/core/dom/Node.hpp
index 98a51d9..249d1f2 100644
--- a/src/core/dom/Node.hpp
+++ b/src/core/dom/Node.hpp
@@ -19,9 +19,10 @@
#ifndef _OUSIA_DOM_NODE_HPP_
#define _OUSIA_DOM_NODE_HPP_
+#include <functional>
+#include <map>
#include <string>
#include <vector>
-#include <functional>
#include <unordered_set>
#include <core/Managed.hpp>
@@ -29,6 +30,189 @@
namespace ousia {
namespace dom {
+/* Forward declarations */
+class Node;
+class Event;
+
+/**
+ * EventType is an enum containing all possible node events. New event types
+ * should be added here.
+ */
+enum class EventType : int {
+ /**
+ * Generic update event which may be triggered if some important property
+ * of the node is changed.
+ */
+ UPDATE,
+
+ /**
+ * The NAME_CHANGE event is used to inform listeners that the name of the
+ * node has changed.
+ */
+ NAME_CHANGE,
+
+ /**
+ * The ADD_CHILD event is used to inform listeners that the node got a new
+ * child in any of its child node lists.
+ */
+ ADD_CHILD,
+
+ /**
+ * The DELETE_CHILD event is used to inform listeners that the node got a
+ * new child in any of its child node lists.
+ */
+ DELETE_CHILD
+};
+
+/**
+ * Definition of the EventHandler function.
+ *
+ * @param event is a reference to the object holding the event data.
+ * @param owner is a reference to the managed object that was given in the
+ * registerEventHandler function.
+ */
+using EventHandler = void (*)(const Event &event, Handle<Managed> owner);
+
+/**
+ * The Event class and its child classes are responsible for containing the
+ * actual event data which further describes the event to the event handlers.
+ * Instances of this class and its children must be declared on the stack or as
+ * a temporary.
+ */
+class Event {
+private:
+ /**
+ * True as long as the event can bubble up the node hirarchy.
+ */
+ mutable bool bubble;
+
+public:
+ /**
+ * Contains the actual event type of this class.
+ */
+ const EventType type;
+
+ /**
+ * Node on which the event was triggered.
+ */
+ Rooted<Node> sender;
+
+ /**
+ * Constructor of the Event class.
+ *
+ * @param type is an element from the EventType enum.
+ * @param bubble if set to true, the event can bubble up the node hirarchy.
+ */
+ Event(EventType type, bool bubble = true) : bubble(bubble), type(type){};
+
+ /**
+ * Delete the copy constructor.
+ */
+ Event(const Event &) = delete;
+
+ /**
+ * Delete the assignment operator.
+ */
+ Event &operator=(const Event &) = delete;
+
+ /**
+ * Stops the propagation of this event to the parent element.
+ */
+ void stopPropagation() const { bubble = false; }
+
+ /**
+ * Returns true if the event can still bubble.
+ */
+ bool canBubble() const { return bubble; }
+};
+
+/**
+ * Event used when the name of a node has changed.
+ */
+class NameChangeEvent : public Event {
+public:
+ /**
+ * Reference to a string containing the old name of the node.
+ */
+ const std::string &oldName;
+
+ /**
+ * Reference to a string containing the new name of the node.
+ */
+ const std::string &newName;
+
+ /**
+ * Constructor of the NameChangeEvent class.
+ *
+ * @param oldName is a reference to a string containing the old name of the
+ * node.
+ * @param newName is a reference to a string containing the new name of the
+ * node.
+ * @param bubble if set to true, the event can bubble up the node hirarchy.
+ */
+ NameChangeEvent(const std::string &oldName, const std::string &newName,
+ bool bubble = true)
+ : Event(EventType::NAME_CHANGE, bubble),
+ oldName(oldName),
+ newName(newName)
+ {
+ }
+};
+
+/**
+ * Struct containing the data which describes a single registered event handler.
+ * Note that the event type (e.g. which type of event this element was
+ * registered for) is stored outside the EventHandlerDescriptor (in the map
+ * storing the registered event handlers).
+ */
+struct EventHandlerDescriptor {
+ /**
+ * Unique id of the event handler.
+ */
+ const int id;
+
+ /**
+ * Reference to the event handler containing the events.
+ */
+ const EventHandler handler;
+
+ /**
+ * Reference to the managed element which owns the event handler. The object
+ * which owns the Owned handler is given in the constructor.
+ */
+ const Owned<Managed> owner;
+
+ /**
+ * Set to true, if this event handler listens to bubbled events comming from
+ * child nodes.
+ */
+ const bool includeChildren;
+
+ /**
+ * Constructor of the EventHandlerDescriptor struct.
+ *
+ * @param id is the node-unique id of the EventHandlerDescriptor.
+ * @param handler is the function pointer which is going to be called once
+ * the associated event handler has fired.
+ * @param owner is a user-specified object which owns the method that is
+ * going to be called. This can be used to make sure that the method which
+ * handles the events has access to its owned object as long as the event
+ * handler lives.
+ * @param parent is the parent element this descriptor belongs to. The
+ * a handle to the "owner" object will be created on behalf of the parent.
+ * @param includeChildren is set to true if the event handler should handle
+ * events comming from child elements.
+ */
+ EventHandlerDescriptor(int id, EventHandler handler, Handle<Managed> owner,
+ Managed *parent, bool includeChildren)
+ : id(id),
+ handler(handler),
+ owner(owner, parent),
+ includeChildren(includeChildren)
+ {
+ }
+};
+
/**
* The Node class builds the base class for any Node within the DOM graph. A
* node may either be a descriptive node (such as a domain description etc.)
@@ -85,6 +269,17 @@ private:
Owned<Node> parent;
/**
+ * Current id counter. The id counter may be used to create ids which are
+ * unique inside the realm of this manager instance.
+ */
+ int handlerIdCounter = 0;
+
+ /**
+ * Multimap containing all registered event handlers for this node.
+ */
+ std::multimap<EventType, EventHandlerDescriptor> handlers;
+
+ /**
* Private version of the "path" function used to construct the path. Calls
* the path function of the parent node and adds the own name to the given
* vector.
@@ -149,7 +344,7 @@ public:
*
* @param name is the name that should be assigned to the node.
*/
- void setName(std::string name) { this->name = std::move(name); }
+ void setName(std::string name);
/**
* Returns the name of the node.
@@ -281,6 +476,40 @@ public:
{
return resolve(std::vector<std::string>{name}, nullptr, nullptr);
}
+
+ /**
+ * Registers a new event handler for listening to the given event type.
+ *
+ * @param type is the event type the handler should listen to.
+ * @param handler is the handler that should be called.
+ * @param owner is an object the handler belongs to. May be nullptr.
+ * @param includeChildren if set to true, the event handler is also called
+ * if the same event is triggered on one of the child nodes.
+ * @return a unique event handler.
+ */
+ int registerEventHandler(EventType type, EventHandler handler,
+ Handle<Managed> owner = nullptr,
+ bool includeChildren = false);
+
+ /**
+ * Unregisters the given event handler from the node. Note that removing an
+ * event handler has linear time.
+ *
+ * @param id is the unique event handler id.
+ * @return true if the given event handler was successfully unregistered.
+ */
+ bool unregisterEventHandler(int id);
+
+ /**
+ * Triggers an event on this node.
+ *
+ * @param event is a pointer at the event that should be triggered. The
+ * calling function has ownership over the given event.
+ * @param fromChild is set to true if the triggerEvent function is called
+ * from a child node.
+ * @return true if any event handler was found.
+ */
+ bool triggerEvent(Event &event, bool fromChild = false);
};
}
}