/* glunarclock.c:
 *
 * 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.
 *
 * Copyright (C) 2003-2004 Josh Buhl
 *
 * This was originally the fish.c file from the Gnome Fish Applet
 * which I've mangled into the glunarclock applet. -- Josh Buhl
 *
 * Original Copyright (C)  1998-2002 Free Software Foundation, Inc.
 *
 * Authors:
 *      George Lebl  <jirka@5z.com> (Gnome Fish Applet)
 *      Mark McLoughlin <mark@skynet.ie> (Gnome Fish Applet)
 *      Josh Buhl <jbuhl@users.sourceforge.net>
 */

#include "glunarclock.h"
#include "moondata.h"

#define UPDATE_APPLET_INTERVAL 1	/* seconds */

#define FALLBACK_MOONIMAGE_FILE "glunarclock/moon_56frames.png"
#define FALLBACK_MOONIMAGE_FRAMES 56
#define ALTERNATIVE_MOONIMAGE_FILE "glunarclock/cartoonmoon_12frames.png"
#define ALTERNATIVE_MOONIMAGE_FRAMES 12

static void update_moon(MoonApplet * moon);
static void update_image_number(MoonApplet * moon);
static void compose_image   (MoonApplet *moon);
static gboolean load_moon_image (MoonApplet *moon);
static GType moon_applet_get_type (void);
static GObjectClass *parent_class;

void
show_error(gchar * message1, gchar * message2){
	GtkWidget *dialog;
	
	dialog = gtk_message_dialog_new (
		NULL,
		GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_MESSAGE_ERROR,
		GTK_BUTTONS_CLOSE,
		message1, message2);

	g_signal_connect (dialog, "response",
			  G_CALLBACK (gtk_widget_destroy),
			  NULL);

	gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
/* 	gtk_window_set_screen (GTK_WINDOW (dialog), */
/* 			       gtk_widget_get_screen (GTK_WIDGET (moon))); */
	gtk_widget_show (dialog);
}

void
show_help (MoonApplet *moon, const char *link_id)
{
	GError *error = NULL;

	egg_screen_help_display (
		gtk_widget_get_screen (GTK_WIDGET (moon)),
		"glunarclock-applet-2", link_id, &error);

	if (error) {
		show_error(_("There was an error displaying help: %s"),
			   error->message);
		
		g_error_free (error);
	}
}

static void
image_value_changed (GtkEntry   *entry,
		     MoonApplet *moon)
{	char *path;

	path = gnome_file_entry_get_full_path (
			GNOME_FILE_ENTRY (moon->pixmap_entry), TRUE);
	if (!path || !path [0]) {
		g_free (path);
		return;
	}

	panel_applet_gconf_set_string (PANEL_APPLET (moon), "image", path, NULL);

	g_free (path);
}

static void
ns_value_changed (GtkToggleButton *toggle,
		  MoonApplet    *moon)
{ 
	panel_applet_gconf_set_bool (
			PANEL_APPLET (moon), "is_north",
			gtk_toggle_button_get_active (toggle), NULL);
}

static void
ew_value_changed (GtkToggleButton *toggle,
		  MoonApplet    *moon)
{ 
	panel_applet_gconf_set_bool (
			PANEL_APPLET (moon), "is_east",
			gtk_toggle_button_get_active (toggle), NULL);
}

static void
latitude_value_changed (GtkSpinButton *button,
			MoonApplet    *moon)
{ 
        panel_applet_gconf_set_float (
			PANEL_APPLET (moon), "latitude",
			gtk_spin_button_get_value_as_float (button), NULL);
}

static void
longitude_value_changed (GtkSpinButton *button,
			MoonApplet    *moon)
{ 
        panel_applet_gconf_set_float (
			PANEL_APPLET (moon), "longitude",
			gtk_spin_button_get_value_as_float (button), NULL);
}

static void
n_frames_value_changed (GtkSpinButton *button,
			MoonApplet    *moon)
{
        panel_applet_gconf_set_int (
			PANEL_APPLET (moon), "frames",
			gtk_spin_button_get_value_as_int (button), NULL);
}

static void
flip_value_changed (GtkToggleButton *toggle,
		      MoonApplet      *moon)
{
	panel_applet_gconf_set_bool (
			PANEL_APPLET (moon), "flip",
			gtk_toggle_button_get_active (toggle), NULL);
}

static gboolean
delete_event (GtkWidget  *widget,
	      MoonApplet *moon)
{
	gtk_widget_hide (widget);
}

static gboolean
close_dialog (GtkWidget  *widget,
	      MoonApplet *moon)
{
	gtk_widget_hide (moon->preferences_dialog);
}

static void
handle_response (GtkWidget  *widget,
		 int         id,
		 MoonApplet *moon)
{
	if (id == GTK_RESPONSE_HELP) {
		show_help (moon, "glunarclock-settings");
		return;
	}

	gtk_widget_hide (moon->preferences_dialog);
}

static void
display_help_dialog (BonoboUIComponent *uic,
		     MoonApplet        *moon,
		     const char        *verbname)
{
	show_help (moon, NULL);
}


