You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
910 lines
27 KiB
910 lines
27 KiB
/***************************************************************************
|
|
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 <klocale.h>
|
|
#include <kdebug.h>
|
|
#include <kxmlgui.h>
|
|
#include <kxmlguiclient.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kglobalsettings.h>
|
|
#include <kmessagebox.h>
|
|
|
|
// include files for Qt
|
|
#include "qdom_add.h"
|
|
#include <qheader.h>
|
|
#include <qdragobject.h>
|
|
#include <qtimer.h>
|
|
#include <qdom.h>
|
|
#include <qcursor.h>
|
|
#include <qevent.h>
|
|
|
|
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<KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (pSelItem);
|
|
pSelTreeItem->collapseSubTree(nLevel);
|
|
}
|
|
|
|
void KXE_TreeView::bookmarksToggle()
|
|
{
|
|
// get selected item from tree view
|
|
KXE_TreeViewItem * pSelItem = static_cast <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (selectedItem());
|
|
if ( ! pSelItem )
|
|
{ // If there is no item selected
|
|
pSelItem = static_cast <KXE_TreeViewItem*> (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 <KXE_TreeViewItem *> (pSelItem);
|
|
return pSelTreeItem->xmlNode();
|
|
}
|
|
|
|
QDomNode * KXE_TreeView::getSpecProcInstrNode(const QString& target) const
|
|
{
|
|
KXE_TreeViewItem * pTreeItem = static_cast<KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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 <KXE_TreeViewItem *> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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<KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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 <KXMLEditorPart *> (m_pGUIClient))->slotXmlCharDataEdit();
|
|
|
|
else if(pXMLItem->xmlNode()->isProcessingInstruction()) // launch dialog for editing proc.instr.
|
|
(dynamic_cast <KXMLEditorPart *> (m_pGUIClient))->slotXmlProcInstrEdit();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
// misc functions
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
KXE_TreeViewItem * KXE_TreeView::findCorrespondingItem( const QDomNode & node )
|
|
{
|
|
KXE_TreeViewItem * pItem = static_cast<KXE_TreeViewItem*> (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 <KXE_TreeViewItem *> (item);
|
|
QTextDrag *pDrag = (dynamic_cast <KXMLEditorPart *> (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 <KXE_TreeViewItem *> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (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 <KXE_TreeViewItem*> (m_pCurrentBeforeDropItem)->xmlNode();
|
|
QDomElement domTargetElement = pTargetNode->toElement();
|
|
|
|
if((dynamic_cast <KXMLEditorPart *> (m_pGUIClient))->dropMoveNode(domTargetElement, *pSourceNode))
|
|
{
|
|
pDropEvent->acceptAction();
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-- If Copy, do standart Paste function
|
|
if((dynamic_cast <KXMLEditorPart *> (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);
|
|
}
|