/*************************************************************************** kxe_treeview.cpp - description ------------------- begin : Thu Sep 20 2001 copyright : (C) 2001, 2002, 2003 by The KXMLEditor Team email : OleBowle@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 "kxe_treeview.h" #include "kxe_treeviewitem.h" #include "kxesearchdialog.h" #include "kxmleditorpart.h" #include "kxmleditorfactory.h" #include "kxeconfiguration.h" #include "kxetreeviewsettings.h" #include #include #include #include #include #include #include // include files for Qt #include "qdom_add.h" #include #include #include #include #include #include static const int autoOpenTimeout = 750; KXE_TreeView::KXE_TreeView( KXMLGUIClient * pGUIClient, QWidget * pParent, const char * pszName ) : KListView(pParent,pszName), m_pGUIClient(pGUIClient), m_nBookmarkedItems(0) { setSorting(-1); // no sorting addColumn(i18n("Qualified name")); setSelectionMode(QListView::Single); connect( this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()) ); connect( this, SIGNAL(expanded(QListViewItem*)), this, SLOT(slotItemExpanded(QListViewItem*)) ); setReadWrite(false); m_bDrag = false; m_pCurrentBeforeDropItem = 0; m_pDropItem = 0; m_autoOpenTimer = new QTimer(this); connect(m_autoOpenTimer, SIGNAL(timeout()), this, SLOT(slotAutoOpenFolder())); // Apply current configuration slotTreeViewSettingsChanged(); // and make sure to be informed about its changes. connect( KXMLEditorFactory::configuration()->treeview(), SIGNAL(sigChanged()), this, SLOT(slotTreeViewSettingsChanged()) ); } void KXE_TreeView::setReadWrite( bool fReadWrite ) { setItemsRenameable( fReadWrite ); setRenameable( 0, fReadWrite ); if ( fReadWrite ) // If the widget enters read/write mode, then enable/disable { // dropping (according to the configuration data). setAcceptDrops( KXMLEditorFactory::configuration()->treeview()->enableDropping() ); viewport()->setAcceptDrops( KXMLEditorFactory::configuration()->treeview()->enableDropping() ); } else // If the widget enter read only mode, { // then disable dropping. setAcceptDrops( false ); viewport()->setAcceptDrops( false ); } } ////////////////////////////////////////////////////////////// // configuration slots ////////////////////////////////////////////////////////////// void KXE_TreeView::slotTreeViewSettingsChanged() { setRootIsDecorated( KXMLEditorFactory::configuration()->treeview()->decorateRoot() ); if ( KXMLEditorFactory::configuration()->treeview()->elemDisplMode() == KXETreeViewSettings::NoAttributes ) { if ( columns() > 1 ) removeColumn(1); } else { if ( columns() < 2 ) addColumn( i18n("Attributes") ); } KXE_TreeViewItem * pItem = static_cast (firstChild()); while (pItem) { pItem->setTexts(); pItem = pItem->nextItem(); } if ( itemsRenameable() ) // If the widget is in read/write mode, then enable/disable { // dropping (according to the configuration data). setAcceptDrops( KXMLEditorFactory::configuration()->treeview()->enableDropping() ); viewport()->setAcceptDrops( KXMLEditorFactory::configuration()->treeview()->enableDropping() ); } } ////////////////////////////////////////////////////////////// // action slots ////////////////////////////////////////////////////////////// void KXE_TreeView::editDeselect() { clearSelection(); } void KXE_TreeView::viewNodeUp() { // get selected item from tree view QListViewItem * pSelItem = selectedItem(); if ( ! pSelItem ) { kdDebug() << "KXE_TreeView::slotViewNodeUp no item selected" << endl; return; } // get parent item QListViewItem * pParentItem = pSelItem->parent(); // select parent item in tree view if (pParentItem) { setCurrentItem(pParentItem); ensureItemVisible(pParentItem); } } void KXE_TreeView::viewExpNode( int nLevel ) { // get selected item from tree view (if any) QListViewItem * pSelItem = selectedItem(); if ( ! pSelItem ) { kdDebug() << "KXE_TreeView::slotViewExpNode no item selected" << endl; return; } // expand node KXE_TreeViewItem * pSelTreeItem = static_cast (pSelItem); pSelTreeItem->expandSubTree(nLevel); } void KXE_TreeView::viewColNode( int nLevel ) { // get selected item from tree view (if any) QListViewItem * pSelItem = selectedItem(); if ( ! pSelItem ) { kdDebug() << "KXE_TreeView::slotViewColNode no item selected" << endl; return; } // expand node KXE_TreeViewItem * pSelTreeItem = static_cast (pSelItem); pSelTreeItem->collapseSubTree(nLevel); } void KXE_TreeView::bookmarksToggle() { // get selected item from tree view KXE_TreeViewItem * pSelItem = static_cast (selectedItem()); if ( ! pSelItem ) { kdDebug() << "KXE_TreeView::bookmarksToggle: no item selected" << endl; return; } // toggle bookmark on selected item if(pSelItem->toggleBookmark()) m_nBookmarkedItems++; else m_nBookmarkedItems--; } void KXE_TreeView::bookmarksPrev() { if ( childCount() < 1 ) { kdDebug() << "KXE_TreeView::bookmarksPrev: internal error - this tree view is empty" << endl; return; } // get selected item from tree view KXE_TreeViewItem * pSelItem = static_cast (selectedItem()); if ( ! pSelItem ) // If there is no item selected we take { // the last root items last grand child. QListViewItem * pTmpItem = firstChild(); // Take first child and while ( pTmpItem->nextSibling() ) // find last child by pTmpItem = pTmpItem->nextSibling(); // traversing all childs pSelItem = static_cast (pTmpItem); // this is the last root item while ( pSelItem->lastChild() ) // find its last pSelItem = pSelItem->lastChild(); // grand child if ( pSelItem->isBookmarked() ) // We have to check its { // bookmarked-status selectItem(pSelItem); // and select it, in case return; // it is bookmarked. } } // Search items above the selected one while ( (pSelItem = pSelItem->prevItem()) != 0 ) { if ( pSelItem->isBookmarked() ) { selectItem(pSelItem); return; } } } void KXE_TreeView::bookmarksNext() { if ( childCount() < 1 ) { kdDebug() << "KXE_TreeView::bookmarksNext: internal error - this tree view is empty" << endl; return; } // get selected item from tree view KXE_TreeViewItem * pSelItem = static_cast (selectedItem()); if ( ! pSelItem ) { // If there is no item selected pSelItem = static_cast (firstChild()); // we take the first root item, if ( pSelItem->isBookmarked() ) // but we have to check its { // bookmarked-status selectItem(pSelItem); // and select it, in case return; // it is bookmarked. } } // Search items below the selected one while ( (pSelItem = pSelItem->nextItem()) != 0 ) { if ( pSelItem->isBookmarked() ) { selectItem(pSelItem); return; } } } void KXE_TreeView::selectItem( KXE_TreeViewItem * const pItem ) { if ( ! pItem ) { kdDebug() << "KXE_TreeView::selectItem: the given pointer is a null pointer" << endl; return; } setSelected( pItem, true ); setCurrentItem( pItem ); ensureItemVisible( pItem ); } bool KXE_TreeView::selectNode( const QDomNode & node ) { if ( node.isNull() ) { kdError() << "KXE_TreeView::selectNode: the given node is an empty one" << endl; return false; } KXE_TreeViewItem * pItem = findCorrespondingItem(node); if ( ! pItem ) // can't find the corresponding item { kdError() << "KXE_TreeView::selectNode can't find an item to the given node." << endl; return false; } selectItem(pItem); return true; } QDomNode * KXE_TreeView::getSelectedNode() const { // get selected item from tree view QListViewItem * pSelItem = selectedItem(); if ( ! pSelItem ) return 0; KXE_TreeViewItem * pSelTreeItem = static_cast (pSelItem); return pSelTreeItem->xmlNode(); } QDomNode * KXE_TreeView::getSpecProcInstrNode(const QString& target) const { KXE_TreeViewItem * pTreeItem = static_cast (firstChild()); while ( pTreeItem ) { if (pTreeItem->xmlNode()->isProcessingInstruction()) { QDomProcessingInstruction domProcInstr = pTreeItem->xmlNode()->toProcessingInstruction(); if(domProcInstr.target() == target) return pTreeItem->xmlNode(); } pTreeItem = pTreeItem->nextItem(); } return 0; } // Return info, is root element is already created bool KXE_TreeView::hasRootNode() { KXE_TreeViewItem * pTreeItem = static_cast (firstChild()); while ( pTreeItem ) { if (pTreeItem->xmlNode()->isElement()) { return true; } pTreeItem = pTreeItem->nextItem(); } return false; } QString KXE_TreeView::getSelectedPath() const { // get selected item from tree view QListViewItem * pSelItem = selectedItem(); if ( ! pSelItem ) return QString(); KXE_TreeViewItem * pSelTreeItem = static_cast (pSelItem); return domTool_getPath( * pSelTreeItem->xmlNode() ); } void KXE_TreeView::contentsMousePressEvent( QMouseEvent * pEvent ) { KListView::contentsMousePressEvent(pEvent); if ( pEvent->button() == RightButton ) { QString szMenuName; QListViewItem * pItem = itemAt( contentsToViewport(pEvent->pos()) ); if (pItem) { KXE_TreeViewItem * pTreeItem = static_cast (pItem); switch( pTreeItem->xmlNode()->nodeType() ) { case QDomNode::ElementNode: szMenuName = "popupXmlElement"; break; case QDomNode::TextNode: case QDomNode::CDATASectionNode: case QDomNode::CommentNode: szMenuName = "popupXmlContent"; break; case QDomNode::ProcessingInstructionNode: szMenuName = "popupXmlProcInstr"; break; default: kdDebug() << "KXE_TreeView::contentsMousePressEvent unknown item type" << endl; return; } } else szMenuName = "popupXmlTree"; emit sigContextMenuRequested( szMenuName, QCursor::pos() ); return; } //--- Drag & Drop ------------------------------------------------------ QPoint p(contentsToViewport(pEvent->pos())); QListViewItem *i = itemAt(p); if(pEvent->button() == LeftButton && i) { // if the user clicked into the root decoration of the item, don't try to start a drag! if(p.x() > header()->cellPos(header()->mapToActual(0)) + treeStepSize() * ( i->depth() + (rootIsDecorated() ? 1 : 0)) + itemMargin() || p.x() < header()->cellPos(header()->mapToActual(0))) { m_dragPos = pEvent->pos(); m_bDrag = true; } } } void KXE_TreeView::slotSelectionChanged() { KXE_TreeViewItem * pItem = static_cast (selectedItem()); if ( ! pItem ) emit sigSelectionCleared(hasRootNode()); else { QDomNode selectedNode = * ( pItem->xmlNode() ); // uses QDomNode copy constructor // choose appropriate object kind switch ( selectedNode.nodeType() ) { case QDomNode::ElementNode: emit sigSelectionChanged( selectedNode.toElement()); break; case QDomNode::TextNode: case QDomNode::CDATASectionNode: case QDomNode::CommentNode: emit sigSelectionChanged( selectedNode.toCharacterData()); break; case QDomNode::ProcessingInstructionNode: emit sigSelectionChanged( selectedNode.toProcessingInstruction()); break; default: kdDebug() << "KXE_TreeView::slotSelectionChanged unknown object type selected" << endl; return; } } } void KXE_TreeView::slotItemExpanded( QListViewItem * pItem ) { KXE_TreeViewItem * pTreeViewItem = static_cast (pItem); pTreeViewItem->ensureGrandChildItemsCreated(); } ////////////////////////////////////////////////////////////// // update slots ////////////////////////////////////////////////////////////// void KXE_TreeView::updateNodeCreated( const QDomNode & node ) { if ( node.isNull() ) { kdError() << "KXE_TreeView::slotUpdateNodeCreated the given node is an empty one." << endl; return; } KXE_TreeViewItem * pNewItem; if ( node.parentNode().isDocument() ) // the new nodes parent is the document itself, { // so we have to create a root item. // Now it depends: either it's a processing instruction, or element if (node.isProcessingInstruction()) // Tree looks much nicer if root processing instructions are ont the top... { QDomNode *pNode = getSpecProcInstrNode("xml"); if (pNode) pNewItem = new KXE_TreeViewItem( node, this,findCorrespondingItem(*pNode)); else pNewItem = new KXE_TreeViewItem( node, this); } else // ...and root element is placed at the bottom. pNewItem = new KXE_TreeViewItem( node, this,lastChild()); // pNewItem = new KXE_TreeViewItem( node, this); if ( ! rootIsDecorated() ) pNewItem->setOpen(true); } else { if ( node.parentNode().isNull() ) { kdError() << "KXE_TreeView::slotUpdateNodeCreated the given node has no parent node (but should)." << endl; return; } // To create the new item, we need (1st) the item corresponding to the parent node of the given one. QDomNode parentNode = node.parentNode(); // Because the currently selected item is very likely (in many cases) the correct one, try it first. KXE_TreeViewItem * pParentItem = static_cast (selectedItem()); if ( (!pParentItem) || ( *(pParentItem->xmlNode()) != parentNode ) ) { // no strike :-( pParentItem = findCorrespondingItem(parentNode); // do it the "long" way } if ( ! pParentItem ) // can't find the corresponding item { kdError() << "KXE_TreeView::slotUpdateNodeCreated can't find an item to the given nodes parent node." << endl; return; } // Now we need (2nd) the item corresponding to the previous sibling of the given one, // because, the new item has to be inserted behind the given one. QDomNode prevNode = node.previousSibling(); if ( prevNode.isNull() ) { // it seems to be the first child node, so create a first child item pNewItem = new KXE_TreeViewItem( node, pParentItem ); } else { KXE_TreeViewItem * pPrevItem = findCorrespondingItem(prevNode); if ( ! pParentItem ) // can't find the corresponding item :-( { kdError() << "KXE_TreeView::slotUpdateNodeCreated can't find an item to the given nodes previous sibling." << endl; return; } // everything's alright, let's create the new item pNewItem = new KXE_TreeViewItem( node, pParentItem, pPrevItem ); } } setSelected( pNewItem, true ); ensureItemVisible( pNewItem ); } void KXE_TreeView::updateNodeChanged( const QDomNode & node ) { if ( node.isNull() ) { kdError() << "KXE_TreeView::slotUpdateNodeChanged the given node is an empty one." << endl; return; } // To change the item, we have to find it. // Because the currently selected item is very likely (in many cases) the correct one, try it first. KXE_TreeViewItem * pItem = static_cast (selectedItem()); if ( (!pItem) || ( *(pItem->xmlNode()) != node ) ) // no strike :-( pItem = findCorrespondingItem(node); // do it the "long" way if ( ! pItem ) // can't find the corresponding item { kdError() << "KXE_TreeView::slotUpdateNodeChanged can't find an item to the given node." << endl; return; } pItem->setTexts(); // update the item setSelected( pItem, true ); ensureItemVisible( pItem ); } void KXE_TreeView::updateNodeDeleted( const QDomNode & node ) { if ( node.isNull() ) { kdError() << "KXE_TreeView::slotUpdateNodeDeleted the given node is an empty one." << endl; return; } // To remove the item, we have to find it. // Because the currently selected item is very likely (in many cases) the correct one, try it first. KXE_TreeViewItem * pItem = static_cast (selectedItem()); if ( (!pItem) || ( *(pItem->xmlNode()) != node ) ) // no strike :-( pItem = findCorrespondingItem(node); // do it the "long" way if ( ! pItem ) // can't find the corresponding item { kdError() << "KXE_TreeView::slotUpdateNodeDeleted can't find an item to the given node." << endl; return; } clearSelection(); delete pItem; emit sigSelectionCleared(hasRootNode()); } void KXE_TreeView::updateNodeMoved( const QDomNode & node ) { if ( node.isNull() ) { kdError() << "KXE_TreeView::slotUpdateNodeMoved the given node is an empty one." << endl; return; } // To move the item, we have to find it. // Because the currently selected item is very likely (in many cases) the correct one, try it first. KXE_TreeViewItem * pItem = static_cast (selectedItem()); if ( (!pItem) || ( *(pItem->xmlNode()) != node ) ) // no strike :-( pItem = findCorrespondingItem(node); // do it the "long" way if ( ! pItem ) // can't find the corresponding item { kdError() << "KXE_TreeView::slotUpdateNodeMoved can't find an item to the given node." << endl; return; } // Now we can move the item (of the moved node). // We have to differenciate between the following 2 cases. if ( node.previousSibling().isNull() ) { // The node does not has a previous sibling. This means, it has been moved // to be its parent first child. In this case, we have to find the tree // view item of the node's next sibling to swap them. // It's very likely the previous sibling of the item corresponding to the // moved node. KXE_TreeViewItem * pOldPrevItem = pItem->prevSibling(); // Was it really? if ( ! pOldPrevItem || ( *(pOldPrevItem->xmlNode()) != node.nextSibling() ) ) // It wasn't (how can it be?) - we have to find it using the "long" way. pOldPrevItem = findCorrespondingItem( node.nextSibling() ); if ( ! pOldPrevItem ) // something went wrong { kdError() << "KXE_TreeView::slotUpdateNodeMoved can't find the item to the given node's next sibling." << endl; return; } // Now we can swap them (make the old previous item the new next item of // the moved node's item). pOldPrevItem->moveItem( pItem ); } else { // The node has a previous sibling. In this case we have to find the // corresponding tree view item to swap it with the item corresponding to // the moved node. KXE_TreeViewItem * pNewPrevItem = findCorrespondingItem( node.previousSibling() ); if ( ! pNewPrevItem ) { kdError() << "KXE_TreeView::slotUpdateNodeMoved can't find the new prev.item to the given nodes prev.node." << endl; return; } // swap them (move the moved node's item after the previous sibling's item) pItem->moveItem( pNewPrevItem ); } setSelected( pItem, true ); ensureItemVisible( pItem ); } void KXE_TreeView::updateClear() { clear(); } void KXE_TreeView::rename( QListViewItem * pItem, int nColumn ) { if ( nColumn != 0 ) // inplace editing only return; // for the first column KXE_TreeViewItem * pXMLItem = static_cast (pItem); if ( pXMLItem->xmlNode()->isElement() ) // inplace-renaming only for items representing XML elements KListView::rename( pItem, nColumn ); // inplace-renaming via base class functionality else if(pXMLItem->xmlNode()->isCharacterData()) // launch dialog for editing text nodes (dynamic_cast (m_pGUIClient))->slotXmlCharDataEdit(); else if(pXMLItem->xmlNode()->isProcessingInstruction()) // launch dialog for editing proc.instr. (dynamic_cast (m_pGUIClient))->slotXmlProcInstrEdit(); } ////////////////////////////////////////////////////////////// // misc functions ////////////////////////////////////////////////////////////// KXE_TreeViewItem * KXE_TreeView::findCorrespondingItem( const QDomNode & node ) { KXE_TreeViewItem * pItem = static_cast (firstChild()); while ( pItem ) { if ( *(pItem->xmlNode()) == node ) return pItem; pItem = pItem->nextItem(); } return 0; } ////////////////////////////////////////////////////////////// // Drag & Drop ////////////////////////////////////////////////////////////// /** Overrides KListView::contentsMouseMoveEvent */ void KXE_TreeView::contentsMouseMoveEvent(QMouseEvent *e) { KListView::contentsMouseMoveEvent(e); // exit, if dragging is disabled if ( ! KXMLEditorFactory::configuration()->treeview()->enableDragging() ) return; if(!m_bDrag || (e->pos() - m_dragPos).manhattanLength() <= KGlobalSettings::dndEventDelay()) return; m_bDrag = false; QListViewItem *item = itemAt(contentsToViewport(m_dragPos)); if(!item || !item->isSelectable()) return; // copy item into clipboard KXE_TreeViewItem *pXmlTreeItem = static_cast (item); QTextDrag *pDrag = (dynamic_cast (m_pGUIClient))->copyNode(pXmlTreeItem->xmlNode()); // Start a drag const QPixmap *pix = item->pixmap(0); if(pix && pDrag->pixmap().isNull()) { QPoint hotspot(pix->width() / 2, pix->height() / 2); pDrag->setPixmap(*pix, hotspot); } pDrag->drag(); } /** Overrides KListView::contentsMouseReleaseEvent */ void KXE_TreeView::contentsMouseReleaseEvent(QMouseEvent *e) { KListView::contentsMouseReleaseEvent(e); m_bDrag = false; } /** Overrides QScrollView::contentsDragEnterEvent */ void KXE_TreeView::contentsDragEnterEvent(QDragEnterEvent *e) { m_pDropItem = 0; m_pCurrentBeforeDropItem = selectedItem(); // Save the available formats m_lstDropFormats.clear(); for(int i = 0; e->format(i); i++) { if(*(e->format(i))) { m_lstDropFormats.append(e->format(i)); } } } /** Overrides QScrollView::contentsDragMoveEvent */ void KXE_TreeView::contentsDragMoveEvent(QDragMoveEvent *e) { QListViewItem *item = itemAt(contentsToViewport(e->pos())); // Accept drops on the background, if Texts if(!item && (m_lstDropFormats.contains("text/"))) { m_pDropItem = 0; e->acceptAction(); if(selectedItem()) setSelected(selectedItem(), false); // no item selected return; } if(!item || !item->isSelectable()) { m_pDropItem = 0; m_autoOpenTimer->stop(); e->ignore(); return; } e->acceptAction(); setSelected(item, true); if(item != m_pDropItem ) { m_autoOpenTimer->stop(); m_pDropItem = item; m_autoOpenTimer->start(autoOpenTimeout); } } /** Overrides QScrollView::contentsDragLeaveEvent */ void KXE_TreeView::contentsDragLeaveEvent(QDragLeaveEvent *e) { e=e; // Restore the current item to what it was before the dragging (#17070) if(m_pCurrentBeforeDropItem) setSelected(m_pCurrentBeforeDropItem, true); else setSelected(m_pDropItem, false); // no item selected m_pCurrentBeforeDropItem = 0; m_pDropItem = 0; m_lstDropFormats.clear(); } /** Overrides QScrollView::contentsDropEvent */ void KXE_TreeView::contentsDropEvent(QDropEvent *pDropEvent) { m_autoOpenTimer->stop(); drop(selectedItem(), pDropEvent); } /** Called, when m_autoOpenTimer timeout occured */ void KXE_TreeView::slotAutoOpenFolder() { m_autoOpenTimer->stop(); if(!m_pDropItem || m_pDropItem->isOpen()) return; m_pDropItem->setOpen( true ); m_pDropItem->repaint(); } /** Drop or paste text into item */ bool KXE_TreeView::drop(QListViewItem *pItem, QDropEvent *pDropEvent) { KXE_TreeViewItem* pTreeItem = 0; if(pItem) pTreeItem = static_cast (pItem); QDomNode *pTargetNode = pTreeItem->xmlNode(); // First, make check, if moved item is not moved to their children if((pDropEvent->source() == this) && (pDropEvent->action() == QDropEvent::Move)) { // make check, if moved item is not moved to itself if(m_pCurrentBeforeDropItem && pTreeItem && (m_pCurrentBeforeDropItem == pTreeItem)) { return false; } if(m_pCurrentBeforeDropItem && pTreeItem && static_cast (m_pCurrentBeforeDropItem)->isMyChildren(pTreeItem)) { KMessageBox::sorry(0, i18n("An XML element can't be moved to its own subtree.")); return false; } if (pTreeItem->xmlNode()->isProcessingInstruction()) { KMessageBox::sorry(0, i18n("An XML node can't be moved in a processing instruction.")); return false; } QDomNode * pNode = static_cast (m_pCurrentBeforeDropItem)->xmlNode(); if (pNode->isProcessingInstruction()) { QDomProcessingInstruction domProcInstr = pNode->toProcessingInstruction(); if(domProcInstr.target() == "xml") { KMessageBox::sorry(0, i18n("This processing instruction cannot be moved !")); return false; } } } //-- If Move from same instance of this widget if((pDropEvent->source() == this) && (pDropEvent->action() == QDropEvent::Move) && (m_pCurrentBeforeDropItem) && pTargetNode->isElement()) { // remove source item QDomNode * pSourceNode = static_cast (m_pCurrentBeforeDropItem)->xmlNode(); QDomElement domTargetElement = pTargetNode->toElement(); if((dynamic_cast (m_pGUIClient))->dropMoveNode(domTargetElement, *pSourceNode)) { pDropEvent->acceptAction(); return true; } } else { //-- If Copy, do standart Paste function if((dynamic_cast (m_pGUIClient))->pasteNode(pTargetNode, pDropEvent)) { pDropEvent->acceptAction(); return true; } } return false; } // // returns last child on the tree (top-level) // KXE_TreeViewItem* KXE_TreeView::lastChild() { QListViewItem* pItem = firstChild(); if (pItem && pItem->nextSibling()) do pItem = pItem->nextSibling(); while (pItem->nextSibling()); // here we have it... return (KXE_TreeViewItem*) pItem; } void KXE_TreeView::keyPressEvent(QKeyEvent *e) { KListView::keyPressEvent(e); emit sigKeyPressed(e); }