/*
 *  Copyright (C) 2003 Tommi Komulainen
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  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.
 *
 *  $Id: gul-state.c,v 1.10 2004/03/29 21:55:56 crispin Exp $
 */

#include "gul-state.h"

#include <gtk/gtkpaned.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkexpander.h>
#include <libgnome/gnome-config.h>

#include <string.h>

#define CONFIG_SYNC_INTERVAL (60 * 5 * 1000)

static guint timeout_id = 0;

static gboolean
config_periodic_sync_cb (gpointer dummy)
{
	gnome_config_sync ();
	return TRUE;
}


/**
 * gul_state_init:
 *
 * Prepares gnome-config for saving state in a single file.  Everyone can then
 * just use relative keys ('section/key') when calling gnome_config_get_* and
 * gnome_config_set_* functions, without needing to worry about the location of
 * the file.
 */
void
gul_state_init (void)
{
	char *state_file;
	char *config_prefix;

	/* Using ~/.galeon/state.ini to keep all files together. */
	state_file = g_build_filename (g_get_home_dir(),
			               ".galeon", "state.ini", NULL);

	config_prefix = g_strconcat ("=", state_file, "=/", NULL);
	g_free (state_file);

	gnome_config_push_prefix (config_prefix);
	g_free (config_prefix);


	timeout_id = g_timeout_add (CONFIG_SYNC_INTERVAL, 
				    config_periodic_sync_cb, 
				    NULL);
}

/**
 * gul_state_shutdown:
 *
 * Ensures that any state changes are synced to disk
 */
void
gul_state_shutdown (void)
{
	gnome_config_sync ();
	if (timeout_id)
	{
		g_source_remove (timeout_id);
		timeout_id = 0;
	}
}


static void
gul_state_window_load (GtkWindow  *window,
		       const char *name,
		       int         default_width,
		       int         default_height)
{
	GString *key;
	int      width, height;
	gboolean maximized;

	key = g_string_new ("");

	g_string_printf (key, "%s/width=%d", name, default_width);
	width = gnome_config_get_int (key->str);

	g_string_printf (key, "%s/height=%d", name, default_height);
	height = gnome_config_get_int (key->str);

	g_string_printf (key, "%s/maximized=false", name);
	maximized = gnome_config_get_bool (key->str);

	g_string_free (key, TRUE);

	if (width > 0 && height > 0)
	{
		gtk_window_set_default_size (window, width, height);
	}

	if (maximized)
	{
		gtk_window_maximize (window);
	}
}

static void
gul_state_window_save (GtkWindow  *window,
		       const char *name)
{
	GString        *key;
	GdkWindowState  state;
	gboolean        maximized;

	key = g_string_new ("");

	state = gdk_window_get_state (GTK_WIDGET(window)->window);
	maximized = (state & GDK_WINDOW_STATE_MAXIMIZED);

	g_string_printf (key, "%s/maximized", name);
	gnome_config_set_bool (key->str, maximized);

	if (!maximized)
	{
		int width, height;

		gtk_window_get_size (window, &width, &height);

		g_string_printf (key, "%s/width", name);
		gnome_config_set_int (key->str, width);

		g_string_printf (key, "%s/height", name);
		gnome_config_set_int (key->str, height);
	}

	g_string_free (key, TRUE);

	/* don't sync after every mouse move */
}

static gboolean
on_configure_event (GtkWidget         *widget, 
		    GdkEventConfigure *event,
		    const char        *name)
{
	GdkWindowState state;

	state = gdk_window_get_state (widget->window);

	if (!(state & GDK_WINDOW_STATE_FULLSCREEN))
	{
		gul_state_window_save (GTK_WINDOW(widget), name);
	}

	return FALSE;
}

static gboolean
on_window_state_event (GtkWidget           *widget,
		       GdkEventWindowState *event,
		       const char          *name)
{
	if (!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN))
	{
		gul_state_window_save (GTK_WINDOW(widget), name);
	}
	return FALSE;
}

