/***************************************************************************
                          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 <tdelocale.h>
#include <kdebug.h>
#include <kxmlgui.h>
#include <kxmlguiclient.h>
#include <tdepopupmenu.h>
#include <tdeglobalsettings.h>
#include <tdemessagebox.h>

// include files for TQt
#include "qdom_add.h"
#include <tqheader.h>
#include <tqdragobject.h>
#include <tqtimer.h>
#include <tqdom.h>
#include <tqcursor.h>
#include <tqevent.h>

static const int autoOpenTimeout = 750;


KXE_TreeView::KXE_TreeView( KXMLGUIClient * pGUIClient, TQWidget * pParent, const char * pszName )
	: TDEListView(pParent,pszName),
	  m_pGUIClient(pGUIClient),
	  m_nBookmarkedItems(0)
{
	setSorting(-1); // no sorting

	addColumn(i18n("Qualified name"));

	setSelectionMode(TQListView::Single);

	connect( this, SIGNAL(selectionChanged()), this, SLOT(slotSelectionChanged()) );
	connect( this, SIGNAL(expanded(TQListViewItem*)), this, SLOT(slotItemExpanded(TQListViewItem*)) );

	setReadWrite(false);

	m_bDrag = false;
	m_pCurrentBeforeDropItem = 0;
  m_pDropItem = 0;

	m_autoOpenTimer = new TQTimer(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
	TQListViewItem * pSelItem = selectedItem();
	if ( ! pSelItem )
	{
		kdDebug() << "KXE_TreeView::slotViewNodeUp no item selected" << endl;
		return;
	}

	// get parent item
	TQListViewItem * 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)
	TQListViewItem * 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)
	TQListViewItem * 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.
		TQListViewItem * 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 TQDomNode & 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;
}

TQDomNode * KXE_TreeView::getSelectedNode() const
{
	// get selected item from tree view
	TQListViewItem * pSelItem = selectedItem();
	if ( ! pSelItem )
		return 0;

	KXE_TreeViewItem * pSelTreeItem = static_cast <KXE_TreeViewItem *> (pSelItem);
	return pSelTreeItem->xmlNode();
}

TQDomNode * KXE_TreeView::getSpecProcInstrNode(const TQString& target) const
{
	KXE_TreeViewItem * pTreeItem = static_cast<KXE_TreeViewItem*> (firstChild());
	while ( pTreeItem )
	{
		if (pTreeItem->xmlNode()->isProcessingInstruction())
			{
        TQDomProcessingInstruction 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;
}


TQString KXE_TreeView::getSelectedPath() const
{
	// get selected item from tree view
	TQListViewItem * pSelItem = selectedItem();
	if ( ! pSelItem )
		return TQString();

	KXE_TreeViewItem * pSelTreeItem = static_cast <KXE_TreeViewItem *> (pSelItem);
	return domTool_getPath( * pSelTreeItem->xmlNode() );
}

void KXE_TreeView::contentsMousePressEvent( TQMouseEvent * pEvent )
{
	TDEListView::contentsMousePressEvent(pEvent);

	if ( pEvent->button() == RightButton )
	{
		TQString szMenuName;

		TQListViewItem * pItem = itemAt( contentsToViewport(pEvent->pos()) );
		if (pItem)
		{
			KXE_TreeViewItem * pTreeItem = static_cast <KXE_TreeViewItem*> (pItem);
			switch( pTreeItem->xmlNode()->nodeType() )
			{
				case  TQDomNode::ElementNode:
					szMenuName = "popupXmlElement";
					break;
				case	TQDomNode::TextNode:
				case	TQDomNode::CDATASectionNode:
				case	TQDomNode::CommentNode:
					szMenuName = "popupXmlContent";
					break;
				case	TQDomNode::ProcessingInstructionNode:
					szMenuName = "popupXmlProcInstr";
					break;
				default:
					kdDebug() << "KXE_TreeView::contentsMousePressEvent unknown item type" << endl;
					return;
			}
		}
		else
			szMenuName = "popupXmlTree";

		emit sigContextMenuRequested( szMenuName, TQCursor::pos() );
		return;
	}

	//--- Drag & Drop ------------------------------------------------------
	TQPoint p(contentsToViewport(pEvent->pos()));
  TQListViewItem *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
	{
		TQDomNode selectedNode = * ( pItem->xmlNode() ); // uses TQDomNode copy constructor

		// choose appropriate object kind
		switch ( selectedNode.nodeType() )
		{
			case TQDomNode::ElementNode:
				emit sigSelectionChanged( selectedNode.toElement());
				break;

			case TQDomNode::TextNode:
			case TQDomNode::CDATASectionNode:
			case TQDomNode::CommentNode:
				emit sigSelectionChanged( selectedNode.toCharacterData());
				break;

			case TQDomNode::ProcessingInstructionNode:
				emit sigSelectionChanged( selectedNode.toProcessingInstruction());
				break;

			default:
				kdDebug() << "KXE_TreeView::slotSelectionChanged unknown object type selected" << endl;
				return;
		}
	}
}


void KXE_TreeView::slotItemExpanded( TQListViewItem * pItem )
{
	KXE_TreeViewItem * pTreeViewItem = static_cast<KXE_TreeViewItem*> (pItem);
	pTreeViewItem->ensureGrandChildItemsCreated();
}


//////////////////////////////////////////////////////////////
// update slots
//////////////////////////////////////////////////////////////

void KXE_TreeView::updateNodeCreated( const TQDomNode & 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...
		{
			TQDomNode *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.
		TQDomNode 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.
		TQDomNode 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 TQDomNode & 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 TQDomNode & 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 TQDomNode & 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( TQListViewItem * 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
		TDEListView::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 TQDomNode & 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 TDEListView::contentsMouseMoveEvent */
void KXE_TreeView::contentsMouseMoveEvent(TQMouseEvent *e)
{
	TDEListView::contentsMouseMoveEvent(e);

	// exit, if dragging is disabled
	if ( ! KXMLEditorFactory::configuration()->treeview()->enableDragging() )
		return;

  if(!m_bDrag || (e->pos() - m_dragPos).manhattanLength() <= TDEGlobalSettings::dndEventDelay())
        return;

  m_bDrag = false;

  TQListViewItem *item = itemAt(contentsToViewport(m_dragPos));

  if(!item || !item->isSelectable())
  	return;

	// copy item into clipboard
	KXE_TreeViewItem *pXmlTreeItem = static_cast <KXE_TreeViewItem *> (item);
	TQTextDrag *pDrag = (dynamic_cast <KXMLEditorPart *> (m_pGUIClient))->copyNode(pXmlTreeItem->xmlNode());


	// Start a drag
  const TQPixmap *pix = item->pixmap(0);
  if(pix && pDrag->pixmap().isNull())
	{ TQPoint hotspot(pix->width() / 2, pix->height() / 2);
      pDrag->setPixmap(*pix, hotspot);
    }

  pDrag->drag();
}

/** Overrides TDEListView::contentsMouseReleaseEvent */
void KXE_TreeView::contentsMouseReleaseEvent(TQMouseEvent *e)
{
	TDEListView::contentsMouseReleaseEvent(e);
  m_bDrag = false;
}

/** Overrides TQScrollView::contentsDragEnterEvent */
void KXE_TreeView::contentsDragEnterEvent(TQDragEnterEvent *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 TQScrollView::contentsDragMoveEvent */
void KXE_TreeView::contentsDragMoveEvent(TQDragMoveEvent *e)
{
	TQListViewItem *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 TQScrollView::contentsDragLeaveEvent */
void KXE_TreeView::contentsDragLeaveEvent(TQDragLeaveEvent *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 TQScrollView::contentsDropEvent */
void KXE_TreeView::contentsDropEvent(TQDropEvent *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(TQListViewItem *pItem, TQDropEvent *pDropEvent)
{
	KXE_TreeViewItem* pTreeItem = 0;
  if(pItem)
  	pTreeItem = static_cast <KXE_TreeViewItem *> (pItem);

  TQDomNode *pTargetNode = pTreeItem->xmlNode();

	// First, make check, if moved item is not moved to their children
  if((pDropEvent->source() == this) && (pDropEvent->action() == TQDropEvent::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;
      }

      TQDomNode * pNode = static_cast <KXE_TreeViewItem*> (m_pCurrentBeforeDropItem)->xmlNode();
      if (pNode->isProcessingInstruction())
			{
        TQDomProcessingInstruction 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() == TQDropEvent::Move) && (m_pCurrentBeforeDropItem) && pTargetNode->isElement())
 	{
    // remove source item
    TQDomNode * pSourceNode = static_cast <KXE_TreeViewItem*> (m_pCurrentBeforeDropItem)->xmlNode();
    TQDomElement 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()
{
        TQListViewItem* 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(TQKeyEvent *e)
{
	TDEListView::keyPressEvent(e);
	emit sigKeyPressed(e);
}

#include "kxe_treeview.moc"
