/***************************************************************************
 *   Copyright (C) 2005 by Joe Ferris                                      *
 *   jferris@optimistictech.com                                            *
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#include <tdeapplication.h>
#include <kiconloader.h>
#include <tdeglobal.h>
#include <twin.h>
#include <kkeydialog.h>
#include <tdeaction.h>
#include <tdeparts/componentfactory.h>
#include <kpassivepopup.h>
#include <tdepopupmenu.h>
#include <khelpmenu.h>
#include <tdelocale.h>
#include <tdeaboutapplication.h>
#include <dcopclient.h>

#include <tqpopupmenu.h>
#include <tqmenudata.h>
#include <tqcursor.h>

#include "katapult.h"
#include "actionregistry.h"
#include "katapultcatalog.h"
#include "katapultdisplay.h"
#include "katapultsettings.h"
#include "status.h"

Katapult::Katapult()
	: DCOPObject("Katapult"),
	  KSystemTray(0, "katapultSysTray")
{
	// Register with DCOP
	tdeApp->dcopClient()->registerAs("katapult", false);
	tdeApp->dcopClient()->setDefaultObject(objId());

	setPixmap(TDEGlobal::iconLoader()->loadIcon("katapult", TDEIcon::Small));

	display = 0;
	globalAccel = 0;
	actions = 0;
	action = 0;
	executing = false;
	_query = "";
	
	settings = new KatapultSettings();
  helpmenu = new KHelpMenu(this,TQString(),false);
  connect(helpmenu, TQ_SIGNAL(showAboutApplication()),this, TQ_SLOT(showAboutDialog()));

	hideTimer = new TQTimer(this);
	connect(hideTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(hideLauncher()));
	clearTimer = new TQTimer(this);
	connect(clearTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(clearQuery()));
	
	connect(settings, TQ_SIGNAL(catalogsChanged()), this, TQ_SLOT(loadCatalogPlugins()));
	connect(settings, TQ_SIGNAL(displayChanged()), this, TQ_SLOT(initDisplay()));
	connect(settings, TQ_SIGNAL(systrayChanged()), this, TQ_SLOT(updateSystray()));

	loadCatalogPlugins();
	initDisplay();

	if(settings->systrayIcon())
	{
		show();
	}

	//show popup (unless we're session restored)
	if (!settings->hideNotification()) {
		TQString msg = i18n("Application successfully started !\nPress %1 to use it...")
				.arg(globalAccel->shortcut("show_katapult").toString());
		KPassivePopup::message(i18n("Katapult Notification"), msg, tdeApp->miniIcon(), this, 0, settings->notificationTimeout());
	}
}

Katapult::~Katapult()
{
	if(globalAccel != 0)
		delete globalAccel;
	if(actions != 0)
		delete actions;
	delete settings;
}

void Katapult::loadCatalogPlugins()
{
	catalogs = settings->activeCatalogs();
}

void Katapult::initDisplay()
{
	display = settings->display();
	if(display == 0) {
		initAccel(this);
	} else {
		initAccel(display);
		setQuery("");
		connect(display, TQ_SIGNAL(keyReleased(TQKeyEvent *)), this, TQ_SLOT(keyReleased(TQKeyEvent *)));
		connect(display, TQ_SIGNAL(focusOut()), this, TQ_SLOT(hideLauncher()));
	}
}

void Katapult::initAccel(TQWidget *parent)
{
	globalAccel = new TDEGlobalAccel(parent);
	globalAccel->insert("show_katapult", i18n("Show Launcher"), i18n("Shows the Katapult launcher"), ALT+Key_Space, ALT+Key_Space,
		this, TQ_SLOT(showLauncher()));
	globalAccel->readSettings();
	globalAccel->updateConnections();
	
	actions = new TDEActionCollection(parent);
	actionCollection()->clear();
	
	contextMenu()->clear();
	
	new TDEAction(i18n("Execute"), "application-x-executable", Key_Return, this, TQ_SLOT(execute()), actions, "execute");
	new TDEAction(i18n("Clear"), "edit-clear", Key_Left, this, TQ_SLOT(clearQuery()), actions, "clear");
	new TDEAction(i18n("Close"), "window-close", Key_Escape, this, TQ_SLOT(hideLauncher()), actions, "close");
	new TDEAction(i18n("Complete Query"), "next", Key_Right, this, TQ_SLOT(completeQuery()), actions, "complete_query");
	new TDEAction(i18n("Show Context Menu"), "menu", CTRL+Key_C, this, TQ_SLOT(showContextMenu()), actions, "show_menu");
	
	TDEAction *actGlobAccel = KStdAction::keyBindings(this, TQ_SLOT(showGlobalShortcutsDialog()), actions);
	actGlobAccel->setText(i18n("Configure &Global Shortcuts..."));
	actGlobAccel->plug((TQWidget *) contextMenu());
	
	KStdAction::keyBindings(this, TQ_SLOT(showShortcutsDialog()), actions)->plug((TQWidget *) contextMenu());
	KStdAction::preferences(settings, TQ_SLOT(configure()), actions)->plug((TQWidget *) contextMenu());
	
	contextMenu()->insertItem(SmallIconSet("help"), KStdGuiItem::help().text(), helpmenu->menu());
  contextMenu()->insertSeparator();
	
	KStdAction::quit(this, TQ_SLOT(close()), actions)->plug((TQWidget *) contextMenu());
	
	actions->readShortcutSettings();
}

void Katapult::completeQuery()
{
	if(!bestMatch.isNull()) {
		TQString newQuery = bestMatch.item()->text();
		setQuery("");
		setQuery(newQuery);
	}
}

void Katapult::clearQuery()
{
	setQuery("");
}

void Katapult::setQuery(TQString _query)
{
	allStatus=0;
	bestMatch = Match();
		
	this->_query = _query;
	if(display != 0)
		display->setQuery(_query);
	if(_query == "")
	{
		TQDictIterator<KatapultCatalog> it(catalogs);
		KatapultCatalog *catalog;
		while((catalog = it.current()) != 0)
		{
			++it;
			catalog->setQuery("");
		}
		display->setItem(0);
		display->setAction(0);
		display->setStatus(0);
		display->setSelected(0);
		action = 0;
	} else if(catalogs.count() == 0) {
		allStatus = S_Active | S_NoResults;
		display->setStatus(allStatus);
	} else {
		TQDictIterator<KatapultCatalog> it(catalogs);
		KatapultCatalog *catalog;
		int status;
		
		while((catalog = it.current()) != 0)
		{
			++it;
			catalog->setQuery(_query);
				
			status = catalog->status();
			if(status & S_HasResults)
			{
				if(allStatus & S_HasResults)
					allStatus |= S_Multiple;
				Match match = catalog->bestMatch();
				if(!match.isNull())
				{
					if(bestMatch.isNull() || bestMatch.rank() < match.rank())
						bestMatch = match;
				}
			}
			allStatus |= status;
		}
		if(bestMatch.isNull() || bestMatch.rank() == 0)
			bestMatch = Match();
		if(!bestMatch.isNull()) {
			TQPtrList<KatapultAction> itemActions = ActionRegistry::self()->actionsForItem(bestMatch.item());
			action = itemActions.at(0);
			connect(bestMatch.item(), TQ_SIGNAL(itemChanged()), this, TQ_SLOT(updateDisplay()));
		}
	}
	if(!(allStatus & S_HasResults) && allStatus & S_Active) {
			// no results
		switch(settings->noResultsAction()) {
			case KatapultSettings::NR_HideDisplay:
				hideTimer->start(settings->noResultsDelay(), true);
				break;
			case KatapultSettings::NR_ClearQuery:
				clearTimer->start(settings->noResultsDelay(), true);
				break;
			default:
				break;
		}
	} else {
			//stop timers if a catalog has suddenly realized it
			//does accept the input after all.
		if (hideTimer->isActive())
			hideTimer->stop();
		if (clearTimer->isActive())
			clearTimer->stop();
	}
	
	if(!executing && settings->isAutoExecute() && allStatus & S_HasResults && !(allStatus & S_Multiple)) {
		execute();
	}
	
	updateDisplay();
}

void Katapult::showAboutDialog()
{
	TDEAboutApplication *aboutDialog = new TDEAboutApplication(this);
	aboutDialog->exec();
	delete aboutDialog;
}

void Katapult::updateDisplay()
{
	if(display != 0 && !executing)
	{
		display->setItem(bestMatch.item());
		if(bestMatch.isNull()) {
			display->setAction(0);
			display->setSelected(0);
		} else {
			display->setAction(action);
			display->setSelected(bestMatch.matched());
		}
		display->setStatus(allStatus);
		display->update();
	}
}

void Katapult::showLauncher()
{
	if(!settings->isConfiguring() && display != 0)
	{
		setQuery("");
		display->setStatus(0);
		display->setQuery("");
		display->show();
		display->update();
		KWin::forceActiveWindow(display->winId());
	}
}

void Katapult::hideLauncher()
{
	if(display != 0)
		display->hide();
	setQuery("");
}

void Katapult::showShortcutsDialog()
{
	KKeyDialog::configure(actions);
}

void Katapult::showGlobalShortcutsDialog()
{
	KKeyDialog::configure(globalAccel);
}

void Katapult::execute()
{
	executing = true;
	if(action == 0)
	{
		display->setStatus(S_Active | S_NoResults);
		display->update();
		hideTimer->start(settings->hideDelay(), true);
	} else {
		completeQuery();
		if(!bestMatch.isNull())
			action->execute(bestMatch.item());
		hideTimer->start(settings->hideDelay(), true);
	}
	executing = false;
}

void Katapult::updateSystray()
{
	if(settings->systrayIcon())
		show();
	else
		hide();
}

void Katapult::showContextMenu()
{
	contextMenu()->popup(TQCursor::pos());
}

void Katapult::keyReleased(TQKeyEvent *e)
{
	if(e->key() == Key_BackSpace) {
		if(hideTimer->isActive())
			hideTimer->stop();
		if(clearTimer->isActive())
			clearTimer->stop();
		TQString newQuery = _query.left(_query.length()-1);
		setQuery("");
		setQuery(newQuery);
	} else {
		TQString t = e->text();
		if ( !t.isEmpty() && (!e->ascii() || e->ascii()>=32) &&
				    e->key() != Key_Delete) 
			setQuery(_query + t);
	}
}

void Katapult::mousePressEvent(TQMouseEvent *e)
{
	if(e->button() == TQt::LeftButton)
		showLauncher();
	else
		KSystemTray::mousePressEvent(e);
}

#include "katapult.moc"
