/*
* Copyright (C) 2001 Matthew Mueller
* Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
* Copyright (C) 2003 Marco Pesenti Gritti <mpeseng@tin.it>
*
* 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, 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.
*
* $Id$
*/
#include "config.h"
#include "ephy-state.h"
#include "ephy-lib-type-builtins.h"
#include "ephy-file-helpers.h"
#include "ephy-node-db.h"
#include "ephy-node-common.h"
#include <string.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkpaned.h>
#include <gtk/gtkexpander.h>
#define EPHY_STATES_XML_FILE "states.xml"
#define EPHY_STATES_XML_ROOT (const xmlChar *)"ephy_states"
#define EPHY_STATES_XML_VERSION (const xmlChar *)"1.0"
enum
{
EPHY_NODE_STATE_PROP_NAME = 2,
EPHY_NODE_STATE_PROP_WIDTH = 3,
EPHY_NODE_STATE_PROP_HEIGHT = 4,
EPHY_NODE_STATE_PROP_MAXIMIZE = 5,
EPHY_NODE_STATE_PROP_POSITION_X = 6,
EPHY_NODE_STATE_PROP_POSITION_Y = 7,
EPHY_NODE_STATE_PROP_SIZE = 8,
EPHY_NODE_STATE_PROP_POSITION = 9,
EPHY_NODE_STATE_PROP_EXPANDED = 10
};
static EphyNode *states = NULL;
static EphyNodeDb *states_db = NULL;
static void
ephy_states_save (void)
{
char *xml_file;
xml_file = g_build_filename (ephy_dot_dir (),
EPHY_STATES_XML_FILE,
NULL);
ephy_node_db_write_to_xml_safe
(states_db,
(const xmlChar *)xml_file,
EPHY_STATES_XML_ROOT,
EPHY_STATES_XML_VERSION,
NULL, /* comment */
states, NULL, NULL,
NULL);
g_free (xml_file);
}
static EphyNode *
find_by_name (const char *name)
{
EphyNode *result = NULL;
GPtrArray *children;
int i;
children = ephy_node_get_children (states);
for (i = 0; i < children->len; i++)
{
EphyNode *kid;
const char *node_name;
kid = g_ptr_array_index (children, i);
node_name = ephy_node_get_property_string
(kid, EPHY_NODE_STATE_PROP_NAME);
if (strcmp (node_name, name) == 0)
{
result = kid;
}
}
return result;
}
static void
ensure_states (void)
{
if (states == NULL)
{
volatile GType flags_type; /* work around gcc's optimiser */
char *xml_file;
/* make sure the type is known when we read the db */
flags_type = EPHY_TYPE_STATE_WINDOW_FLAGS;
xml_file = g_build_filename (ephy_dot_dir (),
EPHY_STATES_XML_FILE,
NULL);
states_db = ephy_node_db_new (EPHY_NODE_DB_STATES);
states = ephy_node_new_with_id (states_db, STATES_NODE_ID);
ephy_node_db_load_from_file (states_db, xml_file,
EPHY_STATES_XML_ROOT,
EPHY_STATES_XML_VERSION);
g_free (xml_file);
}
}
static void
ephy_state_window_set_size (GtkWidget *window, EphyNode *node)
{
int width, height, w = -1, h = -1;
gboolean maximize, size;
width = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_WIDTH);
height = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_HEIGHT);
maximize = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_MAXIMIZE);
size = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_SIZE);
gtk_window_get_default_size (GTK_WINDOW (window), &w, &h);
if (size && w == -1 && h == -1)
{
GdkScreen *screen;
int screen_width, screen_height;
screen = gdk_screen_get_default ();
screen_width = gdk_screen_get_width (screen);
screen_height = gdk_screen_get_height (screen);
gtk_window_set_default_size (GTK_WINDOW (window),
MIN (width, screen_width),
MIN (height, screen_height));
}
if (maximize)
{
gtk_window_maximize (GTK_WINDOW (window));
}
}
static void
ephy_state_window_set_position (GtkWidget *window, EphyNode *node)
{
GdkScreen *screen;
int x, y;
int screen_width, screen_height;
gboolean maximize, size;
g_return_if_fail (GTK_IS_WINDOW (window));
/* Setting the default size doesn't work when the window is already showing. */
g_return_if_fail (!GTK_WIDGET_VISIBLE (window));
maximize = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_MAXIMIZE);
size = ephy_node_get_property_boolean (node, EPHY_NODE_STATE_PROP_POSITION);
/* Don't set the position of the window if it is maximized */
if ((!maximize) && size)
{
x = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_POSITION_X);
y = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_POSITION_Y);
screen = gtk_window_get_screen (GTK_WINDOW (window));
screen_width = gdk_screen_get_width (screen);
screen_height = gdk_screen_get_height (screen);
if ((x <= screen_width) && (y <= screen_height) &&
(x >= 0) && (y >= 0))
{
gtk_window_move (GTK_WINDOW (window), x, y);
}
}
}
static void
ephy_state_window_save_size (GtkWidget *window, EphyNode *node)
{
int width, height;
gboolean maximize;
GdkWindowState state;
GValue value = { 0, };
state = gdk_window_get_state (GTK_WIDGET (window)->window);
maximize = ((state & GDK_WINDOW_STATE_MAXIMIZED) > 0);
gtk_window_get_size (GTK_WINDOW(window),
&width, &height);
if (!maximize)
{
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, width);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, height);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_HEIGHT,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, TRUE);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_SIZE,
&value);
g_value_unset (&value);
}
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, maximize);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_MAXIMIZE,
&value);
g_value_unset (&value);
}
static void
ephy_state_window_save_position (GtkWidget *window, EphyNode *node)
{
int x,y;
gboolean maximize;
GdkWindowState state;
GValue value = { 0, };
state = gdk_window_get_state (GTK_WIDGET (window)->window);
maximize = ((state & GDK_WINDOW_STATE_MAXIMIZED) > 0);
/* Don't save the position if maximized */
if (!maximize)
{
gtk_window_get_position (GTK_WINDOW (window), &x, &y);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, x);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION_X,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, y);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION_Y,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, TRUE);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_POSITION,
&value);
g_value_unset (&value);
}
}
static void
ephy_state_window_save (GtkWidget *widget, EphyNode *node)
{
EphyStateWindowFlags flags;
flags = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "state_flags"));
if (flags & EPHY_STATE_WINDOW_SAVE_SIZE)
{
ephy_state_window_save_size (widget, node);
}
if (flags & EPHY_STATE_WINDOW_SAVE_POSITION)
{
ephy_state_window_save_position (widget, node);
}
}
static gboolean
window_configure_event_cb (GtkWidget *widget,
GdkEventConfigure *event,
EphyNode *node)
{
GdkWindowState state;
state = gdk_window_get_state (widget->window);
if (!(state & GDK_WINDOW_STATE_FULLSCREEN))
{
ephy_state_window_save (widget, node);
}
return FALSE;
}
static gboolean
window_state_event_cb (GtkWidget *widget,
GdkEventWindowState *event,
EphyNode *node)
{
if (!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN))
{
ephy_state_window_save (widget, node);
}
return FALSE;
}
static EphyNode *
create_window_node (const char *name,
int default_width,
int default_height,
gboolean maximize,
EphyStateWindowFlags flags)
{
EphyNode *node;
GValue value = { 0, };
node = ephy_node_new (states_db);
ephy_node_add_child (states, node);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, name);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_NAME,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, maximize);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_MAXIMIZE,
&value);
g_value_unset (&value);
if (flags & EPHY_STATE_WINDOW_SAVE_SIZE)
{
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, TRUE);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_SIZE,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, default_width);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, default_height);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_HEIGHT,
&value);
g_value_unset (&value);
}
return node;
}
void
ephy_state_add_window (GtkWidget *window,
const char *name,
int default_width,
int default_height,
gboolean maximize,
EphyStateWindowFlags flags)
{
EphyNode *node;
ensure_states ();
node = find_by_name (name);
if (node == NULL)
{
node = create_window_node (name, default_width, default_height,
maximize, flags);
}
ephy_state_window_set_size (window, node);
ephy_state_window_set_position (window, node);
g_object_set_data (G_OBJECT (window), "state_flags", GINT_TO_POINTER (flags));
g_signal_connect (window, "configure_event",
G_CALLBACK (window_configure_event_cb), node);
g_signal_connect (window, "window_state_event",
G_CALLBACK (window_state_event_cb), node);
}
static gboolean
paned_sync_position_cb (GtkWidget *paned,
GParamSpec *pspec,
EphyNode *node)
{
int width;
GValue value = { 0, };
width = gtk_paned_get_position (GTK_PANED (paned));
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, width);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
&value);
g_value_unset (&value);
return FALSE;
}
void
ephy_state_add_paned (GtkWidget *paned,
const char *name,
int default_width)
{
EphyNode *node;
int width;
ensure_states ();
node = find_by_name (name);
if (node == NULL)
{
GValue value = { 0, };
node = ephy_node_new (states_db);
ephy_node_add_child (states, node);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, name);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_NAME,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_INT);
g_value_set_int (&value, default_width);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_WIDTH,
&value);
g_value_unset (&value);
}
width = ephy_node_get_property_int (node, EPHY_NODE_STATE_PROP_WIDTH);
gtk_paned_set_position (GTK_PANED (paned), width);
g_signal_connect (paned, "notify::position",
G_CALLBACK (paned_sync_position_cb), node);
}
static void
sync_expander_cb (GtkExpander *expander,
GParamSpec *pspec,
EphyNode *node)
{
GValue value = { 0, };
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, gtk_expander_get_expanded (expander));
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_EXPANDED, &value);
g_value_unset (&value);
}
void
ephy_state_add_expander (GtkWidget *expander,
const char *name,
gboolean default_state)
{
EphyNode *node;
gboolean expanded;
ensure_states ();
node = find_by_name (name);
if (node == NULL)
{
GValue value = { 0, };
node = ephy_node_new (states_db);
ephy_node_add_child (states, node);
g_value_init (&value, G_TYPE_STRING);
g_value_set_string (&value, name);
ephy_node_set_property (node, EPHY_NODE_STATE_PROP_NAME,
&value);
g_value_unset (&value);
g_value_init (&value, G_TYPE_BOOLEAN);
g_value_set_boolean (&value, default_state);
ephy_node_set_property
(node, EPHY_NODE_STATE_PROP_EXPANDED, &value);
g_value_unset (&value);
}
expanded = ephy_node_get_property_boolean
(node, EPHY_NODE_STATE_PROP_EXPANDED);
gtk_expander_set_expanded (GTK_EXPANDER (expander), expanded);
g_signal_connect (expander, "notify::expanded",
G_CALLBACK (sync_expander_cb), node);
}
void
ephy_state_save (void)
{
if (states)
{
ephy_states_save ();
ephy_node_unref (states);
g_object_unref (states_db);
states = NULL;
states_db = NULL;
}
}