static void
display_about_dialog (BonoboUIComponent *uic,
		      MoonApplet        *moon,
		      const char        *verbname)
{
	char        *authors [6];
	GdkPixbuf   *pixbuf;
	GError      *error = NULL;
	char        *file;

	const char *about = _("GNOME Lunar Clock displays the current phase of the moon "
			      "and pertinent astronomical data.");

	if (moon->about_dialog) {
		gtk_window_set_screen (GTK_WINDOW (moon->about_dialog),
				       gtk_widget_get_screen (GTK_WIDGET (moon)));
		gtk_window_present (GTK_WINDOW (moon->about_dialog));
		return;
	}

	authors [0] =  _("Josh Buhl <jbuhl@users.sourceforge.net>");
	authors [1] =  _("Mike Henderson: wmMoonClock");
	authors [2] =  _("Wanda & George: Applet Stuff");
	authors [3] =  _("Nissen: cartoon moon graphics");
	authors [4] =  _("Lots of friendly translators: po files");
	authors [5] =  NULL;

	file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP,
					   "glunarclock-logo.png", FALSE, NULL);

	pixbuf = gdk_pixbuf_new_from_file (file, &error);
	g_free (file);

	if (error) {
		g_warning (G_STRLOC ": cannot open %s: %s", file, error->message);
		g_error_free (error);
	}

	moon->about_dialog =
		gnome_about_new (_("GNOME Lunar Clock"),
				 VERSION,
				 "Copyright \xc2\xa9 1999-2005 Josh Buhl, et al.",
				 (const char *) about,
				 (const char **) authors,
				 NULL,
				 NULL,
				 pixbuf);

	if (pixbuf)
		gdk_pixbuf_unref (pixbuf);

	gtk_window_set_wmclass (GTK_WINDOW (moon->about_dialog), "moon", "Moon");
	gtk_window_set_screen  (GTK_WINDOW (moon->about_dialog),
			       gtk_widget_get_screen (GTK_WIDGET (moon)));
	gnome_window_icon_set_from_file (GTK_WINDOW (moon->about_dialog),
					 GNOME_ICONDIR"/glunarclock-logo.png");

	g_signal_connect (moon->about_dialog, "destroy",
			  G_CALLBACK (gtk_widget_destroyed),
			  &moon->about_dialog);

	gtk_widget_show (moon->about_dialog);
}

static void 
display_preferences_dialog (BonoboUIComponent *uic,
			    MoonApplet        *moon,
			    const char        *verbname)
{
	GladeXML  *xml;
	GtkWidget *button;

	if (moon->preferences_dialog) {
		gtk_window_set_screen (GTK_WINDOW (moon->preferences_dialog),
				       gtk_widget_get_screen (GTK_WIDGET (moon)));
		gtk_window_present (GTK_WINDOW (moon->preferences_dialog));
		return;
	}

	xml = glade_xml_new (MOON_GLADEDIR "/glunarclock.glade",
			     "moon_preferences_dialog", NULL);

	moon->preferences_dialog = glade_xml_get_widget (xml, "moon_preferences_dialog");

	g_object_add_weak_pointer (G_OBJECT (moon->preferences_dialog),
				   (void**) &moon->preferences_dialog);

	gtk_window_set_wmclass (GTK_WINDOW (moon->preferences_dialog),
				"moon", "Moon");

	gtk_dialog_set_default_response (GTK_DIALOG (moon->preferences_dialog),
					 GTK_RESPONSE_OK);

	gnome_window_icon_set_from_file (GTK_WINDOW (moon->preferences_dialog),
					 GNOME_ICONDIR"/glunarclock-logo.png");


	moon->pixmap_entry = glade_xml_get_widget (xml, "image_entry");

	moon->image_entry = gnome_file_entry_gtk_entry (
		GNOME_FILE_ENTRY (moon->pixmap_entry));

	gtk_entry_set_text (GTK_ENTRY (moon->image_entry), moon->image);

	g_signal_connect (moon->image_entry, "changed",
			  G_CALLBACK (image_value_changed), moon);



	moon->frames_spin = glade_xml_get_widget (xml, "frames_spin");
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (moon->frames_spin),
				   moon->n_frames);

	g_signal_connect (moon->frames_spin, "value_changed",
			  G_CALLBACK (n_frames_value_changed), moon);



	moon->latitude_spin = glade_xml_get_widget (xml, "latitude_spin");
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (moon->latitude_spin),
				   moon->latitude);

	g_signal_connect (moon->latitude_spin, "value_changed",
			  G_CALLBACK (latitude_value_changed), moon);



	moon->longitude_spin = glade_xml_get_widget (xml, "longitude_spin");
	gtk_spin_button_set_value (GTK_SPIN_BUTTON (moon->longitude_spin),
				   moon->longitude);

	g_signal_connect (moon->longitude_spin, "value_changed",
			  G_CALLBACK (longitude_value_changed), moon);


	moon->flip_toggle = glade_xml_get_widget (xml, "flip_toggle");
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (moon->flip_toggle), 
				      moon->flip);

	g_signal_connect (moon->flip_toggle, "toggled",
			  G_CALLBACK (flip_value_changed), moon);


	moon->lat_radio = glade_xml_get_widget (xml, "latitude_north");
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (moon->lat_radio), 
				      moon->is_north);

	button = glade_xml_get_widget (xml, "latitude_south");
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), 
				      !(moon->is_north));

	g_signal_connect (G_OBJECT(moon->lat_radio), "toggled",
			  G_CALLBACK (ns_value_changed), moon);


	moon->long_radio = glade_xml_get_widget (xml, "longitude_east");
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (moon->long_radio), 
				      moon->is_east);

	button = glade_xml_get_widget (xml, "longitude_west");
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), 
				      !(moon->is_east));

	g_signal_connect (G_OBJECT(moon->long_radio), "toggled",
			  G_CALLBACK (ew_value_changed), moon);


	g_signal_connect (moon->preferences_dialog, "delete_event",
			  G_CALLBACK (delete_event), moon);

	g_signal_connect (moon->preferences_dialog, "response",
			  G_CALLBACK (handle_response), moon);

	button = glade_xml_get_widget (xml, "done_button");
        g_signal_connect (button, "clicked", G_CALLBACK (close_dialog), moon);

	gtk_window_set_screen (GTK_WINDOW (moon->preferences_dialog),
			       gtk_widget_get_screen (GTK_WIDGET (moon)));

	gtk_window_present (GTK_WINDOW (moon->preferences_dialog));

	g_object_unref (xml);
}


