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/domtreeview.cpp

1227 lines
34 KiB

/***************************************************************************
domtreeview.cpp
-------------------
copyright : (C) 2001 - The Kafka Team/Andreas Schlapbach
(C) 2005 - Leo Savernik
email : kde-kafka@master.kde.org
schlpbch@iam.unibe.ch
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "domtreeview.h"
#include "domlistviewitem.h"
#include "domtreewindow.h"
#include "domtreecommands.h"
#include "attributeeditdialog.h"
#include "elementeditdialog.h"
#include "texteditdialog.h"
#include "signalreceiver.h"
#include <assert.h>
#include <qapplication.h>
#include <qcheckbox.h>
#include <qevent.h>
#include <qfont.h>
#include <qfile.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qpopupmenu.h>
#include <qtextstream.h>
#include <qtimer.h>
#include <qwidgetstack.h>
#include <dom/dom_core.h>
#include <dom/html_base.h>
#include <kaction.h>
#include <kdebug.h>
#include <kcombobox.h>
#include <kdialog.h>
#include <keditcl.h>
#include <kfiledialog.h>
#include <kglobalsettings.h>
#include <khtml_part.h>
#include <klineedit.h>
#include <klistview.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpushbutton.h>
#include <kshortcut.h>
#include <kstdguiitem.h>
#include <ktextedit.h>
using namespace domtreeviewer;
DOMTreeView::DOMTreeView(QWidget *parent, const char* name, bool /*allowSaving*/)
: DOMTreeViewBase(parent, name), m_expansionDepth(5), m_maxDepth(0),
m_bPure(true), m_bShowAttributes(true), m_bHighlightHTML(true),
m_findDialog(0), focused_child(0)
{
part = 0;
const QFont font(KGlobalSettings::generalFont());
m_listView->setFont( font );
m_listView->setSorting(-1);
m_rootListView = m_listView;
m_pureCheckBox->setChecked(m_bPure);
connect(m_pureCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotPureToggled(bool)));
m_showAttributesCheckBox->setChecked(m_bShowAttributes);
connect(m_showAttributesCheckBox, SIGNAL(toggled(bool)), this,
SLOT(slotShowAttributesToggled(bool)));
m_highlightHTMLCheckBox->setChecked(m_bHighlightHTML);
connect(m_highlightHTMLCheckBox, SIGNAL(toggled(bool)), this,
SLOT(slotHighlightHTMLToggled(bool)));
connect(m_listView, SIGNAL(clicked(QListViewItem *)), this,
SLOT(slotItemClicked(QListViewItem *)));
connect(m_listView, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
SLOT(showDOMTreeContextMenu(QListViewItem *, const QPoint &, int)));
connect(m_listView, SIGNAL(moved(QPtrList<QListViewItem> &, QPtrList<QListViewItem> &, QPtrList<QListViewItem> &)),
SLOT(slotMovedItems(QPtrList<QListViewItem> &, QPtrList<QListViewItem> &, QPtrList<QListViewItem> &)));
// set up message line
messageLinePane->hide();
connect(messageHideBtn, SIGNAL(clicked()), SLOT(hideMessageLine()));
connect(messageListBtn, SIGNAL(clicked()), mainWindow(), SLOT(showMessageLog()));
installEventFilter(m_listView);
ManipulationCommand::connect(SIGNAL(nodeChanged(const DOM::Node &)), this, SLOT(slotRefreshNode(const DOM::Node &)));
ManipulationCommand::connect(SIGNAL(structureChanged()), this, SLOT(refresh()));
initDOMNodeInfo();
installEventFilter(this);
}
DOMTreeView::~DOMTreeView()
{
delete m_findDialog;
disconnectFromActivePart();
}
void DOMTreeView::setHtmlPart(KHTMLPart *_part)
{
KHTMLPart *oldPart = part;
part = _part;
if (oldPart) {
// nothing here yet
}
parentWidget()->setCaption( part ? i18n( "DOM Tree for %1" ).arg(part->url().prettyURL()) : i18n("DOM Tree") );
QTimer::singleShot(0, this, SLOT(slotSetHtmlPartDelayed()));
}
DOMTreeWindow *DOMTreeView::mainWindow() const
{
return static_cast<DOMTreeWindow *>(parentWidget());
}
bool DOMTreeView::eventFilter(QObject *o, QEvent *e)
{
if (e->type() == QEvent::AccelOverride) {
QKeyEvent *ke = static_cast<QKeyEvent *>(e);
kdDebug(90180) << " acceloverride " << ke->key() << " o " << o->name() << endl;
if (o == m_listView) { // DOM tree
KKey ks = mainWindow()->deleteNodeAction()->shortcut().seq(0).key(0);
if (ke->key() == ks.keyCodeQt())
return true;
} else if (o == nodeAttributes) {
KKey ks = mainWindow()->deleteAttributeAction()->shortcut().seq(0).key(0);
if (ke->key() == ks.keyCodeQt())
return true;
}
} else if (e->type() == QEvent::FocusIn) {
kdDebug(90180) << " focusin o " << o->name() << endl;
if (o != this) {
focused_child = o;
}
} else if (e->type() == QEvent::FocusOut) {
kdDebug(90180) << " focusout o " << o->name() << endl;
if (o != this) {
focused_child = 0;
}
}
return false;
}
void DOMTreeView::activateNode(const DOM::Node &node)
{
slotShowNode(node);
initializeOptionsFromNode(node);
}
void DOMTreeView::slotShowNode(const DOM::Node &pNode)
{
if (QListViewItem *item = m_itemdict[pNode.handle()]) {
m_listView->setCurrentItem(item);
m_listView->ensureItemVisible(item);
}
}
void DOMTreeView::slotShowTree(const DOM::Node &pNode)
{
DOM::Node child;
m_listView->clear();
m_itemdict.clear();
try
{
child = pNode.firstChild();
}
catch (DOM::DOMException &)
{
return;
}
while(!child.isNull()) {
showRecursive(0, child, 0);
child = child.nextSibling();
}
m_maxDepth--;
//kdDebug(90180) << " Max Depth: " << m_maxDepth << endl;
}
void DOMTreeView::showRecursive(const DOM::Node &pNode, const DOM::Node &node, uint depth)
{
DOMListViewItem *cur_item;
DOMListViewItem *parent_item = m_itemdict[pNode.handle()];
if (depth > m_maxDepth) {
m_maxDepth = depth;
}
if (depth == 0) {
cur_item = new DOMListViewItem(node, m_listView);
m_document = pNode.ownerDocument();
} else {
cur_item = new DOMListViewItem(node, parent_item);
}
//kdDebug(90180) << node.nodeName().string() << " [" << depth << "]" << endl;
addElement (node, cur_item, false);
cur_item->setOpen(depth < m_expansionDepth);
if(node.handle()) {
m_itemdict.insert(node.handle(), cur_item);
}
DOM::Node child = node.lastChild();
if (child.isNull()) {
DOM::HTMLFrameElement frame = node;
if (!frame.isNull()) child = frame.contentDocument().documentElement();
}
while(!child.isNull()) {
showRecursive(node, child, depth + 1);
child = child.previousSibling();
}
const DOM::Element element = node;
if (!m_bPure) {
if (!element.isNull() && !element.firstChild().isNull()) {
if(depth == 0) {
cur_item = new DOMListViewItem(node, m_listView, cur_item);
m_document = pNode.ownerDocument();
} else {
cur_item = new DOMListViewItem(node, parent_item, cur_item);
}
//kdDebug(90180) << "</" << node.nodeName().string() << ">" << endl;
addElement(element, cur_item, true);
// cur_item->setOpen(depth < m_expansionDepth);
}
}
}
void DOMTreeView::addElement ( const DOM::Node &node, DOMListViewItem *cur_item, bool isLast)
{
cur_item->setClosing(isLast);
const QString nodeName(node.nodeName().string());
QString text;
const DOM::Element element = node;
if (!element.isNull()) {
if (!m_bPure) {
if (isLast) {
text ="</";
} else {
text = "<";
}
text += nodeName;
} else {
text = nodeName;
}
if (m_bShowAttributes && !isLast) {
QString attributes;
DOM::Attr attr;
DOM::NamedNodeMap attrs = element.attributes();
unsigned long lmap = attrs.length();
for( unsigned int j=0; j<lmap; j++ ) {
attr = static_cast<DOM::Attr>(attrs.item(j));
attributes += " " + attr.name().string() + "=\"" + attr.value().string() + "\"";
}
if (!(attributes.isEmpty())) {
text += " ";
}
text += attributes.simplifyWhiteSpace();
}
if (!m_bPure) {
if(element.firstChild().isNull()) {
text += "/>";
} else {
text += ">";
}
}
cur_item->setText(0, text);
} else {
text = "`" + node.nodeValue().string() + "'";
// Hacks to deal with PRE
QTextStream ts( text, IO_ReadOnly );
while (!ts.eof()) {
const QString txt(ts.readLine());
const QFont font(KGlobalSettings::fixedFont());
cur_item->setFont( font );
cur_item->setText(0, txt);
if(node.handle()) {
m_itemdict.insert(node.handle(), cur_item);
}
DOMListViewItem *parent;
if (cur_item->parent()) {
parent = static_cast<DOMListViewItem *>(cur_item->parent());
} else {
parent = cur_item;
}
cur_item = new DOMListViewItem(node, parent, cur_item);
}
// This is one is too much
DOMListViewItem *notLastItem = static_cast<DOMListViewItem *>(cur_item->itemAbove());
delete cur_item;
cur_item = notLastItem;
}
if (m_bHighlightHTML && node.ownerDocument().isHTMLDocument()) {
highlightHTML(cur_item, nodeName);
}
}
void DOMTreeView::highlightHTML(DOMListViewItem *cur_item, const QString &nodeName)
{
/* This is slow. I could make it O(1) be using the tokenizer of khtml but I don't
* think it's worth it.
*/
QColor namedColor(palette().active().text());
QString tagName = nodeName.upper();
if ( tagName == "HTML" ) {
namedColor = "#0000ff";
cur_item->setBold(true);
} else if ( tagName == "HEAD" ) {
namedColor = "#0022ff";
cur_item->setBold(true);
} else if ( tagName == "TITLE" ) {
namedColor = "#2200ff";
} else if ( tagName == "SCRIPT" ) {
namedColor = "#4400ff";
} else if ( tagName == "NOSCRIPT" ) {
namedColor = "#0044ff";
} else if ( tagName == "STYLE" ) {
namedColor = "#0066ff";
} else if ( tagName == "LINK" ) {
namedColor = "#6600ff";
} else if ( tagName == "META" ) {
namedColor = "#0088ff";
} else if ( tagName == "BODY" ) {
namedColor = "#ff0000";
cur_item->setBold(true);
} else if ( tagName == "A") {
namedColor = "blue";
cur_item->setUnderline(true);
} else if ( tagName == "IMG") {
namedColor = "magenta";
cur_item->setUnderline(true);
} else if ( tagName == "DIV" ) {
namedColor = "#ff0044";
} else if ( tagName == "SPAN" ) {
namedColor = "#ff4400";
} else if ( tagName == "P" ) {
namedColor = "#ff0066";
} else if ( tagName == "DL" || tagName == "OL"|| tagName == "UL" || tagName == "TABLE" ) {
namedColor = "#880088";
} else if ( tagName == "LI" ) {
namedColor = "#884488";
} else if ( tagName == "TBODY" ){
namedColor = "#888888";
} else if ( tagName == "TR" ) {
namedColor = "#882288";
} else if ( tagName == "TD" ) {
namedColor = "#886688";
} else if ((tagName == "H1")||(tagName == "H2")||(tagName == "H3") ||
(tagName == "H4")||(tagName == "H5")||(tagName == "H6")) {
namedColor = "#008800";
} else if (tagName == "HR" ) {
namedColor = "#228822";
} else if ( tagName == "FRAME" || tagName == "IFRAME" ) {
namedColor = "#ff22ff";
} else if ( tagName == "FRAMESET" ) {
namedColor = "#dd22dd";
} else if ( tagName == "APPLET" || tagName == "OBJECT" ) {
namedColor = "#bb22bb";
} else if ( tagName == "BASEFONT" || tagName == "FONT" ) {
namedColor = "#097200";
} else if ( tagName == "B" || tagName == "STRONG" ) {
cur_item->setBold(true);
} else if ( tagName == "I" || tagName == "EM" ) {
cur_item->setItalic(true);
} else if ( tagName == "U") {
cur_item->setUnderline(true);
}
cur_item->setColor(namedColor);
}
void DOMTreeView::slotItemClicked(QListViewItem *cur_item)
{
DOMListViewItem *cur = static_cast<DOMListViewItem *>(cur_item);
if (!cur) return;
DOM::Node handle = cur->node();
if (!handle.isNull()) {
part->setActiveNode(handle);
}
}
void DOMTreeView::slotFindClicked()
{
if (m_findDialog == 0) {
m_findDialog = new KEdFind(this);
connect(m_findDialog, SIGNAL(search()), this, SLOT(slotSearch()));
}
m_findDialog->show();
}
void DOMTreeView::slotRefreshNode(const DOM::Node &pNode)
{
DOMListViewItem *cur = static_cast<DOMListViewItem *>(m_itemdict[pNode.handle()]);
if (!cur) return;
addElement(pNode, cur, false);
}
void DOMTreeView::slotPrepareMove()
{
DOMListViewItem *item = static_cast<DOMListViewItem *>(m_listView->currentItem());
if (!item)
current_node = DOM::Node();
else
current_node = item->node();
}
void DOMTreeView::slotMovedItems(QPtrList<QListViewItem> &items, QPtrList<QListViewItem> &/*afterFirst*/, QPtrList<QListViewItem> &afterNow)
{
MultiCommand *cmd = new MultiCommand(i18n("Move Nodes"));
_refreshed = false;
QPtrList<QListViewItem>::Iterator it = items.begin();
QPtrList<QListViewItem>::Iterator anit = afterNow.begin();
for (; it != items.end(); ++it, ++anit) {
DOMListViewItem *item = static_cast<DOMListViewItem *>(*it);
DOMListViewItem *anitem = static_cast<DOMListViewItem *>(*anit);
DOM::Node parent = static_cast<DOMListViewItem *>(item->parent())->node();
Q_ASSERT(!parent.isNull());
// kdDebug(90180) << " afternow " << anitem << " node " << (anitem ? anitem->node().nodeName().string() : QString()) << "=" << (anitem ? anitem->node().nodeValue().string() : QString()) << endl;
cmd->addCommand(new MoveNodeCommand(item->node(), parent,
anitem ? anitem->node().nextSibling() : parent.firstChild())
);
}
mainWindow()->executeAndAddCommand(cmd);
// refresh *anyways*, otherwise consistency is disturbed
if (!_refreshed) refresh();
slotShowNode(current_node);
}
void DOMTreeView::slotSearch()
{
assert(m_findDialog);
const QString& searchText = m_findDialog->getText();
bool caseSensitive = m_findDialog->case_sensitive();
searchRecursive(static_cast<DOMListViewItem*>(m_rootListView->firstChild()),
searchText, caseSensitive);
m_findDialog->hide();
}
void DOMTreeView::searchRecursive(DOMListViewItem* cur_item, const QString& searchText,
bool caseSensitive)
{
const QString text(cur_item->text(0));
if (text.contains(searchText, caseSensitive) > 0) {
cur_item->setUnderline(true);
cur_item->setItalic(true);
m_listView->setCurrentItem(cur_item);
m_listView->ensureItemVisible(cur_item);
} else {
cur_item->setOpen(false);
}
DOMListViewItem* child = static_cast<DOMListViewItem *>(cur_item->firstChild());
while( child ) {
searchRecursive(child, searchText, caseSensitive);
child = static_cast<DOMListViewItem *>(child->nextSibling());
}
}
#if 0
void DOMTreeView::slotSaveClicked()
{
//kdDebug(90180) << "void KfingerCSSWidget::slotSaveAs()" << endl;
KURL url = KFileDialog::getSaveFileName( part->url().url(), "*.html",
this, i18n("Save DOM Tree as HTML") );
if (!(url.isEmpty()) && url.isValid()) {
QFile file(url.path());
if (file.exists()) {
const QString title = i18n( "File Exists" );
const QString text = i18n( "Do you really want to overwrite: \n%1?" ).arg(url.url());
if (KMessageBox::Continue != KMessageBox::warningContinueCancel(this, text, title, i18n("Overwrite") ) ) {
return;
}
}
if (file.open(IO_WriteOnly) ) {
kdDebug(90180) << "Opened File: " << url.url() << endl;
m_textStream = new QTextStream(&file); //(stdOut)
saveTreeAsHTML(part->document());
file.close();
kdDebug(90180) << "File closed " << endl;
delete m_textStream;
} else {
const QString title = i18n( "Unable to Open File" );
const QString text = i18n( "Unable to open \n %1 \n for writing" ).arg(url.path());
KMessageBox::sorry( this, text, title );
}
} else {
const QString title = i18n( "Invalid URL" );
const QString text = i18n( "This URL \n %1 \n is not valid." ).arg(url.url());
KMessageBox::sorry( this, text, title );
}
}
void DOMTreeView::saveTreeAsHTML(const DOM::Node &pNode)
{
assert(m_textStream);
// Add a doctype
(*m_textStream) <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" << endl;
if(pNode.ownerDocument().isNull()) {
saveRecursive(pNode, 0);
} else {
saveRecursive(pNode.ownerDocument(), 0);
}
}
void DOMTreeView::saveRecursive(const DOM::Node &pNode, int indent)
{
const QString nodeName(pNode.nodeName().string());
QString text;
QString strIndent;
strIndent.fill(' ', indent);
const DOM::Element element = static_cast<const DOM::Element>(pNode);
text = strIndent;
if ( !element.isNull() ) {
if (nodeName.at(0)=='-') {
/* Don't save khtml internal tags '-konq..'
* Approximating it with <DIV>
*/
text += "<DIV> <!-- -KONG_BLOCK -->";
} else {
text += "<" + nodeName;
QString attributes;
DOM::Attr attr;
const DOM::NamedNodeMap attrs = element.attributes();
unsigned long lmap = attrs.length();
for( uint j=0; j<lmap; j++ ) {
attr = static_cast<DOM::Attr>(attrs.item(j));
attributes += " " + attr.name().string() + "=\"" + attr.value().string() + "\"";
}
if (!(attributes.isEmpty())){
text += " ";
}
text += attributes.simplifyWhiteSpace();
if(element.firstChild().isNull()) {
text += "/>";
} else {
text += ">";
}
}
} else {
text = strIndent + pNode.nodeValue().string();
}
kdDebug(90180) << text << endl;
if (!(text.isEmpty())) {
(*m_textStream) << text << endl;
}
DOM::Node child = pNode.firstChild();
while(!child.isNull()) {
saveRecursive(child, indent+2);
child = child.nextSibling();
}
if (!(element.isNull()) && (!(element.firstChild().isNull()))) {
if (nodeName.at(0)=='-') {
text = strIndent + "</DIV> <!-- -KONG_BLOCK -->";
} else {
text = strIndent + "</" + pNode.nodeName().string() + ">";
}
kdDebug(90180) << text << endl;
(*m_textStream) << text << endl;
}
}
#endif
void DOMTreeView::updateIncrDecreaseButton()
{
#if 0
m_decreaseButton->setEnabled((m_expansionDepth > 0));
m_increaseButton->setEnabled((m_expansionDepth < m_maxDepth));
#endif
}
void DOMTreeView::refresh()
{
if (!part) return;
scroll_ofs_x = m_listView->contentsX();
scroll_ofs_y = m_listView->contentsY();
m_listView->setUpdatesEnabled(false);
slotShowTree(part->document());
QTimer::singleShot(0, this, SLOT(slotRestoreScrollOffset()));
_refreshed = true;
}
void DOMTreeView::increaseExpansionDepth()
{
if (!part) return;
if (m_expansionDepth < m_maxDepth) {
++m_expansionDepth;
adjustDepth();
updateIncrDecreaseButton();
} else {
QApplication::beep();
}
}
void DOMTreeView::decreaseExpansionDepth()
{
if (!part) return;
if (m_expansionDepth > 0) {
--m_expansionDepth;
adjustDepth();
updateIncrDecreaseButton();
} else {
QApplication::beep();
}
}
void DOMTreeView::adjustDepth()
{
// get current item in a hypersmart way
DOMListViewItem *cur_node_item = m_itemdict[infoNode.handle()];
if (!cur_node_item)
cur_node_item = static_cast<DOMListViewItem *>(m_listView->currentItem());
adjustDepthRecursively(m_rootListView->firstChild(), 0);
// make current item visible again if possible
if (cur_node_item)
m_listView->ensureVisible(0, cur_node_item->itemPos());
}
void DOMTreeView::adjustDepthRecursively(QListViewItem *cur_item, uint currDepth)
{
if (!(cur_item == 0)) {
while( cur_item ) {
cur_item->setOpen( (m_expansionDepth > currDepth) );
adjustDepthRecursively(cur_item->firstChild(), currDepth+1);
cur_item = cur_item->nextSibling();
}
}
}
void DOMTreeView::setMessage(const QString &msg)
{
messageLine->setText(msg);
messageLinePane->show();
}
void DOMTreeView::hideMessageLine()
{
messageLinePane->hide();
}
void DOMTreeView::moveToParent()
{
// This is a hypersmart algorithm.
// If infoNode is defined, go to the parent of infoNode, otherwise, go
// to the parent of the tree view's current item.
// Hope this isn't too smart.
DOM::Node cur = infoNode;
if (cur.isNull()) cur = static_cast<DOMListViewItem *>(m_listView->currentItem())->node();
if (cur.isNull()) return;
cur = cur.parentNode();
activateNode(cur);
}
void DOMTreeView::showDOMTreeContextMenu(QListViewItem */*lvi*/, const QPoint &pos, int /*col*/)
{
QPopupMenu *ctx = mainWindow()->domTreeViewContextMenu();
Q_ASSERT(ctx);
ctx->popup(pos);
}
void DOMTreeView::slotPureToggled(bool b)
{
m_bPure = b;
refresh();
}
void DOMTreeView::slotShowAttributesToggled(bool b)
{
m_bShowAttributes = b;
refresh();
}
void DOMTreeView::slotHighlightHTMLToggled(bool b)
{
m_bHighlightHTML = b;
refresh();
}
void DOMTreeView::deleteNodes()
{
// kdDebug(90180) << k_funcinfo << endl;
DOM::Node last;
MultiCommand *cmd = new MultiCommand(i18n("Delete Nodes"));
QListViewItemIterator it(m_listView, QListViewItemIterator::Selected);
for (; *it; ++it) {
DOMListViewItem *item = static_cast<DOMListViewItem *>(*it);
// kdDebug(90180) << " item->node " << item->node().nodeName().string() << " clos " << item->isClosing() << endl;
if (item->isClosing()) continue;
// don't regard node more than once
if (item->node() == last) continue;
// check for selected parent
bool has_selected_parent = false;
for (QListViewItem *p = item->parent(); p; p = p->parent()) {
if (p->isSelected()) { has_selected_parent = true; break; }
}
if (has_selected_parent) continue;
// kdDebug(90180) << " item->node " << item->node().nodeName().string() << ": schedule for removal" << endl;
// remove this node if it isn't already recursively removed by its parent
cmd->addCommand(new RemoveNodeCommand(item->node(), item->node().parentNode(), item->node().nextSibling()));
last = item->node();
}
mainWindow()->executeAndAddCommand(cmd);
}
void DOMTreeView::disconnectFromTornDownPart()
{
if (!part) return;
m_listView->clear();
initializeOptionsFromNode(DOM::Node());
// remove all references to nodes
infoNode = DOM::Node(); // ### have this handled by dedicated info node panel method
current_node = DOM::Node();
active_node_rule = DOM::CSSRule();
stylesheet = DOM::CSSStyleSheet();
}
void DOMTreeView::connectToPart()
{
if (part) {
connect(part, SIGNAL(nodeActivated(const DOM::Node &)), this,
SLOT(activateNode(const DOM::Node &)));
connect(part, SIGNAL(completed()), this, SLOT(refresh()));
// insert a style rule to indicate activated nodes
try {
kdDebug(90180) << "(1) part.document: " << part->document().handle() << endl;
stylesheet = part->document().implementation().createCSSStyleSheet("-domtreeviewer-style", "screen");
kdDebug(90180) << "(2)" << endl;
stylesheet.insertRule(":focus { outline: medium #f00 solid }", 0);
// ### for testing only
// stylesheet.insertRule("body { background: #f0f !important }", 1);
kdDebug(90180) << "(3)" << endl;
active_node_rule = stylesheet.cssRules().item(0);
kdDebug(90180) << "(4)" << endl;
part->document().addStyleSheet(stylesheet);
kdDebug(90180) << "(5)" << endl;
} catch (DOM::CSSException &ex) {
kdDebug(90180) << "CSS Exception " << ex.code << endl;
} catch (DOM::DOMException &ex) {
kdDebug(90180) << "DOM Exception " << ex.code << endl;
}
}
slotShowTree(part ? (DOM::Node)part->document() : DOM::Node());
updateIncrDecreaseButton();
}
void DOMTreeView::disconnectFromActivePart()
{
if (!part) return;
// remove style sheet
try {
part->document().removeStyleSheet(stylesheet);
} catch (DOM::CSSException &ex) {
kdDebug(90180) << "CSS Exception " << ex.code << endl;
} catch (DOM::DOMException &ex) {
kdDebug(90180) << "DOM Exception " << ex.code << endl;
}
}
void DOMTreeView::slotSetHtmlPartDelayed()
{
connectToPart();
emit htmlPartChanged(part);
}
void DOMTreeView::slotRestoreScrollOffset()
{
m_listView->setUpdatesEnabled(true);
m_listView->setContentsPos(scroll_ofs_x, scroll_ofs_y);
}
void DOMTreeView::slotAddElementDlg()
{
DOMListViewItem *item = static_cast<DOMListViewItem *>(m_listView->currentItem());
if (!item) return;
QString qname;
QString namespc;
SignalReceiver addBefore;
{
ElementEditDialog dlg(this, "ElementEditDialog", true);
connect(dlg.insBeforeBtn, SIGNAL(clicked()), &addBefore, SLOT(slot()));
// ### activate when namespaces are supported
dlg.elemNamespace->setEnabled(false);
if (dlg.exec() != QDialog::Accepted) return;
qname = dlg.elemName->text();
namespc = dlg.elemNamespace->currentText();
}
DOM::Node curNode = item->node();
try {
DOM::Node parent = addBefore() ? curNode.parentNode() : curNode;
DOM::Node after = addBefore() ? curNode : 0;
// ### take namespace into account
DOM::Node newNode = curNode.ownerDocument().createElement(qname);
ManipulationCommand *cmd = new InsertNodeCommand(newNode, parent, after);
mainWindow()->executeAndAddCommand(cmd);
if (cmd->isValid()) activateNode(newNode);
} catch (DOM::DOMException &ex) {
mainWindow()->addMessage(ex.code, domErrorMessage(ex.code));
}
}
void DOMTreeView::slotAddTextDlg()
{
DOMListViewItem *item = static_cast<DOMListViewItem *>(m_listView->currentItem());
if (!item) return;
QString text;
SignalReceiver addBefore;
{
TextEditDialog dlg(this, "TextEditDialog", true);
connect(dlg.insBeforeBtn, SIGNAL(clicked()), &addBefore, SLOT(slot()));
if (dlg.exec() != QDialog::Accepted) return;
text = dlg.textPane->text();
}
DOM::Node curNode = item->node();
try {
DOM::Node parent = addBefore() ? curNode.parentNode() : curNode;
DOM::Node after = addBefore() ? curNode : 0;
DOM::Node newNode = curNode.ownerDocument().createTextNode(text);
ManipulationCommand *cmd = new InsertNodeCommand(newNode, parent, after);
mainWindow()->executeAndAddCommand(cmd);
if (cmd->isValid()) activateNode(newNode);
} catch (DOM::DOMException &ex) {
mainWindow()->addMessage(ex.code, domErrorMessage(ex.code));
}
}
// == DOM Node info panel =============================================
static QString *clickToAdd;
/**
* List view item for attribute list.
*/
class AttributeListItem : public QListViewItem
{
typedef QListViewItem super;
bool _new;
public:
AttributeListItem(QListView *parent, QListViewItem *prev)
: super(parent, prev), _new(true)
{
}
AttributeListItem(const QString &attrName, const QString &attrValue,
QListView *parent, QListViewItem *prev)
: super(parent, prev), _new(false)
{
setText(0, attrName);
setText(1, attrValue);
}
bool isNew() const { return _new; }
void setNew(bool s) { _new = s; }
virtual int compare(QListViewItem *item, int column, bool ascend) const
{
return _new ? 1 : super::compare(item, column, ascend);
}
protected:
virtual void paintCell( QPainter *p, const QColorGroup &cg,
int column, int width, int alignment )
{
bool updates_enabled = listView()->isUpdatesEnabled();
listView()->setUpdatesEnabled(false);
QColor c = cg.text();
bool text_changed = false;
QString oldText;
if (_new) {
c = QApplication::palette().color( QPalette::Disabled, QColorGroup::Text );
if (!clickToAdd) clickToAdd = new QString(i18n("<Click to add>"));
oldText = text(column);
text_changed = true;
if (column == 0) setText(0, *clickToAdd); else setText(1, QString());
}
QColorGroup _cg( cg );
_cg.setColor( QColorGroup::Text, c );
super::paintCell( p, _cg, column, width, alignment );
if (text_changed) setText(column, oldText);
listView()->setUpdatesEnabled(updates_enabled);
}
};
void DOMTreeView::initDOMNodeInfo()
{
connect(m_listView, SIGNAL(clicked(QListViewItem *)),
SLOT(initializeOptionsFromListItem(QListViewItem *)));
connect(nodeAttributes, SIGNAL(itemRenamed(QListViewItem *, const QString &, int)),
SLOT(slotItemRenamed(QListViewItem *, const QString &, int)));
connect(nodeAttributes, SIGNAL(executed(QListViewItem *, const QPoint &, int)),
SLOT(slotEditAttribute(QListViewItem *, const QPoint &, int)));
connect(nodeAttributes, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
SLOT(showInfoPanelContextMenu(QListViewItem *, const QPoint &, int)));
connect(applyContent, SIGNAL(clicked()), SLOT(slotApplyContent()));
ManipulationCommand::connect(SIGNAL(nodeChanged(const DOM::Node &)), this, SLOT(initializeOptionsFromNode(const DOM::Node &)));
nodeAttributes->setRenameable(0, true);
nodeAttributes->setRenameable(1, true);
nodeInfoStack->raiseWidget(EmptyPanel);
installEventFilter(nodeAttributes);
}
void DOMTreeView::initializeOptionsFromNode(const DOM::Node &node)
{
infoNode = node;
nodeName->clear();
nodeType->clear();
nodeNamespace->clear();
nodeValue->clear();
if (node.isNull()) {
nodeInfoStack->raiseWidget(EmptyPanel);
return;
}
nodeName->setText(node.nodeName().string());
nodeType->setText(QString::number(node.nodeType()));
nodeNamespace->setText(node.namespaceURI().string());
// nodeValue->setText(node.value().string());
DOM::Element element = node;
if (!element.isNull()) {
initializeOptionsFromElement(element);
return;
}
DOM::CharacterData cdata = node;
if (!cdata.isNull()) {
initializeOptionsFromCData(cdata);
return;
}
// Fallback
nodeInfoStack->raiseWidget(EmptyPanel);
}
void DOMTreeView::initializeOptionsFromListItem(QListViewItem *item)
{
const DOMListViewItem *cur_item = static_cast<const DOMListViewItem *>(item);
// kdDebug(90180) << "cur_item: " << cur_item << endl;
initializeOptionsFromNode(cur_item ? cur_item->node() : DOM::Node());
}
void DOMTreeView::initializeOptionsFromElement(const DOM::Element &element)
{
QListViewItem *last = 0;
nodeAttributes->clear();
DOM::NamedNodeMap attrs = element.attributes();
unsigned long lmap = attrs.length();
for (unsigned int j = 0; j < lmap; j++) {
DOM::Attr attr = attrs.item(j);
// kdDebug(90180) << attr.name().string() << "=" << attr.value().string() << endl;
QListViewItem *item = new AttributeListItem(attr.name().string(),
attr.value().string(), nodeAttributes, last);
last = item;
}
// append new item
last = new AttributeListItem(nodeAttributes, last);
nodeInfoStack->raiseWidget(ElementPanel);
}
void DOMTreeView::initializeOptionsFromCData(const DOM::CharacterData &cdata)
{
contentEditor->setText(cdata.data().string());
DOM::Text text = cdata;
contentEditor->setEnabled(!text.isNull());
nodeInfoStack->raiseWidget(CDataPanel);
}
void DOMTreeView::slotItemRenamed(QListViewItem *lvi, const QString &str, int col)
{
AttributeListItem *item = static_cast<AttributeListItem *>(lvi);
DOM::Element element = infoNode;
if (element.isNull()) return; // Should never happen
switch (col) {
case 0: {
ManipulationCommand *cmd;
// kdDebug(90180) << k_funcinfo << "col 0: " << element.nodeName() << " isNew: " << item->isNew() << endl;
if (item->isNew()) {
cmd = new AddAttributeCommand(element, str, item->text(1));
item->setNew(false);
} else
cmd = new RenameAttributeCommand(element, item->text(0), str);
mainWindow()->executeAndAddCommand(cmd);
break;
}
case 1: {
if (item->isNew()) { lvi->setText(1, QString()); break; }
ChangeAttributeValueCommand *cmd = new ChangeAttributeValueCommand(
element, item->text(0), str);
mainWindow()->executeAndAddCommand(cmd);
break;
}
}
}
void DOMTreeView::slotEditAttribute(QListViewItem *lvi, const QPoint &, int col)
{
if (!lvi) return;
QString attrName = lvi->text(0);
QString attrValue = lvi->text(1);
int res = 0;
{
AttributeEditDialog dlg(this, "AttributeEditDialog", true);
dlg.attrName->setText(attrName);
dlg.attrValue->setText(attrValue);
if (col == 0) {
dlg.attrName->setFocus();
dlg.attrName->selectAll();
} else {
dlg.attrValue->setFocus();
dlg.attrValue->selectAll();
}
res = dlg.exec();
attrName = dlg.attrName->text();
attrValue = dlg.attrValue->text();
}
// kdDebug(90180) << "name=" << attrName << " value=" << attrValue << endl;
if (res == QDialog::Accepted) do {
if (attrName.isEmpty()) break;
if (lvi->text(0) != attrName) {
// hack: set value to assign attribute/value pair in one go
lvi->setText(1, attrValue);
slotItemRenamed(lvi, attrName, 0);
// Reget, item may have been changed
lvi = nodeAttributes->findItem(attrName, 0);
}
if (lvi && lvi->text(1) != attrValue)
slotItemRenamed(lvi, attrValue, 1);
} while(false) /*end if*/;
}
void DOMTreeView::slotApplyContent()
{
DOM::CharacterData cdata = infoNode;
if (cdata.isNull()) return;
ManipulationCommand *cmd = new ChangeCDataCommand(cdata, contentEditor->text());
mainWindow()->executeAndAddCommand(cmd);
}
void DOMTreeView::showInfoPanelContextMenu(QListViewItem */*lvi*/, const QPoint &pos, int /*col*/)
{
QPopupMenu *ctx = mainWindow()->infoPanelAttrContextMenu();
Q_ASSERT(ctx);
ctx->popup(pos);
}
void DOMTreeView::copyAttributes()
{
// TODO implement me
}
void DOMTreeView::cutAttributes()
{
// TODO implement me
}
void DOMTreeView::pasteAttributes()
{
// TODO implement me
}
void DOMTreeView::deleteAttributes()
{
MultiCommand *cmd = new MultiCommand(i18n("Delete Attributes"));
QListViewItemIterator it(nodeAttributes, QListViewItemIterator::Selected);
for (; *it; ++it) {
AttributeListItem *item = static_cast<AttributeListItem *>(*it);
if (item->isNew()) continue;
cmd->addCommand(new RemoveAttributeCommand(infoNode, item->text(0)));
}
mainWindow()->executeAndAddCommand(cmd);
}
#include "domtreeview.moc"