/* This file is part of the KDE project * * Copyright (C) 2005 Leo Savernik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "domtreecommands.h" #include #include #include #include using namespace domtreeviewer; static const char * const dom_error_msgs[] = { I18N_NOOP("No error"), I18N_NOOP("Index size exceeded"), I18N_NOOP("DOMString size exceeded"), I18N_NOOP("Hierarchy request error"), I18N_NOOP("Wrong document"), I18N_NOOP("Invalid character"), I18N_NOOP("No data allowed"), I18N_NOOP("No modification allowed"), I18N_NOOP("Not found"), I18N_NOOP("Not supported"), I18N_NOOP("Attribute in use"), I18N_NOOP("Invalid state"), I18N_NOOP("Syntax error"), I18N_NOOP("Invalid modification"), I18N_NOOP("Namespace error"), I18N_NOOP("Invalid access") }; // == global functions ============================================== TQString domtreeviewer::domErrorMessage(int dom_err) { if ((unsigned)dom_err > sizeof dom_error_msgs/sizeof dom_error_msgs[0]) return i18n("Unknown Exception %1").arg(dom_err); else return i18n(dom_error_msgs[dom_err]); } // == ManipulationCommandSignalEmitter ============================== static ManipulationCommandSignalEmitter *_mcse; ManipulationCommandSignalEmitter::ManipulationCommandSignalEmitter() {} ManipulationCommandSignalEmitter::~ManipulationCommandSignalEmitter() {} namespace domtreeviewer { ManipulationCommandSignalEmitter* ManipulationCommand::mcse() { if (!_mcse) _mcse = new ManipulationCommandSignalEmitter; return _mcse; } } // == ChangedNodeSet ================================================ namespace domtreeviewer { // collection of nodes for which to emit the nodeChanged signal inline static bool operator <(const DOM::Node &n1, const DOM::Node &n2) { return (long)n1.handle() - (long)n2.handle() < 0; } class ChangedNodeSet : public TQMap { }; } // == ManipulationCommand =========================================== ManipulationCommand::ManipulationCommand() : _exception(0), changedNodes(0) , _reapplied(false) , allow_signals(true) { } ManipulationCommand::~ManipulationCommand() { } void ManipulationCommand::connect(const char *signal, TQObject *recv, const char *slot) { TQObject::connect(mcse(), signal, recv, slot); } void ManipulationCommand::handleException(DOM::DOMException &ex) { _exception = ex; TQString msg = name() + ": " + domErrorMessage(ex.code); emit mcse()->error(ex.code, msg); } void ManipulationCommand::checkAndEmitSignals() { if (allow_signals) { if (changedNodes) { ChangedNodeSet::Iterator end = changedNodes->end(); for (ChangedNodeSet::Iterator it = changedNodes->begin(); it != end; ++it) { emit mcse()->nodeChanged(it.key()); } } if (struc_changed) emit mcse()->structureChanged(); } if (changedNodes) changedNodes->clear(); } void ManipulationCommand::addChangedNode(const DOM::Node &node) { if (!changedNodes) changedNodes = new ChangedNodeSet; changedNodes->insert(node, true); } void ManipulationCommand::execute() { if (!isValid()) return; struc_changed = false; try { if (shouldReapply()) reapply(); else apply(); checkAndEmitSignals(); } catch(DOM::DOMException &ex) { handleException(ex); } _reapplied = true; } void ManipulationCommand::unexecute() { if (!isValid()) return; struc_changed = false; try { unapply(); checkAndEmitSignals(); } catch(DOM::DOMException &ex) { handleException(ex); } } void ManipulationCommand::reapply() { apply(); } // == MultiCommand =========================================== MultiCommand::MultiCommand(const TQString &desc) : _name(desc) { cmds.setAutoDelete(true); } MultiCommand::~MultiCommand() { } void MultiCommand::addCommand(ManipulationCommand *cmd) { cmd->allow_signals = false; cmds.append(cmd); } void MultiCommand::apply() { // apply in forward order for (TQPtrListIterator it = cmds; *it; ++it) { try { if (shouldReapply()) (*it)->reapply(); else (*it)->apply(); struc_changed |= (*it)->struc_changed; mergeChangedNodesFrom(*it); } catch (DOM::DOMException &) { // rollback for (--it; *it; --it) { try { (*it)->unapply(); } catch(DOM::DOMException &) { // ignore } } throw; } } } void MultiCommand::unapply() { // unapply in reverse order TQPtrListIterator it = cmds; for (it.toLast(); *it; --it) { try { (*it)->unapply(); struc_changed |= (*it)->struc_changed; mergeChangedNodesFrom(*it); } catch (DOM::DOMException &) { // rollback for (++it; *it; ++it) { try { (*it)->reapply(); } catch(DOM::DOMException &) { // ignore } } throw; } } } void MultiCommand::mergeChangedNodesFrom(ManipulationCommand *cmd) { if (!cmd->changedNodes) return; ChangedNodeSet::ConstIterator end = cmd->changedNodes->end(); for (ChangedNodeSet::ConstIterator it = cmd->changedNodes->begin(); it != end; ++it) { addChangedNode(it.key()); } cmd->changedNodes->clear(); } TQString MultiCommand::name() const { return _name; } // == AddAttributeCommand =========================================== AddAttributeCommand::AddAttributeCommand(const DOM::Element &element, const TQString &attrName, const TQString &attrValue) : _element(element), attrName(attrName), attrValue(attrValue) { if (attrValue.isEmpty()) this->attrValue = ""; } AddAttributeCommand::~AddAttributeCommand() { } void AddAttributeCommand::apply() { _element.setAttribute(attrName, attrValue); addChangedNode(_element); } void AddAttributeCommand::unapply() { _element.removeAttribute(attrName); addChangedNode(_element); } TQString AddAttributeCommand::name() const { return i18n("Add attribute"); } // == ChangeAttributeValueCommand ==================================== ChangeAttributeValueCommand::ChangeAttributeValueCommand( const DOM::Element &element, const TQString &attr, const TQString &value) : _element(element), _attr(attr), new_value(value) { } ChangeAttributeValueCommand::~ChangeAttributeValueCommand() { } void ChangeAttributeValueCommand::apply() { if (!shouldReapply()) old_value = _element.getAttribute(_attr); _element.setAttribute(_attr, new_value); addChangedNode(_element); } void ChangeAttributeValueCommand::unapply() { _element.setAttribute(_attr, old_value); addChangedNode(_element); } TQString ChangeAttributeValueCommand::name() const { return i18n("Change attribute value"); } // == RemoveAttributeCommand ======================================== RemoveAttributeCommand::RemoveAttributeCommand(const DOM::Element &element, const TQString &attrName) : _element(element), attrName(attrName) { } RemoveAttributeCommand::~RemoveAttributeCommand() { } void RemoveAttributeCommand::apply() { // kdDebug(90180) << k_funcinfo << _element.nodeName().string() << ": " << attrName.string() << endl; if (!shouldReapply()) oldAttrValue = _element.getAttribute(attrName); _element.removeAttribute(attrName); addChangedNode(_element); } void RemoveAttributeCommand::unapply() { _element.setAttribute(attrName, oldAttrValue); addChangedNode(_element); } TQString RemoveAttributeCommand::name() const { return i18n("Remove attribute"); } // == RenameAttributeCommand ======================================== RenameAttributeCommand::RenameAttributeCommand(const DOM::Element &element, const TQString &attrOldName, const TQString &attrNewName) : _element(element), attrOldName(attrOldName), attrNewName(attrNewName) { } RenameAttributeCommand::~RenameAttributeCommand() { } void RenameAttributeCommand::apply() { if (!shouldReapply()) attrValue = _element.getAttribute(attrOldName); _element.removeAttribute(attrOldName); _element.setAttribute(attrNewName, attrValue); addChangedNode(_element); } void RenameAttributeCommand::unapply() { _element.removeAttribute(attrNewName); _element.setAttribute(attrOldName, attrValue); addChangedNode(_element); } TQString RenameAttributeCommand::name() const { return i18n("Rename attribute"); } // == ChangeCDataCommand ======================================== ChangeCDataCommand::ChangeCDataCommand(const DOM::CharacterData &cdata, const TQString &value) : cdata(cdata), value(value), has_newlines(false) { } ChangeCDataCommand::~ChangeCDataCommand() { } void ChangeCDataCommand::apply() { if (!shouldReapply()) { oldValue = cdata.data(); has_newlines = TQConstString(value.unicode(), value.length()).string().contains('\n') || TQConstString(oldValue.unicode(), oldValue.length()).string().contains('\n'); } cdata.setData(value); addChangedNode(cdata); struc_changed = has_newlines; } void ChangeCDataCommand::unapply() { cdata.setData(oldValue); addChangedNode(cdata); struc_changed = has_newlines; } TQString ChangeCDataCommand::name() const { return i18n("Change textual content"); } // == ManipulateNodeCommand =========================================== ManipulateNodeCommand::ManipulateNodeCommand(const DOM::Node &node, const DOM::Node &parent, const DOM::Node &after) : _node(node), _parent(parent), _after(after) { } ManipulateNodeCommand::~ManipulateNodeCommand() { } void ManipulateNodeCommand::insert() { _parent.insertBefore(_node, _after); } void ManipulateNodeCommand::remove() { DOM::DocumentFragment frag = _node; if (frag.isNull()) { // do a normal remove _node = _parent.removeChild(_node); } else { // remove fragment nodes and recreate fragment DOM::DocumentFragment newfrag = _parent.ownerDocument().createDocumentFragment(); for (DOM::Node i = frag.firstChild(); !i.isNull(); i = i.nextSibling()) { newfrag.appendChild(_parent.removeChild(i)); } _node = newfrag; } } // == InsertNodeCommand =========================================== InsertNodeCommand::InsertNodeCommand(const DOM::Node &node, const DOM::Node &parent, const DOM::Node &after) : ManipulateNodeCommand(node, parent, after) { } InsertNodeCommand::~InsertNodeCommand() { } void InsertNodeCommand::apply() { insert(); struc_changed = true; } void InsertNodeCommand::unapply() { remove(); struc_changed = true; } TQString InsertNodeCommand::name() const { return i18n("Insert node"); } // == RemoveNodeCommand =========================================== RemoveNodeCommand::RemoveNodeCommand(const DOM::Node &node, const DOM::Node &parent, const DOM::Node &after) : ManipulateNodeCommand(node, parent, after) { } RemoveNodeCommand::~RemoveNodeCommand() { } void RemoveNodeCommand::apply() { remove(); struc_changed = true; } void RemoveNodeCommand::unapply() { insert(); struc_changed = true; } TQString RemoveNodeCommand::name() const { return i18n("Remove node"); } // == MoveNodeCommand =========================================== MoveNodeCommand::MoveNodeCommand(const DOM::Node &node, const DOM::Node &parent, const DOM::Node &after) : _node(node), new_parent(parent), new_after(after) { old_parent = node.parentNode(); old_after = node.nextSibling(); } MoveNodeCommand::~MoveNodeCommand() { } void MoveNodeCommand::apply() { old_parent.removeChild(_node); try { new_parent.insertBefore(_node, new_after); } catch (DOM::DOMException &) { try { // rollback old_parent.insertBefore(_node, old_after); } catch (DOM::DOMException &) {} throw; } struc_changed = true; } void MoveNodeCommand::unapply() { new_parent.removeChild(_node); try { old_parent.insertBefore(_node, old_after); } catch (DOM::DOMException &) { try { // rollback new_parent.insertBefore(_node, new_after); } catch (DOM::DOMException &) {} throw; } struc_changed = true; } TQString MoveNodeCommand::name() const { return i18n("Move node"); } #include "domtreecommands.moc" #undef MCSE