static void
image_changed_notify (GConfClient *client,
		      guint        cnxn_id,
		      GConfEntry  *entry,
		      MoonApplet  *moon)
{
	const char *value;
	char      *path = NULL;

	if (!entry->value || entry->value->type != GCONF_VALUE_STRING)
		return;

	value = gconf_value_get_string (entry->value);

	if (!value [0] || (moon->image && !strcmp (moon->image, value)))
		return;

	if (moon->image)
		g_free (moon->image);

	moon->image = g_strdup (value);

	path = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,
					  ALTERNATIVE_MOONIMAGE_FILE, FALSE, NULL);

	if ((!strcmp(moon->image,ALTERNATIVE_MOONIMAGE_FILE)) ||
	    (!strcmp(moon->image, path))) {
		moon->rotation_type = MIRROR;
		panel_applet_gconf_set_int (
			PANEL_APPLET (moon), "frames",
			ALTERNATIVE_MOONIMAGE_FRAMES, NULL);
	}
	else
		moon->rotation_type = ROTATE;

	g_free(path);

	path = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,
					  FALLBACK_MOONIMAGE_FILE, FALSE, NULL);

	if ((!strcmp(moon->image,FALLBACK_MOONIMAGE_FILE)) ||
	    (!strcmp(moon->image, path))) {
		panel_applet_gconf_set_int (
			PANEL_APPLET (moon), "frames",
			FALLBACK_MOONIMAGE_FRAMES, NULL);
	}

	g_free(path);

	update_image_number(moon);
	load_moon_image (moon);
	compose_image (moon);

	if (moon->image_entry &&
	    strcmp (gtk_entry_get_text (GTK_ENTRY (moon->image_entry)), moon->image))
		gtk_entry_set_text (GTK_ENTRY (moon->image_entry), moon->image);
}

static void
n_frames_changed_notify (GConfClient *client,
			 guint        cnxn_id,
			 GConfEntry  *entry,
			 MoonApplet  *moon)
{
	int value;

	if (!entry->value || entry->value->type != GCONF_VALUE_INT)
		return;

	value = gconf_value_get_int (entry->value);

	if (moon->n_frames == value)
		return;

	moon->n_frames = value;

	if (moon->n_frames <= 0)
		moon->n_frames = 24;

	update_image_number(moon);
	load_moon_image(moon);
	compose_image(moon);

	if (moon->frames_spin &&
	    gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (moon->frames_spin)) != moon->n_frames)
		gtk_spin_button_set_value (GTK_SPIN_BUTTON (moon->frames_spin), moon->n_frames);
}

static void
flip_changed_notify (GConfClient *client,
		       guint        cnxn_id,
		       GConfEntry  *entry,
		       MoonApplet  *moon)
{
	gboolean value;

	if (!entry->value || entry->value->type != GCONF_VALUE_BOOL)
		return;

	value = gconf_value_get_bool (entry->value);

	if (moon->flip == value)
		return;

	moon->flip = value;

	load_moon_image(moon);
	compose_image (moon);


	if (moon->flip_toggle &&
	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (moon->flip_toggle))
	    != moon->flip)
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (moon->flip_toggle), moon->flip);
}


static void
ns_changed_notify (GConfClient *client,
		   guint        cnxn_id,
		   GConfEntry  *entry,
		   MoonApplet  *moon)
{
	gboolean value;

	if (!entry->value || entry->value->type != GCONF_VALUE_BOOL)
		return;

	value = gconf_value_get_bool (entry->value);

	if (moon->is_north == value)
		return;

	moon->is_north = value;

	if ( moon->flip  ){
		load_moon_image(moon);
		compose_image (moon);
	}

	update_moon(moon);

	if (moon->lat_radio &&
	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (moon->lat_radio))
	    != moon->is_north)
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (moon->lat_radio), moon->is_north);
}


static void
ew_changed_notify (GConfClient *client,
		   guint        cnxn_id,
		   GConfEntry  *entry,
		   MoonApplet  *moon)
{
	gboolean value;

	if (!entry->value || entry->value->type != GCONF_VALUE_BOOL)
		return;

	value = gconf_value_get_bool (entry->value);

	if (moon->is_east == value)
		return;

	moon->is_east = value;

	update_moon(moon);

	if (moon->long_radio &&
	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (moon->long_radio)) != moon->is_east)
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (moon->long_radio), moon->is_east);
}


