/* systray.c
*
* Systray support for the gui
*
* By Axel C. <tautologist@users.sourceforge.net>
*
*	TODO:
*		- add a proper icon for the systray widget.
*		- doesn't look extremely well. Perhaps try a different layout.
*		- KDE support (kde panel bug?)
*
*/

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

#include <gtk/gtkversion.h>
#include <gtk/gtkwidget.h>

#undef HAVE_SLIDER

#if (((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2)) || defined(GUI_DISABLE_SYSTRAY))

/* No systray stuff possible in gtk+-2.0, because that stuff
 *  depends on gdk-stuff which is only in 2.2,or systray
 *  disabled for other reasons (probably because X headers
 *  not found or MacOSX system or manuall disabled via
 *  configure switch) */

int          gui_systray_init       (GtkWidget *mainwindow)                   { return 0; }
void         gui_systray_set_status (const char *network, const char *server) { return; }

#else

#include "core-conn.h"
#include "global.h"
#include "eggtrayicon.h"
#include "misc.h"
#include "misc_strings.h"
#include "icons.h"
#include "options.h"

#include "systray.h"

#include <string.h>

#include <gtk/gtkeventbox.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkframe.h>
#include <gtk/gtkimage.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkvscale.h>
#include <gtk/gtktooltips.h>

/***************  systray code for gtk+ >= 2.2 follows *****************************************************/



static GtkWidget     *gui_tray_icon = NULL;               /* will carbonated beverage beat me for this? */
static GtkWidget     *gui_tray_icon_speed_up = NULL;      /* up speed label */
static GtkWidget     *gui_tray_icon_speed_down = NULL;    /* down speed label */
static GtkTooltips   *gui_tray_icon_tooltips = NULL;

#ifdef HAVE_SLIDER
static GtkWidget     *gui_tray_speed_window = NULL;
static GtkWidget     *gui_tray_speed_scale;
#endif

static gboolean       gui_tray_icon_borked = TRUE;        /* If TRUE, all systray calls will be foo */

#ifdef HAVE_SLIDER
static GtkWidget     *gui_systray_create_speed_window (void);
#endif

static GtkWidget     *systray_mainwindow; /* NULL */ /* need to save this to be able to re-instate the systray on kde bork */


/***************************************************************************
 *
 *   onTimeoutTryToFixBorkedSystray
 *
 ***************************************************************************/

static gboolean
onTimeoutTryToFixBorkedSystray (gpointer baz)
{
	g_print ("Trying to re-instate systray, probably due to borked KDE systray panel.\n");

	(void) gui_systray_init(systray_mainwindow);

	return FALSE;
}


/***************************************************************************
 *
 *   gui_tray_icon_onDestroy
 *
 ***************************************************************************/

static void
gui_tray_icon_onDestroy (GtkWidget *icon, gpointer bah)
{
	g_print ("%s\n", __FUNCTION__);

	gui_tray_icon_borked = TRUE;

	/* the KDE systray panel seems to be broken; our systray icon will
	 *  be destroyed when I exit another application that uses the
	 *  systray, for example PSI, or sometimes just without any
	 *  apparent reason. We'll have to work around that. If we haven't
	 *  quit by then, our timeout will be run in 10 seconds and re-instate
	 *  the syspanel. */

	g_timeout_add(5*1000, (GSourceFunc) onTimeoutTryToFixBorkedSystray, NULL);
}


/***************************************************************************
 *
 *   get_options_delayed
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static gboolean
get_options_delayed (gpointer data)
{
	gui_core_conn_send_get_options(core);

	return FALSE; /* don't call again */
}

#endif

