You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeaddons/konq-plugins/domtreeviewer/domtreecommands.cpp

563 lines
12 KiB

/* 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 <qmap.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 ==============================================
QString 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 QMap<DOM::Node, bool>
{
};
}
// == ManipulationCommand ===========================================
ManipulationCommand::ManipulationCommand() : _exception(0), changedNodes(0)
, _reapplied(false) , allow_signals(true)
{
}
ManipulationCommand::~ManipulationCommand()
{
}
void ManipulationCommand::connect(const char *signal, QObject *recv, const char *slot)
{
QObject::connect(mcse(), signal, recv, slot);
}
void ManipulationCommand::handleException(DOM::DOMException &ex)
{
_exception = ex;
QString 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 QString &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 (QPtrListIterator<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
QPtrListIterator<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();
}
QString MultiCommand::name() const
{
return _name;
}
// == AddAttributeCommand ===========================================
AddAttributeCommand::AddAttributeCommand(const DOM::Element &element, const QString &attrName, const QString &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);
}
QString AddAttributeCommand::name() const
{
return i18n("Add attribute");
}
// == ChangeAttributeValueCommand ====================================
ChangeAttributeValueCommand::ChangeAttributeValueCommand(
const DOM::Element &element, const QString &attr, const QString &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);
}
QString ChangeAttributeValueCommand::name() const
{
return i18n("Change attribute value");
}
// == RemoveAttributeCommand ========================================
RemoveAttributeCommand::RemoveAttributeCommand(const DOM::Element &element, const QString &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);
}
QString RemoveAttributeCommand::name() const
{
return i18n("Remove attribute");
}
// == RenameAttributeCommand ========================================
RenameAttributeCommand::RenameAttributeCommand(const DOM::Element &element, const QString &attrOldName, const QString &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);
}
QString RenameAttributeCommand::name() const
{
return i18n("Rename attribute");
}
// == ChangeCDataCommand ========================================
ChangeCDataCommand::ChangeCDataCommand(const DOM::CharacterData &cdata, const QString &value)
: cdata(cdata), value(value), has_newlines(false)
{
}
ChangeCDataCommand::~ChangeCDataCommand()
{
}
void ChangeCDataCommand::apply()
{
if (!shouldReapply()) {
oldValue = cdata.data();
has_newlines =
QConstString(value.unicode(), value.length()).string().contains('\n')
|| QConstString(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;
}
QString 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;
}
QString 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;
}
QString 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;
}
QString MoveNodeCommand::name() const
{
return i18n("Move node");
}
#include "domtreecommands.moc"
#undef MCSE