static void
latitude_changed_notify (GConfClient *client,
			 guint        cnxn_id,
			 GConfEntry  *entry,
			 MoonApplet  *moon)
{
	gfloat value;

	if (!entry->value || entry->value->type != GCONF_VALUE_FLOAT)
		return;

	value = gconf_value_get_float (entry->value);

	if (moon->latitude == value)
		return;

	moon->latitude = value;

	update_moon(moon);

	if (moon->flip_toggle &&
	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (moon->flip_toggle)) != moon->flip)
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (moon->flip_toggle), moon->flip);
}


static void
longitude_changed_notify (GConfClient *client,
			 guint        cnxn_id,
			 GConfEntry  *entry,
			 MoonApplet  *moon)
{
	gfloat value;

	if (!entry->value || entry->value->type != GCONF_VALUE_FLOAT)
		return;

	value = gconf_value_get_float (entry->value);

	if (moon->longitude == value)
		return;

	moon->longitude = value;

	update_moon(moon);

	if (moon->flip_toggle &&
	    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (moon->flip_toggle)) != moon->flip)
		gtk_toggle_button_set_active (
			GTK_TOGGLE_BUTTON (moon->flip_toggle), moon->flip);
}

static void
set_tooltip (MoonApplet *moon)
{
	GtkTooltips *tooltips;
	gchar *tooltip;

	tooltips = gtk_tooltips_new ();
	g_object_ref (tooltips);
	gtk_object_sink (GTK_OBJECT (tooltips));

	g_object_set_data (G_OBJECT (moon), "tooltips", tooltips);

	tooltip = (visible(moon)) ?
	    g_strdup_printf(_("The moon is currently above the horizon."))
	    :
	    g_strdup_printf(_("The moon is currently below the horizon."));

	gtk_tooltips_set_tip (tooltips, GTK_WIDGET (moon),
			      tooltip, NULL);
}

static void
destroy_tooltip (MoonApplet *moon)
{
	GtkTooltips *tooltips;

	tooltips = g_object_get_data (G_OBJECT (moon), "tooltips");
	if (tooltips) {
		g_object_unref (tooltips);
		g_object_set_data (G_OBJECT (moon), "tooltips", NULL);
	}
}

static void
update_image_number (MoonApplet * moon)
{
	gdouble image_float;
	gint image_int;

	/* MoonPhase expresses phase of moon as fraction of 1; 0.5=full. */
	image_float = moonphase() * (gdouble) (moon->n_frames);
	image_int = (gint) image_float;

	/* ergo image number ranges from 0 to (frames - 1), which is why
	 * it's mod frames and not mod (frames + 1). */
	moon->image_number = ((image_float - image_int) >= 0.5) ?
	    (image_int + 1)
		% moon->n_frames : image_int % moon->n_frames;

	return;
}

static void 
update_moon(MoonApplet * moon)
{
/* 	gboolean has_focus = FALSE; */
	gboolean old_visible;
	gint old_image_number;

	old_image_number = moon->image_number;
	old_visible = visible(moon);

	update_moondata(moon);
	update_image_number(moon);

	if (moon->moondata_dialog)
		update_moondata_dialog(moon);

	if (old_image_number != moon->image_number) {
		load_moon_image(moon);
/*  		has_focus = GTK_WIDGET_HAS_FOCUS(moon->applet); */
/* 		compose_image(moon, has_focus); */
		compose_image(moon);
		gtk_widget_queue_draw(moon->drawing_area);
	}

	if(old_visible != visible(moon)) {
		destroy_tooltip(moon);
		set_tooltip(moon);
}

	return;
}


static gboolean
timeout_handler (gpointer data)
{
	MoonApplet *moon = (MoonApplet *) data;

	update_moon(moon);

	return TRUE;
}

static void
setup_timeout (MoonApplet *moon)
{
	if (moon->timeout)
		g_source_remove (moon->timeout);

	moon->timeout = g_timeout_add (1000 * UPDATE_APPLET_INTERVAL,
				       timeout_handler, moon);
}

static void
setup_gconf (MoonApplet *moon)
{
	PanelApplet *applet = (PanelApplet *) moon;
	char        *key;
	int          i = 0;

	key = panel_applet_gconf_get_full_key (applet, "image");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) image_changed_notify,
					moon, NULL, NULL);
	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "frames");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) 
					n_frames_changed_notify,
					moon, NULL, NULL);

	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "flip");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) flip_changed_notify,
					moon, NULL, NULL);

	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "longitude");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) 
					longitude_changed_notify,
					moon, NULL, NULL);

	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "latitude");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) latitude_changed_notify,
					moon, NULL, NULL);
	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "is_north");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) ns_changed_notify,
					moon, NULL, NULL);
	g_free (key);

	key = panel_applet_gconf_get_full_key (applet, "is_east");
	moon->listeners [i++] = gconf_client_notify_add (
					moon->client, key,
					(GConfClientNotifyFunc) ew_changed_notify,
					moon, NULL, NULL);
	g_free (key);

	g_assert (i == N_MOON_PREFS);
}


static void free_pixbuf(guchar * rgb, gpointer data)
{
	g_free(rgb);
	return;
}