/***************************************************************************
 *
 *   gui_tray_icon_onSpeedWindowValueChangedTimeout
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static gboolean
gui_tray_icon_onSpeedWindowValueChangedTimeout (guint *handler)
{
	gchar	*core_command;

	core_command = g_strdup_printf ("dumax %u %u", (guint) gtk_range_get_value (GTK_RANGE(gui_tray_speed_scale)),
	                                                gui_core_conn_get_core_options(core)->max_ul_speed);

	gui_core_conn_send_command(core, core_command);

	gtk_widget_hide(GTK_WIDGET(g_object_get_data(G_OBJECT(gui_tray_speed_scale), "SliderWindow")));

	/* It's not clear how quickly the core updates its values, so let's get it multiple times */
	g_timeout_add(1000, get_options_delayed, NULL);
	g_timeout_add(2000, get_options_delayed, NULL);
	g_timeout_add(5000, get_options_delayed, NULL);

	if (handler)
		*handler = 0;

	g_free (core_command);

	return FALSE; /* don't call again */
}

#endif

/***************************************************************************
 *
 *   gui_tray_icon_onSpeedWindowValueChanged
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static void
gui_tray_icon_onSpeedWindowValueChanged (GtkRange *slider, gpointer baath)
{
	static guint  delay_handler;  /* 0 */  /* for event-packing - activate new value only when position looks final */

	if (delay_handler > 0)
		g_source_remove(delay_handler);

	/* If slider value does not change again within the next
	 *  1 second (1000ms), set new max. ul/dl speed values */
	delay_handler = g_timeout_add(1000, (GSourceFunc)gui_tray_icon_onSpeedWindowValueChangedTimeout, &delay_handler);
}

#endif

/***************************************************************************
 *
 *   gui_tray_icon_onSpeedWindowFormatValue
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static gchar *
gui_tray_icon_onSpeedWindowFormatValue (GtkScale *scale, gdouble val, gpointer data)
{
	if (val == 0.0)
		return g_strdup("unlimited");

	return g_strdup_printf("%.0f", val);
}

#endif

/***************************************************************************
 *
 *   gui_tray_icon_popup_slider
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static void
gui_tray_icon_popup_slider (GdkEventButton *bevent)
{
	GtkRequisition  req;
	GtkWidget      *win;
	gint            x,y, width, height;

	if (GTK_WIDGET_VISIBLE (gui_tray_speed_window))
	{
		gtk_widget_hide (gui_tray_speed_window);
		return;
	}

	g_signal_handlers_block_by_func(gui_tray_speed_scale, gui_tray_icon_onSpeedWindowValueChanged, NULL);

	gtk_range_set_range (GTK_RANGE (gui_tray_speed_scale), 0.0, 256.0);
	gtk_range_set_value (GTK_RANGE (gui_tray_speed_scale), (gdouble) gui_core_conn_get_core_options(core)->max_dl_speed);

	g_signal_handlers_unblock_by_func(gui_tray_speed_scale, gui_tray_icon_onSpeedWindowValueChanged, NULL);

	win = GTK_WIDGET(g_object_get_data(G_OBJECT(gui_tray_speed_scale), "SliderWindow"));
	g_assert(GTK_IS_WINDOW(win));

	/* position the slider */
	gtk_widget_size_request (win, &req);
	gdk_window_get_origin (gui_tray_icon->window, &x, &y);
	gdk_drawable_get_size (gui_tray_icon->window, &width, &height);

	x += (width - req.width) / 2;
	y -= req.height + 1;

	if (x < 0)
		x = 0;
	if (y < 0)
		y = 0;

	gtk_window_move (GTK_WINDOW (win), x + width/2, y + height/2);

	gtk_widget_grab_focus (gui_tray_speed_window);
	gtk_widget_show_all (gui_tray_speed_window);
}

#endif /* HAVE_SLIDER */

/***************************************************************************
 *
 *   gui_tray_icon_onButtonPress
 *
 ***************************************************************************/

