/***************************************************************************
 *   Copyright (C) 2003 by S�astien Laot                                 *
 *   slaout@linux62.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.                                   *
 *                                                                         *
 *   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.             *
 ***************************************************************************/

/** KSystemTray2 */

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

// To draw the systray screenshot image:
#include <tqdockwindow.h>
#include <tqmovie.h>
#include <tqvariant.h>
#include "linklabel.h"
#include "note.h"

#include <tqdesktopwidget.h>
#include <tqmime.h>
#include <tqpainter.h>
#include <tqpoint.h>
#include <tqpixmap.h>
// To know the program name:
#include <tdeglobal.h>
#include <kinstance.h>
#include <tdeaboutdata.h>
#include <kiconeffect.h>
// Others:
#include <tdemessagebox.h>
#include <kmanagerselection.h>
#include <tdeversion.h>
#include <tdeapplication.h>
#include <tdepopupmenu.h>
#include <kiconloader.h>
#include <kdebug.h>
#include "systemtray.h"
#include "basket.h"
#include "settings.h"
#include "global.h"
#include "tools.h"

KSystemTray2::KSystemTray2(TQWidget *parent, const char *name)
 : KSystemTray(parent, name)
{
}

KSystemTray2::~KSystemTray2()
{
}

void KSystemTray2::displayCloseMessage(TQString fileMenu)
{
	/* IDEAS OF IMPROVEMENTS:
	*  - Use queuedMessageBox() but it need a dontAskAgainName parameter
	*    and image in TQMimeSourceFactory shouldn't be removed.
	*  - Sometimes the systray icon is covered (a passive popup...)
	*    Use XComposite extension, if available, to get the kicker pixmap.
	*  - Perhapse desaturate the area around the proper SysTray icon,
	*    helping bring it into sharper focus. Or draw the cicle with XOR
	*    brush.
	*  - Perhapse add the icon in the text (eg. "... in the
	*    system tray ([icon])."). Add some clutter to the dialog.
	*/
#if KDE_IS_VERSION( 3, 1, 90 )
	// Don't do all the computations if they are unneeded:
	if ( ! KMessageBox::shouldBeShownContinue("hideOnCloseInfo") )
		return;
#endif
	// "Default parameter". Here, to avoid a i18n() call and dependancy in the .h
	if (fileMenu.isEmpty())
		fileMenu = i18n("File");

	// Some values we need:
	TQPoint g = mapToGlobal(pos());
	int desktopWidth  = kapp->desktop()->width();
	int desktopHeight = kapp->desktop()->height();
	int tw = width();
	int th = height();

	// We are triying to make a live screenshot of the systray icon to circle it
	//  and show it to the user. If no systray is used or if the icon is not visible,
	//  we should not show that screenshot but only a text!

	// 1. Determine if the user use a system tray area or not:
	TQCString screenstr;
	screenstr.setNum(tqt_xscreen());
	TQCString trayatom = "_NET_SYSTEM_TRAY_S" + screenstr;
	bool useSystray = (TDESelectionWatcher(trayatom.data()).owner() != 0L);

	// 2. And then if the icon is visible too (eg. this->show() has been called):
	useSystray = useSystray && isVisible();

	// 3. Kicker (or another systray manager) can be visible but masked out of
	//    the screen (ie. on right or on left of it). We check if the icon isn't
	//    out of screen.
	if (useSystray) {
		TQRect deskRect(0, 0, desktopWidth, desktopHeight);
		if ( !deskRect.contains(g.x(), g.y()) ||
		     !deskRect.contains(g.x() + tw, g.y() + th) )
			useSystray = false;
	}

	// 4. We raise the window containing the systray icon (typically the kicker) to
	//    have the most chances it is visible during the capture:
/*	if (useSystray) {
		// We are testing if one of the corners is hidden, and if yes, we would enter
		// a time consuming process (raise kicker and wait some time):
//		if (kapp->widgetAt(g) != this ||
//		    kapp->widgetAt(g + TQPoint(tw-1, 0)) != this ||
//		    kapp->widgetAt(g + TQPoint(0, th-1)) != this ||
//		    kapp->widgetAt(g + TQPoint(tw-1, th-1)) != this) {
			int systrayManagerWinId = topLevelWidget()->winId();
			KWin::forceActiveWindow(systrayManagerWinId);
			kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
//			KWin::activateWindow(systrayManagerWinId);
//			kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
//				KWin::raiseWindow(systrayManagerWinId);
//			kapp->processEvents(); // Because without it the systrayManager is raised only after the messageBox is displayed
			sleep(1);
			// TODO: Re-verify that at least one corner is now visible
//		}
	}*/

//	KMessageBox::information(this, TQString::number(g.x()) + ":" + TQString::number(g.y()) + ":" +
//	                         TQString::number((int)(kapp->widgetAt(g+TQPoint(1,1)))));

	TQString message = i18n(
		"<p>Closing the main window will keep %1 running in the system tray. "
		"Use <b>Quit</b> from the <b>Basket</b> menu to quit the application.</p>"
			).arg(TDEGlobal::instance()->aboutData()->programName());
	// We are sure the systray icon is visible: ouf!
	if (useSystray) {
		// Compute size and position of the pixmap to be grabbed:
		int w = desktopWidth / 4;
		int h = desktopHeight / 9;
		int x = g.x() + tw/2 - w/2; // Center the rectange in the systray icon
		int y = g.y() + th/2 - h/2;
		if (x < 0)                 x = 0; // Move the rectangle to stay in the desktop limits
		if (y < 0)                 y = 0;
		if (x + w > desktopWidth)  x = desktopWidth - w;
		if (y + h > desktopHeight) y = desktopHeight - h;

		// Grab the desktop and draw a circle arround the icon:
		TQPixmap shot = TQPixmap::grabWindow(tqt_xrootwin(), x, y, w, h);
		TQPainter painter(&shot);
		const int CIRCLE_MARGINS = 6;
		const int CIRCLE_WIDTH   = 3;
		const int SHADOW_OFFSET  = 1;
		const int IMAGE_BORDER   = 1;
		int ax = g.x() - x - CIRCLE_MARGINS - 1;
		int ay = g.y() - y - CIRCLE_MARGINS - 1;
		painter.setPen( TQPen(TDEApplication::palette().active().dark(), CIRCLE_WIDTH) );
		painter.drawArc(ax + SHADOW_OFFSET, ay + SHADOW_OFFSET,
		                tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
		painter.setPen( TQPen(TQt::red/*TDEApplication::palette().active().highlight()*/, CIRCLE_WIDTH) );
		painter.drawArc(ax, ay, tw + 2*CIRCLE_MARGINS, th + 2*CIRCLE_MARGINS, 0, 16*360);
#if 1
		// Draw the pixmap over the screenshot in case a window hide the icon:
		painter.drawPixmap(g.x() - x, g.y() - y + 1, *pixmap());
#endif
		painter.end();

		// Then, we add a border arround the image to make it more visible:
		TQPixmap finalShot(w + 2*IMAGE_BORDER, h + 2*IMAGE_BORDER);
		finalShot.fill(TDEApplication::palette().active().foreground());
		painter.begin(&finalShot);
		painter.drawPixmap(IMAGE_BORDER, IMAGE_BORDER, shot);
		painter.end();

		// Associate source to image and show the dialog:
		TQMimeSourceFactory::defaultFactory()->setPixmap("systray_shot", finalShot);
		KMessageBox::information(TQT_TQWIDGET(kapp->activeWindow()),
			message + "<p><center><img source=\"systray_shot\"></center></p>",
			i18n("Docking in System Tray"), "hideOnCloseInfo");
		TQMimeSourceFactory::defaultFactory()->setData("systray_shot", 0L);
	} else {
		KMessageBox::information(TQT_TQWIDGET(kapp->activeWindow()),
			message,
			i18n("Docking in System Tray"), "hideOnCloseInfo");
	}
}

/** SystemTray */

SystemTray::SystemTray(TQWidget *parent, const char *name)
 : KSystemTray2(parent, name != 0 ? name : "SystemTray"), m_showTimer(0), m_autoShowTimer(0)
{
	setAcceptDrops(true);

	m_showTimer = new TQTimer(this);
	connect( m_showTimer, TQT_SIGNAL(timeout()), Global::bnpView, TQT_SLOT(setActive()) );

	m_autoShowTimer = new TQTimer(this);
	connect( m_autoShowTimer, TQT_SIGNAL(timeout()), Global::bnpView, TQT_SLOT(setActive()) );

	// Create pixmaps for the icon:
	m_iconPixmap              = loadIcon("basket");
//	FIXME: When main window is shown at start, the icon is loaded 1 pixel too high
//	       and then reloaded instantly after at the right position.
//	setPixmap(m_iconPixmap); // Load it the sooner as possible to avoid flicker
	TQImage  lockedIconImage   = m_iconPixmap.convertToImage();
	TQPixmap lockOverlayPixmap = loadIcon("lockoverlay");
	TQImage  lockOverlayImage  = lockOverlayPixmap.convertToImage();
	TDEIconEffect::overlay(lockedIconImage, lockOverlayImage);
	m_lockedIconPixmap.convertFromImage(lockedIconImage);

	updateToolTip(); // Set toolTip AND icon
}

SystemTray::~SystemTray()
{
}

void SystemTray::mousePressEvent(TQMouseEvent *event)
{
	if (event->button() & Qt::LeftButton) {          // Prepare drag
		m_pressPos = event->globalPos();
		m_canDrag  = true;
		event->accept();
	} else if (event->button() & Qt::MidButton) {    // Paste
		Global::bnpView->currentBasket()->setInsertPopupMenu();
		Global::bnpView->currentBasket()->pasteNote(TQClipboard::Selection);
		Global::bnpView->currentBasket()->cancelInsertPopupMenu();
		if (Settings::usePassivePopup())
			Global::bnpView->showPassiveDropped(i18n("Pasted selection to basket <i>%1</i>"));
		event->accept();
	} else if (event->button() & Qt::RightButton) { // Popup menu
		TDEPopupMenu menu(this);
		menu.insertTitle( SmallIcon("basket"), kapp->aboutData()->programName() );

		Global::bnpView->actNewBasket->plug(&menu);
		Global::bnpView->actNewSubBasket->plug(&menu);
		Global::bnpView->actNewSiblingBasket->plug(&menu);
		menu.insertSeparator();
		Global::bnpView->m_actPaste->plug(&menu);
		Global::bnpView->m_actGrabScreenshot->plug(&menu);
		Global::bnpView->m_actColorPicker->plug(&menu);

		if(!Global::bnpView->isPart())
		{
			TDEAction* action;

			menu.insertSeparator();

			action = Global::bnpView->actionCollection()->action("options_configure_global_keybinding");
			if(action)
				action->plug(&menu);

			action = Global::bnpView->actionCollection()->action("options_configure");
			if(action)
				action->plug(&menu);

			menu.insertSeparator();

			// Minimize / restore : since we manage the popup menu by ourself, we should do that work :
			action = Global::bnpView->actionCollection()->action("minimizeRestore");
			if(action)
			{
				if (Global::mainWindow()->isVisible())
					action->setText(i18n("&Minimize"));
				else
					action->setText(i18n("&Restore"));
				action->plug(&menu);
			}

			action = Global::bnpView->actionCollection()->action("file_quit");
			if(action)
				action->plug(&menu);
		}

		Global::bnpView->currentBasket()->setInsertPopupMenu();
		connect( &menu, TQT_SIGNAL(aboutToHide()), Global::bnpView->currentBasket(), TQT_SLOT(delayedCancelInsertPopupMenu()) );
		menu.exec(event->globalPos());
		event->accept();
	} else
		event->ignore();
}

void SystemTray::mouseMoveEvent(TQMouseEvent *event)
{
	event->ignore();
}

void SystemTray::mouseReleaseEvent(TQMouseEvent *event)
{
	m_canDrag = false;
	if (event->button() == Qt::LeftButton)         // Show / hide main window
		if ( TQT_TQRECT_OBJECT(rect()).contains(event->pos()) ) {     // Accept only if released in systemTray
			toggleActive();
			emit showPart();
			event->accept();
		} else
			event->ignore();
}

void SystemTray::dragEnterEvent(TQDragEnterEvent *event)
{
	m_showTimer->start( Settings::dropTimeToShow() * 100, true );
	Global::bnpView->currentBasket()->showFrameInsertTo();
///	m_parentContainer->setStatusBarDrag(); // FIXME: move this line in Basket::showFrameInsertTo() ?
	Basket::acceptDropEvent(event);
}

void SystemTray::dragMoveEvent(TQDragMoveEvent *event)
{
	Basket::acceptDropEvent(event);
}

void SystemTray::dragLeaveEvent(TQDragLeaveEvent*)
{
	m_showTimer->stop();
	m_canDrag = false;
	Global::bnpView->currentBasket()->resetInsertTo();
	Global::bnpView->updateStatusBarHint();
}

#include <iostream>

void SystemTray::dropEvent(TQDropEvent *event)
{
	m_showTimer->stop();

	Global::bnpView->currentBasket()->blindDrop(event);

/*	Basket *basket = Global::bnpView->currentBasket();
	if (!basket->isLoaded()) {
		Global::bnpView->showPassiveLoading(basket);
		basket->load();
	}
	basket->contentsDropEvent(event);
	std::cout << (long) basket->selectedNotes() << std::endl;

	if (Settings::usePassivePopup())
		Global::bnpView->showPassiveDropped(i18n("Dropped to basket <i>%1</i>"));*/
}

/* This function comes directly from JuK: */

/*
 * This function copies the entirety of src into dest, starting in
 * dest at x and y.  This function exists because I was unable to find
 * a function like it in either TQImage or tdefx
 */
static bool copyImage(TQImage &dest, TQImage &src, int x, int y)
{
	if(dest.depth() != src.depth())
		return false;
	if((x + src.width()) >= dest.width())
		return false;
	if((y + src.height()) >= dest.height())
		return false;

	// We want to use TDEIconEffect::overlay to do this, since it handles
	// alpha, but the images need to be the same size.  We can handle that.

	TQImage large_src(dest);

	// It would perhaps be better to create large_src based on a size, but
	// this is the easiest way to make a new image with the same depth, size,
	// etc.

	large_src.detach();

	// However, we do have to specifically ensure that setAlphaBuffer is set
	// to false

	large_src.setAlphaBuffer(false);
	large_src.fill(0); // All transparent pixels
	large_src.setAlphaBuffer(true);

	int w = src.width();
	int h = src.height();
	for(int dx = 0; dx < w; dx++)
		for(int dy = 0; dy < h; dy++)
			large_src.setPixel(dx + x, dy + y, src.pixel(dx, dy));

	// Apply effect to image

	TDEIconEffect::overlay(dest, large_src);

	return true;
}

void SystemTray::updateToolTip()
{
//	return; /////////////////////////////////////////////////////

	Basket *basket = Global::bnpView->currentBasket();
	if (!basket)
		return;

	if (basket->icon().isEmpty() || basket->icon() == "basket" || ! Settings::showIconInSystray())
		setPixmap(basket->isLocked() ? m_lockedIconPixmap : m_iconPixmap);
	else {
		// Code that comes from JuK:
		TQPixmap bgPix = loadIcon("basket");
		TQPixmap fgPix = SmallIcon(basket->icon());

		TQImage bgImage = bgPix.convertToImage(); // Probably 22x22
		TQImage fgImage = fgPix.convertToImage(); // Should be 16x16
		TQImage lockOverlayImage = loadIcon("lockoverlay").convertToImage();

		TDEIconEffect::semiTransparent(bgImage);
		copyImage(bgImage, fgImage, (bgImage.width() - fgImage.width()) / 2,
				(bgImage.height() - fgImage.height()) / 2);
		if (basket->isLocked())
			TDEIconEffect::overlay(bgImage, lockOverlayImage);

		bgPix.convertFromImage(bgImage);
		setPixmap(bgPix);
	}

	//TQTimer::singleShot( Container::c_delayTooltipTime, this, TQT_SLOT(updateToolTipDelayed()) );
	// No need to delay: it's be called when notes are changed:
	updateToolTipDelayed();
}

void SystemTray::updateToolTipDelayed()
{
	Basket *basket = Global::bnpView->currentBasket();

	TQString tip = "<p><nobr>" + ( basket->isLocked() ? kapp->makeStdCaption(i18n("%1 (Locked)"))
	                                                 : kapp->makeStdCaption(     "%1")          )
	                            .arg(Tools::textToHTMLWithoutP(basket->basketName()));

	TQToolTip::add(this, tip);
}

void SystemTray::wheelEvent(TQWheelEvent *event)
{
	if (event->delta() > 0)
		Global::bnpView->goToPreviousBasket();
	else
		Global::bnpView->goToNextBasket();

	if (Settings::usePassivePopup())
		Global::bnpView->showPassiveContent();
}

void SystemTray::enterEvent(TQEvent*)
{
	if (Settings::showOnMouseIn())
		m_autoShowTimer->start(Settings::timeToShowOnMouseIn() * 100, true );
}

void SystemTray::leaveEvent(TQEvent*)
{
	m_autoShowTimer->stop();
}

#include "systemtray.moc"