static GdkPixbuf * 
flip_image(const GdkPixbuf * pb, RotationType flip_type)
{
	GdkPixbuf *new_pb;
	gboolean has_alpha;
	guchar *rgb, *new_rgb;
	gint i,j,k, width, height, bytepp, rowstride;
			
	has_alpha = gdk_pixbuf_get_has_alpha(pb);
	rowstride = gdk_pixbuf_get_rowstride(pb);
	width = gdk_pixbuf_get_width(pb);
	height = gdk_pixbuf_get_height(pb);
	
	bytepp= (has_alpha) ? 4:3;

	new_rgb = g_new0(guchar, height * rowstride);
	g_assert(new_rgb);
	
	rgb = gdk_pixbuf_get_pixels(pb);

	switch(flip_type){
	case ROTATE :
		/*rotate 180 degrees*/
		for(i=0; i<rowstride*height; i+=bytepp){
			j=i;
			for(k = rowstride*height-i-bytepp; 
			    k<rowstride*height-i; k++){
				new_rgb[k] = rgb[j++];
			}
		}
		break;

	case MIRROR :
		/*mirror along vertical axis*/
		for(i=0; i<height; i++){
			for(j=0; j<rowstride; j+=bytepp){
				for(k = 0; k<bytepp; k++){
					new_rgb[i*rowstride+rowstride-j+k]
						= rgb[i*rowstride+j+k];
				}
			}
		}
	break;

	default : 
		g_error("invalid flip type parameter %d", flip_type);
	}

	new_pb= gdk_pixbuf_new_from_data(new_rgb,
					   GDK_COLORSPACE_RGB,
					   has_alpha, 8, width, 
					   height, rowstride,
					   free_pixbuf, NULL);

	return new_pb;
}

static gboolean
load_moon_image (MoonApplet *moon)
{
	GdkPixbuf *frame_pb, *test_pb;
	GdkPixbuf *pixbuf = NULL;
	GError    *error = NULL;
	char      *path = NULL;

	if (!moon->image)
		return FALSE;

	if (g_path_is_absolute (moon->image))
		path = g_strdup (moon->image);
	else {
		path = gnome_program_locate_file (
					NULL, GNOME_FILE_DOMAIN_PIXMAP,
					moon->image, FALSE, NULL);
		if (!path)
			path = gnome_program_locate_file (
					NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,
					moon->image, FALSE, NULL);
	}

	if (!path) {
		g_warning ("Cannot locate '%s'", moon->image);
		show_error("Cannot locate '%s'", moon->image);
	} else {
		pixbuf = gdk_pixbuf_new_from_file (path, &error);
		if (error) {
			g_warning ("Cannot load '%s': %s", path, error->message);
			show_error(_("There was an error loading moon image: %s"),
				   error->message);
			g_error_free(error);
		}

		g_free (path);
	}

	if (pixbuf) {
		guchar *frame_rgb, *rgb;
		gint width, height, xsrc, i, rowstride;
		gboolean has_alpha;

		width = gdk_pixbuf_get_width(pixbuf) / moon->n_frames;
		height = gdk_pixbuf_get_height(pixbuf);

		xsrc = moon->image_number * width;

		frame_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
					  width, height);

		if (!frame_pb)
			g_error("cannot allocate image pixbuf.");

		gdk_pixbuf_scale(pixbuf, frame_pb, 0, 0, width,
				 height, -xsrc, 0, 1, 1,
				 GDK_INTERP_HYPER);
			
		has_alpha = gdk_pixbuf_get_has_alpha(frame_pb);
		rowstride = gdk_pixbuf_get_rowstride(frame_pb);


		if(moon->flip && ! moon->is_north){
			test_pb = flip_image (frame_pb, moon->rotation_type);
			gdk_pixbuf_unref(frame_pb);
			frame_pb = test_pb;
		}

/* 		rgb = g_new0(guchar, height * rowstride); */

/* 		if(rgb){ */
/* 			frame_rgb = gdk_pixbuf_get_pixels(frame_pb); */

/* 			/\* Hilight moon image for mouseover.  */
/* 			 * Alpha transparency should not be changed. *\/ */
/* 			for(i=0; i<rowstride*height; i++){ */
/* 				rgb[i] = (!has_alpha || i % 4 != 3) ? */
/* 					MIN(255, frame_rgb[i] + 24) */
/* 					: */
/* 					frame_rgb[i]; */
/* 			} */
/* 		} */

/* 		moon->hilit_image_pb = */
/* 			gdk_pixbuf_new_from_data(rgb, GDK_COLORSPACE_RGB, */
/* 						 has_alpha, 8, width, */
/* 						 height, rowstride, */
/* 						 free_pixbuf, NULL); */

		gdk_pixbuf_unref(pixbuf);
		pixbuf = NULL;

	} else {
		guchar *rgb;
		gint rowstride=120;
		gint height=40;
		gint width=40;

		rgb = g_new0(guchar, height * rowstride);

		frame_pb =
		    gdk_pixbuf_new_from_data(rgb, GDK_COLORSPACE_RGB,
					     FALSE, 8, width,
					     height, rowstride,
					     free_pixbuf, NULL);

		moon->hilit_image_pb = NULL;
	}

	if (moon->image_pb)
		g_object_unref (moon->image_pb);
	moon->image_pb = frame_pb;

	return TRUE;
}

