/***************************************************************************
                          kxmleditorshell.cpp  -  description
                             -------------------
    begin                : Thu Oct 18 2001
    copyright            : (C) 2001, 2002, 2003 by The KXMLEditor Team
    email                : lvanek@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
 /**
 @file
 */

#include "kxmleditorshell.h"
#include "kxeshellmanager.h"
#include "../part/kxmleditorpart.h"
#include "../part/kxedocument.h"

#include <klibloader.h>
#include <kstdaction.h>
#include <tdeapplication.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <tdefiledialog.h>
#include <kkeydialog.h>
#include <kedittoolbar.h>
#include <kstatusbar.h>
#include <tdeparts/browserextension.h>
#include <tdeparts/partmanager.h>
#include <tdemessagebox.h>

#include <tdepopupmenu.h>

#include <tqevent.h>

#include "dcopiface_shell.h"


KXEShellManager *KXMLEditorShell::s_manager=0L;

KXMLEditorShell::KXMLEditorShell( const char * name, WFlags f )
	: KParts::MainWindow( name, f ),
	  m_pPart(0),
	  m_pKXEShellIface(0)
{
	//////////////////////////////
	// CREATE ACTIONS
	//////////////////////////////

	setXMLFile("kxmleditorshell.rc");

	KStdAction::openNew( this,TQ_SLOT(slotActFileNew()),actionCollection() );
	KStdAction::open( this, TQ_SLOT(slotFileOpen()), actionCollection() );
	m_pActFileOpenRecent = KStdAction::openRecent( this, TQ_SLOT(slotFileOpenRecent(const KURL&)), actionCollection() );
	m_pActFileOpenRecent->loadEntries( instance()->config() );
	m_pActReload = new TDEAction( i18n("Reloa&d"), "reload", Key_F5, this, TQ_SLOT(slotFileReload()), actionCollection(), "reload" );
	
	TDEToggleAction * pActToggleMainToolBar = KStdAction::showToolbar( this, TQ_SLOT(slotToggleMainToolBar()), actionCollection() );
	TDEToggleAction * pActToggleStatusBar = KStdAction::showStatusbar( this, TQ_SLOT(slotToggleStatusBar()), actionCollection() );
	KStdAction::keyBindings( this, TQ_SLOT(slotConfigureKeys()), actionCollection() );
	KStdAction::configureToolbars( this, TQ_SLOT(slotConfigureToolbars()), actionCollection() );

	KStdAction::mail( this, TQ_SLOT(mail()), actionCollection() );
	KStdAction::close( this, TQ_SLOT(slotActClose()), actionCollection() );
	KStdAction::quit( this, TQ_SLOT(close()), actionCollection());

	// L.V. moved to part pActPrint = KStdAction::print( 0, 0, actionCollection(), "print" );

	pActCloseW = new TDEAction( i18n("&Close"), 0, 0, this,TQ_SLOT(slotActWindowClose()), actionCollection(), "window_close" );
	pActCloseAllW = new TDEAction( i18n("Close &all"), 0, 0, this,TQ_SLOT(slotActWindowCloseAll()), actionCollection(), "window_close_all" );
	pActCloseAllO = new TDEAction( i18n("Close all &others"), 0, 0, this,TQ_SLOT(slotActWindowCloseAllOthers()), actionCollection(), "window_close_all_others" );

	//////////////////////////////
	// CREATE STATUSBAR
	//////////////////////////////
	statusBar()->message( i18n("Ready.") );

	//////////////////////////////
	// MISC. INITIALIZATION
	//////////////////////////////

	manager()->addShell(this);

	setAutoSaveSettings();
	pActToggleMainToolBar->setChecked( ! toolBar()->isHidden() );
	pActToggleStatusBar->setChecked( ! statusBar()->isHidden() );

	m_pKXEShellIface = new KXMLEditorShellIface(*this);

	statusBar()->setSizeGripEnabled(true);

	setPart(0L);		// start empty

}

KXMLEditorShell::~KXMLEditorShell()
{
	if (m_pKXEShellIface)
		delete m_pKXEShellIface;

	manager()->removeShell(this);
}

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

void KXMLEditorShell::slotFileOpen()
{
	statusBar()->message( i18n("Opening file...") );

	KURL url = KFileDialog::getOpenURL( TQString::null,
	                                    i18n(FILE_DIALOG_FILTER),
	                                    this );
	openURL(url);
	
	// L.V.: tried to meve save functionality to Part
	//m_pActFileSave->setEnabled(true); // TODO: L.V. Why File Save ? Why not Save as... ?

	statusBar()->message( i18n("Ready.") );
}

void KXMLEditorShell::slotFileOpenRecent(const KURL & url)
{
	statusBar()->message( i18n("Opening file...") );

	openURL(url);

	statusBar()->message( i18n("Ready.") );
}

void KXMLEditorShell::slotFileReload()
{
	statusBar()->message( i18n("Reloading file...") );

	// Try to (re)open the file.
	// In case the file is modified, a Save/Discard/Cancel message box pops up.
	if ( part() && ! part()->url().isEmpty() )
		part()->openURL( part()->url() ); 

	statusBar()->message( i18n("Ready.") );
}

