/*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 &, QPtrList &, QPtrList &)), SLOT(slotMovedItems(QPtrList &, QPtrList &, QPtrList &))); // 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(parentWidget()); } bool DOMTreeView::eventFilter(QObject *o, QEvent *e) { if (e->type() == QEvent::AccelOverride) { QKeyEvent *ke = static_cast(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) << "" << 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 ="(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(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(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(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(m_itemdict[pNode.handle()]); if (!cur) return; addElement(pNode, cur, false); } void DOMTreeView::slotPrepareMove() { DOMListViewItem *item = static_cast(m_listView->currentItem()); if (!item) current_node = DOM::Node(); else current_node = item->node(); } void DOMTreeView::slotMovedItems(QPtrList &items, QPtrList &/*afterFirst*/, QPtrList &afterNow) { MultiCommand *cmd = new MultiCommand(i18n("Move Nodes")); _refreshed = false; QPtrList::Iterator it = items.begin(); QPtrList::Iterator anit = afterNow.begin(); for (; it != items.end(); ++it, ++anit) { DOMListViewItem *item = static_cast(*it); DOMListViewItem *anitem = static_cast(*anit); DOM::Node parent = static_cast(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(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(cur_item->firstChild()); while( child ) { searchRecursive(child, searchText, caseSensitive); child = static_cast(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) <<"" << 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(pNode); text = strIndent; if ( !element.isNull() ) { if (nodeName.at(0)=='-') { /* Don't save khtml internal tags '-konq..' * Approximating it with
*/ text += "
"; } else { text += "<" + nodeName; QString attributes; DOM::Attr attr; const DOM::NamedNodeMap attrs = element.attributes(); unsigned long lmap = attrs.length(); for( uint j=0; j(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 + "
"; } else { text = strIndent + ""; } 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(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(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(*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(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(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("")); 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(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(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(*it); if (item->isNew()) continue; cmd->addCommand(new RemoveAttributeCommand(infoNode, item->text(0))); } mainWindow()->executeAndAddCommand(cmd); } #include "domtreeview.moc"