static void
compose_image (MoonApplet *moon)
{
	GtkWidget *widget = moon->drawing_area;
	GdkPixbuf *composite_pb, *bg_pb;
	GdkGC     *gc;
	int        width  = -1;
	int        height = -1;
	int        rowstride = 4;
	int        pixbuf_width = -1;
	int        pixbuf_height = -1;
	gboolean   rotate = FALSE;
	gdouble    scale = 1.0;
	guchar    *rgb;

	int i;

	if (!GTK_WIDGET_REALIZED (widget) ||
	    widget->allocation.width <= 0 ||
	    widget->allocation.height <= 0)
		return;

	if (moon->orientation == PANEL_APPLET_ORIENT_LEFT ||
	     moon->orientation == PANEL_APPLET_ORIENT_RIGHT)
		rotate = TRUE;

	if (moon->image_pb || load_moon_image (moon)) {
		pixbuf_width  = gdk_pixbuf_get_width  (moon->image_pb);
		pixbuf_height = gdk_pixbuf_get_height (moon->image_pb);

		if (moon->orientation == PANEL_APPLET_ORIENT_UP ||
		    moon->orientation == PANEL_APPLET_ORIENT_DOWN) {
			height = widget->allocation.height;
			width  = pixbuf_width * ((gdouble) height / pixbuf_height);
			widget->requisition.width = width;
		} else {
			if (!rotate) {
				width = widget->allocation.width;
				height = pixbuf_height * ((gdouble) width / pixbuf_width);
				widget->requisition.height = height;
			} else {
				width = widget->allocation.width;
				height = pixbuf_width * ((gdouble) width / pixbuf_height);
				widget->requisition.height = height;
			}
		}

	} else {
		if (rotate) {
			width  = widget->allocation.width;
			height = widget->allocation.height;
		} else {
			height = widget->allocation.height;
			width  = widget->allocation.width;
		}
	}

	g_assert (width != -1 && height != -1);

	if (width == 0 || height == 0)
		return;

	scale = ((gdouble) height/pixbuf_height);

	if (moon->pixmap)
		g_object_unref (moon->pixmap);
	moon->pixmap = gdk_pixmap_new (widget->window, width, height, -1);

	if (!moon->image_pb)
		return;

	gtk_widget_queue_resize (widget);

	g_assert (pixbuf_width != -1 && pixbuf_height != -1);

	composite_pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
				      width, height);
	if (!composite_pb)
		g_error("cannot allocate composing pixbuf.");
	
	gdk_pixbuf_composite_color(moon->image_pb, composite_pb,
			     0, 0, width, height,
			     0, 0, scale, scale,
			     GDK_INTERP_HYPER, 255, 0,10,10,0,0);


	gdk_pixbuf_render_to_drawable_alpha(composite_pb,
					    moon->pixmap, 0, 0, 0,
					    0, width, height,
					    GDK_PIXBUF_ALPHA_FULL,
					    128, GDK_RGB_DITHER_NORMAL, 0,
					    0);	

	gdk_pixbuf_unref(composite_pb);
	composite_pb = NULL;
	
}

static gboolean
moon_applet_expose_event (GtkWidget      *widget,
			  GdkEventExpose *event,
			  MoonApplet     *moon)
{
	g_return_val_if_fail (moon->pixmap != NULL, FALSE);

	gdk_draw_pixmap (widget->window,
			 widget->style->fg_gc [GTK_WIDGET_STATE (widget)],
			 moon->pixmap,
			 event->area.x, event->area.y,
			 event->area.x, event->area.y,
			 event->area.width, event->area.height);

        return FALSE;
}



/*******************************************************************************/
static gint
mouse_enter_cb(GtkWidget * widget, GdkEvent * e, MoonApplet * moon)
{
/* 	g_message("mouse enter event"); */
/*         compose_image(moon, TRUE); */
	gtk_widget_queue_draw(moon->drawing_area);
	return TRUE;
}

static gint
mouse_leave_cb(GtkWidget * widget, GdkEvent * e, MoonApplet * moon)
{
/* 	g_message("mouse leave event"); */
/*         compose_image(moon, FALSE); */
	gtk_widget_queue_draw(moon->drawing_area);
	return TRUE;
}

/*******************************************************************************/


static void
moon_applet_size_allocate (GtkWidget     *widget,
			   GtkAllocation *allocation,
			   MoonApplet    *moon)
{
	if (widget->allocation.width  != moon->prev_allocation.width ||
	    widget->allocation.height != moon->prev_allocation.height)
		compose_image (moon);

	moon->prev_allocation = *allocation;
}

static void
moon_applet_realize (GtkWidget  *widget,
		     MoonApplet *moon)
{
	if (!moon->pixmap)
		compose_image (moon);
}

static void
moon_applet_unrealize (GtkWidget  *widget,
		       MoonApplet *moon)
{
	if (moon->pixmap)
		g_object_unref (moon->pixmap);
	moon->pixmap = NULL;
}

static void
moon_applet_change_orient (PanelApplet       *applet,
			   PanelAppletOrient  orientation)
{
	MoonApplet *moon = (MoonApplet *) applet;

	if (moon->orientation == orientation)
		return;

	moon->orientation = orientation;

	if (moon->pixmap)
		compose_image (moon);
}

static gboolean
handle_keypress (GtkWidget   *widget,
		 GdkEventKey *event,
		 MoonApplet  *moon)
{
	switch (event->keyval) {
	case GDK_space:
	case GDK_KP_Space:
	case GDK_Return:
	case GDK_KP_Enter:
	case GDK_ISO_Enter:
	case GDK_3270_Enter:
		display_moondata_dialog (moon);
		break;
	default:
		return FALSE;
		break;
	}

	return TRUE;
}

static gboolean 
handle_button_press (GtkWidget      *widget,
		     GdkEventButton *event,
		     MoonApplet     *moon)
{
	if (event->button != 1)
		return FALSE; 

	display_moondata_dialog (moon);

	return TRUE; 
}

