/***************************************************************************
 *   Copyright (C) 2003-2005 by David Saxton                               *
 *   david@bluehaze.org                                                    *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "circuitdocument.h"
#include "contexthelp.h"
#include "docmanager.h"
#include "filemetainfo.h"
#include "flowcodedocument.h"
#include "itemeditor.h"
#include "itemgroup.h"
#include "iteminterface.h"
#include "itemlibrary.h"
#include "ktechlab.h"
#include "core/ktlconfig.h"
#include "languagemanager.h"
#include "mechanicsdocument.h"
#include "microlibrary.h"
#include "newfiledlg.h"
#include "oscilloscope.h"
#include "projectmanager.h"
#include "recentfilesaction.h"
#include "settingsdlg.h"
#include "subcircuits.h"
#include "symbolviewer.h"
#include "textdocument.h"
#include "textview.h"
#include "viewcontainer.h"

#include <tqdockarea.h>
#include <tqtimer.h>
#include <tqtoolbutton.h>
#include <tqwhatsthis.h>

#include <tdeaccel.h>
#include <tdeapplication.h>
#include <kdebug.h>
#include <kedittoolbar.h>
#include <tdefiledialog.h>
#include <kiconloader.h>
#include <tdeio/netaccess.h>
#include <kkeydialog.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <tdepopupmenu.h>
#include <kstandarddirs.h>
#include <ktabwidget.h> 
#include <kurldrag.h>
#include <twin.h>

KTechlab::KTechlab()
	: KateMDI::MainWindow( 0, "KTechlab" )
{
	TQTime ct;
	ct.start();
	
	m_bIsShown = false;
	m_pContainerDropSource = 0l;
	m_pContainerDropReceived = 0l;
	m_pContextMenuContainer = 0l;
	m_pFocusedContainer = 0l;
	m_pToolBarOverlayLabel = 0l;
	
	m_pUpdateCaptionsTimer = new TQTimer( this );
	connect( m_pUpdateCaptionsTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotUpdateCaptions()) );
	
	setMinimumSize( 400, 400 );
	
	DocManager::self(this);
	ItemInterface::self(this);
	
	setupTabWidget();
	setupToolDocks();
	setupActions();
	setupView();
	readProperties( TDEGlobal::config() );
	
// 	kdDebug() << "Constructor time: " << ct.elapsed() << endl;
}


KTechlab::~KTechlab()
{
	fileMetaInfo()->saveAllMetaInfo();
	
	delete fileMetaInfo();
	delete itemLibrary(); // This better be the last time the item library is used!
	delete subcircuits();
}


void KTechlab::show()
{
	KateMDI::MainWindow::show();
	m_bIsShown = true;
}


void KTechlab::load( const KURL & url )
{
	if ( url.url().endsWith( ".ktechlab", false ) )
	{
		// This is a ktechlab project; it has to be handled separetly from a
		// normal file.
		
		ProjectManager::self()->slotOpenProject( url );
		return;
	}
	
	
	TQString target;
	// the below code is what you should normally do.  in this
	// example case, we want the url to our own.  you probably
	// want to use this code instead for your app
	
	// download the contents
	if ( !TDEIO::NetAccess::download( url, target, this ) )
	{
		// If the file could not be downloaded, for example does not
		// exist on disk, NetAccess will tell us what error to use
		KMessageBox::error(this, TDEIO::NetAccess::lastErrorString());
		
		return;
	}
	
	addRecentFile(url);
	
	// set our caption
	setCaption( url.prettyURL() );
	
	// load in the file (target is always local)
	DocManager::self()->openURL( target );
	
	// and remove the temp file
	TDEIO::NetAccess::removeTempFile( target );
}


TQStringList KTechlab::recentFiles()
{
	return m_recentFiles->items();
}


void KTechlab::setupToolDocks()
{
	setToolViewStyle( KMultiTabBar::KDEV3ICON );
	
	TQPixmap pm;
	TDEIconLoader * loader = TDEGlobal::iconLoader();
	KateMDI::ToolView * tv = 0l;
	
	tv = createToolView( ProjectManager::toolViewIdentifier(),
						 KMultiTabBar::Left,
						 loader->loadIcon( "attach", TDEIcon::Small ),
						 i18n("Project") );
	ProjectManager::self( this, tv );
	
	pm.load( locate( "appdata", "icons/circuit.png" ) );
	tv = createToolView( ComponentSelector::toolViewIdentifier(),
						 KMultiTabBar::Left,
						 pm,
						 i18n("Components") );
	ComponentSelector::self(tv);
	
	// Create an instance of the subcircuits interface, now that we have created the component selector
	subcircuits();
	Subcircuits::loadSubcircuits();
	
	pm.load( locate( "appdata", "icons/flowcode.png" ) );
	tv = createToolView( FlowPartSelector::toolViewIdentifier(),
						 KMultiTabBar::Left,
						 pm,
						 i18n("Flow Parts") );
	FlowPartSelector::self(tv);
	
#ifdef MECHANICS
	pm.load( locate( "appdata", "icons/mechanics.png" ) );
	tv = createToolView( MechanicsSelector::toolViewIdentifier(),
						 KMultiTabBar::Left,
						 pm,
						 i18n("Mechanics") );
	MechanicsSelector::self(tv);
#endif
	
	pm.load( locate( "appdata", "icons/item.png" ) );
	tv = createToolView( ItemEditor::toolViewIdentifier(),
						 KMultiTabBar::Right,
						 pm,
						 i18n("Item Editor") );
	ItemEditor::self(tv);
	
	tv = createToolView( ContextHelp::toolViewIdentifier(),
						 KMultiTabBar::Right,
						 loader->loadIcon( "contents", TDEIcon::Small ),
						 i18n("Context Help") );
	ContextHelp::self(tv);
	
	tv = createToolView( LanguageManager::toolViewIdentifier(),
						 KMultiTabBar::Bottom,
						 loader->loadIcon( "text-x-log", TDEIcon::Small ),
						 i18n("Messages") );
	LanguageManager::self( tv, this );
	
#ifndef NO_GPSIM
	tv = createToolView( SymbolViewer::toolViewIdentifier(),
						 KMultiTabBar::Right,
						 loader->loadIcon( "blockdevice", TDEIcon::Small ),
						 i18n("Symbol Viewer") );
	SymbolViewer::self(tv);
#endif
	
	addOscilloscopeAsToolView(this);
}


void KTechlab::addWindow( ViewContainer * vc )
{
	if ( vc && !m_viewContainerList.contains(vc) )
	{
		m_viewContainerList << vc;
		connect( vc, TQ_SIGNAL(destroyed(TQObject* )), this, TQ_SLOT(slotViewContainerDestroyed(TQObject* )) );
	}
	
	m_viewContainerList.remove((ViewContainer*)0);
	slotUpdateTabWidget();
	slotDocModifiedChanged();
}


void KTechlab::setupView()
{
	setAcceptDrops(true);
	setStandardToolBarMenuEnabled(true);
	setXMLFile("ktechlabui.rc");
	createShellGUI(true);
	action("newfile_popup")->plug( toolBar("mainToolBar"), 0 );
	action("file_new")->unplug( toolBar("mainToolBar") );
	statusBar()->show();
}


void KTechlab::overlayToolBarScreenshot()
{
	if ( !m_pToolBarOverlayLabel )
	{
		m_pToolBarOverlayLabel = new TQLabel( 0, 0, WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WNoAutoErase | WType_Popup  );
		m_pToolBarOverlayLabel->hide();
		m_pToolBarOverlayLabel->setBackgroundMode( NoBackground );
	}
	
	if ( !m_bIsShown )
	{
		// The window isn't visible yet, so there's nothing to overlay (and if we tried,
		// it would appear as a strange floating toolbar).
		return;
	}
	
	if ( m_pToolBarOverlayLabel->isShown() )
	{
		// This is to avoid successive calls to removeGUIClient when we have
		// already popped it up for the first call, and don't want to take
		// another screenshot (as that would be without the toolbar).
		return;
	}
	
	TQPtrListIterator<TDEToolBar> toolBarIterator();
	
// 	TQWidget * toolsWidget = toolBar( "toolsToolBar" );
// 	TQWidget * debugWidget = toolBar( "debugTB" );
	
	TDEToolBar * toolsWidget = static_cast<TDEToolBar*>(child( "toolsToolBar", "TDEToolBar" ));
	TDEToolBar * debugWidget = static_cast<TDEToolBar*>(child( "debugTB", "TDEToolBar" ));
	
	if ( !toolsWidget && !debugWidget )
		return;
	
	TQWidget * parent = static_cast<TQWidget*>(toolsWidget ? toolsWidget->parent() : debugWidget->parent());
	
	TQRect grabRect;
	
	// 128 is a sanity check (widget can do strange things when being destroyed)
	
	if ( toolsWidget && toolsWidget->height() <= 128 )
		grabRect |= toolsWidget->geometry();
	if ( debugWidget && debugWidget->height() <= 128 )
		grabRect |= debugWidget->geometry();
	
	if ( !grabRect.isValid() )
		return;
	
	TQPixmap shot = TQPixmap::grabWidget( parent, grabRect.x(), grabRect.y(), grabRect.width(), grabRect.height() );
	
	m_pToolBarOverlayLabel->move( parent->mapToGlobal( grabRect.topLeft() ) );
	m_pToolBarOverlayLabel->setFixedSize( grabRect.size() );
	m_pToolBarOverlayLabel->setPixmap( shot );
	m_pToolBarOverlayLabel->show();
	
	TQTimer::singleShot( 100, this, TQ_SLOT( hideToolBarOverlay() ) );
}


void KTechlab::hideToolBarOverlay()
{
	if ( !m_pToolBarOverlayLabel )
		return;
	
// 	TQWidget * hiddenWidget = toolBar( "toolsToolBar" );
// 	if ( !hiddenWidget )
// 		return;
	
// 	hiddenWidget->setBackgroundMode( NoBackground );
// 	hiddenWidget->setWFlags( WNoAutoErase ); 
// 	hiddenWidget->setUpdatesEnabled( false );
	
	m_pToolBarOverlayLabel->hide();
}


void KTechlab::addNoRemoveGUIClient( KXMLGUIClient * client )
{
	if ( client && !m_noRemoveGUIClients.contains( client ) )
		m_noRemoveGUIClients << client;
}


void KTechlab::removeGUIClients()
{
	TQValueList<KXMLGUIClient*> clientsToRemove;
	
	TQPtrList<KXMLGUIClient> clients = factory()->clients();
	for ( KXMLGUIClient * client = clients.first(); client; client = clients.next() )
	{
		if ( client && client != this && !m_noRemoveGUIClients.contains( client ) )
			clientsToRemove << client;
	}
	
	if ( clients.isEmpty() )
		return;
	
	overlayToolBarScreenshot();
	
	TQValueList<KXMLGUIClient*>::iterator end = clientsToRemove.end();
	for ( TQValueList<KXMLGUIClient*>::iterator it = clientsToRemove.begin(); it != end; ++it )
		factory()->removeClient(*it);
}


void KTechlab::setupTabWidget()
{	
	m_pViewContainerTabWidget = new KTabWidget(centralWidget());
	connect( tabWidget(), TQ_SIGNAL(currentChanged(TQWidget* )), this, TQ_SLOT(slotViewContainerActivated(TQWidget* )) );
	connect( tabWidget(), TQ_SIGNAL(testCanDecode(const TQDragMoveEvent*, bool& )), this, TQ_SLOT(slotTabDragEvent(const TQDragMoveEvent*, bool& )) );
	connect( tabWidget(), TQ_SIGNAL(initiateDrag(TQWidget* )), this, TQ_SLOT(slotTabDragInitiate(TQWidget* )) );
	connect( tabWidget(), TQ_SIGNAL(receivedDropEvent(TQDropEvent* )), this, TQ_SLOT(slotTabReceivedDropEvent(TQDropEvent* )) );
	connect( tabWidget(), TQ_SIGNAL(receivedDropEvent(TQWidget*, TQDropEvent* )), this, TQ_SLOT(slotTabReceivedDropEvent(TQWidget*, TQDropEvent* )) );
	
	TDEConfig *config = kapp->config();
	config->setGroup("UI");
		
	bool CloseOnHover = config->readBoolEntry( "CloseOnHover", false );
	tabWidget()->setHoverCloseButton( CloseOnHover );
		
	bool CloseOnHoverDelay = config->readBoolEntry( "CloseOnHoverDelay", false );
	tabWidget()->setHoverCloseButtonDelayed( CloseOnHoverDelay );
		
// 	bool openNewTabAfterCurrent = config->readBoolEntry( "OpenNewTabAfterCurrent", false );
// 	bool showTabIcons = config->readBoolEntry( "ShowTabIcons", true );
  
	if (config->readBoolEntry( "ShowCloseTabsButton", true ))
	{
		TQToolButton *but = new TQToolButton(tabWidget());
		but->setIconSet(SmallIcon("tab_remove"));
		but->adjustSize();
		but->hide();
		connect( but, TQ_SIGNAL(clicked()), this, TQ_SLOT(slotViewContainerClose()) );
		tabWidget()->setCornerWidget(but, TopRight);
	}
// 	tabWidget()->setTabReorderingEnabled(true);
// 	connect(tabWidget(), TQ_SIGNAL(movedTab(int, int)), this, TQ_SLOT(tabMoved(int, int)));
	connect(tabWidget(), TQ_SIGNAL(contextMenu(TQWidget*,const TQPoint &)), this, TQ_SLOT(slotTabContext(TQWidget*,const TQPoint &)));
	//END Tab bar stuff
}


void KTechlab::slotUpdateTabWidget()
{
	m_viewContainerList.remove( (ViewContainer*)0 );
	
	bool noWindows = m_viewContainerList.isEmpty();
	
	if ( TQWidget * button = tabWidget()->cornerWidget(TopRight) )
		button->setHidden( noWindows );
	
	if ( noWindows )
		setCaption( 0 );
}


void KTechlab::setupActions()
{
	TDEActionCollection *ac = actionCollection();
	
    KStdAction::openNew(			this, TQ_SLOT(slotFileNew()),					ac );
    KStdAction::open(				this, TQ_SLOT(slotFileOpen()),					ac );
    KStdAction::save(				this, TQ_SLOT(slotFileSave()),					ac );
    KStdAction::saveAs(				this, TQ_SLOT(slotFileSaveAs()),				ac );
	KStdAction::close(				this, TQ_SLOT(slotViewClose()),				ac );
    KStdAction::print(				this, TQ_SLOT(slotFilePrint()),				ac );
    KStdAction::quit(				this, TQ_SLOT(slotFileQuit()),					ac );
	KStdAction::undo(				this, TQ_SLOT(slotEditUndo()),					ac );
	KStdAction::redo(				this, TQ_SLOT(slotEditRedo()),					ac );
	KStdAction::cut(				this, TQ_SLOT(slotEditCut()),					ac );
	KStdAction::copy(				this, TQ_SLOT(slotEditCopy()),					ac );
	KStdAction::paste(				this, TQ_SLOT(slotEditPaste()),				ac );
	KStdAction::keyBindings(		this, TQ_SLOT(slotOptionsConfigureKeys()),		ac );
	KStdAction::configureToolbars(	this, TQ_SLOT(slotOptionsConfigureToolbars()),	ac );
	KStdAction::preferences(		this, TQ_SLOT(slotOptionsPreferences()),		ac );
	
	//BEGIN New file popup
	TDEToolBarPopupAction *p = new TDEToolBarPopupAction( i18n("&New"), "document-new", TDEStdAccel::shortcut(TDEStdAccel::New), this, TQ_SLOT(slotFileNew()), ac, "newfile_popup" );
	p->popupMenu()->insertTitle( i18n("New File") );
	(new TDEAction( i18n("Assembly"), "text-x-src", 0, this, TQ_SLOT(slotFileNewAssembly()), ac, "newfile_asm" ))->plug( p->popupMenu() );
	(new TDEAction( i18n("C source"), "text-x-csrc", 0, this, TQ_SLOT(slotFileNewC()), ac, "newfile_c" ))->plug( p->popupMenu() );
	(new TDEAction( i18n("Circuit"), "ktechlab_circuit", 0, this, TQ_SLOT(slotFileNewCircuit()), ac, "newfile_circuit" ))->plug( p->popupMenu() );
	(new TDEAction( i18n("FlowCode"), "ktechlab_flowcode", 0, this, TQ_SLOT(slotFileNewFlowCode()), ac, "newfile_flowcode" ))->plug( p->popupMenu() );
#ifdef MECHANICS
	(new TDEAction( i18n("Mechanics"), "ktechlab_mechanics", 0, this, TQ_SLOT(slotFileNewMechanics()), ac, "newfile_mechanics" ))->plug( p->popupMenu() );
#endif
	(new TDEAction( "Microbe", "ktechlab_microbe", 0, this, TQ_SLOT(slotFileNewMicrobe()), ac, "newfile_microbe" ))->plug( p->popupMenu() );
	//END New File popup
	

// 	m_recentFiles = KStdAction::openRecent( this, TQ_SLOT(load(const KURL&)), ac );
	m_recentFiles = new RecentFilesAction( "Recent Files", i18n("Open Recent"), this, TQ_SLOT(load(const KURL &)), ac, "file_open_recent" );
    m_statusbarAction = KStdAction::showStatusbar( this, TQ_SLOT(slotOptionsShowStatusbar()), ac );
	
	//BEGIN Project Actions
	ProjectManager *pm = ProjectManager::self(this);
	new TDEAction( i18n("New Project.."), "window-new",			0, pm, TQ_SLOT(slotNewProject()),			ac, 	"project_new" );
	new TDEAction( i18n("Open Project..."), "project_open",		0, pm, TQ_SLOT(slotOpenProject()),			ac, 	"project_open" );
// 	m_recentProjects = new TDERecentFilesAction( i18n("Open &Recent Project..."), 0, ProjectManager::self(), TQ_SLOT(slotOpenProject(const KURL&)), ac, "project_open_recent" );
	m_recentProjects = new RecentFilesAction( "Recent Projects", i18n("Open &Recent Project..."), ProjectManager::self(), TQ_SLOT(slotOpenProject(const KURL&)), ac, "project_open_recent" );
	new TDEAction( i18n("Export to Makefile..."), "fileexport",	0, pm, TQ_SLOT(slotExportToMakefile()),		ac, "project_export_makefile" );
	new TDEAction( i18n("Create Subproject..."), 0,				0, pm, TQ_SLOT(slotCreateSubproject()),		ac, "project_create_subproject" );
	new TDEAction( i18n("Add Existing File..."), "document-open",		0, pm, TQ_SLOT(slotAddFile()),					ac, "project_add_existing_file" );
	new TDEAction( i18n("Add Current File..."), "fileimport",		0, pm, TQ_SLOT(slotAddCurrentFile()),			ac, "project_add_current_file" );
// 	new TDEAction( i18n("Project Options"), "configure",			0, pm, TQ_SLOT(slotProjectOptions()),			ac, "project_options" );
	new TDEAction( i18n("Close Project"), "window-close",			0, pm, TQ_SLOT(slotCloseProject()),			ac, "project_close" );
	new TDEAction( i18n("Remove from Project"), "edit-delete",		0, pm, TQ_SLOT(slotRemoveSelected()),			ac, "project_remove_selected" );
	new TDEAction( i18n("Insert Existing File..."), "document-open",	0, pm, TQ_SLOT(slotSubprojectAddExistingFile()),	ac, "subproject_add_existing_file" );
	new TDEAction( i18n("Insert Current File..."), "fileimport",	0, pm, TQ_SLOT(slotSubprojectAddCurrentFile()),ac, "subproject_add_current_file" );
	new TDEAction( i18n("Linker Options..."), "configure",		0, pm, TQ_SLOT(slotSubprojectLinkerOptions()),	ac, "project_item_linker_options" );
	new TDEAction( i18n("Build..."), "launch",					0, pm, TQ_SLOT(slotItemBuild()),				ac, "project_item_build" );
	new TDEAction( i18n("Upload..."), "convert_to_pic",			0, pm, TQ_SLOT(slotItemUpload()),				ac, "project_item_upload" );
	new TDEAction( i18n("Processing Options..."), "configure",	0, pm, TQ_SLOT(slotItemProcessingOptions()),	ac, "project_item_processing_options" );
	//END Project Actions
	
	new TDEAction( i18n("Split View Left/Right"), "view_right", TQt::CTRL|TQt::SHIFT|TQt::Key_L, this, TQ_SLOT(slotViewSplitLeftRight()), ac, "view_split_leftright" );
	new TDEAction( i18n("Split View Top/Bottom"), "view_bottom", TQt::CTRL|TQt::SHIFT|TQt::Key_T, this, TQ_SLOT(slotViewSplitTopBottom()), ac, "view_split_topbottom" );
	
	TDEToggleAction * ta = new TDEToggleAction( i18n("Run Simulation"), "media-playback-start", TQt::Key_F10, 0, 0, ac, "simulation_run" );
	ta->setChecked(true);
	connect( ta, TQ_SIGNAL(toggled(bool )), Simulator::self(), TQ_SLOT(slotSetSimulating(bool )) );
	ta->setCheckedState( KGuiItem( i18n("Pause Simulation"), "media-playback-pause", 0 ) );
	
	// We can call slotCloseProject now that the actions have been created
	ProjectManager::self(this)->updateActions();
	
	DocManager::self(this)->disableContextActions();
}


void KTechlab::slotViewContainerActivated( TQWidget * viewContainer )
{
	if (m_pFocusedContainer)
		m_pFocusedContainer->setUnfocused();
	
	m_pFocusedContainer = dynamic_cast<ViewContainer*>(viewContainer);
	if ( !m_pFocusedContainer )
		return;
	
	m_pFocusedContainer->setFocused();
}


void KTechlab::slotViewContainerDestroyed( TQObject * object )
{
	m_viewContainerList.remove( static_cast<ViewContainer*>(object) );
	m_viewContainerList.remove( (ViewContainer*)0 );
	slotUpdateTabWidget();
}


void KTechlab::slotTabDragEvent( const TQDragMoveEvent *e, bool &accept )
{
	// Hmm...this function doesn't actually seem to get called. Instead,
	// KTabBar just seems to go straight to slotTabDragInitiate.
	Q_UNUSED(e);
	accept = true;
}


void KTechlab::slotTabDragInitiate( TQWidget *widget )
{
	ViewContainer *viewContainer = dynamic_cast<ViewContainer*>(widget);
	if (!viewContainer)
		return;
	TQDragObject *dragObject = new ViewContainerDrag(viewContainer);
	dragObject->drag();
}


void KTechlab::slotTabReceivedDropEvent( TQDropEvent *e )
{
	if (!e)
		return;
	ViewContainer *viewContainerSource = dynamic_cast<ViewContainer*>(e->source());
	if (!viewContainerSource)
	{
		e->ignore();
		return;
	}
	e->accept(true);
	viewContainerSource->duplicateViewContainer();
}


void KTechlab::slotTabReceivedDropEvent( TQWidget *widget, TQDropEvent *e )
{
	if (!e)
		return;
	m_pContainerDropSource = dynamic_cast<ViewContainer*>(e->source());
	m_pContainerDropReceived = dynamic_cast<ViewContainer*>(widget);
	if ( !m_pContainerDropSource || !m_pContainerDropReceived || (m_pContainerDropSource == m_pContainerDropReceived) )
	{
		e->ignore();
		return;
	}
	e->accept(true);
	
	TDEPopupMenu dropMenu;
	dropMenu.insertItem( TDEGlobal::iconLoader()->loadIcon( "goto", TDEIcon::Small ), i18n("&Insert Into"), 0 );
	dropMenu.insertItem( TDEGlobal::iconLoader()->loadIcon( "edit-copy", TDEIcon::Small ), i18n("&Copy Into"), 1 );
	dropMenu.insertSeparator();
	dropMenu.insertItem( TDEGlobal::iconLoader()->loadIcon( "process-stop", TDEIcon::Small ), i18n("C&ancel"), 2 );
	
	connect( &dropMenu, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotDragContextActivated(int)) );
// 	dropMenu.exec(e->pos() + widget->pos() );
	dropMenu.exec( TQCursor::pos() );
}


void KTechlab::slotDragContextActivated( int id )
{
	if ( !m_pContainerDropSource || !m_pContainerDropReceived )
		return;
	
	switch (id)
	{
		case 0:
			m_pContainerDropSource->copyViewContainerIntoExisting(m_pContainerDropReceived);
			m_pContainerDropSource->closeViewContainer();
			break;
		case 1:
			m_pContainerDropSource->copyViewContainerIntoExisting(m_pContainerDropReceived);
			break;
		case 2:
		default:
			break;
	}
}


TDEAction * KTechlab::action( const TQString & name ) const
{
	TDEAction * action = actionCollection()->action(name.utf8());
	if ( !action )
		kdError() << k_funcinfo << "No such action: " << name << endl;
	return action;
}


void KTechlab::saveProperties( TDEConfig *conf )
{
	// Dumbass TDEMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?!
	conf->setGroup("UI");
	conf->writeEntry( "Width", width() );
	conf->writeEntry( "Height", height() );
	conf->writeEntry( "WinState", KWin::windowInfo( winId(), NET::WMState ).state() );
	
#ifndef NO_GPSIM
	SymbolViewer::self()->saveProperties( conf );
#endif
	
	if ( ProjectManager::self()->currentProject() )
	{
		conf->setGroup("Project");
		conf->writePathEntry( "Open", ProjectManager::self()->currentProject()->url().prettyURL() );
	}
	else
		conf->deleteGroup("Project");
	
	//BEGIN Open Views State
	// Remvoe old entries describing the save state - we don't want a horrible mish-mash of saved states
	const TQStringList groupList = conf->groupList();
	const TQStringList::const_iterator groupListEnd = groupList.end();
	for ( TQStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it )
	{
		if ( (*it).startsWith("ViewContainer") )
			conf->deleteGroup(*it);
	}
	
	uint viewContainerId = 1;
	const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
	for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it )
	{
		if ( !(*it) || !(*it)->canSaveUsefulStateInfo() )
			continue;
		
		// To make sure the ViewContainers are restored in the right order, we must create them in alphabetical order,
		// as TDEConfig stores them as such...
		const TQString id = TQString::number(viewContainerId++).rightJustify( 4, '0' );
			
		conf->setGroup( "ViewContainer " + id );
		(*it)->saveState(conf);
	}
	//END Open Views State
	
	saveSession( conf, "KateMDI" );
	// Piss off TDEMainWindow
	conf->setGroup("KateMDI");
	int scnum = TQApplication::desktop()->screenNumber(parentWidget());
	TQRect desk = TQApplication::desktop()->screenGeometry(scnum);
	conf->deleteEntry( TQString::fromLatin1("Width %1").arg(desk.width()) );
	conf->deleteEntry( TQString::fromLatin1("Height %1").arg(desk.height()) );
	
	conf->sync();
}


void KTechlab::readProperties( TDEConfig *conf )
{
	startRestore( conf, "KateMDI" );
	
	m_recentFiles->loadEntries();
	m_recentProjects->loadEntries();
	
	//BEGIN Restore Open Views
	if ( KTLConfig::restoreDocumentsOnStartup() )
	{
		// If we have a lot of views open from last time, then opening them will take a long time.
		// So we want to enter the tqt event loop to finish drawing the window et al before adding the views.
		tqApp->processEvents();
	
		const TQStringList groupList = conf->groupList();
		const TQStringList::const_iterator groupListEnd = groupList.end();
		for ( TQStringList::const_iterator it = groupList.begin(); it != groupListEnd; ++it )
		{
			if ( (*it).startsWith("ViewContainer") )
			{
				ViewContainer *viewContainer = new ViewContainer( *it, this );
			
				conf->setGroup(*it);
				viewContainer->restoreState( conf, *it );
			
				addWindow( viewContainer );
			}
		}
	}
	//END Restore Open Views
	
	conf->setGroup("Project");
	if ( conf->readPathEntry("Open") != TQString() )
		ProjectManager::self()->slotOpenProject( KURL( conf->readPathEntry("Open") ) );
	
#ifndef NO_GPSIM
	SymbolViewer::self()->readProperties( conf );
#endif
	
	finishRestore();
	
	// Dumbass TDEMainWindow - can't handle my width/height correctly. Whoever thought of the "+1" hack anyway?!
	conf->setGroup("UI");
	resize( conf->readNumEntry( "Width", 800 ), conf->readNumEntry( "Height", 500 ) );
	KWin::setState( winId(), conf->readLongNumEntry( "WinState", NET::Max ) );
}


void KTechlab::dragEnterEvent(TQDragEnterEvent *event)
{
    // accept uri drops only
    event->accept(KURLDrag::canDecode(event));
}


void KTechlab::dropEvent(TQDropEvent *event)
{
    // this is a very simplistic implementation of a drop event.  we
    // will only accept a dropped URL.  the TQt dnd code can do *much*
    // much more, so please read the docs there
    KURL::List urls;

    // see if we can decode a URI.. if not, just ignore it
    if (KURLDrag::decode(event, urls) && !urls.isEmpty())
    {
        // okay, we have a URI.. process it
        const KURL &url = urls.first();

        // load in the file
        load(url);
    }
}


void KTechlab::slotOptionsShowStatusbar()
{
    // this is all very cut and paste code for showing/hiding the
    // statusbar
    if (m_statusbarAction->isChecked())
        statusBar()->show();
    else
        statusBar()->hide();
}


void KTechlab::slotOptionsConfigureKeys()
{
//     KKeyDialog::configureKeys(actionCollection(), "ktechlabui.rc");
	KKeyDialog::configure( actionCollection(), this, true );
}


void KTechlab::slotOptionsConfigureToolbars()
{
	KEditToolbar *dlg = new KEditToolbar(guiFactory());

	if (dlg->exec())
	{
		createShellGUI( false );
		createShellGUI( true );
	}

	delete dlg;
}


void KTechlab::slotOptionsPreferences()
{
	// An instance of your dialog could be already created and could be cached,
	// in which case you want to display the cached dialog instead of creating
	// another one
	if ( TDEConfigDialog::showDialog( "settings" ) )
		return;
	
	// TDEConfigDialog didn't find an instance of this dialog, so lets create it:
	SettingsDlg* dialog = new SettingsDlg( this, "settings", KTLConfig::self() );
	
	// User edited the configuration - update your local copies of the
	// configuration data
	connect( dialog, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(slotUpdateConfiguration()) );
	dialog->show();
}


void KTechlab::slotUpdateConfiguration()
{
	emit configurationChanged();
}


void KTechlab::slotChangeStatusbar( const TQString & text )
{
	// Avoid flicker by repeatedly displaying the same message, as TQStatusBar does not check for this
	if ( m_lastStatusBarMessage == text )
		return;
	
    statusBar()->message(text);
	m_lastStatusBarMessage = text;
}


void KTechlab::slotTabContext( TQWidget* widget,const TQPoint & pos )
{
	// Shamelessly stolen from KDevelop...
	
	TDEPopupMenu * tabMenu = new TDEPopupMenu;
	tabMenu->insertTitle( (dynamic_cast<ViewContainer*>(widget))->caption() );

	//Find the document on whose tab the user clicked
	m_pContextMenuContainer = 0l;
	
	m_viewContainerList.remove((ViewContainer*)0l);
	
	const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
	for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it )
	{
		ViewContainer * viewContainer = *it;
		if ( viewContainer == widget )
		{
			m_pContextMenuContainer = viewContainer;
			
			tabMenu->insertItem( i18n("Close"), 0 );
			
			View *view = (viewContainer->viewCount() == 1) ? viewContainer->activeView() : 0l;
			
			if ( view && view->document()->isModified() )
				tabMenu->insertItem( i18n("Save"), 1 );
			
			if ( view && !view->document()->url().isEmpty() )
				tabMenu->insertItem( i18n("Reload"), 2 );
			
			if ( m_viewContainerList.count() > 1 )
				tabMenu->insertItem( i18n("Close All Others"), 4 );

		}
	}
	
	connect( tabMenu, TQ_SIGNAL( activated(int) ), this, TQ_SLOT(slotTabContextActivated(int)) );

	tabMenu->exec(pos);
	delete tabMenu;
}


void KTechlab::slotTabContextActivated( int id )
{
	// Shamelessly stolen from KDevelop...
	
	if( !m_pContextMenuContainer )
		return;
	
	View *view = m_pContextMenuContainer->activeView();
	if (!view)
		return;
	TQGuardedPtr<Document> document = view->document();

	switch(id)
	{
		case 0:
		{
			m_pContextMenuContainer->closeViewContainer();
			break;
		}
		case 1:
			document->fileSave();
			break;
		case 2:
		{
			KURL url = document->url();
			if ( document->fileClose() )
			{
				delete document;
				DocManager::self()->openURL(url);
			}
			break;
		}
		case 4:
		{
			const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
			for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it )
			{
				ViewContainer *viewContainer = *it;
				if ( viewContainer && viewContainer != m_pContextMenuContainer )
				{
					if ( !viewContainer->closeViewContainer() )
						return;
				}
			}
			break;
		}
		default:
			break;
	}
}
	


void KTechlab::slotFileNewAssembly()
{
	TextDocument *document = DocManager::self()->createTextDocument();
	if (document)
		document->slotInitLanguage( TextDocument::ct_asm );
}
void KTechlab::slotFileNewMicrobe()
{
	TextDocument *document = DocManager::self()->createTextDocument();
	if (document)
		document->slotInitLanguage( TextDocument::ct_microbe );
}
void KTechlab::slotFileNewC()
{
	TextDocument *document = DocManager::self()->createTextDocument();
	if (document)
		document->slotInitLanguage( TextDocument::ct_c );
}
void KTechlab::slotFileNewCircuit()
{
	DocManager::self()->createCircuitDocument();
}
void KTechlab::slotFileNewFlowCode()
{
	slotFileNew();
}
void KTechlab::slotFileNewMechanics()
{
	DocManager::self()->createMechanicsDocument();
}

void KTechlab::slotFileNew()
{
	NewFileDlg *newFileDlg = new NewFileDlg(this);
	
	newFileDlg->exec();
	
	bool addToProject = newFileDlg->addToProject();
	bool accepted = newFileDlg->accepted();
	int finalType = newFileDlg->fileType();
	TQString microID = newFileDlg->microID();
	int codeType = newFileDlg->codeType();
	
	delete newFileDlg;
	if (!accepted)
		return;
	
	Document *created = 0l;
	
	if ( finalType == Document::dt_circuit )
		created = DocManager::self()->createCircuitDocument();
	
	else if ( finalType == Document::dt_flowcode )
	{
		FlowCodeDocument * fcd = DocManager::self()->createFlowCodeDocument();
		fcd->setPicType(microID);
		created = fcd;
	}
	
	else if ( finalType == Document::dt_mechanics )
		created = DocManager::self()->createMechanicsDocument();
	
	else
	{
		// Presumably a text document
		TextDocument * textDocument = DocManager::self()->createTextDocument();
	
		if (textDocument)
			textDocument->slotInitLanguage( (TextDocument::CodeType)codeType );
		
		created = textDocument;
	}
	
	if ( created && addToProject )
		created->setAddToProjectOnSave(true);
}

void KTechlab::slotFileOpen()
{
    // this slot is called whenever the File->Open menu is selected,
    // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar
    // button is clicked
	
    // standard filedialog
	KURL::List urls = getFileURLs();
	const KURL::List::iterator end = urls.end();
	for ( KURL::List::iterator it = urls.begin(); it != end; ++ it)
		load(*it);
}

void KTechlab::addRecentFile( const KURL &url )
{
	m_recentFiles->addURL( url );
	emit recentFileAdded(url);
}


KURL::List KTechlab::getFileURLs()
{
	return KFileDialog::getOpenURLs(
			TQString(),
	        "*|All Files\n"
			"*.asm *.src *.inc|Assembly Code (*.asm, *.src, *.inc)\n"
			"*.hex|Intel Hex (*.hex)\n"
			"*.circuit|Circuit (*.circuit)\n"
			"*.flowcode|FlowCode (*.flowcode)\n"
			"*.basic *.microbe|Microbe (*.microbe, *.basic)\n"
			"*.mechanics|Mechanics (*.mechanics)\n",
			0L,
			i18n("Open Location") );
}


void KTechlab::slotDocModifiedChanged()
{
	//BEGIN Set tab icons
	TDEIconLoader *loader = TDEGlobal::iconLoader();
	const ViewContainerList::iterator vcEnd = m_viewContainerList.end();
	for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != vcEnd; ++it )
	{
		ViewContainer * vc = *it;
		if ( !vc || !vc->activeView() || !vc->activeView()->document() )
			continue;
		
		TQString iconName;
		
		if ( vc->activeView()->document()->isModified() )
			iconName = "document-save";
		
		else switch ( vc->activeView()->document()->type() )
		{
			case Document::dt_circuit:
				iconName = "ktechlab_circuit";
				break;
				
			case Document::dt_flowcode:
				iconName = "ktechlab_flowcode";
				break;
				
			case Document::dt_mechanics:
				iconName = "ktechlab_mechanics";
				break;
				
			case Document::dt_text:
				iconName = "text-plain";
				break;
				
			case Document::dt_pinMapEditor:
				break;
				
			case Document::dt_none:
				iconName = "unknown";
				break;
		}
		
		tabWidget()->setTabIconSet( vc, loader->loadIcon( iconName, TDEIcon::Small ) );
	}
	//END Set tab icons
}


void KTechlab::requestUpdateCaptions()
{
	m_pUpdateCaptionsTimer->start( 0, true );
}


void KTechlab::slotUpdateCaptions()
{
	//BEGIN Set KTechlab caption
	Document *document = DocManager::self()->getFocusedDocument();
	TQString newCaption;
	if ( document )
	{
		KURL url = document->url();
		if ( url.isEmpty() )
			newCaption = document->caption();
		else
		{
			if ( url.isLocalFile() && url.ref().isNull() && url.query().isNull() )
				newCaption = url.path();
			else
				newCaption = url.prettyURL();
		}
	}
	else
		newCaption = "";
	
	if (newCaption != caption().remove(" - KTechlab"))
		setCaption(newCaption);
	//END Set KTechlab caption
	
	
	//BEGIN Set tab captions
	emit needUpdateCaptions();
	
	if ( document && document->activeView() && document->activeView()->viewContainer() )
	{
		document->activeView()->viewContainer()->updateCaption();
	}
	//END Set tab captions
}


void KTechlab::slotDocUndoRedoChanged()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (!document)
		return;
	
	action("edit_undo")->setEnabled( document->isUndoAvailable() );
	action("edit_redo")->setEnabled( document->isRedoAvailable() );
}

void KTechlab::slotFileSave()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->fileSave();
}

void KTechlab::slotFileSaveAs()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->fileSaveAs();
}

void KTechlab::slotFilePrint()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->print();
}


bool KTechlab::queryClose()
{
	saveProperties( TDEGlobal::config() );
	
	if ( DocManager::self()->closeAll() && ProjectManager::self()->slotCloseProject() )
	{
		ViewContainerList::iterator end = m_viewContainerList.end();
		for ( ViewContainerList::iterator it = m_viewContainerList.begin(); it != end; ++it )
		{
			if ( *it )
				(*it)->setKTechlabDeleted();
		}
		
		return true;
	}
	
	return false;
}

void KTechlab::slotFileQuit()
{
	// close the first window, the list makes the next one the first again.
	// This ensures that queryClose() is called on each window to ask for closing
	TDEMainWindow* w;
	if(memberList)
	{
		for( w=memberList->first(); w!=0; w=memberList->next() )
		{
			// only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog,
			// the window and the application stay open.
			if( !w->close() ) break;
		}
	}
	
    slotChangeStatusbar( i18n("Exiting...") );
}

void KTechlab::slotEditUndo()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->undo();
}

void KTechlab::slotEditRedo()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->redo();
}

void KTechlab::slotEditCut()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->cut();
}

void KTechlab::slotEditCopy()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->copy();
}

void KTechlab::slotEditPaste()
{
	Document *document = DocManager::self()->getFocusedDocument();
	if (document)
		document->paste();
}

void KTechlab::slotViewContainerClose()
{
	if (m_pFocusedContainer)
		m_pFocusedContainer->closeViewContainer();
}
void KTechlab::slotViewClose()
{
	View *view = DocManager::self()->getFocusedView();
	if (view)
		view->closeView();
}
void KTechlab::slotViewSplitLeftRight()
{
	View *view = DocManager::self()->getFocusedView();
	if (!view)
		return;
	ViewContainer *vc = view->viewContainer();
	uint vaId = vc->createViewArea( view->viewAreaId(), ViewArea::Right );
	view->document()->createView( vc, vaId );
}
void KTechlab::slotViewSplitTopBottom()
{
	View *view = DocManager::self()->getFocusedView();
	if (!view)
		return;
	ViewContainer *vc = view->viewContainer();
	uint vaId = vc->createViewArea( view->viewAreaId(), ViewArea::Bottom );
	view->document()->createView( vc, vaId );
}

#include "ktechlab.moc"