/** gul_state_monitor_window:
 * @widget: a #GtkWindow
 * @name: name of the window
 * @default_width: default width for the window in pixels, or -1
 * @default_height: default height for the window in pixels, or -1
 *
 * Monitor the size and state changes of a #GtkWindow and save the current
 * values in the state under the section @name.  The keys used in the section
 * are "width", "height", and "maximized".
 *
 * The initial size and state for the @window is set from previous state, or if
 * state is not found @default_width and @default_height are used.
 */
void
gul_state_monitor_window (GtkWidget  *window, 
		          const char *name,
		          int         default_width,
			  int         default_height)
{
	g_return_if_fail (GTK_IS_WINDOW(window));
	g_return_if_fail (name != NULL);
	g_return_if_fail (strchr(name, '/') == NULL);
	g_return_if_fail (default_width > 0 || default_width == -1);
	g_return_if_fail (default_height > 0 || default_height == -1);

	gul_state_window_load (GTK_WINDOW(window), name,
			       default_width, default_height);

	g_signal_connect_data (G_OBJECT(window), "configure-event",
			       G_CALLBACK(on_configure_event),
			       g_strdup (name), (GClosureNotify)g_free, 0);
	g_signal_connect_data (G_OBJECT(window), "window-state-event",
			       G_CALLBACK(on_window_state_event),
			       g_strdup (name), (GClosureNotify)g_free, 0);
}

static void
on_size_allocate (GtkPaned      *paned,
		  GtkAllocation *allocation,
		  const char    *key)
{
	int size;

	size = gtk_paned_get_position (paned);
	gnome_config_set_int (key, size);

	/* don't sync after every mouse move */
}

/** gul_state_monitor_paned:
 * @paned: a #GtkPaned
 * @key: a gnome-config key
 * @default_size: default size for the paned in pixels
 *
 * Monitor the size changes of a #GtkPaned and save current size in the state
 * using the key @key.  The initial size for the @paned is set from previous
 * state, or if state is not found @default_size is used.
 */
void
gul_state_monitor_paned (GtkWidget  *paned,
		         const char *key,
			 int         default_size)
{
	char *tmp;
	int   size;

	g_return_if_fail (GTK_IS_PANED(paned));
	g_return_if_fail (key != NULL);
	g_return_if_fail (strchr (key, '/') != NULL);
	g_return_if_fail (default_size > 0);

	tmp = g_strdup_printf ("%s=%d", key, default_size);
	size = gnome_config_get_int (tmp);
	g_free (tmp);

	if (size > 0)
	{
		gtk_paned_set_position (GTK_PANED(paned), size);
	}

	g_signal_connect_data (G_OBJECT(paned), "size-allocate",
			       G_CALLBACK(on_size_allocate),
			       g_strdup(key), (GClosureNotify)g_free, 
			       G_CONNECT_AFTER);
}


static void
on_expander_toggle (GtkExpander *expander, GParamSpec *pspec, char *key)
{
	gnome_config_set_bool (key, gtk_expander_get_expanded (expander));
}

/** gul_state_monitor_expander:
 *  @expander: an #CDDBDisclosure
 *  @key: a gnome-config key
 *  @default_visibility: the default visiblity for the expander
 *
 *  Monitor the visibilty of an #CDDBDisclosure and save the current
 *  visibility using the key @key.
 *
 */
void
gul_state_monitor_expander (GtkWidget *expander,
			    const char *key,
			    gboolean default_visibility)
{
	char *tmp;
	gboolean visible;

	g_return_if_fail (GTK_IS_EXPANDER (expander));
	g_return_if_fail (key != NULL);
	g_return_if_fail (strchr (key, '/') != NULL);

	tmp = g_strdup_printf ("%s=%s", key, default_visibility ? "true" : "false");
	visible = gnome_config_get_bool (tmp);
	g_free (tmp);

	gtk_expander_set_expanded (GTK_EXPANDER (expander), visible);

	g_signal_connect_data (G_OBJECT (expander),
			       "notify::expanded",
			       G_CALLBACK (on_expander_toggle),
			       g_strdup (key), (GClosureNotify)g_free, 
			       G_CONNECT_AFTER);
}
