diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 84da08d7b7fcda12c85caeb5a10b4903770a6f69 (patch) | |
tree | 2a6aea76f2dfffb4cc04bb907c4725af94f70e72 /konq-plugins/domtreeviewer/domtreeview.cpp | |
download | tdeaddons-84da08d7b7fcda12c85caeb5a10b4903770a6f69.tar.gz tdeaddons-84da08d7b7fcda12c85caeb5a10b4903770a6f69.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaddons@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'konq-plugins/domtreeviewer/domtreeview.cpp')
-rw-r--r-- | konq-plugins/domtreeviewer/domtreeview.cpp | 1226 |
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" |