/*
	extras.cpp - Extras
	Copyright (C) 2004  Konrad Twardowski <kdtonline@poczta.onet.pl>

	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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <sys/types.h>

#include "extras.h"
#include "miscutils.h"
#include "mmainwindow.h"

#include <tqdir.h>

#include <kdebug.h>
#include <tdedesktopfile.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <kmimetype.h>
#include <tdepopupmenu.h>
#include <kpushbutton.h>
#include <krun.h>
#include <tdestandarddirs.h>
#include <kstringhandler.h>

Extras *Extras::_instance = 0;

// public

Extras::~Extras()
{
	if (_idToFileMap)
		delete _idToFileMap;
	if (_menu)
		delete _menu;
}

void Extras::createButton(TQWidget *parent)
{
	_button = new KPushButton(parent, "KPushButton::_button");
	_button->hide();
	_button->setPopup(_menu);
	MiscUtils::setHint(_button, i18n("More actions..."));
	setAction(TQString::null, TQString::null); /* evil */
}

bool Extras::execAction() const
{
	if (_fileToExecute.isNull())
		return false; // error

	if (!execFile())
	{
		MiscUtils::showRunErrorMessage(_fileToExecute);

		return false; // error
	}

	return true; // ok
}

TQString Extras::getActionDescription() const
{
	return
		_description.isEmpty()
			? TQString::null
			// "Substitute characters at the middle of a string by \"...\""
			: KStringHandler::csqueeze(_description, 100);
}

void Extras::setAction(const TQString &fileToExecute, const TQString &description, TQIconSet *icon)
{
	_description = description;
	_fileToExecute = fileToExecute;
	if (_fileToExecute.isNull())
	{
		_button->setText(i18n("Select a command..."));
		_button->setIconSet(ks_actions->getIcon(Action::Extras));
	}
	else
	{
		_button->setText(getActionDescription());
		if (!icon)
			_button->setIconSet(ks_actions->getIcon(Action::Extras));
		else
			_button->setIconSet(*icon);
	}
}

// private

Extras::Extras()
	: TQObject(ks_main, "Extras"),
	_idToFileMap(0) // initialized in getIdToFileMap()
{
	_menu = new TDEPopupMenu(0, "TDEPopupMenu::_menu");
	connect(_menu, TQ_SIGNAL(aboutToShow()), TQ_SLOT(slotShowMenu()));
	connect(_menu, TQ_SIGNAL(activated(int)), TQ_SLOT(slotActivated(int)));
}

// TODO: 2.0: recent items
void Extras::buildMenu(TDEPopupMenu *parentMenu, const TQString &subdir)
{
	int id;
	TQDir d(subdir);
	TQFileInfo fi;
	TQString
		description,
		icon,
		mimeType,
		name,
		path;

	TQStringList list = d.entryList(TQDir::DefaultFilter, TQDir::Name | TQDir::DirsFirst);
	list.remove(".");
	list.remove("..");

	uint count = list.count();
	for (uint i = 0; i < count; i++)
	{
		fi.setFile(d, list[i]);
		path = fi.filePath();

		// dir
		if (fi.isDir())
		{
			TDEPopupMenu *submenu = new TDEPopupMenu(parentMenu, "TDEPopupMenu::submenu");
			connect(submenu, TQ_SIGNAL(activated(int)), TQ_SLOT(slotActivated(int)));

			TDEDesktopFile *desktopFile = new TDEDesktopFile(path + "/.directory", true);
			if (desktopFile->getConfigState() != TDEConfigBase::NoAccess)
			{
				icon = desktopFile->readIcon();
				if (icon.isEmpty())
					icon = "folder";
				name = desktopFile->readName();
				if (name.isEmpty())
					name = fi.baseName();
				description = desktopFile->readComment();
				// add item
				parentMenu->insertItem(
					SmallIcon(icon),
					description.isEmpty() ? name : description,
					submenu
				);
			}
			else
			{
				// add item
				parentMenu->insertItem(SmallIcon("folder"), fi.baseName(), submenu);
			}
			delete desktopFile;

			// recurse
			buildMenu(submenu, fi.filePath());
		}
		// file
		else if (fi.isFile())
		{
			mimeType = KMimeType::findByPath(path)->name();

			if (mimeType == "application/x-trash")
				continue;

			if (mimeType != "application/x-desktop")
			{
				// add item
				id = parentMenu->insertItem(
					KMimeType::pixmapForURL(KURL::fromPathOrURL(path)),
					fi.baseName()
				);
				// map item
				getIdToFileMap()->insert(id, path);

				continue;
			}

			// application/x-desktop
			TDEDesktopFile *desktopFile = new TDEDesktopFile(path, true);
			if (desktopFile->getConfigState() != TDEConfigBase::NoAccess)
			{
				name = desktopFile->readName();
				if (name.isEmpty())
					name = desktopFile->readEntry("Exec");
				description = desktopFile->readComment();
				// add item
				id = parentMenu->insertItem(
					SmallIcon(desktopFile->readIcon()),
					description.isEmpty()
						? name
						: (description + " (" + name + ")")
				);
				// map item
				getIdToFileMap()->insert(id, path);
			}
			delete desktopFile;
		}
	}
}