static void
gui_tray_icon_onButtonPress (GtkWidget *tray, GdkEventButton *ev, GtkWidget *mainwindow)
{

	if (ev->type == GDK_BUTTON_PRESS)
	{

		switch (ev->button)
		{
#ifdef HAVE_SLIDER
			case 3:
			{
				gui_tray_icon_popup_slider(ev);
				return;
			}
#endif

			case 1:
			{
#ifdef HAVE_SLIDER
				if (GTK_WIDGET_VISIBLE (gui_tray_speed_window))
				{
					gtk_widget_hide (gui_tray_speed_window);
					return;
				}
#endif

				if (GTK_WIDGET_VISIBLE (mainwindow))
				{
					gtk_window_iconify (GTK_WINDOW (mainwindow));
					gtk_widget_hide (mainwindow);
					return;
				}
				else
				{
					/* some window managers seem to pop-up the window
					 *  at a different position on un-hide (at least
					 *  openbox does) */
					gtk_window_move (GTK_WINDOW (mainwindow),
					                 opt_get_int (OPT_GUI_WINDOW_POS_X),
					                 opt_get_int (OPT_GUI_WINDOW_POS_Y));
					gtk_widget_show (mainwindow);
					gtk_window_deiconify (GTK_WINDOW (mainwindow));
					return;
				}
			}

			default : break;

		}

	}
}


/******************************************************************************
 *
 *   onCoreConnStatus
 *
 ******************************************************************************/

static void
onCoreConnStatus (GuiCoreConn *conn, guint status, gpointer data)
{
	gchar *buf = NULL;

	if (gui_tray_icon_borked)
		return;

	if (status != CONN_STATUS_COMMUNICATING  &&  status != CONN_STATUS_AUTHENTICATING)
		buf = g_strdup_printf ("ed2k: %s", _(" not connected to a core yet "));

	else if (status == CONN_STATUS_AUTHENTICATING)
		buf = g_strdup_printf ("ed2k: %s", _(" Not connected to a server. "));

	if ((buf) && (gui_tray_icon_tooltips))
		gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, buf, "");

	G_FREE(buf);
}

/***************************************************************************
 *
 *   onCoreSpeedTotal
 *
 *   Signal handler for the "speed-total" signal emitted by the
 *    GuiCoreConn object after it has received a new upload/download
 *    status message pair from the core
 *
 ***************************************************************************/

static void
onCoreSpeedTotal (GuiCoreConn *conn, gfloat dlspeed, gfloat ulspeed, gpointer data)
{
	gchar	 buf[64];

	if (gui_tray_icon_borked == TRUE || !gui_tray_icon_speed_down || !gui_tray_icon_speed_up)
		return;

	g_snprintf (buf, sizeof(buf), "<span size='small'>%3.1f</span>", dlspeed);
	gtk_label_set_markup (GTK_LABEL(gui_tray_icon_speed_down), buf);

	g_snprintf (buf, sizeof(buf), "<span size='small'>%3.1f</span>", ulspeed);
	gtk_label_set_markup (GTK_LABEL(gui_tray_icon_speed_up), buf);
}


/***************************************************************************
 *
 *   onServerConnected
 *
 ***************************************************************************/

#define SYSTRAY_MAX_SRV_NAME_LEN 20

static void
onServerConnected (GuiCoreConn *conn, const gchar *name, gpointer data)
{
	gchar *cleanname;

	if (gui_tray_icon_borked == TRUE || !gui_tray_icon_tooltips || !gui_tray_icon)
		return;

	/* is it really a servername? (don't want to cleanse our own '[bla]' strings */
	cleanname = misc_remove_junk_from_servername(name);

	if ((cleanname) && strlen(cleanname) > SYSTRAY_MAX_SRV_NAME_LEN+1)
	{
		cleanname[SYSTRAY_MAX_SRV_NAME_LEN]='>';
		cleanname[SYSTRAY_MAX_SRV_NAME_LEN+1]=0x00;
	}

	gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon,
	                      UTF8_SHORT_PRINTF("ed2k: %s", (cleanname!=NULL) ? cleanname : name), "");

	G_FREE(cleanname);
}

/***************************************************************************
 *
 *   onServerDisconnected
 *
 ***************************************************************************/

static void
onServerDisconnected (GuiCoreConn *conn, gpointer data)
{
	if (gui_tray_icon_borked == TRUE || !gui_tray_icon_tooltips || !gui_tray_icon)
		return;

	gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, UTF8_SHORT_PRINTF("ed2k: %s", _("Not connected to a server")), "");
}


/***************************************************************************
 *
 *   gui_systray_set_status
 *
 ***************************************************************************/