void KXMLEditorShell::slotToggleMainToolBar()
{
	statusBar()->message( i18n("Toggle the toolbar...") );

	if( toolBar()->isHidden() )
		toolBar()->show();
	else
		toolBar()->hide();

	statusBar()->message( i18n("Ready.") );
}


void KXMLEditorShell::slotToggleStatusBar()
{
	statusBar()->message( i18n("Toggle the statusbar...") );

	if( statusBar()->isHidden() )
		statusBar()->show();
	else
		statusBar()->hide();

	statusBar()->message( i18n("Ready.") );
}

void KXMLEditorShell::slotConfigureKeys()
{
	KKeyDialog dlg;
	dlg.insert(part()->actionCollection());
	dlg.insert(actionCollection());
	dlg.configure();
}

void KXMLEditorShell::slotConfigureToolbars()
{
	KEditToolbar dlg( factory() );
	if ( dlg.exec() )
		createGUI( part() );
}

/////////////////////////////////////////////////////////////////////
//                    ADDITIONAL FUNCTIONS
/////////////////////////////////////////////////////////////////////

bool KXMLEditorShell::openURL( const KURL & url )
{
	if ( url.isEmpty() )
		return false;

	// About this function, the style guide (
	// http://developer.kde.org/documentation/standards/kde/style/basics/index.html )
	// says that it should open a new window if the document is _not_
	// in its initial state.  This is what we do here.

	// If there is no part yet created, create new one...
	if (!part())
  {
		setPart(createXMLPart(this));
	}

	// We have to differenciate, if this shell could
	// be used or a new one has to be created.
	if ( part()->url().isEmpty() && ! part()->isModified() )
	{
		// this one can be used - try to open the file in it
		if ( ! part()->openURL(url) )
			return false;
	}
	else
	{
		// a new one has to be created
		KXMLEditorShell * pShell = new KXMLEditorShell;
		// and a new part too...
		pShell->setPart(createXMLPart(pShell));

		// try to open the file in the shell
		if ( ! pShell->part()->openURL(url) )
			return false;

		// show the new shell
		pShell->show();
	}

	// on success, the URL could be added to our list of recent files
	m_pActFileOpenRecent->addURL(url);

	return true;
}

bool KXMLEditorShell::queryClose()
{
	if ( part() && ! part()->closeURL() )
		return false;

	m_pActFileOpenRecent->saveEntries( instance()->config() );

	return KParts::MainWindow::queryClose();
}

void KXMLEditorShell::saveProperties( TDEConfig * pConfig )
{
	if (part())
	{
		pConfig->writeEntry( "CurrentURL", part()->url().url() );
	}
	KParts::MainWindow::saveProperties(pConfig);
}

void KXMLEditorShell::readProperties( TDEConfig * pConfig )
{
	KParts::MainWindow::readProperties(pConfig);

/// @todo Check if ths is a deadcode
//	TQString strURL = pConfig->readEntry( "CurrentURL", "" );
//	if ( strURL.isEmpty())
//	part()->openURL( strURL );
}

void KXMLEditorShell::slotEnableBrowserExtActions( const char * pszName, bool bEnabled )
{
	TDEAction * pAct = actionCollection()->action( pszName );
	if ( pAct )
		pAct->setEnabled( bEnabled );
	else
		kdDebug() << "KXMLEditor " << k_funcinfo << " unknown action (" << pszName << ")" << endl;
}

// Set main window caption (by using the base class functionality),
// Enables/disables Save button (corresponding to the parts modified status)
void KXMLEditorShell::setCaption( const TQString & strCaption )
{
	if (part())
	{
	  KParts::MainWindow::setCaption( strCaption, part()->isModified() ); // base class functionality
	}
}


//
// Called when creating new file is requested.
//
void KXMLEditorShell::slotActFileNew()
{

	// About this function, the style guide (
	// http://developer.kde.org/documentation/standards/kde/style/basics/index.html )
	// says that it should open a new window if the document is _not_
	// in its initial state.  This is what we do here..
	KXMLEditorShell * pShell;
	KParts::ReadWritePart* pPart;

	// 1. This shell is empty
	if (!part())
	{
		pPart = createXMLPart(this);
		pShell=this;
	}
	// 2. The shell contains modified part
	else if (! part()->url().isEmpty() || part()->isModified() )
	{
		pShell = new KXMLEditorShell;
		pPart = createXMLPart(pShell);
		pShell->show();
	}
	// 3. The shell contains part, and it is empty and not modified
	else
	{
		pPart=part();
		pShell=this;
	}

	pShell->setPart(pPart);
	// if there is a way to determine in KXMLEditorPart that it was created "for new file" then
	// this part should be changed.
	((KXMLEditorPart *)pPart)->document()->newFile();
}