bool Extras::execFile() const
{
	if (_fileToExecute.isNull())
	{
		// kdDebug() << "Extras::execFile(): No file to execute" << endl;

		return false;
	}


	// HACK: It seems that KRun::runURL sometimes does not work for hibernate :/ ?
	TQFileInfo fi(_fileToExecute);
	if (fi.fileName() == "hibernate.desktop") {
		kdDebug() << "Extras::execFile(): Using hacked run..." << endl;

		pid_t pid = KRun::runCommand("kfmclient exec \"" + _fileToExecute + "\"");
		
		return pid;	
	}
	else {	
		pid_t pid = KRun::runURL(
			KURL::fromPathOrURL(_fileToExecute),
			KMimeType::findByPath(_fileToExecute)->name()
		);
		
		return pid;
	}
}

TQMap<int, TQString> *Extras::getIdToFileMap()
{
	if (!_idToFileMap)
		_idToFileMap = new TQMap<int, TQString>;

	return _idToFileMap;
}

// public slots

void Extras::slotModify()
{
	KMessageBox::information(
		0,
		MiscUtils::HTML(
			i18n("Use context menu to add/edit/remove links.") +
			"<ul>" +
				"<li>" + i18n("Use <b>Context Menu</b> to create a new link to application") + "</li>" +
				"<li>" + i18n("Use <b>Create New|Folder...</b> to create a new submenu") + "</li>" +
				"<li>" + i18n("Use <b>Properties</b> to change icon, name, or comment") + "</li>" +
			"</ul>"
		),
		i18n("Extras"), // title
		"ModifyExtras" // config key
	);

	// launch Konqueror as an item editor
	MiscUtils::runCommand("konqueror \"" + TDEGlobal::dirs()->saveLocation("data", "kshutdown/extras") + "\"");
}

// private slots

void Extras::slotActivated(int id)
{
	TQMap<int, TQString>::iterator i = getIdToFileMap()->find(id);
	if (i == getIdToFileMap()->end())
	{
		setAction(TQString::null, TQString::null);
		KMessageBox::error(
			0,
			"Internal error!\nSelected menu item is broken.",
			i18n("Extras")
		);
	}
	else
	{
		setAction(i.data(), _menu->text(id), _menu->iconSet(id));
	}
}

void Extras::slotShowMenu()
{
	setAction(TQString::null, TQString::null);
	_menu->clear(); // reset menu
	getIdToFileMap()->clear(); // reset map
	TQStringList extrasDirs(TDEGlobal::dirs()->findDirs("data", "kshutdown/extras"));
	TQStringList::ConstIterator
		begin = extrasDirs.begin(),
		end = extrasDirs.end();
	for (TQStringList::ConstIterator it = begin; it != end; ++it)
	{
		if ((it != begin) && (_menu->count() > 0))
			_menu->insertSeparator();
		buildMenu(_menu, *it);
	}
}
#include "extras.moc"