void
gui_systray_set_status (const char *network, const char *server)
{
	gchar	*buf;

	if (gui_tray_icon_borked == TRUE)
		return;

	buf = g_strdup_printf ("%s: %s", network, server);

	if (gui_tray_icon_tooltips)
		gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, buf, "");

	g_free (buf);
}

/***************************************************************************
 *
 *   onStatusMessage
 *
 ***************************************************************************/

static void
onStatusMessage (GuiCoreConn *conn, const gchar *msg, gpointer data)
{
	gchar	*buf;

	if (gui_tray_icon_borked || !gui_tray_icon_tooltips)
		return;

	if (g_ascii_strncasecmp(msg,"Connecting to",12) == 0)
	{
		buf = g_strdup_printf ("ed2k: %s", _(" [connecting] "));
		gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, buf, "");
		g_free (buf);
	}
	else if (g_ascii_strncasecmp(msg,"Placed on connection Queue",25) == 0)
	{
		buf = g_strdup_printf ("ed2k: %s", _(" [on connection queue] "));
		gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, buf, "");
		g_free (buf);
	}
}

/******************************************************************************
 *
 *   onHybridStatus
 *
 ******************************************************************************/

static void
onHybridStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gpointer data)
{
	if (gui_tray_icon_borked || !gui_tray_icon_tooltips)
		return;

	gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, "hybrid", "");
}

/******************************************************************************
 *
 *   onOvernetStatus
 *
 ******************************************************************************/

static void
onOvernetStatus (GuiCoreConn *conn, const gchar *idstr, const gchar *fwstatus, gpointer data)
{
	if (gui_tray_icon_borked || !gui_tray_icon_tooltips)
		return;

	gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, "overnet", "");
}

/***************************************************************************
 *
 *   gui_systray_init
 *
 ***************************************************************************/

int
gui_systray_init (GtkWidget *mainwindow)
{
	GdkPixbuf   *pixbuf;
	GtkWidget	  *frame;
	GtkWidget   *hbox;
	GtkWidget   *speed_vbox;

	GtkWidget   *down_hbox, *down_img;
	GtkWidget   *up_hbox, *up_img;

	GtkWidget   *evbox;

	systray_mainwindow = mainwindow;

	if (!opt_get_bool(OPT_GUI_USE_APPLET_IF_SUPPORTED))
	{
		gui_tray_icon_borked = TRUE;
		return -1;
	}

	gui_tray_icon = GTK_WIDGET (egg_tray_icon_new ("eDonkey2000"));

	if (!gui_tray_icon)
	{
		g_print (_("*** WARNING *** Unable to create systray icon. Icon will not be usable\n"));
		gui_tray_icon_borked = TRUE;
		return -1;
	}

	frame = gtk_frame_new (NULL);
	hbox = gtk_hbox_new (FALSE, 0);
	speed_vbox = gtk_hbox_new (FALSE, 2);

	pixbuf = gdk_pixbuf_scale_simple (get_icon(ICON_TAB_DOWNLOADS), 16, 16, GDK_INTERP_BILINEAR);
	down_img = gtk_image_new_from_pixbuf (pixbuf);
	g_object_unref(pixbuf);

	pixbuf = gdk_pixbuf_scale_simple (get_icon(ICON_TAB_UPLOADS), 16, 16, GDK_INTERP_BILINEAR);
	up_img = gtk_image_new_from_pixbuf (pixbuf);
	g_object_unref(pixbuf);

	gui_tray_icon_speed_up = gtk_label_new ("");
	gtk_label_set_use_markup (GTK_LABEL (gui_tray_icon_speed_up), TRUE);
	gui_tray_icon_speed_down = gtk_label_new ("");
	gtk_label_set_use_markup (GTK_LABEL (gui_tray_icon_speed_down), TRUE);


	down_hbox = gtk_hbox_new (FALSE, 0);
	up_hbox = gtk_hbox_new (FALSE, 0);

	gtk_box_pack_start (GTK_BOX (down_hbox), down_img, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (down_hbox), gui_tray_icon_speed_down, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (up_hbox), up_img, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (up_hbox), gui_tray_icon_speed_up, TRUE, TRUE, 0);



	gtk_box_pack_start (GTK_BOX (speed_vbox), down_hbox, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (speed_vbox), up_hbox, FALSE, FALSE, 0);

	evbox = gtk_event_box_new ();

	gtk_container_add (GTK_CONTAINER (gui_tray_icon), evbox);
	gtk_container_add (GTK_CONTAINER (evbox), frame);
	gtk_container_add (GTK_CONTAINER (frame), hbox);

	gtk_box_pack_start (GTK_BOX (hbox), speed_vbox, TRUE, TRUE, 2);

	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);

	gtk_widget_show_all (gui_tray_icon);

	gtk_widget_realize (gui_tray_icon);
	gtk_widget_show (gui_tray_icon);

	gui_tray_icon_tooltips = gtk_tooltips_new ();
	gtk_tooltips_enable (gui_tray_icon_tooltips);

	gtk_tooltips_set_tip (gui_tray_icon_tooltips, gui_tray_icon, "eDonkey2000", "");