void KXMLEditorShell::mail()
{
	// L.V.: tried to meve save functionality to Part
	//slotFileSave();							
	
	// first - our XML file have to be saved.
	part()->save();
	
	if (!part()->url().isEmpty())
	{
		TQStringList urls(part()->url().url());			// we can prepare then list of attachments (1 element in the list)
		TQString theSubject(part()->url().fileName(false));	// and set the mail subject
		kapp->invokeMailer(TQString::null,				// finally we can compose e-mail (addressed to no-one) out of that
						TQString::null,
						TQString::null,
						theSubject,				// subject
						TQString::null,				// body
						TQString::null,
						urls);						// attachments
	}
}

void KXMLEditorShell::setPart(KParts::ReadWritePart* pPart)
{
	if (pPart==m_pPart)
		return;	// nothing to do here...
	else
	{
		if(pPart)
		{
			connect(static_cast <KXMLEditorPart *> (pPart), TQ_SIGNAL(sigAddRecentURL(const KURL &)), 
							this, TQ_SLOT(slotAddRecentURL(const KURL &)) );
		}
	}

	if (m_pPart)
	{
		factory()->removeClient(m_pPart);
		delete m_pPart;
	}
	m_pPart = pPart;
	if (pPart)
	{
		// get the parts browser extension and connect additional actions to it
		KParts::BrowserExtension * pBrowserExt = 0;
		if ( (pBrowserExt = KParts::BrowserExtension::childObject(pPart)) )  // if there is one
		{
			connect( pBrowserExt, TQ_SIGNAL(enableAction( const char *, bool )), this, TQ_SLOT(slotEnableBrowserExtActions( const char *, bool )) );

			// L.V. moved to part. connect(pActPrint,TQ_SIGNAL(activated()),pBrowserExt, TQ_SLOT(print()));
			//pActPrint->setEnabled(pBrowserExt->isActionEnabled("print"));
		}

		createGUI(pPart);
		setCentralWidget(pPart->widget() );
		pPart->widget()->show();
		stateChanged("part");
	}
	else
	{
		createGUI(0L);
		setCentralWidget(0L);
		stateChanged("empty");
	}

	// keep pointer to window menu
	m_windowMenu = static_cast<TDEPopupMenu*>(factory()->container("window", this));
	if(!m_windowMenu)
		kdDebug() << "KXMLEditorShell::setPart - m_windowMenu is NULL" << endl;
	else
		connect(m_windowMenu,TQ_SIGNAL(aboutToShow()),this,TQ_SLOT(slotBeforeWindowMenuShown()));
}

KParts::ReadWritePart* KXMLEditorShell::createXMLPart(TQObject *parent)
{
	return  createPart(parent,"libkxmleditorpart", "KParts::ReadWritePart");
}

KParts::ReadWritePart* KXMLEditorShell::createPart(TQObject *parent, const TQString& libName, const TQString& className)
{
	KParts::ReadWritePart* newPart = 0L;
	KLibFactory * pFactory = KLibLoader::self()->factory(libName.local8Bit());
	if (pFactory)
		newPart = static_cast <KParts::ReadWritePart*> ( pFactory->create( parent, "KXMLEditor view", className.utf8() ) );
	else
		kdFatal() << "KXMLEditorShell::KXMLEditorShell no " << libName << " found" << endl;
	return newPart;
}

void KXMLEditorShell::slotActClose()
{
	if (part() && part()->queryClose())
		setPart(0L);
}

void KXMLEditorShell::close()
{
	if (queryClose())
	{
		if (TDEMainWindow::memberList->count()==1)
			kapp->deref();
		delete this;
	}
}

void KXMLEditorShell::slotActWindowCloseAllOthers()
{
	TQPtrListIterator<TDEMainWindow> it(*TDEMainWindow::memberList);
	for (it.toFirst();it.current();++it)
	{
		if (it.current()!=this)
		it.current()->close();
	}
}

    void KXMLEditorShell::slotActWindowClose()
{
	this->close();
}

void KXMLEditorShell::slotActWindowCloseAll()
{
	slotActWindowCloseAllOthers();
	slotActWindowClose();
}

KXEShellManager* KXMLEditorShell::manager()
{
	if (!s_manager)
	{
		s_manager = new KXEShellManager();
	}
	return s_manager;
}

void KXMLEditorShell::slotBeforeWindowMenuShown()
{
	// first we have to clean all shell items...
	m_windowMenu->clear();

	// then we plug-in neccessary actions...
	pActCloseW->plug(m_windowMenu);
	pActCloseAllW->plug(m_windowMenu);
	pActCloseAllO->plug(m_windowMenu);
	m_windowMenu->insertSeparator();

	// and then we can add entries for all shells
	// that are registered in Shell Manager...
	TQPtrListIterator<KXMLEditorShell> it (*(manager()->shells()));
	for (it.toFirst(); it.current();++it)
	{
		int id = m_windowMenu->insertItem(it.current()->caption(),it.current(),TQ_SLOT(slotActivate()));
		m_windowMenu->setItemChecked(id,it.current()==this);
	}
}

void KXMLEditorShell::slotActivate()
{
	/// @todo windows have to be activated over EWMH
	showNormal();			// show it if it's minimized
	raise();				// bring that to front
}

// Add URL to recent file list
void KXMLEditorShell::slotAddRecentURL(const KURL &url)
{
	m_pActFileOpenRecent->addURL(url);
}

#include "kxmleditorshell.moc"
