summaryrefslogtreecommitdiffstats
path: root/konq-plugins/domtreeviewer/domtreeview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'konq-plugins/domtreeviewer/domtreeview.cpp')
-rw-r--r--konq-plugins/domtreeviewer/domtreeview.cpp1226
1 files changed, 1226 insertions, 0 deletions
diff --git a/konq-plugins/domtreeviewer/domtreeview.cpp b/konq-plugins/domtreeviewer/domtreeview.cpp
new file mode 100644
index 0000000..167d85a
--- /dev/null
+++ b/konq-plugins/domtreeviewer/domtreeview.cpp
@@ -0,0 +1,1226 @@
+/***************************************************************************
+ 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"