#ifdef HAVE_SLIDER
	gui_tray_speed_window = gui_systray_create_speed_window ();
#endif

	gui_tray_icon_borked = FALSE;

	g_signal_connect (G_OBJECT (gui_tray_icon), "destroy", G_CALLBACK (gui_tray_icon_onDestroy), NULL);
	g_signal_connect (G_OBJECT (evbox), "button_press_event", G_CALLBACK (gui_tray_icon_onButtonPress), mainwindow);

	g_signal_connect(core, "speed-total",         (GCallback) onCoreSpeedTotal, NULL);
	g_signal_connect(core, "server-connected",    (GCallback) onServerConnected, NULL);
	g_signal_connect(core, "server-disconnected", (GCallback) onServerDisconnected, NULL);
	g_signal_connect(core, "status-message",      (GCallback) onStatusMessage, NULL);
	g_signal_connect(core, "overnet-status",      (GCallback) onOvernetStatus, NULL);
	g_signal_connect(core, "hybrid-status",       (GCallback) onHybridStatus, NULL);
	g_signal_connect(core, "core-conn-status",    (GCallback) onCoreConnStatus,NULL);

	return 0;
}


/***************************************************************************
 *
 *   gui_systray_create_speed_window
 *
 ***************************************************************************/

#ifdef HAVE_SLIDER

static GtkWidget *
gui_systray_create_speed_window (void)
{
	GtkWidget  *win;
	GtkWidget  *frame;

	win = gtk_window_new (GTK_WINDOW_POPUP);
	gtk_window_set_transient_for (GTK_WINDOW (win), GTK_WINDOW (gui_tray_icon));
	gtk_widget_set_size_request (GTK_WIDGET (win), -1, 100);

	frame = gtk_frame_new (NULL);
	gtk_container_add (GTK_CONTAINER (win), frame);
	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);

	gui_tray_speed_scale = gtk_vscale_new_with_range (0.0, (gfloat) gui_core_conn_get_core_options(core)->max_dl_speed + 0.001, 1.0);
	gtk_range_set_inverted (GTK_RANGE (gui_tray_speed_scale), TRUE);
	g_signal_connect (G_OBJECT (gui_tray_speed_scale), "value-changed", G_CALLBACK (gui_tray_icon_onSpeedWindowValueChanged), NULL);
	g_signal_connect (G_OBJECT (gui_tray_speed_scale), "format-value", G_CALLBACK (gui_tray_icon_onSpeedWindowFormatValue), NULL);

	g_object_set_data(G_OBJECT(gui_tray_speed_scale), "SliderWindow", win);

	gtk_widget_show (gui_tray_speed_scale);

	gtk_container_add (GTK_CONTAINER (frame), gui_tray_speed_scale);

	gtk_widget_hide (win);

	return win;
}

#endif /* HAVE_SLIDER */


#endif /* if (((GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION < 2)) || defined(GUI_DISABLE_SYSTRAY))  ... else ... */