static void
setup_moon_widget (MoonApplet *moon)
{
	GtkWidget *widget = (GtkWidget *) moon;

	moon->frame = gtk_frame_new (NULL);
	gtk_frame_set_shadow_type (GTK_FRAME (moon->frame), GTK_SHADOW_IN);
	gtk_container_add (GTK_CONTAINER (widget), moon->frame);

	moon->drawing_area = gtk_drawing_area_new ();
	gtk_container_add (GTK_CONTAINER (moon->frame), moon->drawing_area);

	g_signal_connect (moon->drawing_area, "realize",
			  G_CALLBACK (moon_applet_realize), moon);
	g_signal_connect (moon->drawing_area, "unrealize",
			  G_CALLBACK (moon_applet_unrealize), moon);
	g_signal_connect (moon->drawing_area, "size-allocate",
			  G_CALLBACK (moon_applet_size_allocate), moon);
	g_signal_connect (moon->drawing_area, "expose-event",
			  G_CALLBACK (moon_applet_expose_event), moon);

	gtk_widget_set_events (	moon->drawing_area,
				gtk_widget_get_events (moon->drawing_area) |
				GDK_BUTTON_PRESS_MASK | GDK_ENTER_NOTIFY_MASK |
				GDK_LEAVE_NOTIFY_MASK );
	g_signal_connect (moon->drawing_area, "button_press_event",
			  G_CALLBACK (handle_button_press), moon);
	g_signal_connect(moon->drawing_area, "enter_notify_event",
			   G_CALLBACK(mouse_enter_cb), moon);
	g_signal_connect(moon->drawing_area, "leave_notify_event",
			   G_CALLBACK(mouse_leave_cb), moon);

	update_moon(moon);

	load_moon_image (moon);

	compose_image (moon);

	set_tooltip (moon);

	setup_timeout (moon);

	g_signal_connect (moon, "key_press_event",
			  G_CALLBACK (handle_keypress), moon);

	gtk_widget_show_all (widget);
}

static const BonoboUIVerb moon_menu_verbs [] = {
	BONOBO_UI_UNSAFE_VERB ("Preferences", display_preferences_dialog),
	BONOBO_UI_UNSAFE_VERB ("Help",        display_help_dialog),
	BONOBO_UI_UNSAFE_VERB ("About",       display_about_dialog),

        BONOBO_UI_VERB_END
};

static gboolean
moon_applet_fill (MoonApplet *moon)
{
	PanelApplet *applet = (PanelApplet *) moon;
	GError      *error = NULL;
	char      *path = NULL;

	moon->orientation = panel_applet_get_orient (applet);

	panel_applet_add_preferences (
		applet, "/schemas/apps/glunarclock_applet/prefs", NULL);

	setup_gconf (moon);

	moon->image = panel_applet_gconf_get_string (applet, "image", &error);
	if (error) {
		g_warning ("Error getting 'image' preference: %s", error->message);
		g_error_free (error);
		error = NULL;
	}
	if (!moon->image)
		moon->image = g_strdup ( FALLBACK_MOONIMAGE_FILE ); /* Fallback */

	path = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,
					  ALTERNATIVE_MOONIMAGE_FILE, FALSE, NULL);

	if((!strcmp (moon->image,ALTERNATIVE_MOONIMAGE_FILE)) || (!strcmp(moon->image, path)))
		moon->rotation_type = MIRROR;
	else
		moon->rotation_type = ROTATE;
	g_free(path);

	moon->n_frames = panel_applet_gconf_get_int (applet, "frames", &error);
	if (error) {
		g_warning ("Error getting 'frames' preference: %s", error->message);
		g_error_free (error);
		error = NULL;
	}
	path = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP,
					  FALLBACK_MOONIMAGE_FILE, FALSE, NULL);
	if (moon->n_frames <= 0	&& ((!strcmp(moon->image, path)) ||
				    (!strcmp (moon->image, FALLBACK_MOONIMAGE_FILE ))))
		moon->n_frames =  FALLBACK_MOONIMAGE_FRAMES;
	else if (moon->n_frames <= 0 && (moon->rotation_type == MIRROR))
		moon->n_frames = ALTERNATIVE_MOONIMAGE_FRAMES;
	else if (moon->n_frames <= 0)
		moon->n_frames = 20; /* Fallback */

	g_free(path);

	moon->latitude = panel_applet_gconf_get_float (applet, "latitude", &error);
	if (error) {
		g_warning ("Error getting 'latitude' preference: %s", error->message);
		g_error_free (error);
		error = NULL;
		moon->latitude = 50.7; /* Fallback */
	}
	if (moon->latitude <= 0)
		moon->latitude = 0.0;

	moon->longitude = panel_applet_gconf_get_float (applet, "longitude", &error);
	if (error) {
		g_warning ("Error getting 'longitude' preference: %s", error->message);
		g_error_free (error);
		error = NULL;
		moon->longitude = 7.1; /* Fallback */
	}
	if (moon->longitude <= 0)
		moon->longitude = 0.0;

	moon->flip = panel_applet_gconf_get_bool (applet, "flip", &error);
	if (error) {
		g_warning ("Error getting 'flip' preference: %s", error->message);
		g_error_free (error);
		error = NULL;

		moon->flip = TRUE; /* Fallback */
	}

	moon->is_north = panel_applet_gconf_get_bool (applet, "is_north", &error);
	if (error) {
		g_warning ("Error getting 'North South' preference: %s", error->message);
		g_error_free (error);
		error = NULL;

		moon->is_north = TRUE; /* Fallback */
	}

	moon->is_east = panel_applet_gconf_get_bool (applet, "is_east", &error);
	if (error) {
		g_warning ("Error getting 'East West' preference: %s", error->message);
		g_error_free (error);
		error = NULL;

		moon->is_east = TRUE; /* Fallback */
	}

	panel_applet_setup_menu_from_file (
		applet, NULL, "GNOME_GLunarclockApplet.xml",
		NULL, moon_menu_verbs, moon);

	setup_moon_widget (moon);

	return TRUE;
}

