// SPDX-License-Identifier: GPL-2.0-or-later /** @file * @brief Event object representing a change of the XML document *//* * Authors: * Unknown author(s) * Krzysztof KosiƄski (documentation) * * Copyright (C) 2018 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #ifndef SEEN_INKSCAPE_XML_SP_REPR_ACTION_H #define SEEN_INKSCAPE_XML_SP_REPR_ACTION_H typedef unsigned int GQuark; #include #include #include "util/share.h" #include "util/forward-pointer-iterator.h" #include "inkgc/gc-managed.h" #include "xml/node.h" namespace Inkscape { namespace XML { /** * @brief Enumeration of all XML event types */ // enum EventType { // EVENT_ADD, ///< Child added // EVENT_DEL, ///< Child removed // EVENT_CHG_ATTR, ///< Attribute changed // EVENT_CHG_CONTENT, ///< Content changed // EVENT_CHG_ORDER ///< Order of children changed // }; /** * @brief Generic XML modification event * * This is the base class for all other modification events. It is actually a singly-linked * list of events, called an event chain or an event log. Logs of events that happened * in a transaction can be obtained from Document::commitUndoable(). Events can be replayed * to a NodeObserver, or undone (which is equivalent to replaying opposite events in reverse * order). * * Event logs are built by appending to the front, so by walking the list one iterates over * the events in reverse chronological order. */ class Event : public Inkscape::GC::Managed { public: virtual ~Event() = default; /** * @brief Pointer to the next event in the event chain * * Note that the event this pointer points to actually happened before this event. * This is because the event log is built by appending to the front. */ Event *next; /** * @brief Serial number of the event, not used at the moment */ int serial; /** * @brief Pointer to the node that was the object of the event * * Because the nodes are garbage-collected, this pointer guarantees that the node * will stay in memory as long as the event does. This simplifies rolling back * extensive deletions. */ Node *repr; struct IteratorStrategy { static Event const *next(Event const *action) { return action->next; } }; typedef Inkscape::Util::ForwardPointerIterator Iterator; typedef Inkscape::Util::ForwardPointerIterator ConstIterator; /** * @brief If possible, combine this event with the next to reduce memory use * @return Pointer to the optimized event chain, which may have changed */ Event *optimizeOne() { return _optimizeOne(); } /** * @brief Undo this event to an observer * * This method notifies the specified observer of an action opposite to the one that * is described by this event. */ void undoOne(NodeObserver &observer) const { _undoOne(observer); } /** * @brief Replay this event to an observer * * This method notifies the specified event of the same action that it describes. */ void replayOne(NodeObserver &observer) const { _replayOne(observer); } protected: Event(Node *r, Event *n) : next(n), serial(_next_serial++), repr(r) {} virtual Event *_optimizeOne()=0; virtual void _undoOne(NodeObserver &) const=0; virtual void _replayOne(NodeObserver &) const=0; private: static int _next_serial; }; /** * @brief Object representing child addition */ class EventAdd : public Event { public: EventAdd(Node *repr, Node *c, Node *rr, Event *next) : Event(repr, next), child(c), ref(rr) {} /// The added child node Node *child; /// The node after which the child has been added, or NULL if it was added as first Node *ref; private: Event *_optimizeOne() override; void _undoOne(NodeObserver &observer) const override; void _replayOne(NodeObserver &observer) const override; }; /** * @brief Object representing child removal */ class EventDel : public Event { public: EventDel(Node *repr, Node *c, Node *rr, Event *next) : Event(repr, next), child(c), ref(rr) {} /// The child node that was removed Node *child; /// The node after which the removed node was in the sibling order, or NULL if it was first Node *ref; private: Event *_optimizeOne() override; void _undoOne(NodeObserver &observer) const override; void _replayOne(NodeObserver &observer) const override; }; /** * @brief Object representing attribute change */ class EventChgAttr : public Event { public: EventChgAttr(Node *repr, GQuark k, Inkscape::Util::ptr_shared ov, Inkscape::Util::ptr_shared nv, Event *next) : Event(repr, next), key(k), oldval(ov), newval(nv) {} /// GQuark corresponding to the changed attribute's name GQuark key; /// Value of the attribute before the change Inkscape::Util::ptr_shared oldval; /// Value of the attribute after the change Inkscape::Util::ptr_shared newval; private: Event *_optimizeOne() override; void _undoOne(NodeObserver &observer) const override; void _replayOne(NodeObserver &observer) const override; }; /** * @brief Object representing content change */ class EventChgContent : public Event { public: EventChgContent(Node *repr, Inkscape::Util::ptr_shared ov, Inkscape::Util::ptr_shared nv, Event *next) : Event(repr, next), oldval(ov), newval(nv) {} /// Content of the node before the change Inkscape::Util::ptr_shared oldval; /// Content of the node after the change Inkscape::Util::ptr_shared newval; private: Event *_optimizeOne() override; void _undoOne(NodeObserver &observer) const override; void _replayOne(NodeObserver &observer) const override; }; /** * @brief Object representing child order change */ class EventChgOrder : public Event { public: EventChgOrder(Node *repr, Node *c, Node *orr, Node *nrr, Event *next) : Event(repr, next), child(c), oldref(orr), newref(nrr) {} /// The node that was relocated in sibling order Node *child; /// The node after which the relocated node was in the sibling order before the change, or NULL if it was first Node *oldref; /// The node after which the relocated node is after the change, or if it's first Node *newref; private: Event *_optimizeOne() override; void _undoOne(NodeObserver &observer) const override; void _replayOne(NodeObserver &observer) const override; }; /** * @brief Object representing element name change. */ class EventChgElementName : public Event { public: EventChgElementName(Node* repr, GQuark old_name, GQuark new_name, Event* next) : Event(repr, next), old_name(old_name), new_name(new_name) {} /// GQuark corresponding to the old element name. GQuark old_name; /// GQuark corresponding to the new element name. GQuark new_name; private: Event* _optimizeOne() override; void _undoOne(NodeObserver& observer) const override; void _replayOne(NodeObserver& observer) const override; }; } } #endif /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :