/***************************************************************************
                          kxmleditorpart.cpp  -  description
                             -------------------
    begin                : Wed Sep 19 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 "kxmleditorpart.h"
#include "kxmleditorfactory.h"

#include "kxedocument.h"

#include "kxeconfiguration.h"
#include "kxenewfilesettings.h"
#include "kxearchiveextssettings.h"
#include "kxeprintsettings.h"
#include "kxetextviewsettings.h"

#include "commands_edit.h"
#include "commands_insert.h"

#include "kxe_viewelement.h"
#include "kxe_treeviewitem.h"

#include "kxesearchdialog.h"
#include "kxechoosestringdialog.h"
#include "kxeelementdialog.h"
#include "kxeattributedialog.h"
#include "kxeprocinstrdialog.h"
#include "kxespecprocinstrdialog.h"
#include "kxefilenewdialog.h"
#include "kxechardatadialog.h"
#include "kxeattachdialogbase.h"
#include "kxetexteditordialog.h"

#include "actions.h"
#include "qdom_add.h"

#include <kinstance.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <tdeaction.h>
#include <kstdaction.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <kprinter.h>
#include <ktar.h>
#include <kzip.h>
#include <tdetempfile.h>
#include <tdeconfig.h>
#include <kurlrequester.h>
#include <kcommand.h>
#include <tdetoolbar.h>
#include <tdefiledialog.h>

#include <tqregexp.h>
#include <tqtextcodec.h>
#include <tqstringlist.h>
#include <tqsplitter.h>
#include <tqtabwidget.h>
#include <tqtextedit.h>
#include <tqcombobox.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqclipboard.h>
#include <tqdragobject.h>
#include <tqapplication.h>
#include <tqbuffer.h>
#include <tqlabel.h>
#include <tqpainter.h>
#include <tqpaintdevicemetrics.h>
#include <tqevent.h>

#include "dcopiface_part_ro.h" // DCOP Iface

#define CONFIG_MAIN_SPLITTER_SIZES "Main splitter sizes"


KXMLEditorPart::KXMLEditorPart( bool fReadWrite, KXEDocument* pDocument, TQWidget * pParent, const char * pszName )
	: KParts::ReadWritePart(pParent,pszName),
	m_pDlgSearch(0),
	m_pDocument(0)
{
	//////////////////////////////
	// INIT PART
	//////////////////////////////

	setInstance(KXMLEditorFactory::instance());

	if(fReadWrite)
		m_pBrowserExt = 0L; // Create Browser extension only for read-only part
	else
		m_pBrowserExt = new KXMLEditorBrowserExtension( this, "KXMLEditorPart browser extension" );
	
	m_pPrinter = 0L;

	m_bAlreadyModified = false;

	//////////////////////////////
	// CREATE ACTIONS
	//////////////////////////////

	// file actions
	if(fReadWrite)
		{
			KStdAction::save(this, SLOT(save()), actionCollection());
			KStdAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection());
		}
		
	// edit actions
	m_pActEditFind = KStdAction::find( this, SLOT(slotEditFind()), actionCollection());
	m_pActEditFindNext = KStdAction::findNext( this, SLOT(slotEditFindNext()), actionCollection());
	m_pActEditDeselect = new TDEAction( i18n("D&eselect Node"), CTRL+Key_E, this,
	                                SLOT(slotEditDeselect()), actionCollection(), "deselect" );

	// view actions
	m_pActViewNodeUp = new TDEAction( i18n("To &Parent Node"), "up", 0, this,
	                                SLOT(slotViewNodeUp()), actionCollection(), "treeitem_up" );

	m_pActViewExpNode = new TDEToolBarPopupAction( i18n("&Expand Node"), "expand_node", CTRL+Key_Plus, this,
	                                             SLOT(slotViewExpNode()), actionCollection(), "treeitem_expand" );
	TDEPopupMenu * pMenuExpNode = m_pActViewExpNode->popupMenu();
	connect( pMenuExpNode, SIGNAL(activated(int)), this, SLOT(slotViewExpNode(int)) );
	for ( uint i = 1; i <= 8; i++ )
		pMenuExpNode->insertItem( i18n("Expand To Level %1").arg(i), i-1 );

	m_pActViewColNode = new TDEToolBarPopupAction( i18n("&Collapse Node"), "collapse_node", CTRL+Key_Minus, this,
	                                             SLOT(slotViewColNode()), actionCollection(), "treeitem_collapse" );
	TDEPopupMenu * pMenuColNode = m_pActViewColNode->popupMenu();
	connect( pMenuColNode, SIGNAL(activated(int)), this, SLOT(slotViewColNode(int)) );
	for ( uint i = 0; i <= 7; i++ )
		pMenuColNode->insertItem( i18n("Collapse To Level %1").arg(i), i );

	// bookmark actions
	m_pActBookmarksToggle = new TDEAction( i18n("&Toggle Bookmark"), "bookmark_add", CTRL+Key_B, this,
	                                   SLOT(slotBookmarksToggle()), actionCollection(), "bookmark_toggle" );
	m_pActBookmarksPrev = new TDEAction( i18n("&Previous Bookmark"), "bookmark_prev", SHIFT+Key_F5, this,
	                                   SLOT(slotBookmarksPrev()), actionCollection(), "bookmark_prev" );
	m_pActBookmarksNext = new TDEAction( i18n("&Next Bookmark"), "bookmark_next", Key_F5, this,
	                                   SLOT(slotBookmarksNext()), actionCollection(), "bookmark_next" );

	// settings actions
	new TDEAction( i18n("&Configure KXMLEditor..."), "configure", 0, this,
	             SLOT (slotConfigure()), actionCollection(), "configure" );

	// path toolbar
	m_pActPathCombo = new KXmlEditorComboAction( i18n("Path Bar"), 0, this,
	                                             SLOT(slotPathSelected(const TQString &)), actionCollection(), "path_combo" );
	new TDEAction( i18n("Clear Path Bar"), BarIcon("locationbar_erase", 16), 0, this,
	             SLOT(slotPathClear()), actionCollection(), "path_clear" );
	TQLabel *m_locationLabel = new ToolbarLabel( i18n("Path: ") );
	new KWidgetAction( m_locationLabel, i18n("Path: "), 0, this, 0, actionCollection(), "path_label" );
	m_locationLabel->setBuddy( m_pActPathCombo->comboBox());

	if ( ! fReadWrite )
		{
			setXMLFile( "kxmleditorpartBrowseUI.rc", true );
			
			KStdAction::cut( m_pBrowserExt, SLOT(slotEditCut()), actionCollection(), "cut" );
			KStdAction::copy( m_pBrowserExt, SLOT(slotEditCopy()), actionCollection(), "copy" );
			KStdAction::paste( m_pBrowserExt, SLOT(slotEditPaste()), actionCollection(), "paste");
			
			m_pCmdHistory = 0L;
			
			m_pActVersionEncoding =  0L;
			m_pActAttachSchema =  0L;
			m_pActDetachSchema =  0L;
			m_pActAttachStylesheet =  0L;
			m_pActDetachStylesheet =  0L;
		}
	else
	{
		// document specific actions here
		m_pActVersionEncoding = new TDEAction( i18n("&Version && Encoding..."), 0, 0, this, SLOT(slotActVersionEncoding()), actionCollection(), "xml_ins_spec_procins" );
		m_pActAttachSchema = new TDEAction(i18n("Attach Schema..."),0,0,this,SLOT(slotActAttachSchema()), actionCollection(), "xml_attach_schema");
		m_pActDetachSchema = new TDEAction(i18n("Detach Schema"),0,0,this,SLOT(slotActDetachSchema()), actionCollection(), "xml_detach_schema");
		m_pActAttachStylesheet = new TDEAction(i18n("Attach Stylesheet..."),0,0,this,SLOT(slotActAttachStylesheet()), actionCollection(), "xml_attach_stylesheet");
		m_pActDetachStylesheet = new TDEAction(i18n("Detach Stylesheet"),0,0,this,SLOT(slotActDetachStylesheet()), actionCollection(), "xml_detach_stylesheet");
		
		KStdAction::print( this, SLOT(slotActPrint()), actionCollection(), "print" );
		
		m_pActAttachSchema->setEnabled(false);
		m_pActDetachSchema->setEnabled(false);
		m_pActDetachStylesheet->setEnabled(false);
	
	
		// undo & redo
    KStdAction::undo(this, SLOT(slotActUndo()), actionCollection());
    KStdAction::redo(this, SLOT(slotActRedo()), actionCollection());
		
		m_pCmdHistory = new KCommandHistory(actionCollection());
		
		m_pActEditCut = KStdAction::cut( this, SLOT(slotEditCut()), actionCollection());
		m_pActEditCopy = KStdAction::copy( this, SLOT(slotEditCopy()), actionCollection());
		m_pActEditPaste = KStdAction::paste( this, SLOT(slotEditPaste()), actionCollection());

		// Move node Up & Down
		m_pActXmlMoveNodeUp = new TDEAction( i18n("&Move Up"), "xml_move_item_up", CTRL+Key_U, this, SLOT(slotXmlMoveNodeUp()), actionCollection(), "xml_move_item_up" );
		m_pActXmlMoveNodeDown = new TDEAction( i18n("Move &Down"), "xml_move_item_down", CTRL+Key_D, this, SLOT(slotXmlMoveNodeDown()), actionCollection(), "xml_move_item_down" );

		// Insert actions
		m_pActXmlElementInsert = new TDEAction( i18n("&Element..."), "xml_insert_element", CTRL+SHIFT+Key_E, this, SLOT(slotXmlElementInsert()), actionCollection(), "xml_ins_element" );
		m_pActXmlAttributesAdd = new TDEAction( i18n("&Attribute..."), "xml_insert_attribute", CTRL+SHIFT+Key_A, this, SLOT(slotXmlAttributesAdd()), actionCollection(), "xml_add_attribute" );
		m_pActInsertText = new TDEAction( i18n("&Text..."), "xml_text", CTRL+SHIFT+Key_T, this, SLOT(slotActInsertText()), actionCollection(), "insert_text" );
		m_pActInsertCDATA = new TDEAction( i18n("C&DATA..."), "xml_cdata", CTRL+SHIFT+Key_D, this, SLOT(slotActInsertCDATA()), actionCollection(), "insert_cdata" );
		m_pActInsertComment = new TDEAction( i18n("&Comment..."), "xml_comment", CTRL+SHIFT+Key_C, this, SLOT(slotActInsertComment()), actionCollection(), "insert_comment" );
		m_pActXmlProcInstrInsert = new TDEAction( i18n("&Processing Instruction..."), "xml_insert_procins", CTRL+SHIFT+Key_P, this, SLOT(slotXmlProcInstrInsert()), actionCollection(), "xml_ins_procins" );

		// Edit node properties
		m_pActProperties = new TDEAction(i18n("&Properties..."),"edit",0,this,SLOT(slotActProperties()),actionCollection(),"edit_properties");
		m_pActEditRawXml = new TDEAction(i18n("Edit &raw XML..."), 0, 0, this, SLOT(slotActEditRawXml()),actionCollection(),"edit_as_raw_xml");

		// delete actions
		m_pActXmlAttributeDel = new TDEAction( i18n("&Delete"), "editdelete", 0, this, SLOT(slotXmlAttributeDel()), actionCollection(), "xml_del_attribute" );
		m_pActXmlAttributesDel = new TDEAction( i18n("Delete all Attributes..."), 0, 0, this, SLOT(slotXmlAttributesDel()), actionCollection(), "xml_del_attributes" );
		m_pActDelete = new TDEAction(i18n("&Delete"),"editdelete",0,this,SLOT(slotActDelete()),actionCollection(),"edit_delete");

		setXMLFile( "kxmleditorpartEditUI.rc", true );
		
		// we are not modified since we haven't done anything yet
    setModified(false);
	}

	//////////////////////////////
	// CREATE WIDGETS
	//////////////////////////////

	pSplitter = new TQSplitter( pParent, "KXMLEditorPart main widget (Splitter)" );
	pSplitter->setFocusPolicy( TQWidget::NoFocus );
	pSplitter->setOpaqueResize(true);
	setWidget( pSplitter );

	// create the tree view -------------------
	m_pViewTree = new KXE_TreeView( this, pSplitter, "KXMLEditorPart treeview" );

	connect( m_pViewTree, SIGNAL(sigSelectionCleared(bool)), this, SLOT(slotSelectionCleared(bool)) );
	connect( m_pViewTree, SIGNAL(sigSelectionChanged(const TQDomElement &)), this, SLOT(slotSelectionChanged(const TQDomElement &)) );
	connect( m_pViewTree, SIGNAL(sigSelectionChanged(const TQDomCharacterData &)), this, SLOT(slotSelectionChanged(const TQDomCharacterData &)) );
	connect( m_pViewTree, SIGNAL(sigSelectionChanged(const TQDomProcessingInstruction &)), this, SLOT(slotSelectionChanged(const TQDomProcessingInstruction &)) );
	connect( m_pViewTree, SIGNAL(sigContextMenuRequested(const TQString&,const TQPoint&)), this, SLOT(slotContextMenuRequested(const TQString&,const TQPoint&)) );
	connect( m_pViewTree, SIGNAL(itemRenamed(TQListViewItem *)), this, SLOT(slotItemRenamedInplace(TQListViewItem *)) );
	connect( m_pViewTree, SIGNAL(sigKeyPressed(TQKeyEvent* )),this,SLOT(slotTreeViewKeyPressed(TQKeyEvent*)));

	// create tab widget ----------------------
	m_pTabWidget = new TQTabWidget( pSplitter, "KXMLEditorPart tabwidget");
	m_pTabWidget->setFocusPolicy( TQWidget::NoFocus );

	// create element view
	m_pViewElement = new KXE_ViewElement( m_pTabWidget, instance()->config(), "KXMLEditorPart element view" );
	m_pTabWidget->addTab( m_pViewElement, g_iconElement, i18n("Element") );
	connect( m_pViewElement, SIGNAL(sigContextMenuRequested(const TQString&,const TQPoint&)), this, SLOT(slotContextMenuRequested(const TQString&,const TQPoint&)) );
	connect( m_pViewElement, SIGNAL(sigAttributeNameChangedInplace(const TQDomAttr&, const TQString)), this, SLOT(slotAttributeNameChangedInplace(const TQDomAttr&, const TQString)) );
	connect( m_pViewElement, SIGNAL(sigAttributeValueChangedInplace(const TQDomAttr&, const TQString)), this, SLOT(slotAttributeValueChangedInplace(const TQDomAttr&, const TQString)) );

	// create edit widget, that display XML character data contents
	m_pViewContents = new TQTextEdit( m_pTabWidget, "KXMLEditorPart contents view" );
	m_pTabWidget->addTab( m_pViewContents, g_iconText, i18n("Contents") );
	m_pViewContents->setReadOnly( true );
	m_pViewContents->setWordWrap( TQTextEdit::NoWrap );
	m_pViewContents->setTextFormat(TQTextEdit::PlainText);

	// create proc.instr. view
	m_pViewProcInstr = new TQTextEdit( m_pTabWidget, "KXMLEditorPart proc.instr. view" );
	m_pTabWidget->addTab( m_pViewProcInstr, g_iconProcessingInstruction, i18n("Proc.Instruction") );
	m_pViewProcInstr->setReadOnly( true );
	m_pViewProcInstr->setWordWrap( TQTextEdit::NoWrap );

	connect( this, SIGNAL(started(TDEIO::Job*)), this, SLOT(started()) );
	connect( this, SIGNAL(completed()), this, SLOT(completed()) );
	connect( this, SIGNAL(canceled(const TQString &)), this, SLOT(canceled()) );

	//////////////////////////////
	// INIT BEGIN STATE
	//////////////////////////////

	// Disable actions
	if(m_pBrowserExt)
		m_pBrowserExt->emit enableAction("copy", false);
	else
		m_pActEditCopy->setEnabled(false);

	m_pActEditFindNext->setEnabled(false);
	m_pActEditDeselect->setEnabled(false);
	m_pActViewNodeUp->setEnabled(false);
	m_pActViewExpNode->setEnabled(false);
	m_pActViewColNode->setEnabled(false);
	m_pActBookmarksToggle->setEnabled(false);
	m_pActBookmarksPrev->setEnabled(false);
	m_pActBookmarksNext->setEnabled(false);

	if ( fReadWrite )
	{
		m_pActEditCut->setEnabled(false);
		m_pActEditPaste->setEnabled(true);

		m_pActXmlElementInsert->setEnabled(true);
		m_pActXmlAttributesAdd->setEnabled(false);
		m_pActXmlAttributesDel->setEnabled(false);
		m_pActXmlProcInstrInsert->setEnabled(true);
		m_pActInsertText->setEnabled(false);
		m_pActInsertCDATA->setEnabled(false);
		m_pActInsertComment->setEnabled(false);
		m_pActXmlMoveNodeUp->setEnabled(false);
		m_pActXmlMoveNodeDown->setEnabled(false);
		m_pActDelete->setEnabled(false);
		m_pActProperties->setEnabled(false);
		m_pActEditRawXml->setEnabled(false);
	}

	m_pTabWidget->setTabEnabled( m_pViewElement, false );
	m_pTabWidget->setTabEnabled( m_pViewContents, false );
	m_pTabWidget->setTabEnabled( m_pViewProcInstr, false );

	setReadWrite(fReadWrite);

	// configuring splitter sizes
	TQValueList<int> list = instance()->config()->readIntListEntry(CONFIG_MAIN_SPLITTER_SIZES);
	if (!list.isEmpty())
		pSplitter->setSizes(list);

	//////////////////////////////
	// INIT DCOP object (if any)
	//////////////////////////////
	//m_pDCOPIface = NULL;
	//m_pDCOPIface = new KXMLEditorPartIfaceReadOnly(this);
	m_pDCOPIface = new KXMLEditorPartIfaceReadWrite(this); // temporarly to test openUrl

	setDocument(pDocument);

}

KXMLEditorPart::~KXMLEditorPart()
{
	// saving splitter configuration
	TDEConfig *pConfig = instance()->config();
	pConfig->writeEntry( CONFIG_MAIN_SPLITTER_SIZES, pSplitter->sizes() );

	if (m_pDCOPIface)
		delete m_pDCOPIface;

	if (m_pDlgSearch)
		delete m_pDlgSearch;

	if (m_pPrinter)
		delete m_pPrinter;

	if (document())
		delete document();
		
	delete m_pCmdHistory;
}


/////////////////////////////////////////////////////////////////////
//                   KPART FUNCTIONALITY
/////////////////////////////////////////////////////////////////////

bool KXMLEditorPart::openFile()
{
	if( isModified() )
		kdError() << "KXMLEditorPart::openFile the current document is modified." << endl;

	document()->setURL(m_url);
	bool bResult = document()->open(m_file);
	updateActions();
	return bResult;
}

bool KXMLEditorPart::saveFile()
{
  emit setStatusBarText( i18n("Saving file...") );
	
	if( url().isEmpty() )
		{	
			return slotFileSaveAs();
		}
		
	// delegate this operation to underlying document object
	document()->setURL(m_url);
	bool bRetVal = document()->save(m_file);

	emit setStatusBarText( i18n("Ready.") );
	return bRetVal;	
}

void KXMLEditorPart::setModified( bool bModified )
{
	KParts::ReadWritePart::setModified( bModified ); // base functionality

	if ( m_bAlreadyModified != bModified )
	{
		m_bAlreadyModified = bModified;

		TQString szNewCaption = m_url.prettyURL();
		emit setWindowCaption( szNewCaption );
	}
			
	// get a handle on our Save action and make sure it is valid
  TDEAction *pActFileSave = actionCollection()->action(KStdAction::stdName(KStdAction::Save));
  if(!pActFileSave)
        return;

  // if so, we either enable or disable it based on the current state
  pActFileSave->setEnabled(bModified);
	
	// Update others actions
	updateActions();
}

void KXMLEditorPart::setReadWrite( bool fReadWrite )
{
	m_pViewTree->setReadWrite(fReadWrite);
	m_pViewElement->setReadWrite(fReadWrite);

	KParts::ReadWritePart::setReadWrite( fReadWrite ); // base functionality
}

void KXMLEditorPart::started()
{
	kdDebug() << "KXMLEditorPart::started" << endl;
}
void KXMLEditorPart::completed()
{
	kdDebug() << "KXMLEditorPart::completed" << endl;
}
void KXMLEditorPart::canceled()
{
	kdDebug() << "KXMLEditorPart::canceled" << endl;
}


/////////////////////////////////////////////////////////////////////
//                     ACTION SLOTS
/////////////////////////////////////////////////////////////////////

void KXMLEditorPart::slotActPrint()
{
	// this slot is called whenever the File->Print menu is selected,
	// the Print shortcut is pressed (usually CTRL+P) or the Print toolbar
	// button is clicked
	if (!m_pPrinter)
		m_pPrinter = new KPrinter;

	if (m_pPrinter->setup(widget()))
		print(m_pPrinter);
}

void KXMLEditorPart::slotActProperties()
{
	TQDomNode* pNode = m_pViewTree->getSelectedNode();
	if (pNode)
	{
		if (pNode->isElement())
			slotXmlElementEdit();
		else if (pNode->isCharacterData())
			slotXmlCharDataEdit();
		else if (pNode->isProcessingInstruction())
			slotXmlProcInstrEdit();
		else
			kdError() << "Unknown node selected.";
	}
}

void KXMLEditorPart::slotActDelete()
{
	TQDomNode* pNode = m_pViewTree->getSelectedNode();

	if (!m_pViewElement->hasFocus())
	{
		// delete nodes selected there
		if (pNode)
		{
			KCommand *pCmd = new KXEDeleteNodeCommand(document(), *pNode);
			m_pCmdHistory->addCommand(pCmd);
		}
	}
	else
	{
		// we can have also delete attribute selected on a element view
		TQDomAttr domAttr = m_pViewElement->getSelectedAttribute();
		if (!domAttr.isNull())
		{
			TQDomElement domElement = pNode->toElement();
			TQDomAttr domAttr = m_pViewElement->getSelectedAttribute();

			KCommand *pCmd = new KXEDeleteAttrCommand(document(), domElement, domAttr);
			m_pCmdHistory->addCommand(pCmd);
		}
	}
}

void KXMLEditorPart::slotEditCut()
{
	kdDebug() << "KXMLEditor " << k_funcinfo << endl;

	if(! isReadWrite())
	{
		kdError() << "KXMLEditorPart::slotEditCut called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();

	if(pNode)
	{ // copy to clipboard
		slotEditCopy();

		// and cut it
		KCommand *pCmd = new KXECutCommand(document(), *pNode);
		m_pCmdHistory->addCommand(pCmd);
	}
}

void KXMLEditorPart::slotEditCopy()
{
	kdDebug() << "KXMLEditor " << k_funcinfo << endl;

	TQDomNode * pNode = m_pViewTree->getSelectedNode();

	if(pNode)
	{
		TQTextDrag *pDrag = copyNode(m_pViewTree->getSelectedNode());
		if(pDrag)
			TQApplication::clipboard()->setData(pDrag);
	}
	else
		kdError() << "KXMLEditorPart::slotEditCopy no element selected." << endl;
}

void KXMLEditorPart::slotEditPaste()
{
	kdDebug() << "KXMLEditor " << k_funcinfo << endl;

	if(!isReadWrite())
	{
		kdError() << "KXMLEditorPart::slotEditPaste called in readonly mode." << endl;
		return;
	}

	if (document()->documentElement().isNull())
	{
		pasteNode(0, TQApplication::clipboard()->data());
	}
	else
	{
		pasteNode(m_pViewTree->getSelectedNode(), TQApplication::clipboard()->data());
	}
}

void KXMLEditorPart::slotEditFind()
{
	emit setStatusBarText( i18n("Search in XML tree ...") );

	if ( ! m_pDlgSearch )
	{
		m_pDlgSearch = new KXESearchDialog( widget(), "search dialog", true );
	}

	if ( m_pDlgSearch->exec() == KXESearchDialog::Accepted )
		slotEditFindNext();

	m_pActEditFindNext->setEnabled(true);

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotEditFindNext()
{
	emit setStatusBarText( i18n("Search in XML tree ...") );

	if ( ! m_pDlgSearch )
	{
		kdDebug() << "KXMLEditorPart::slotEditFindNext implementation error - no search dialog" << endl;
		emit setStatusBarText( i18n("Ready.") );
		return;
	}

	// get node to start with (either the next node of the item selected at the tree view or documents root node)
	TQDomNode node = ( (m_pViewTree->getSelectedNode()) && (! m_pViewTree->getSelectedNode()->isNull()) ) ? domTool_nextNode(* m_pViewTree->getSelectedNode()) : document()->documentElement();

	// start testing til the last node
	while( ! node.isNull() )
	{
		if ( domTool_match( node, m_pDlgSearch ) )
		{
			m_pViewTree->selectNode(node);
			emit setStatusBarText( i18n("Ready.") );
			return;
		}
		
		node = domTool_nextNode(node);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlElementInsert()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlElementInsert called in readonly mode." << endl;
		return;
	}

	emit setStatusBarText( i18n("Inserting XML element into document...") );

	KXEElementDialog dlg( widget(), "XML element dialog" );
	
	if ( document()->documentElement().isNull() ) // the document doesn't
	{                                         // have a root element yet
		if ( dlg.exec( false, true, false ) == TQDialog::Accepted )
		{
			KCommand *pCmd = new KXEElementCommand(document(), document(), dlg.nsURI(), dlg.prefix(), dlg.name());
			m_pCmdHistory->addCommand(pCmd);
		}
	}
	else  // the document seems to
	{     // have a root element
		TQDomNode * pParentNode = m_pViewTree->getSelectedNode();

		if ( (pParentNode) && (pParentNode->isElement()) )
		{
			TQDomElement domParentElement = pParentNode->toElement();
			
			if ( dlg.exec( false, false, false ) == TQDialog::Accepted )
			{
				KCommand *pCmd = new KXEElementCommand(document(), domParentElement, dlg.nsURI(), dlg.prefix(), dlg.name(), dlg.atTop());
				m_pCmdHistory->addCommand(pCmd);
			}
		}
		else
			kdError() << "KXMLEditorPart::slotXmlElementInsert no element selected." << endl;
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlElementEdit()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlElementEdit called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotXmlElementEdit no node selected or selected node is no XML element." << endl;
		return;
	}

	emit setStatusBarText( i18n("Editing XML element...") );

	TQDomElement domElement = pNode->toElement();

	KXEElementDialog dlg( widget(), "XML element dialog" );

	dlg.setPrefix(domElement.prefix());
	dlg.setName(domElement.tagName());
	if(!domElement.namespaceURI().isNull())
		dlg.setNsURI(domElement.namespaceURI() );

	if ( dlg.exec( true, false, domElement.namespaceURI().isNull() ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXEEditElementCommand(document(), domElement, dlg.prefix(), dlg.name());
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotActEditRawXml()
{
  if ( ! isReadWrite() )
	{
	  kdError() << "KXMLEditorPart::slotActEditRawXml called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotActEditRawXml no node selected or selected node is no XML element." << endl;
		return;
	}

	emit setStatusBarText( i18n("Editing raw XML...") );

	TQDomElement domElement = pNode->toElement();

	TQString strXML;

	TQTextStream streamXML(&strXML, IO_WriteOnly);
	int iIndent = KXMLEditorFactory::configuration()->textview()->indentSteps();
	pNode->save(streamXML, iIndent);

	KXETextEditorDialog dlg(0, "Text dialog");
	dlg.setEditorText(strXML);
	if((dlg.exec() == TQDialog::Accepted) && (strXML != dlg.editorText()))
	{
		TQString strXML = "<root>" + dlg.editorText()  + "</root>";

		// create XML documemt from text
		TQString strErrorMsg;
		int iErrorLine, iErrorColumn;
		TQDomDocument doc;

 		if(!doc.setContent(strXML, true, &strErrorMsg, &iErrorLine, &iErrorColumn) )
 		{
			kdDebug() << "KXMLEditorPart::slotActEditRawXml: Failed parsing the file." << endl;

			KMessageBox::error(m_pViewTree,
											 		i18n("%1 in line %2, column %3").arg(strErrorMsg).arg(iErrorLine).arg(iErrorColumn),
											 		i18n("Parsing error !"));

			return;
		}

    // check if root item already exists
		if(!doc.firstChild().firstChild().isElement())
			{
				KMessageBox::sorry(m_pViewTree, i18n("You are changed root element to another node type, while editing !"));
				return;
			}

		TQDomElement domNewElement = doc.firstChild().firstChild().toElement();
		KCommand *pCmd = new KXEEditRawXmlCommand(document(), domElement, domNewElement);
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlAttributesAdd()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributesAdd called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributesAdd no node selected or selected node is no XML element." << endl;
		return;
	}

	emit setStatusBarText( i18n("Add attribute...") );

	KXEAttributeDialog dlg( widget(), "attribute dialog" );

	TQDomElement domOwnerElement = pNode->toElement();
	if ( dlg.exec( ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXEAttributeCommand(document(), domOwnerElement, dlg.attributeNamespace(), dlg.QName(), dlg.Value());
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlAttributesDel()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributesDel called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributesDel no node selected or selected node is no XML element." << endl;
		return;
	}

	if(KMessageBox::questionYesNo(0, i18n("Remove all attributes from selected node ?")) != KMessageBox::Yes)
		return;

	emit setStatusBarText( i18n("Delete all attributes...") );

	TQDomElement domOwnerElement = pNode->toElement();

	KCommand *pCmd = new KXEDeleteAllAttribCommand(document(), domOwnerElement);
	m_pCmdHistory->addCommand(pCmd);

	emit setStatusBarText( i18n("Ready.") );
}


void KXMLEditorPart::slotXmlAttributeDel()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributeDel called in readonly mode." << endl;

		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotXmlAttributeDel no node selected or selected node is no XML element." << endl;
		return;
	}

	emit setStatusBarText( i18n("Delete attribute...") );

	TQDomElement domElement = pNode->toElement();
	TQDomAttr domAttr = m_pViewElement->getSelectedAttribute();

	KCommand *pCmd = new KXEDeleteAttrCommand(document(), domElement, domAttr);
	m_pCmdHistory->addCommand(pCmd);

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlProcInstrInsert()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlProcInstrInsert called in readonly mode." << endl;
		return;
	}

	KXEProcInstrDialog dlg( widget(), "proc. instr. dialog" );

	TQDomNode * pParentNode = m_pViewTree->getSelectedNode();
	if ( (pParentNode) && (!pParentNode->isElement()) )
	{
		kdError() << k_funcinfo << " The selected node is no XML element." << endl;
		return;
	}
	else
  {
    if ( ! pParentNode )  // no node selected -> the new node should be a direct child of the document
    {
      if ( dlg.exec( false, true ) == TQDialog::Accepted )
      {
        KCommand *pCmd = new KXEProcInstrCommand(document(), document(), dlg.atTop(), dlg.target(), dlg.data());
        m_pCmdHistory->addCommand(pCmd);
      }
    }
    else
    {
      if ( dlg.exec( false, false ) == TQDialog::Accepted )
      {
        TQDomElement domParentElement = pParentNode->toElement();

        KCommand *pCmd = new KXEProcInstrCommand(document(), domParentElement, dlg.atTop(), dlg.target(), dlg.data());
        m_pCmdHistory->addCommand(pCmd);
      }
    }
   }

	emit setStatusBarText( i18n("Inserting processing instruction into document...") );

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlProcInstrEdit()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlProcInstrEdit called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isProcessingInstruction()) )
	{
		kdError() << "KXMLEditorPart::slotXmlProcInstrEdit no node selected or selected node is no processing instruction." << endl;
		return;
	}

	emit setStatusBarText( i18n("Editing processing instruction...") );

	TQDomProcessingInstruction domProcInstr = pNode->toProcessingInstruction();

	// We have two different kinds of processing instructions:
	// - a special one - defining the documents XML version and encoding
	// - others
	// and both cases require different dialogs.
	if( domProcInstr.target() == "xml" ) // 1st case (special proc.instr.)
		document()->actVersionEncoding();
	else                                 // 2nd case (others)
	{
		KXEProcInstrDialog dlg( widget(), "proc. instr. dialog" );

		dlg.setTarget(domProcInstr.target());
		dlg.setData(domProcInstr.data());

		if ( dlg.exec( true, false ) == TQDialog::Accepted )
		{
			KCommand *pCmd = new KXEEditProcInstrCommand(document(), domProcInstr, dlg.data());
			m_pCmdHistory->addCommand(pCmd);
		}
	}
	
	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotActInsertText()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotActInsertText called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotActInsertText no element selected." << endl;
		return;
	}

	emit setStatusBarText( i18n("Inserting text into document...") );

	KXECharDataDialog dlg( widget());
	dlg.setCaption(i18n("Insert text"));

	TQDomElement domParentElement = pNode->toElement();

	if ( dlg.exec( false ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXECharDataCommand(document(), domParentElement, dlg.atTop(), CharDataTextNode, dlg.contents());
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotActInsertCDATA()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotActInsertCDATA called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotActInsertCDATA no element selected." << endl;
		return;
	}

	emit setStatusBarText( i18n("Inserting CDATA into document...") );

	KXECharDataDialog dlg( widget());
        dlg.setCaption(i18n("Insert CDATA"));

	TQDomElement domParentElement = pNode->toElement();

	if ( dlg.exec( false ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXECharDataCommand(document(), domParentElement, dlg.atTop(), CharDataCDATASection, dlg.contents());
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotActInsertComment()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotActInsertComment called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isElement()) )
	{
		kdError() << "KXMLEditorPart::slotActInsertComment no element selected." << endl;
		return;
	}

	emit setStatusBarText( i18n("Inserting comment into document...") );

	KXECharDataDialog dlg( widget());
	dlg.setCaption(i18n("Insert comment"));

	TQDomElement domParentElement = pNode->toElement();

	if ( dlg.exec( false ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXECharDataCommand(document(), domParentElement, dlg.atTop(), CharDataComment, dlg.contents());
		m_pCmdHistory->addCommand(pCmd);
	}
	emit setStatusBarText( i18n("Ready.") );
}



void KXMLEditorPart::slotXmlCharDataEdit()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlCharDataEdit called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( (!pNode) || (!pNode->isCharacterData()) )
	{
		kdError() << "KXMLEditorPart::slotXmlCharDataEdit no node selected or selected node is no character data." << endl;
		return;
	}

	emit setStatusBarText( i18n("Editing character data...") );

	TQDomCharacterData domCharData = pNode->toCharacterData();

	KXECharDataDialog dlg( widget() );

	CharDataKind eCharDataKind;
	if(domCharData.isText())
		eCharDataKind = CharDataTextNode;
	else
	{
		if(domCharData.isCDATASection())
			eCharDataKind = CharDataCDATASection;
		else
			eCharDataKind = CharDataComment;
	}

	//  dlg.setCharDataKind(eCharDataKind);
	dlg.setContents(domCharData.data());

	if ( dlg.exec( true ) == TQDialog::Accepted )
	{
		KCommand *pCmd = new KXEEditCharDataCommand(document(), domCharData, dlg.contents());
		m_pCmdHistory->addCommand(pCmd);
	}

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlMoveNodeUp()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlMoveNodeUp called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( ! pNode )
	{
		kdError() << "KXMLEditorPart::slotXmlMoveNodeUp no node selected." << endl;
		return;
	}

	emit setStatusBarText( i18n("Moving node up...") );

	KCommand *pCmd = new KXEUpCommand(document(), * pNode);
	m_pCmdHistory->addCommand(pCmd);

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotXmlMoveNodeDown()
{
	if ( ! isReadWrite() )
	{
		kdError() << "KXMLEditorPart::slotXmlMoveNodeDown called in readonly mode." << endl;
		return;
	}

	TQDomNode * pNode = m_pViewTree->getSelectedNode();
	if ( ! pNode )
	{
		kdError() << "KXMLEditorPart::slotXmlMoveNodeDown no node selected." << endl;
		return;
	}

	emit setStatusBarText( i18n("Moving node down...") );

	KCommand *pCmd = new KXEDownCommand(document(), * pNode);
	m_pCmdHistory->addCommand(pCmd);

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotBookmarksToggle()
{
	m_pViewTree->bookmarksToggle();
	m_pActBookmarksPrev->setEnabled(m_pViewTree->containsBookmarkedItems());
	m_pActBookmarksNext->setEnabled(m_pViewTree->containsBookmarkedItems());
}

void KXMLEditorPart::slotConfigure()
{
	emit setStatusBarText( i18n("Configure KXML Editor ...") );

	KXMLEditorFactory::configuration()->showDialog();

	emit setStatusBarText( i18n("Ready.") );
}

void KXMLEditorPart::slotPathSelected(  )
{

}

void KXMLEditorPart::slotPathSelected( const TQString & strPath )
{
	TQDomNode node = domTool_matchingNode( document()->toDocument(), strPath );
	if(node.isNull())
	  { // node don't exists, remove item from combo
		m_pActPathCombo->removeItem(strPath);
		m_pActPathCombo->slotClearEdit();
		return;
	  }

	if(!m_pViewTree->selectNode(node))
	{ // node not found, remove item from combo
		m_pActPathCombo->removeItem(strPath);
		m_pActPathCombo->slotClearEdit();
	}
}

void KXMLEditorPart::slotPathClear()
{
	slotEditDeselect();
	m_pActPathCombo->slotFocusEdit();
}

void KXMLEditorPart::slotItemRenamedInplace( TQListViewItem * pItem )
{
	KXE_TreeViewItem * pXMLItem = static_cast <KXE_TreeViewItem*> (pItem);

	if ( ! pXMLItem->xmlNode()->isElement() ) // check, if it really represents an XML element
	{
		kdFatal() << "KXMLEditorPart " << k_funcinfo << " the given item doesn't represent an XML element." << endl;
		return;
	}

	TQDomElement domElement = pXMLItem->xmlNode()->toElement();

	if ( domElement.nodeName() != pItem->text(0) ) // if really something was changed
	{
		// else the element is "namespaced"
 		int nPosColon = pItem->text(0).find(':');

		if ( nPosColon == -1 )   // if no namespace prefix was entered,
		{
			// check name
			TQString strMessage = KXEElementDialog::checkName(pItem->text(0));
			if(strMessage.length() > 0)
			{
				// restore old name
				m_pViewTree->updateNodeChanged(domElement);
				KMessageBox::sorry(m_pViewTree, strMessage);
				return;
			}

						// clear the elements namespace prefix
						// and set the entered text as its tag name
			KCommand *pCmd = new KXEEditElementCommand(document(), domElement, TQString::null, pItem->text(0));
			m_pCmdHistory->addCommand(pCmd);
		}
		else
		{
			// otherwise split up the entered text by the first colon and
			TQString strPrefix(pItem->text(0).left(nPosColon));
			TQString strName(pItem->text(0).right( pItem->text(0).length() - nPosColon - 1 ));

			// check name
			TQString strMessage = KXEElementDialog::checkName(strName);
			if(strMessage.length() > 0)
			{
				// restore old name
				m_pViewTree->updateNodeChanged(domElement);
				KMessageBox::sorry(m_pViewTree, strMessage);
				return;
			}

			KCommand *pCmd = new KXEEditElementCommand(
				document(),
				domElement,
				strPrefix,
				strName
				);

			m_pCmdHistory->addCommand(pCmd);
		}


		if ( m_pViewTree->selectedItem() == pItem )                       // and if the item is still selected
		{
			m_pActPathCombo->insertItem( domTool_getIconForNodeType(pXMLItem->xmlNode()->nodeType(), false),
			                             domTool_getPath(*pXMLItem->xmlNode()) ); // the path combo
		}
	}
}

void KXMLEditorPart::slotAttributeNameChangedInplace( const TQDomAttr & domAttr, const TQString strNewName )
{
	if ( *m_pViewTree->getSelectedNode() == domAttr.ownerElement() ) // if the corresponding element
	{                                                               // is still selected
		KCommand *pCmd = new KXEEditAttrNameCommand(document(), domAttr, strNewName);
		m_pCmdHistory->addCommand(pCmd);
	}
}

void KXMLEditorPart::slotAttributeValueChangedInplace( const TQDomAttr & domAttr, const TQString strNewValue )
{
	if ( *m_pViewTree->getSelectedNode() == domAttr.ownerElement() ) // if the corresponding element
	{                                                               // is still selected
		KCommand *pCmd = new KXEEditAttrValueCommand(document(), domAttr, strNewValue);
		m_pCmdHistory->addCommand(pCmd);
	}
}

/////////////////////////////////////////////////////////////////////
//                     MISC FUNCTIONS
/////////////////////////////////////////////////////////////////////

/** Copy XML node into clipboard */
TQTextDrag * KXMLEditorPart::copyNode(TQDomNode * pNode)
{
	TQTextDrag *pDrag = 0;
	TQString strXML;

	TQTextStream streamXML(&strXML, IO_WriteOnly);
	int iIndent = KXMLEditorFactory::configuration()->textview()->indentSteps();
	pNode->save(streamXML, iIndent);
	pDrag = new TQTextDrag(strXML, m_pViewTree);

	/*if(pNode->isElement())
		pDrag->setSubtype(KXE_TreeViewItem::m_strSubtypeXML);

	if(pNode->isProcessingInstruction())
		pDrag->setSubtype(KXE_TreeViewItem::m_strSubtypeXML_procins);

	if(pNode->isText())
		pDrag->setSubtype(KXE_TreeViewItem::m_strSubtypeXML_text);

	if(pNode->isCDATASection())
		pDrag->setSubtype(KXE_TreeViewItem::m_strSubtypeXML_cdata);

	if(pNode->isComment())
		pDrag->setSubtype(KXE_TreeViewItem::m_strSubtypeXML_comment);		*/

	return pDrag;
}

//-----------------------------------------------------------------------------
//
// Paste XML node from clipboard into document
//
// pTargetNode    - target node
// data           - data to pasted
//
//-----------------------------------------------------------------------------
bool KXMLEditorPart::pasteNode(TQDomNode * pTargetNode, TQMimeSource *data)
{
	TQString strText;

	// Drop XML Processing Instruction
  if(TQTextDrag::decode(data, strText))
		{ if(strText.find("<?xml ") == 0)
      {
        KMessageBox::sorry(0, i18n("This processing instruction cannot be pasted here !"));
        return false;
      }

      //---

      TQString strXML = "<root>" + strText  + "</root>";

			// Paste clipboard contents as XML element
			TQString strErrorMsg;
  		int iErrorLine, iErrorColumn;
			TQDomDocument doc;

 		  if(!doc.setContent(strXML, true, &strErrorMsg, &iErrorLine, &iErrorColumn) )
 				{ kdDebug() << "KXMLEditorPart::pasteNode: Failed parsing the file." << endl;

					KMessageBox::error(m_pViewTree,
												 		i18n("%1 in line %2, column %3").arg(strErrorMsg).arg(iErrorLine).arg(iErrorColumn),
												 		i18n("Parsing error !"));

					return false;
				}

			// Import parsed document to main document
			if(doc.hasChildNodes())
				{ if(pTargetNode == 0)
						{ // check if root item already exists
              if(!document()->documentElement().isNull() /*(this->documentElement().isElement())*/ && (doc.firstChild().firstChild().isElement()) )
              { KMessageBox::sorry(m_pViewTree, i18n("Root element already exists !"));
                return false;
              }

              if(doc.documentElement().firstChild().isElement() == false)
              {
                KMessageBox::sorry(m_pViewTree, i18n("Node pasted to document must be element !"));
                return false;
              }

              // Append it as root node
              TQDomElement newNode = doc.documentElement().firstChild().cloneNode(true).toElement();

              KCommand *pCmd = new KXEPasteToDocumentCommand(document(), document(), newNode);
              m_pCmdHistory->addCommand(pCmd);
		}
		else
		{ TQDomNode sourceNode = doc.firstChild().firstChild().cloneNode(true);
		
			/*
			L.V.
			
			TESTING CODE FOR [ 925668 ] Attribute values in a copied element can't be changed.
						
			if(sourceNode.isElement())
			{
				TQDomElement domSourceElement = sourceNode.toElement();
				
				TQDomNamedNodeMap list = domSourceElement.attributes();
				unsigned int iLength = list.length();
				

				for ( unsigned int iRow = 0; iRow < iLength; iRow++ )
				{
					TQDomAttr a = list.item(iRow).toAttr();
					
					TQDomElement domOwnerElement = a.ownerElement();
					if(domOwnerElement.isNull())
						KMessageBox::sorry(m_pViewTree, i18n("Cloned owner is null !"));
				}*/
              //---

              if(pTargetNode->isElement())
          		{
                TQDomElement domTargetElement = pTargetNode->toElement();
                KCommand *pCmd = new KXEPasteToElementCommand(document(), domTargetElement, sourceNode);
                m_pCmdHistory->addCommand(pCmd);
                return true;
          		}

          		if(pTargetNode->isProcessingInstruction() && sourceNode.isProcessingInstruction())
          		{ // Replace contents of selected node
                TQDomProcessingInstruction domTargetProcInstr = pTargetNode->toProcessingInstruction();
                TQDomProcessingInstruction domSourceProcInstr = sourceNode.toProcessingInstruction();

          			KCommand *pCmd = new KXEPasteToProcInstrCommand(document(), domTargetProcInstr, domSourceProcInstr);
                m_pCmdHistory->addCommand(pCmd);
                return true;
          		}

          		if(pTargetNode->isCharacterData() && sourceNode.isCharacterData())
          		{ // Replace contents of selected node
                TQDomCharacterData domTargetCharData = pTargetNode->toCharacterData();
                TQDomCharacterData domSourceCharData = sourceNode.toCharacterData();

          			KCommand *pCmd = new KXEPasteToCharDataCommand(document(), domTargetCharData, domSourceCharData);
                m_pCmdHistory->addCommand(pCmd);
                return true;
          		}

              KMessageBox::sorry(m_pViewTree, i18n("Incompactible node types for drag&drop !"));
              return false;
						}
				}
			return true;
		}
	return false;
}

//-----------------------------------------------------------------------------
//
// Drag & Drop MOVE operation
//
// domTrgetElement    - target element
// domSourceNode      - source node
//
//-----------------------------------------------------------------------------
bool KXMLEditorPart::dropMoveNode(TQDomElement & domTargetElement, TQDomNode & domSourceNode)
{
  KCommand *pCmd = new KXEDragDropMoveCommand(document(), domTargetElement, domSourceNode);
  m_pCmdHistory->addCommand(pCmd);
  return true;
}

/////////////////////////////////////////////////////////////////////
//                          PRINTING
/////////////////////////////////////////////////////////////////////

// these defines should rather stay fixed and not configurable by the user.
#define headerMargin 30;
#define footerMargin 50;
#define m_printLinespace 0.4			// percent of font size used to make line indent

int headerHeight, footerHeight;	// used in few functions, nevertheless temporary...

void KXMLEditorPart::print(KPrinter* pPrinter)
{
	// setup the printer.  with TQt, you always "print" to a
	// TQPainter.. whether the output medium is a pixmap, a screen, or paper
	TQPainter p;
	TQPainter *painter = &p;
	TQFont font( KXMLEditorFactory::configuration()->print()->fontFamily(),
	            KXMLEditorFactory::configuration()->print()->fontSize() );

	TQPaintDeviceMetrics metrics((TQPrinter*)pPrinter);		// determining
	int width = metrics.width();						// width and
	int height = metrics.height();						// height of the page

	footerHeight = font.pointSize()+footerMargin;
	headerHeight = font.pointSize()+headerMargin;

	int pageNumber = 0;

	painter->begin(pPrinter);						// start print job
	painter->setFont(font);							// set up fonts for printout
	printHeader(painter,pageNumber,0,width);			// draw header on a first page
	while (printPage	(painter,pageNumber,
				headerHeight,width,
				height-headerHeight-footerHeight))	// and start printing loop
	{
		printFooter(painter, pageNumber, height-footerHeight,width);	// draw footer at the end of that page
		pageNumber++;
		pPrinter->newPage();
		printHeader(painter,pageNumber,0,width);				// draw header on new page
	}
	printFooter(painter,pageNumber,height-footerHeight,width);		// draw footer on last page
	painter->end();								// finish print job
}

void KXMLEditorPart::printHeader(TQPainter* painter,int pageNumber, int ypos, int width)
{
	pageNumber = pageNumber;
	if ( KXMLEditorFactory::configuration()->print()->hasHeader() )
	{
		painter->drawText(0,ypos,m_url.prettyURL());
		painter->drawLine(0,ypos,width,ypos);
	}
}

bool KXMLEditorPart::printPage(TQPainter* painter,int pageNumber, int top, int width, int height)
{
	width = width;
	if (pageNumber==0)
	{
		// initialization of working variables is done when
		// first page is about to be printed
		m_printLineNumber = 0;
		m_printLines = TQStringList::split( "\n", document()->toString(KXMLEditorFactory::configuration()->print()->indentSteps()) );
	}
	int lineHeight = (int)(painter->font().pointSize()*(1+m_printLinespace));
	int y = top;
	while ( y <= height )
	{
		painter->drawText(0,y,m_printLines[m_printLineNumber]);
		if (m_printLineNumber++==m_printLines.size())
			return false;	// no more pages to print
		y += lineHeight;
	}
	return true;			// there are still some pages to print
}

void KXMLEditorPart::printFooter(TQPainter* painter,int pageNumber, int ypos, int width)
{
	if ( KXMLEditorFactory::configuration()->print()->hasFooter() )
	{
		int fh = painter->font().pointSize();
		painter->drawText(0,ypos,i18n("Page %1").arg(pageNumber+1));
		painter->drawLine(0,ypos-fh,width,ypos-fh);
	}
}


/////////////////////////////////////////////////////////////////////
//                           OTHER SLOTS
/////////////////////////////////////////////////////////////////////

void KXMLEditorPart::slotTreeViewKeyPressed(TQKeyEvent *e)
{
	// few keypresses are recognized and proper actions
	// are executed.
	switch (e->key())
	{
		case TQt::Key_Delete	: slotActDelete(); break;
		case TQt::Key_Return	:
		case TQt::Key_Enter	: slotActProperties(); break;
	}
}

void KXMLEditorPart::slotSelectionCleared(bool bRootElementExists)
{
	// Enable/Disable actions
	if(m_pBrowserExt)
		m_pBrowserExt->emit enableAction( "copy", false );
	else
		m_pActEditCopy->setEnabled(false);
		
	m_pActEditDeselect->setEnabled(false);
	m_pActViewNodeUp->setEnabled(false);
	m_pActViewExpNode->setEnabled(false);
	m_pActViewColNode->setEnabled(false);
	m_pActBookmarksToggle->setEnabled(false);
  
	if ( isReadWrite() )
	{
		m_pActEditCut->setEnabled(false );
		m_pActEditPaste->setEnabled(true );

		m_pActXmlElementInsert->setEnabled(!bRootElementExists);
		m_pActXmlAttributesAdd->setEnabled(false);
		m_pActXmlAttributesDel->setEnabled(false);
		m_pActXmlProcInstrInsert->setEnabled(true);
		m_pActInsertText->setEnabled(false);
		m_pActInsertCDATA->setEnabled(false);
		m_pActInsertComment->setEnabled(false);
		m_pActXmlMoveNodeUp->setEnabled(false);
		m_pActXmlMoveNodeDown->setEnabled(false);
		m_pActDelete->setEnabled(false);
		m_pActProperties->setEnabled(false);
		m_pActEditRawXml->setEnabled(false);
	}

	m_pActPathCombo->slotClearEdit();

	// change views
	m_pViewContents->clear();

	m_pTabWidget->setTabEnabled( m_pViewElement, false );
	m_pTabWidget->setTabEnabled( m_pViewContents, false );
	m_pTabWidget->setTabEnabled( m_pViewProcInstr, false );

	m_pTabWidget->showPage( m_pViewContents );

	// change path combo
	m_pActPathCombo->slotClearEdit();
}

void KXMLEditorPart::slotSelectionChanged( const TQDomElement & selectedNode)
{
	// Enable/Disable actions
	if(m_pBrowserExt)
		m_pBrowserExt->emit enableAction( "copy", true );
	else
		m_pActEditCopy->setEnabled(true);
		
	m_pActEditDeselect->setEnabled(true);
	m_pActViewNodeUp->setEnabled( ! selectedNode.parentNode().isNull() ); // disable if it's a root item
	m_pActViewExpNode->setEnabled( ! selectedNode.firstChild().isNull() ); // no childs -> disable
	m_pActViewColNode->setEnabled( ! selectedNode.firstChild().isNull() ); // no childs -> disable
	m_pActBookmarksToggle->setEnabled(true);

	if ( isReadWrite() )
	{
		m_pActEditCut->setEnabled(true);
		m_pActEditPaste->setEnabled(true);

		m_pActXmlElementInsert->setEnabled(true);
		m_pActXmlAttributesAdd->setEnabled(true);
		m_pActXmlAttributesDel->setEnabled( (selectedNode.attributes().count() != 0) );
		m_pActXmlProcInstrInsert->setEnabled(true);
//		m_pActXmlCharDataInsert->setEnabled(true);
		m_pActInsertText->setEnabled(true);
		m_pActInsertCDATA->setEnabled(true);
		m_pActInsertComment->setEnabled(true);
		m_pActDelete->setEnabled(true);
		m_pActProperties->setEnabled(true);
		m_pActEditRawXml->setEnabled(true);

		if ( selectedNode.parentNode().isDocument() )
		{
			m_pActXmlMoveNodeUp->setEnabled(false);
			m_pActXmlMoveNodeDown->setEnabled(false);
		}
		else
		{
			m_pActXmlMoveNodeUp->setEnabled( ! selectedNode.previousSibling().isNull() );
			m_pActXmlMoveNodeDown->setEnabled( ! selectedNode.nextSibling().isNull() );
		}
	}

	// change views
	m_pViewElement->slotChange(selectedNode);

	m_pTabWidget->setTabEnabled( m_pViewElement, true );
	m_pTabWidget->setTabEnabled( m_pViewContents, false );
	m_pTabWidget->setTabEnabled( m_pViewProcInstr, false );

	m_pTabWidget->showPage(m_pViewElement);

	// change path combo
	m_pActPathCombo->insertItem( domTool_getIconForNodeType(selectedNode.nodeType(), false), domTool_getPath(selectedNode) );
}

void KXMLEditorPart::slotSelectionChanged( const TQDomCharacterData & selectedNode )
{
	// Enable/Disable actions
	if(m_pBrowserExt)
		m_pBrowserExt->emit enableAction( "copy", true );
	else
		m_pActEditCopy->setEnabled(true);

	m_pActEditDeselect->setEnabled(true);
	m_pActViewNodeUp->setEnabled( ! selectedNode.parentNode().isNull() ); // disable if it's a root item
	m_pActViewExpNode->setEnabled(false);
	m_pActViewColNode->setEnabled(false);
	m_pActBookmarksToggle->setEnabled(true);

	if ( isReadWrite() )
	{
		m_pActEditCut->setEnabled(true);
		m_pActEditPaste->setEnabled(true);

		m_pActXmlElementInsert->setEnabled(false);
		m_pActXmlAttributesAdd->setEnabled(false);
		m_pActXmlAttributesDel->setEnabled(false);
		m_pActXmlProcInstrInsert->setEnabled(false);
		m_pActInsertText->setEnabled(false);
		m_pActInsertCDATA->setEnabled(false);
		m_pActInsertComment->setEnabled(false);
		m_pActXmlMoveNodeUp->setEnabled( ! selectedNode.previousSibling().isNull() );
		m_pActXmlMoveNodeDown->setEnabled( ! selectedNode.nextSibling().isNull() );
		m_pActDelete->setEnabled(true);
		m_pActProperties->setEnabled(true);
		m_pActEditRawXml->setEnabled(false);
	}

	// change views
	m_pViewContents->setText( selectedNode.data() );

	m_pTabWidget->setTabEnabled( m_pViewElement, false );
	m_pTabWidget->setTabEnabled( m_pViewContents, true );
	m_pTabWidget->setTabEnabled( m_pViewProcInstr, false );

	m_pTabWidget->showPage( m_pViewContents );

	// change path combo
	m_pActPathCombo->insertItem( domTool_getIconForNodeType(selectedNode.nodeType(), false), domTool_getPath(selectedNode) );
}

void KXMLEditorPart::slotSelectionChanged( const TQDomProcessingInstruction & selectedNode )
{
	// Enable/Disable actions
	if(m_pBrowserExt)
		m_pBrowserExt->emit enableAction( "copy", true );
	else
		m_pActEditCopy->setEnabled(true);

	m_pActEditDeselect->setEnabled(true);
	m_pActViewNodeUp->setEnabled( ! selectedNode.parentNode().isNull() ); // disable if it's a root item
	m_pActViewExpNode->setEnabled(false);
	m_pActViewColNode->setEnabled(false);
	m_pActBookmarksToggle->setEnabled(true);

	if ( isReadWrite() )
	{
		m_pActEditCut->setEnabled(true);
		m_pActEditPaste->setEnabled(true);

		m_pActXmlAttributesAdd->setEnabled(false);
		m_pActXmlAttributesDel->setEnabled(false);
		m_pActXmlProcInstrInsert->setEnabled(false);
		m_pActInsertText->setEnabled(false);
		m_pActInsertCDATA->setEnabled(false);
		m_pActInsertComment->setEnabled(false);
		m_pActDelete->setEnabled(true);
		m_pActProperties->setEnabled(true);
		m_pActEditRawXml->setEnabled(false);
		m_pActXmlElementInsert->setEnabled(selectedNode.parentNode().isDocument() &&
									document()->documentElement().isNull());

		if ( selectedNode.parentNode().isDocument() )
		{
			m_pActXmlMoveNodeUp->setEnabled(false);
			m_pActXmlMoveNodeDown->setEnabled(false);
		}
		else
		{
			m_pActXmlMoveNodeUp->setEnabled( ! selectedNode.previousSibling().isNull() );
			m_pActXmlMoveNodeDown->setEnabled( ! selectedNode.nextSibling().isNull() );
		}
	}

	// change views
	m_pViewProcInstr->setText( selectedNode.data() );

	m_pTabWidget->setTabEnabled( m_pViewElement, false );
	m_pTabWidget->setTabEnabled( m_pViewContents, false );
	m_pTabWidget->setTabEnabled( m_pViewProcInstr, true );

	m_pTabWidget->showPage( m_pViewProcInstr );

	// change path combo
	m_pActPathCombo->insertItem( domTool_getIconForNodeType(selectedNode.nodeType(), false), domTool_getPath(selectedNode) );
}

void KXMLEditorPart::slotContextMenuRequested( const TQString & szMenuName, const TQPoint & pos )
{
	TQWidget * pContainer = hostContainer(szMenuName);

	if ( ! pContainer )
	{
		kdError() << "KXMLEditor " << k_funcinfo << " Couldn't get a container widget for the given menu name (" << szMenuName << ")" << endl;
		return;
	}

	if ( ! pContainer->inherits("TDEPopupMenu") )
	{
		kdError() << "KXMLEditor " << k_funcinfo << " Wrong container widget" << endl;
		return;
	}

	TDEPopupMenu * pMenu = static_cast <TDEPopupMenu*> (pContainer);
	pMenu->popup( pos );
}


/////////////////////////////////////////////////////////////////////
//        FUNCTIONS CALLED FROM KXECommand CHILD CLASSES
/////////////////////////////////////////////////////////////////////

void KXMLEditorPart::updateNodeCreated(const TQDomNode & node)
{
	m_pViewTree->updateNodeCreated(node);
}

void KXMLEditorPart::updateNodeDeleted( const TQDomNode & node )
{
	m_pViewTree->updateNodeDeleted(node);

	// if root element deleted, enable Insert Element
	if(node.isElement() && (m_pViewTree->firstChild() == 0) && (isReadWrite()))
	m_pActXmlElementInsert->setEnabled(true);
}

void KXMLEditorPart::updateNodeChanged( const TQDomElement & domElement )
{
	m_pViewTree->updateNodeChanged(domElement);
	m_pViewElement->slotChange(domElement);
}

void KXMLEditorPart::updateNodeChanged( const TQDomCharacterData & domCharData )
{
	m_pViewTree->updateNodeChanged(domCharData);
	m_pViewContents->setText( domCharData.data() );
}

void KXMLEditorPart::updateNodeChanged( const TQDomProcessingInstruction &domProcInstr )
{
	m_pViewTree->updateNodeChanged(domProcInstr);
	m_pViewProcInstr->setText( domProcInstr.data() );
}

void KXMLEditorPart::updateNodeMoved( const TQDomNode & domNode )
{
	m_pViewTree->updateNodeMoved(domNode);
}


bool KXMLEditorPart::slotFileSaveAs()
{
	emit setStatusBarText( i18n("Saving file with a new filename...") );

	KFileDialog dlg( TQDir::currentDirPath(),   // start dir.
	                 i18n(FILE_DIALOG_FILTER), // filter
	                 widget(),                 // parent
	                 "file dialog for saving", // name
	                 true );                   // modal
	dlg.setCaption( i18n("Save as...") );
	dlg.setOperationMode( KFileDialog::Saving );
	dlg.exec();

	KURL url = dlg.selectedURL();
	bool bRetVal = false;
	
	if(!url.isEmpty())
	{
    // append filetype if necessary
    if(dlg.currentFilter() != "*.*")
    { TQString strExtension = dlg.currentFilter();
      strExtension.remove('*');

      if(strExtension != url.fileName().right(strExtension.length()))
        url.setFileName(url.fileName() + strExtension );
    }
				
		if((bRetVal = saveAs(url)))	// something may go wrong
		{
			emit sigAddRecentURL(url);
			setModified(false);
		}
	}

	emit setStatusBarText( i18n("Ready.") );
	return bRetVal;
}

void KXMLEditorPart::slotDocOpened()
{
// I temporary introduced code to measure the loading time using
// the different tree view item initialization modes.
// Simply open a document, remember the measured time difference,
// change the mode (in the configuration dialog), load the
// same document again and compare the measurements.
//                                                     Olaf
//
// TODO: the three lines operating with the TQTime objects
//       can be removed later

	// update the view
	m_pViewTree->updateClear();
	int i = document()->childNodes().length();
	//TQTime t1 = TQTime::currentTime(); //TODO: remove this line later (Olaf)
	while ( i > 0  )
	{
		i--;
		m_pViewTree->updateNodeCreated( document()->childNodes().item(i) );
	}
	
	//TQTime t2 = TQTime::currentTime(); //TODO: remove this and the next line later (Olaf)
	//kdDebug() << "KXMLEditorPart::slotDocOpened()   time difference: " << t1.msecsTo(t2) << " msecs" << endl;
	
	m_pActPathCombo->slotClear();
}

void KXMLEditorPart::setDocument(KXEDocument *pDocument)
{
	// detach previously used document
	if (m_pDocument)
	{
		disconnect (m_pDocument,0,this,0);
	}
	m_pDocument = pDocument;
	if (pDocument)
	{
		// open document notification
		connect(pDocument,SIGNAL(sigOpened()),this,SLOT(slotDocOpened()));
		// document modification is also dispatched
		connect(pDocument,SIGNAL(sigModified(bool)),this,SLOT(setModified(bool)));
		// update notifications
		connect(pDocument,SIGNAL(sigNodeChanged(const TQDomElement&)),this,SLOT(updateNodeChanged(const TQDomElement&)));
		connect(pDocument,SIGNAL(sigNodeChanged(const TQDomProcessingInstruction&)),this,SLOT(updateNodeChanged(const TQDomProcessingInstruction&)));
		connect(pDocument,SIGNAL(sigNodeChanged(const TQDomCharacterData&)),this,SLOT(updateNodeChanged(const TQDomCharacterData&)));
		connect(pDocument,SIGNAL(sigNodeCreated(const TQDomNode&)),this,SLOT(updateNodeCreated(const TQDomNode&)));
		connect(pDocument,SIGNAL(sigNodeDeleted(const TQDomNode&)),this,SLOT(updateNodeDeleted(const TQDomNode&)));
		connect(pDocument,SIGNAL(sigNodeMoved(const TQDomNode&)),this,SLOT(updateNodeMoved(const TQDomNode&)));
		// merging document action collection
		insertChildClient(pDocument);
	}
}

void KXMLEditorPart::slotActRedo()
{
	m_pCmdHistory->redo();
}

void KXMLEditorPart::slotActUndo()
{
	m_pCmdHistory->undo();
}

void KXMLEditorPart::slotActDetachStylesheet()
{
	KCommand *pCmd = m_pDocument->actDetachStylesheet();
	if(pCmd) m_pCmdHistory->addCommand(pCmd);
}

void KXMLEditorPart::slotActAttachStylesheet()
{
	KCommand *pCmd = m_pDocument->actAttachStylesheet();
	if(pCmd) m_pCmdHistory->addCommand(pCmd);
}

void KXMLEditorPart::slotActDetachSchema()
{
	KCommand *pCmd = m_pDocument->actDetachSchema();
	if(pCmd) m_pCmdHistory->addCommand(pCmd);
}

void KXMLEditorPart::slotActAttachSchema()
{
	KCommand *pCmd = m_pDocument->actAttachSchema();
	if(pCmd) m_pCmdHistory->addCommand(pCmd);
}

// Instert or edit special processing instruction <?xml ... ?>
void KXMLEditorPart::slotActVersionEncoding()
{
	KCommand *pCmd = m_pDocument->actVersionEncoding();
	if(pCmd) m_pCmdHistory->addCommand(pCmd);
}

void KXMLEditorPart::updateActions()
{
	if(!m_pDocument) return;
	if(!m_pActAttachSchema) return; // not in read-write mode
	
	// Schema
	bool bSchemaExists = !m_pDocument->documentElement().isNull() &&
						// that's a question why it works with negated condition ??????
						!m_pDocument->documentElement().hasAttributeNS(SCHEMA_NAMESPACE,SCHEMA_ATTRIBUTE);

	m_pActAttachSchema->setEnabled(!m_pDocument->documentElement().isNull() && !bSchemaExists);
	m_pActDetachSchema->setEnabled(bSchemaExists);

	// Stylesheet
	m_pActDetachStylesheet->setEnabled(!m_pDocument->getSpecProcInstr("xml-stylesheet").isNull());
}

#include "kxmleditorpart.moc"