static gboolean
glunarclock_applet_factory (PanelApplet *applet,
	       const char  *iid,
	       gpointer     data)
{
	gboolean retval = FALSE;

	if (!strcmp (iid, "OAFIID:GNOME_GLunarclockApplet"))
		retval = moon_applet_fill (MOON_APPLET (applet));

	return retval;
}

static void
moon_applet_destroy (GtkObject *object)
{
	MoonApplet *moon = (MoonApplet *) object;
	int         i;

	if (moon->timeout)
		g_source_remove (moon->timeout);
	moon->timeout = 0;

	for (i = 0; i < N_MOON_PREFS; i++) {
		if (moon->client)
			gconf_client_notify_remove (
				moon->client, moon->listeners [i]);
		moon->listeners [i] = 0;
	}

	if (moon->image)
		g_free (moon->image);
	moon->image = NULL;

	if (moon->client)
		g_object_unref (moon->client);
	moon->client = NULL;

	if (moon->pixmap)
		g_object_unref (moon->pixmap);
	moon->pixmap = NULL;

	if (moon->image_pb)
		g_object_unref (moon->image_pb);
	moon->image_pb = NULL;

	if (moon->hilit_image_pb)
		g_object_unref (moon->hilit_image_pb);
	moon->hilit_image_pb = NULL;

	if (moon->about_dialog)
		gtk_widget_destroy (moon->about_dialog);
	moon->about_dialog = NULL;

	if (moon->preferences_dialog)
		gtk_widget_destroy (moon->preferences_dialog);
	moon->preferences_dialog = NULL;

	if (moon->moondata_dialog)
		gtk_widget_destroy (moon->moondata_dialog);
	moon->moondata_dialog = NULL;

	destroy_tooltip (moon);

	GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
moon_applet_instance_init (MoonApplet      *moon,
			   MoonAppletClass *klass)
{
	int i;

	moon->client = gconf_client_get_default ();

	moon->image     = NULL;
	moon->n_frames  = 1;
	moon->flip      = FALSE;
	moon->is_north  = FALSE;
	moon->is_east   = FALSE;
	moon->latitude  = 0.0;
	moon->longitude = 0.0;

	moon->rotation_type  = ROTATE;

	moon->orientation = PANEL_APPLET_ORIENT_UP;

	moon->frame         = NULL;
	moon->drawing_area  = NULL;
	moon->pixmap        = NULL;
	moon->timeout       = 0;
	moon->image_number  = 0;

	moon->prev_allocation.x      = -1;
	moon->prev_allocation.y      = -1;
	moon->prev_allocation.width  = -1;
	moon->prev_allocation.height = -1;

	moon->image_pb = NULL;
	moon->hilit_image_pb = NULL;

	moon->about_dialog = NULL;

	moon->preferences_dialog = NULL;
	moon->pixmap_entry       = NULL;
	moon->image_entry        = NULL;
	moon->frames_spin        = NULL;
	moon->longitude_spin     = NULL;
	moon->latitude_spin      = NULL;
	moon->flip_toggle        = NULL;
	moon->lat_radio          = NULL;
	moon->long_radio         = NULL;

	moon->moondata_dialog = NULL;

	for (i = 0; i < N_MOON_PREFS; i++)
		moon->listeners [i] = 0;

	panel_applet_set_flags (PANEL_APPLET (moon),
				PANEL_APPLET_EXPAND_MINOR);
}

static void
moon_applet_class_init (MoonAppletClass *klass)
{
	PanelAppletClass *applet_class    = (PanelAppletClass *) klass;
	GtkObjectClass   *gtkobject_class = (GtkObjectClass *) klass;

	parent_class = g_type_class_peek_parent (klass);

	applet_class->change_orient = moon_applet_change_orient;

	gtkobject_class->destroy = moon_applet_destroy;
}

static GType
moon_applet_get_type (void)
{
	static GType type = 0;

	if (!type) {
		static const GTypeInfo info = {
			sizeof (PanelAppletClass),
			NULL, NULL,
			(GClassInitFunc) moon_applet_class_init,
			NULL, NULL,
			sizeof (MoonApplet),
			0,
			(GInstanceInitFunc) moon_applet_instance_init,
			NULL
		};

		type = g_type_register_static (
				PANEL_TYPE_APPLET, "GLunarclockApplet", &info, 0);
	}

	return type;
}

PANEL_APPLET_BONOBO_FACTORY ("OAFIID:GNOME_GLunarclockApplet_Factory",
			     moon_applet_get_type (),
			     "glunarclock-applet-2",
			     "0",
			     glunarclock_applet_factory,
			     NULL)


syntax highlighted by Code2HTML, v. 0.9.1