|
|
|
/* This file is part of the KDE project
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005 Leo Savernik <l.savernik@aon.at>
|
|
|
|
*
|
|
|
|
* 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 <dom/dom_doc.h>
|
|
|
|
#include <dom/dom_exception.h>
|
|
|
|
|
|
|
|
#include <klocale.h>
|
|
|
|
|
|
|
|
#include <tqmap.h>
|
|
|
|
|
|
|
|
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").tqarg(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<DOM::Node, bool>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// == 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<ManipulationCommand> 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<ManipulationCommand> 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 = "<dummy>";
|
|
|
|
}
|
|
|
|
|
|
|
|
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.tqunicode(), value.length()).string().contains('\n')
|
|
|
|
|| TQConstString(oldValue.tqunicode(), 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
|