medit/moo/mooutils/moobigpaned.c
2016-01-08 04:41:48 -08:00

1868 lines
54 KiB
C

/*
* moobigpaned.c
*
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
*
* This file is part of medit. medit is free software; you can
* redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the
* Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* class:MooBigPaned: (parent GtkFrame) (constructable) (moo.private 1)
**/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "moobigpaned.h"
#include "marshals.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "mooutils-misc.h"
#include "moocompat.h"
typedef struct {
GSList *order; /* ids */
int size;
int sticky;
char *active;
} MooPanedConfig;
typedef struct {
MooPanedConfig paned[4];
GHashTable *panes;
} MooBigPanedConfig;
typedef struct {
int bbox_size;
GdkRegion *bbox_region;
GdkRegion *def_region;
} DZ;
struct _MooBigPanedPrivate {
MooPanePosition order[4]; /* inner is paned[order[3]] */
GtkWidget *inner;
GtkWidget *outer;
MooBigPanedConfig *config;
GHashTable *panes;
int drop_pos;
int drop_button_index;
GdkRectangle drop_rect;
GdkRectangle drop_button_rect;
GdkWindow *drop_outline;
DZ *dz;
GdkRegion *drop_region;
guint drop_region_is_buttons : 1;
};
static void moo_big_paned_finalize (GObject *object);
static void moo_big_paned_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void moo_big_paned_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static gboolean moo_big_paned_expose (GtkWidget *widget,
GdkEventExpose *event,
MooBigPaned *paned);
static void child_set_pane_size (GtkWidget *child,
int size,
MooBigPaned *paned);
static void sticky_pane_notify (GtkWidget *child,
GParamSpec *pspec,
MooBigPaned *paned);
static void active_pane_notify (GtkWidget *child,
GParamSpec *pspec,
MooBigPaned *paned);
static gboolean check_children_order (MooBigPaned *paned);
static void handle_drag_start (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned);
static void handle_drag_motion (MooPaned *child,
GtkWidget *pane_widget,
MooBigPaned *paned);
static void handle_drag_end (MooPaned *child,
GtkWidget *pane_widget,
gboolean drop,
MooBigPaned *paned);
static void config_changed (MooBigPaned *paned);
static MooBigPanedConfig *config_new (void);
static void config_free (MooBigPanedConfig *config);
static MooBigPanedConfig *config_parse (const char *string);
static char *config_serialize (MooBigPanedConfig *config);
/* MOO_TYPE_BIG_PANED */
G_DEFINE_TYPE (MooBigPaned, moo_big_paned, GTK_TYPE_FRAME)
enum {
PROP_0,
PROP_PANE_ORDER,
PROP_ENABLE_HANDLE_DRAG,
PROP_ENABLE_DETACHING,
PROP_HANDLE_CURSOR_TYPE
};
enum {
CONFIG_CHANGED,
NUM_SIGNALS
};
static guint signals[NUM_SIGNALS];
static void
moo_big_paned_class_init (MooBigPanedClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (MooBigPanedPrivate));
gobject_class->finalize = moo_big_paned_finalize;
gobject_class->set_property = moo_big_paned_set_property;
gobject_class->get_property = moo_big_paned_get_property;
g_object_class_install_property (gobject_class,
PROP_PANE_ORDER,
g_param_spec_pointer ("pane-order",
"pane-order",
"pane-order",
(GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_ENABLE_HANDLE_DRAG,
g_param_spec_boolean ("enable-handle-drag",
"enable-handle-drag",
"enable-handle-drag",
TRUE,
(GParamFlags) (G_PARAM_CONSTRUCT | G_PARAM_WRITABLE)));
g_object_class_install_property (gobject_class,
PROP_ENABLE_DETACHING,
g_param_spec_boolean ("enable-detaching",
"enable-detaching",
"enable-detaching",
FALSE,
(GParamFlags) (G_PARAM_CONSTRUCT | G_PARAM_WRITABLE)));
g_object_class_install_property (gobject_class,
PROP_HANDLE_CURSOR_TYPE,
g_param_spec_enum ("handle-cursor-type",
"handle-cursor-type",
"handle-cursor-type",
GDK_TYPE_CURSOR_TYPE,
GDK_HAND2,
(GParamFlags) (G_PARAM_CONSTRUCT | G_PARAM_READWRITE)));
signals[CONFIG_CHANGED] =
g_signal_new ("config-changed",
G_OBJECT_CLASS_TYPE (klass),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
_moo_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
#define NTH_CHILD(paned,n) paned->paned[paned->priv->order[n]]
static void
moo_big_paned_init (MooBigPaned *paned)
{
int i;
paned->priv = G_TYPE_INSTANCE_GET_PRIVATE (paned,
MOO_TYPE_BIG_PANED,
MooBigPanedPrivate);
paned->priv->panes = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
paned->priv->config = config_new ();
paned->priv->drop_pos = -1;
/* XXX destroy */
for (i = 0; i < 4; ++i)
{
GtkWidget *child;
paned->paned[i] = child =
GTK_WIDGET (g_object_new (MOO_TYPE_PANED,
"pane-position", (MooPanePosition) i,
(const char*) NULL));
g_object_ref_sink (child);
gtk_widget_show (child);
g_signal_connect_after (child, "set-pane-size",
G_CALLBACK (child_set_pane_size),
paned);
g_signal_connect (child, "notify::sticky-pane",
G_CALLBACK (sticky_pane_notify),
paned);
g_signal_connect (child, "notify::active-pane",
G_CALLBACK (active_pane_notify),
paned);
g_signal_connect (child, "handle-drag-start",
G_CALLBACK (handle_drag_start),
paned);
g_signal_connect (child, "handle-drag-motion",
G_CALLBACK (handle_drag_motion),
paned);
g_signal_connect (child, "handle-drag-end",
G_CALLBACK (handle_drag_end),
paned);
}
paned->priv->order[0] = MOO_PANE_POS_LEFT;
paned->priv->order[1] = MOO_PANE_POS_RIGHT;
paned->priv->order[2] = MOO_PANE_POS_TOP;
paned->priv->order[3] = MOO_PANE_POS_BOTTOM;
paned->priv->inner = NTH_CHILD (paned, 3);
paned->priv->outer = NTH_CHILD (paned, 0);
gtk_container_add (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_add (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
g_assert (check_children_order (paned));
}
static gboolean
check_children_order (MooBigPaned *paned)
{
int i;
if (GTK_BIN (paned)->child != NTH_CHILD (paned, 0))
return FALSE;
for (i = 0; i < 3; ++i)
if (GTK_BIN (NTH_CHILD (paned, i))->child != NTH_CHILD (paned, i+1))
return FALSE;
return TRUE;
}
void
moo_big_paned_set_pane_order (MooBigPaned *paned,
int *order)
{
MooPanePosition new_order[4];
int i;
GtkWidget *child;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (order != NULL);
for (i = 0; i < 4; ++i)
{
g_return_if_fail (0 <= order[i] && order[i] < 4);
new_order[i] = (MooPanePosition) order[i];
}
g_return_if_fail (check_children_order (paned));
for (i = 0; i < 4; ++i)
{
if (new_order[i] != paned->priv->order[i])
break;
}
if (i == 4)
return;
child = moo_big_paned_get_child (paned);
if (child)
g_object_ref (child);
gtk_container_remove (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_remove (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
if (child)
gtk_container_remove (GTK_CONTAINER (NTH_CHILD (paned, 3)), child);
for (i = 0; i < 4; ++i)
paned->priv->order[i] = new_order[i];
gtk_container_add (GTK_CONTAINER (paned), NTH_CHILD (paned, 0));
for (i = 0; i < 3; ++i)
gtk_container_add (GTK_CONTAINER (NTH_CHILD (paned, i)), NTH_CHILD (paned, i+1));
paned->priv->inner = NTH_CHILD (paned, 3);
paned->priv->outer = NTH_CHILD (paned, 0);
if (child)
{
gtk_container_add (GTK_CONTAINER (paned->priv->inner), child);
g_object_unref (child);
}
g_assert (check_children_order (paned));
g_object_notify (G_OBJECT (paned), "pane-order");
}
static void
moo_big_paned_finalize (GObject *object)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
int i;
g_hash_table_destroy (paned->priv->panes);
config_free (paned->priv->config);
for (i = 0; i < 4; ++i)
g_object_unref (paned->paned[i]);
if (paned->priv->drop_outline)
{
g_critical ("oops");
gdk_window_set_user_data (paned->priv->drop_outline, NULL);
gdk_window_destroy (paned->priv->drop_outline);
}
G_OBJECT_CLASS (moo_big_paned_parent_class)->finalize (object);
}
GtkWidget *
moo_big_paned_new (void)
{
return GTK_WIDGET (g_object_new (MOO_TYPE_BIG_PANED, (const char*) NULL));
}
static void
config_changed (MooBigPaned *paned)
{
g_signal_emit (paned, signals[CONFIG_CHANGED], 0);
}
static void
child_set_pane_size (GtkWidget *child,
int size,
MooBigPaned *paned)
{
MooPanePosition pos;
g_object_get (child, "pane-position", &pos, NULL);
g_return_if_fail (paned->paned[pos] == child);
paned->priv->config->paned[pos].size = size;
config_changed (paned);
}
static void
sticky_pane_notify (GtkWidget *child,
G_GNUC_UNUSED GParamSpec *pspec,
MooBigPaned *paned)
{
MooPanePosition pos;
gboolean sticky;
g_object_get (child, "pane-position", &pos,
"sticky-pane", &sticky, NULL);
g_return_if_fail (paned->paned[pos] == child);
if (paned->priv->config->paned[pos].sticky != sticky)
{
paned->priv->config->paned[pos].sticky = sticky;
config_changed (paned);
}
}
static void
active_pane_notify (GtkWidget *child,
G_GNUC_UNUSED GParamSpec *pspec,
MooBigPaned *paned)
{
MooPanePosition pos;
MooPane *pane = NULL;
const char *id = NULL;
MooPanedConfig *pc;
g_object_get (child, "pane-position", &pos,
"active-pane", &pane, NULL);
g_return_if_fail (paned->paned[pos] == child);
if (pane)
id = moo_pane_get_id (pane);
pc = &paned->priv->config->paned[pos];
if ((id && !pc->active) || (!id && pc->active) ||
(id && pc->active && strcmp (id, pc->active) != 0))
{
g_free (pc->active);
pc->active = g_strdup (id);
config_changed (paned);
}
if (pane)
g_object_unref (pane);
}
static void
pane_params_changed (MooPane *pane,
G_GNUC_UNUSED GParamSpec *pspec,
MooBigPaned *paned)
{
const char *id;
MooPaneParams *params;
id = moo_pane_get_id (pane);
g_return_if_fail (id != NULL);
params = moo_pane_get_params (pane);
g_hash_table_insert (paned->priv->config->panes, g_strdup (id), params);
config_changed (paned);
}
static MooPaneParams *
get_pane_config (MooBigPaned *paned,
const char *id,
MooPanePosition *position,
int *index)
{
MooPaneParams *params;
int pos;
gboolean found = FALSE;
g_return_val_if_fail (id != NULL, NULL);
if (!(params = (MooPaneParams*) g_hash_table_lookup (paned->priv->config->panes, id)))
return NULL;
for (pos = 0; pos < 4 && !found; ++pos)
{
GSList *link;
int index_here = 0;
for (link = paned->priv->config->paned[pos].order;
link != NULL && !found;
link = link->next)
{
if (strcmp ((const char*) link->data, id) == 0)
{
*index = index_here;
*position = (MooPanePosition) pos;
found = TRUE;
}
else if (g_hash_table_lookup (paned->priv->panes, link->data))
{
index_here += 1;
}
}
}
if (!found)
{
g_critical ("%s: oops", G_STRFUNC);
params = NULL;
}
return params;
}
static void
add_pane_id_to_list (MooBigPaned *paned,
MooPanedConfig *pc,
const char *id,
int index)
{
GSList *link;
int real_index;
g_return_if_fail (index >= 0);
for (link = pc->order, real_index = 0;
link != NULL;
link = link->next, ++real_index)
{
if (index == 0)
break;
if (g_hash_table_lookup (paned->priv->panes, link->data))
index -= 1;
}
pc->order = g_slist_insert (pc->order, g_strdup (id), real_index);
}
static void
add_pane_config (MooBigPaned *paned,
const char *id,
MooPane *pane,
MooPanePosition pos,
int index)
{
MooPaneParams *params;
g_return_if_fail (index >= 0);
g_return_if_fail (!g_hash_table_lookup (paned->priv->config->panes, id));
params = moo_pane_get_params (pane);
g_hash_table_insert (paned->priv->config->panes, g_strdup (id), params);
add_pane_id_to_list (paned, &paned->priv->config->paned[pos], id, index);
config_changed (paned);
}
static void
move_pane_config (MooBigPaned *paned,
const char *id,
MooPanePosition old_pos,
MooPanePosition new_pos,
int new_index)
{
GSList *old_link;
MooBigPanedConfig *config = paned->priv->config;
MooPanedConfig *old_pc;
g_return_if_fail (new_index >= 0);
g_return_if_fail (g_hash_table_lookup (config->panes, id) != NULL);
old_pc = &config->paned[old_pos];
old_link = g_slist_find_custom (old_pc->order, id, (GCompareFunc) strcmp);
g_return_if_fail (old_link != NULL);
g_free (old_link->data);
old_pc->order = g_slist_delete_link (old_pc->order, old_link);
add_pane_id_to_list (paned, &paned->priv->config->paned[new_pos], id, new_index);
config_changed (paned);
}
void
moo_big_paned_set_config (MooBigPaned *paned,
const char *string)
{
MooBigPanedConfig *config;
int pos;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
if (!string || !string[0])
return;
for (pos = 0; pos < 4; ++pos)
{
if (paned->priv->config->paned[pos].order != NULL)
{
g_critical ("%s: this function may be called only once "
"and before any panes are added", G_STRFUNC);
return;
}
}
if (g_hash_table_size (paned->priv->config->panes) != 0)
{
g_critical ("%s: this function may be called only once "
"and before any panes are added", G_STRFUNC);
return;
}
if (!(config = config_parse (string)))
return;
config_free (paned->priv->config);
paned->priv->config = config;
for (pos = 0; pos < 4; ++pos)
{
if (config->paned[pos].size > 0)
moo_paned_set_pane_size (MOO_PANED (paned->paned[pos]),
config->paned[pos].size);
if (config->paned[pos].sticky >= 0)
moo_paned_set_sticky_pane (MOO_PANED (paned->paned[pos]),
config->paned[pos].sticky);
}
}
char *
moo_big_paned_get_config (MooBigPaned *paned)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
return config_serialize (paned->priv->config);
}
MooPane *
moo_big_paned_insert_pane (MooBigPaned *paned,
GtkWidget *pane_widget,
const char *id,
MooPaneLabel *pane_label,
MooPanePosition position,
int index)
{
MooPane *pane;
MooPaneParams *params = NULL;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (pane_widget), NULL);
g_return_val_if_fail (position < 4, NULL);
if (id && moo_big_paned_lookup_pane (paned, id) != NULL)
{
g_critical ("%s: pane with id '%s' already exists",
G_STRFUNC, id);
return NULL;
}
if (id)
params = get_pane_config (paned, id, &position, &index);
if (index < 0)
index = moo_paned_n_panes (MOO_PANED (paned->paned[position]));
pane = moo_paned_insert_pane (MOO_PANED (paned->paned[position]),
pane_widget, pane_label, index);
if (pane && id)
{
_moo_pane_set_id (pane, id);
if (params)
moo_pane_set_params (pane, params);
g_hash_table_insert (paned->priv->panes,
g_strdup (id),
g_object_ref (pane));
g_signal_connect (pane, "notify::params",
G_CALLBACK (pane_params_changed),
paned);
if (!params)
add_pane_config (paned, id, pane, position, index);
if (paned->priv->config->paned[position].active &&
!strcmp (paned->priv->config->paned[position].active, id))
moo_pane_open (pane);
}
return pane;
}
void
moo_big_paned_reorder_pane (MooBigPaned *paned,
GtkWidget *pane_widget,
MooPanePosition new_position,
int new_index)
{
MooPane *pane;
MooPaned *child;
MooPanePosition old_position;
int old_index;
const char *id;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (pane_widget));
g_return_if_fail (new_position < 4);
pane = moo_big_paned_find_pane (paned, pane_widget, &child);
g_return_if_fail (pane != NULL);
g_object_get (child, "pane-position", &old_position, NULL);
old_index = moo_paned_get_pane_num (child, pane_widget);
g_return_if_fail (old_index >= 0);
if (new_index < 0)
{
if (old_position == new_position)
new_index = (int) moo_paned_n_panes (child) - 1;
else
new_index = moo_paned_n_panes (MOO_PANED (paned->paned[new_position]));
}
if (old_position == new_position && old_index == new_index)
return;
pane = moo_paned_get_pane (child, pane_widget);
if (old_position == new_position)
{
_moo_paned_reorder_child (child, pane, new_index);
}
else
{
g_object_ref (pane);
moo_paned_remove_pane (child, pane_widget);
_moo_paned_insert_pane (MOO_PANED (paned->paned[new_position]), pane, new_index);
moo_pane_open (pane);
g_object_unref (pane);
}
if ((id = moo_pane_get_id (pane)))
move_pane_config (paned, id, old_position, new_position, new_index);
}
void
moo_big_paned_add_child (MooBigPaned *paned,
GtkWidget *child)
{
g_return_if_fail (MOO_IS_BIG_PANED (paned));
gtk_container_add (GTK_CONTAINER (paned->priv->inner), child);
}
void
moo_big_paned_remove_child (MooBigPaned *paned)
{
g_return_if_fail (MOO_IS_BIG_PANED (paned));
gtk_container_remove (GTK_CONTAINER (paned->priv->inner),
GTK_BIN (paned->priv->inner)->child);
}
GtkWidget *
moo_big_paned_get_child (MooBigPaned *paned)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
return GTK_BIN(paned->priv->inner)->child;
}
gboolean
moo_big_paned_remove_pane (MooBigPaned *paned,
GtkWidget *widget)
{
MooPaned *child;
MooPane *pane;
const char *id;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), FALSE);
g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
pane = moo_big_paned_find_pane (paned, widget, &child);
if (!pane)
return FALSE;
if ((id = moo_pane_get_id (pane)))
g_hash_table_remove (paned->priv->panes, id);
return moo_paned_remove_pane (child, widget);
}
MooPane *
moo_big_paned_lookup_pane (MooBigPaned *paned,
const char *pane_id)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (pane_id != NULL, NULL);
return MOO_PANE (g_hash_table_lookup (paned->priv->panes, pane_id));
}
#define PROXY_FUNC(name) \
void \
moo_big_paned_##name (MooBigPaned *paned, \
GtkWidget *widget) \
{ \
MooPane *pane; \
MooPaned *child = NULL; \
\
g_return_if_fail (MOO_IS_BIG_PANED (paned)); \
g_return_if_fail (GTK_IS_WIDGET (widget)); \
\
pane = moo_big_paned_find_pane (paned, widget, &child); \
g_return_if_fail (pane != NULL); \
\
moo_paned_##name (child, pane); \
}
PROXY_FUNC (open_pane)
PROXY_FUNC (present_pane)
PROXY_FUNC (attach_pane)
PROXY_FUNC (detach_pane)
#undef PROXY_FUNC
void
moo_big_paned_hide_pane (MooBigPaned *paned,
GtkWidget *widget)
{
MooPaned *child = NULL;
g_return_if_fail (MOO_IS_BIG_PANED (paned));
g_return_if_fail (GTK_IS_WIDGET (widget));
moo_big_paned_find_pane (paned, widget, &child);
g_return_if_fail (child != NULL);
moo_paned_hide_pane (child);
}
MooPaned *
moo_big_paned_get_paned (MooBigPaned *paned,
MooPanePosition position)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (position < 4, NULL);
return MOO_PANED (paned->paned[position]);
}
MooPane *
moo_big_paned_find_pane (MooBigPaned *paned,
GtkWidget *widget,
MooPaned **child_paned)
{
int i;
MooPane *pane;
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
if (child_paned)
*child_paned = NULL;
for (i = 0; i < 4; ++i)
{
pane = moo_paned_get_pane (MOO_PANED (paned->paned[i]), widget);
if (pane)
{
if (child_paned)
*child_paned = MOO_PANED (paned->paned[i]);
return pane;
}
}
return NULL;
}
static void
moo_big_paned_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
int i;
switch (prop_id)
{
case PROP_PANE_ORDER:
moo_big_paned_set_pane_order (paned, (int*) g_value_get_pointer (value));
break;
case PROP_ENABLE_HANDLE_DRAG:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i],
"enable-handle-drag",
g_value_get_boolean (value),
NULL);
break;
case PROP_ENABLE_DETACHING:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i],
"enable-detaching",
g_value_get_boolean (value),
NULL);
break;
case PROP_HANDLE_CURSOR_TYPE:
for (i = 0; i < 4; ++i)
g_object_set (paned->paned[i], "handle-cursor-type",
(GdkCursorType) g_value_get_enum (value),
NULL);
g_object_notify (object, "handle-cursor-type");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
moo_big_paned_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MooBigPaned *paned = MOO_BIG_PANED (object);
GdkCursorType cursor_type;
switch (prop_id)
{
case PROP_PANE_ORDER:
g_value_set_pointer (value, paned->priv->order);
break;
case PROP_HANDLE_CURSOR_TYPE:
g_object_get (paned->paned[0], "handle-cursor-type",
&cursor_type, NULL);
g_value_set_enum (value, cursor_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
GtkWidget *
moo_big_paned_get_pane (MooBigPaned *paned,
MooPanePosition position,
int index_)
{
g_return_val_if_fail (MOO_IS_BIG_PANED (paned), NULL);
g_return_val_if_fail (position < 4, NULL);
return moo_pane_get_child (moo_paned_get_nth_pane (MOO_PANED (paned->paned[position]), index_));
}
/*****************************************************************************/
/* rearranging panes
*/
static void create_drop_outline (MooBigPaned *paned);
static void get_drop_area (MooBigPaned *paned,
MooPaned *active_child,
MooPanePosition position,
int index,
GdkRectangle *rect,
GdkRectangle *button_rect);
// static void invalidate_drop_outline (MooBigPaned *paned);
static GdkRegion *
region_6 (int x0, int y0,
int x1, int y1,
int x2, int y2,
int x3, int y3,
int x4, int y4,
int x5, int y5)
{
GdkPoint points[6];
points[0].x = x0; points[0].y = y0;
points[1].x = x1; points[1].y = y1;
points[2].x = x2; points[2].y = y2;
points[3].x = x3; points[3].y = y3;
points[4].x = x4; points[4].y = y4;
points[5].x = x5; points[5].y = y5;
return gdk_region_polygon (points, 6, GDK_WINDING_RULE);
}
static GdkRegion *
region_4 (int x0, int y0,
int x1, int y1,
int x2, int y2,
int x3, int y3)
{
GdkPoint points[4];
points[0].x = x0; points[0].y = y0;
points[1].x = x1; points[1].y = y1;
points[2].x = x2; points[2].y = y2;
points[3].x = x3; points[3].y = y3;
return gdk_region_polygon (points, 4, GDK_WINDING_RULE);
}
#define LEFT(rect) ((rect).x)
#define RIGHT(rect) ((rect).x + (rect).width - 1)
#define TOP(rect) ((rect).y)
#define BOTTOM(rect) ((rect).y + (rect).height - 1)
static void
get_drop_zones (MooBigPaned *paned)
{
int pos;
GdkRectangle parent;
GdkRectangle child_rect;
GdkRectangle button_rect;
GdkRectangle drop_rect;
int bbox_size[4];
g_return_if_fail (paned->priv->dz == NULL);
paned->priv->dz = g_new0 (DZ, 4);
for (pos = 0; pos < 4; ++pos)
{
bbox_size[pos] = moo_paned_get_button_box_size (MOO_PANED (paned->paned[pos]));
if (bbox_size[pos] <= 0)
bbox_size[pos] = 30;
paned->priv->dz[pos].bbox_size = bbox_size[pos];
}
parent = paned->priv->outer->allocation;
child_rect = parent;
child_rect.x += bbox_size[MOO_PANE_POS_LEFT];
child_rect.width -= bbox_size[MOO_PANE_POS_LEFT] +
bbox_size[MOO_PANE_POS_RIGHT];
child_rect.y += bbox_size[MOO_PANE_POS_TOP];
child_rect.height -= bbox_size[MOO_PANE_POS_TOP] +
bbox_size[MOO_PANE_POS_BOTTOM];
button_rect = parent;
button_rect.x += 2*bbox_size[MOO_PANE_POS_LEFT];
button_rect.width -= 2*bbox_size[MOO_PANE_POS_LEFT] +
2*bbox_size[MOO_PANE_POS_RIGHT];
button_rect.y += 2*bbox_size[MOO_PANE_POS_TOP];
button_rect.height -= 2*bbox_size[MOO_PANE_POS_TOP] +
2*bbox_size[MOO_PANE_POS_BOTTOM];
drop_rect = button_rect;
drop_rect.x += button_rect.width / 3;
drop_rect.y += button_rect.height / 3;
drop_rect.width -= 2 * button_rect.width / 3;
drop_rect.height -= 2 * button_rect.height / 3;
paned->priv->dz[MOO_PANE_POS_TOP].bbox_region =
region_6 (LEFT (child_rect), TOP (parent),
RIGHT (child_rect), TOP (parent),
RIGHT (child_rect), TOP (child_rect),
RIGHT (button_rect), TOP (button_rect),
LEFT (button_rect), TOP (button_rect),
LEFT (child_rect), TOP (child_rect));
paned->priv->dz[MOO_PANE_POS_TOP].def_region =
region_4 (LEFT (button_rect), TOP (button_rect),
RIGHT (button_rect), TOP (button_rect),
RIGHT (drop_rect), TOP (drop_rect),
LEFT (drop_rect), TOP (drop_rect));
paned->priv->dz[MOO_PANE_POS_LEFT].bbox_region =
region_6 (LEFT (parent), TOP (child_rect),
LEFT (child_rect), TOP (child_rect),
LEFT (button_rect), TOP (button_rect),
LEFT (button_rect), BOTTOM (button_rect),
LEFT (child_rect), BOTTOM (child_rect),
LEFT (parent), BOTTOM (child_rect));
paned->priv->dz[MOO_PANE_POS_LEFT].def_region =
region_4 (LEFT (button_rect), TOP (button_rect),
LEFT (drop_rect), TOP (drop_rect),
LEFT (drop_rect), BOTTOM (drop_rect),
LEFT (button_rect), BOTTOM (button_rect));
paned->priv->dz[MOO_PANE_POS_BOTTOM].bbox_region =
region_6 (LEFT (child_rect), BOTTOM (parent),
LEFT (child_rect), BOTTOM (child_rect),
LEFT (button_rect), BOTTOM (button_rect),
RIGHT (button_rect), BOTTOM (button_rect),
RIGHT (child_rect), BOTTOM (child_rect),
RIGHT (child_rect), BOTTOM (parent));
paned->priv->dz[MOO_PANE_POS_BOTTOM].def_region =
region_4 (LEFT (button_rect), BOTTOM (button_rect),
LEFT (drop_rect), BOTTOM (drop_rect),
RIGHT (drop_rect), BOTTOM (drop_rect),
RIGHT (button_rect), BOTTOM (button_rect));
paned->priv->dz[MOO_PANE_POS_RIGHT].bbox_region =
region_6 (RIGHT (parent), TOP (child_rect),
RIGHT (child_rect), TOP (child_rect),
RIGHT (button_rect), TOP (button_rect),
RIGHT (button_rect), BOTTOM (button_rect),
RIGHT (child_rect), BOTTOM (child_rect),
RIGHT (parent), BOTTOM (child_rect));
paned->priv->dz[MOO_PANE_POS_RIGHT].def_region =
region_4 (RIGHT (button_rect), TOP (button_rect),
RIGHT (drop_rect), TOP (drop_rect),
RIGHT (drop_rect), BOTTOM (drop_rect),
RIGHT (button_rect), BOTTOM (button_rect));
paned->priv->drop_region = NULL;
paned->priv->drop_region_is_buttons = FALSE;
}
#undef LEFT
#undef RIGHT
#undef TOP
#undef BOTTOM
static void
handle_drag_start (G_GNUC_UNUSED MooPaned *child,
G_GNUC_UNUSED GtkWidget *pane_widget,
MooBigPaned *paned)
{
g_return_if_fail (GTK_WIDGET_REALIZED (paned->priv->outer));
g_signal_connect (paned->priv->outer, "expose-event",
G_CALLBACK (moo_big_paned_expose), paned);
paned->priv->drop_pos = -1;
get_drop_zones (paned);
}
static gboolean
get_new_button_index (MooBigPaned *paned,
MooPaned *active_child,
int x,
int y)
{
int new_button;
MooPaned *child;
g_assert (paned->priv->drop_pos >= 0);
child = MOO_PANED (paned->paned[paned->priv->drop_pos]);
new_button = _moo_paned_get_button (child, x, y,
paned->priv->outer->window);
if (child == active_child)
{
int n_buttons = moo_paned_n_panes (child);
if (new_button >= n_buttons || new_button < 0)
new_button = n_buttons - 1;
}
if (new_button != paned->priv->drop_button_index)
{
paned->priv->drop_button_index = new_button;
return TRUE;
}
else
{
return FALSE;
}
}
static void
get_default_button_index (MooBigPaned *paned,
MooPaned *active_child)
{
MooPaned *child;
g_assert (paned->priv->drop_pos >= 0);
child = MOO_PANED (paned->paned[paned->priv->drop_pos]);
if (child == active_child)
paned->priv->drop_button_index =
_moo_paned_get_open_pane_index (child);
else
paned->priv->drop_button_index = -1;
}
static gboolean
get_new_drop_position (MooBigPaned *paned,
MooPaned *active_child,
int x,
int y)
{
int pos;
gboolean changed = FALSE;
if (paned->priv->drop_region)
{
g_assert (paned->priv->drop_pos >= 0);
if (gdk_region_point_in (paned->priv->drop_region, x, y))
{
if (!paned->priv->drop_region_is_buttons)
return FALSE;
else
return get_new_button_index (paned, active_child, x, y);
}
paned->priv->drop_pos = -1;
paned->priv->drop_region = NULL;
changed = TRUE;
}
for (pos = 0; pos < 4; ++pos)
{
if (gdk_region_point_in (paned->priv->dz[pos].def_region, x, y))
{
paned->priv->drop_pos = pos;
paned->priv->drop_region = paned->priv->dz[pos].def_region;
paned->priv->drop_region_is_buttons = FALSE;
get_default_button_index (paned, active_child);
return TRUE;
}
if (gdk_region_point_in (paned->priv->dz[pos].bbox_region, x, y))
{
paned->priv->drop_pos = pos;
paned->priv->drop_region = paned->priv->dz[pos].bbox_region;
paned->priv->drop_region_is_buttons = TRUE;
get_new_button_index (paned, active_child, x, y);
return TRUE;
}
}
return changed;
}
static void
handle_drag_motion (MooPaned *child,
G_GNUC_UNUSED GtkWidget *pane_widget,
MooBigPaned *paned)
{
int x, y;
g_return_if_fail (GTK_WIDGET_REALIZED (paned->priv->outer));
gdk_window_get_pointer (paned->priv->outer->window, &x, &y, NULL);
if (!get_new_drop_position (paned, child, x, y))
return;
if (paned->priv->drop_outline)
{
gdk_window_set_user_data (paned->priv->drop_outline, NULL);
gdk_window_destroy (paned->priv->drop_outline);
paned->priv->drop_outline = NULL;
}
if (paned->priv->drop_pos >= 0)
{
get_drop_area (paned, child,
(MooPanePosition) paned->priv->drop_pos,
paned->priv->drop_button_index,
&paned->priv->drop_rect,
&paned->priv->drop_button_rect);
create_drop_outline (paned);
}
}
static void
cleanup_drag (MooBigPaned *paned)
{
int pos;
if (paned->priv->drop_outline)
{
gdk_window_set_user_data (paned->priv->drop_outline, NULL);
gdk_window_destroy (paned->priv->drop_outline);
paned->priv->drop_outline = NULL;
}
paned->priv->drop_pos = -1;
paned->priv->drop_region = NULL;
g_signal_handlers_disconnect_by_func (paned->priv->outer,
(gpointer) moo_big_paned_expose,
paned);
for (pos = 0; pos < 4; ++pos)
{
gdk_region_destroy (paned->priv->dz[pos].bbox_region);
gdk_region_destroy (paned->priv->dz[pos].def_region);
}
g_free (paned->priv->dz);
paned->priv->dz = NULL;
}
static void
handle_drag_end (MooPaned *child,
GtkWidget *pane_widget,
gboolean drop,
MooBigPaned *paned)
{
int x, y;
MooPanePosition new_pos;
int new_index;
g_return_if_fail (GTK_WIDGET_REALIZED (paned->priv->outer));
if (!drop)
{
cleanup_drag (paned);
return;
}
gdk_window_get_pointer (paned->priv->outer->window, &x, &y, NULL);
get_new_drop_position (paned, child, x, y);
if (paned->priv->drop_pos < 0)
{
cleanup_drag (paned);
return;
}
new_pos = (MooPanePosition) paned->priv->drop_pos;
new_index = paned->priv->drop_button_index;
cleanup_drag (paned);
moo_big_paned_reorder_pane (paned, pane_widget, new_pos, new_index);
}
static void
get_drop_area (MooBigPaned *paned,
MooPaned *active_child,
MooPanePosition position,
int index,
GdkRectangle *rect,
GdkRectangle *button_rect)
{
int width, height, size = 0;
MooPanePosition active_position;
width = paned->priv->outer->allocation.width;
height = paned->priv->outer->allocation.height;
g_object_get (active_child, "pane-position", &active_position, NULL);
g_return_if_fail (active_position < 4);
if (active_position == position)
{
size = moo_paned_get_pane_size (active_child) +
moo_paned_get_button_box_size (active_child);
}
else
{
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
size = width / 3;
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
size = height / 3;
break;
}
}
switch (position)
{
case MOO_PANE_POS_LEFT:
case MOO_PANE_POS_RIGHT:
rect->y = paned->priv->outer->allocation.y;
rect->width = size;
rect->height = height;
break;
case MOO_PANE_POS_TOP:
case MOO_PANE_POS_BOTTOM:
rect->x = paned->priv->outer->allocation.x;
rect->width = width;
rect->height = size;
break;
}
switch (position)
{
case MOO_PANE_POS_LEFT:
rect->x = paned->priv->outer->allocation.x;
break;
case MOO_PANE_POS_RIGHT:
rect->x = paned->priv->outer->allocation.x + width - size;
break;
case MOO_PANE_POS_TOP:
rect->y = paned->priv->outer->allocation.y;
break;
case MOO_PANE_POS_BOTTOM:
rect->y = paned->priv->outer->allocation.y + height - size;
break;
}
_moo_paned_get_button_position (MOO_PANED (paned->paned[position]),
index, button_rect,
paned->priv->outer->window);
}
#define RECT_POINT_IN(rect,x,y) (x < (rect)->x + (rect)->width && \
y < (rect)->height + (rect)->y && \
x >= (rect)->x && y >= (rect)->y)
// static int
// get_drop_position (MooBigPaned *paned,
// MooPaned *child,
// int x,
// int y,
// int *button_index)
// {
// int width, height, i;
// MooPanePosition position;
// GdkRectangle rect, button_rect;
//
// *button_index = -1;
//
// width = paned->priv->outer->allocation.width;
// height = paned->priv->outer->allocation.height;
//
// if (x < paned->priv->outer->allocation.x ||
// x >= paned->priv->outer->allocation.x + width ||
// y < paned->priv->outer->allocation.y ||
// y >= paned->priv->outer->allocation.y + height)
// return -1;
//
// g_object_get (child, "pane-position", &position, NULL);
// g_return_val_if_fail (position < 4, -1);
//
// get_drop_area (paned, child, position, -1, &rect, &button_rect);
//
// if (RECT_POINT_IN (&rect, x, y))
// return position;
//
// for (i = 0; i < 4; ++i)
// {
// if (paned->priv->order[i] == position)
// continue;
//
// get_drop_area (paned, child, paned->priv->order[i], -1,
// &rect, &button_rect);
//
// if (RECT_POINT_IN (&rect, x, y))
// return paned->priv->order[i];
// }
//
// return -1;
// }
// static void
// invalidate_drop_outline (MooBigPaned *paned)
// {
// GdkRectangle line;
// GdkRegion *outline;
//
// outline = gdk_region_new ();
//
// line.x = paned->priv->drop_rect.x;
// line.y = paned->priv->drop_rect.y;
// line.width = 2;
// line.height = paned->priv->drop_rect.height;
// gdk_region_union_with_rect (outline, &line);
//
// line.x = paned->priv->drop_rect.x;
// line.y = paned->priv->drop_rect.y + paned->priv->drop_rect.height;
// line.width = paned->priv->drop_rect.width;
// line.height = 2;
// gdk_region_union_with_rect (outline, &line);
//
// line.x = paned->priv->drop_rect.x + paned->priv->drop_rect.width;
// line.y = paned->priv->drop_rect.y;
// line.width = 2;
// line.height = paned->priv->drop_rect.height;
// gdk_region_union_with_rect (outline, &line);
//
// line.x = paned->priv->drop_rect.x;
// line.y = paned->priv->drop_rect.y;
// line.width = paned->priv->drop_rect.width;
// line.height = 2;
// gdk_region_union_with_rect (outline, &line);
//
// gdk_window_invalidate_region (paned->priv->outer->window, outline, TRUE);
//
// gdk_region_destroy (outline);
// }
static gboolean
moo_big_paned_expose (GtkWidget *widget,
GdkEventExpose *event,
MooBigPaned *paned)
{
GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS (widget))->expose_event (widget, event);
if (paned->priv->drop_pos >= 0)
{
g_return_val_if_fail (paned->priv->drop_outline != NULL, FALSE);
gdk_draw_rectangle (paned->priv->drop_outline,
widget->style->fg_gc[GTK_STATE_NORMAL],
FALSE, 0, 0,
paned->priv->drop_rect.width - 1,
paned->priv->drop_rect.height - 1);
gdk_draw_rectangle (paned->priv->drop_outline,
widget->style->fg_gc[GTK_STATE_NORMAL],
FALSE, 1, 1,
paned->priv->drop_rect.width - 3,
paned->priv->drop_rect.height - 3);
}
return FALSE;
}
static GdkBitmap *
create_rect_mask (int width,
int height,
GdkRectangle *rect)
{
GdkBitmap *bitmap;
GdkGC *gc;
GdkColor white = {0, 0, 0, 0};
GdkColor black = {1, 1, 1, 1};
bitmap = gdk_pixmap_new (NULL, width, height, 1);
gc = gdk_gc_new (bitmap);
gdk_gc_set_foreground (gc, &white);
gdk_draw_rectangle (bitmap, gc, TRUE, 0, 0,
width, height);
gdk_gc_set_foreground (gc, &black);
gdk_draw_rectangle (bitmap, gc, FALSE, 0, 0,
width - 1, height - 1);
gdk_draw_rectangle (bitmap, gc, FALSE, 1, 1,
width - 3, height - 3);
gdk_draw_rectangle (bitmap, gc, FALSE,
rect->x, rect->y,
rect->width, rect->height);
gdk_draw_rectangle (bitmap, gc, FALSE,
rect->x + 1, rect->y + 1,
rect->width - 2, rect->height - 2);
g_object_unref (gc);
return bitmap;
}
static void
create_drop_outline (MooBigPaned *paned)
{
static GdkWindowAttr attributes;
int attributes_mask;
GdkBitmap *mask;
GdkRectangle button_rect;
g_return_if_fail (paned->priv->drop_outline == NULL);
attributes.x = paned->priv->drop_rect.x;
attributes.y = paned->priv->drop_rect.y;
attributes.width = paned->priv->drop_rect.width;
attributes.height = paned->priv->drop_rect.height;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.visual = gtk_widget_get_visual (paned->priv->outer);
attributes.colormap = gtk_widget_get_colormap (paned->priv->outer);
attributes.wclass = GDK_INPUT_OUTPUT;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
paned->priv->drop_outline = gdk_window_new (paned->priv->outer->window,
&attributes, attributes_mask);
gdk_window_set_user_data (paned->priv->drop_outline, paned);
button_rect = paned->priv->drop_button_rect;
button_rect.x -= paned->priv->drop_rect.x;
button_rect.y -= paned->priv->drop_rect.y;
mask = create_rect_mask (paned->priv->drop_rect.width,
paned->priv->drop_rect.height,
&button_rect);
gdk_window_shape_combine_mask (paned->priv->drop_outline, mask, 0, 0);
g_object_unref (mask);
gdk_window_show (paned->priv->drop_outline);
}
/*************************************************************************/
/* Config
*/
#define CONFIG_STRING_MAGIC "mbpconfig-1.0 "
#define CONFIG_STRING_MAGIC_LEN strlen (CONFIG_STRING_MAGIC)
static MooBigPanedConfig *
config_new (void)
{
MooBigPanedConfig *config;
int pos;
config = g_new0 (MooBigPanedConfig, 1);
for (pos = 0; pos < 4; pos++)
{
config->paned[pos].order = NULL;
config->paned[pos].size = -1;
config->paned[pos].sticky = -1;
config->paned[pos].active = NULL;
}
config->panes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) moo_pane_params_free);
return config;
}
static void
config_free (MooBigPanedConfig *config)
{
if (config)
{
int pos;
for (pos = 0; pos < 4; pos++)
{
g_slist_foreach (config->paned[pos].order, (GFunc) g_free, NULL);
g_slist_free (config->paned[pos].order);
g_free (config->paned[pos].active);
}
g_hash_table_destroy (config->panes);
g_free (config);
}
}
static gboolean
parse_int (const char *string,
int *valuep)
{
long value;
char *endptr;
errno = 0;
value = strtol (string, &endptr, 10);
if (errno || endptr == string || *endptr != 0 ||
value < G_MININT || value > G_MAXINT)
return FALSE;
*valuep = value;
return TRUE;
}
static gboolean
parse_bool (const char *string,
gboolean *value)
{
if (!string[0] || string[1] || (string[0] != '0' && string[0] != '1'))
return FALSE;
*value = string[0] == '1';
return TRUE;
}
static gboolean
parse_paned_config (const char *string,
MooPanedConfig *config)
{
char **tokens, **p;
tokens = g_strsplit (string, ",", 0);
if (g_strv_length (tokens) < 3)
{
g_critical ("%s: invalid paned config string '%s'",
G_STRFUNC, string);
goto error;
}
if (!parse_int (tokens[0], &config->size) || config->size < -1 || config->size > 100000)
{
g_critical ("%s: invalid size '%s' in paned config string '%s'",
G_STRFUNC, tokens[0], string);
goto error;
}
if (!parse_int (tokens[1], &config->sticky) ||
config->sticky < -1 || config->sticky > 1)
{
g_critical ("%s: invalid boolean '%s' in paned config string '%s'",
G_STRFUNC, tokens[1], string);
goto error;
}
if (tokens[2][0])
config->active = g_strdup (tokens[2]);
else
config->active = NULL;
for (p = tokens + 3; *p; ++p)
config->order = g_slist_prepend (config->order, g_strdup (*p));
config->order = g_slist_reverse (config->order);
g_strfreev (tokens);
return TRUE;
error:
g_strfreev (tokens);
return FALSE;
}
static void
paned_config_serialize (MooPanedConfig *config,
GString *string)
{
GSList *l;
g_string_append_printf (string, "%d,%d,%s", config->size, config->sticky,
config->active ? config->active : "");
for (l = config->order; l != NULL; l = l->next)
{
char *id = (char*) l->data;
g_string_append_printf (string, ",%s", id);
}
}
static gboolean
parse_pane_config (const char *string,
GHashTable *hash)
{
char **tokens = NULL;
const char *colon;
MooPaneParams params = {{-1, -1, -1, -1}, 0, 0, 0};
gboolean value[3];
if (!(colon = strchr (string, ':')) || colon == string)
{
g_critical ("%s: invalid pane config string '%s'",
G_STRFUNC, string);
goto error;
}
tokens = g_strsplit (colon + 1, ",", 0);
if (g_strv_length (tokens) != 7)
{
g_critical ("%s: invalid pane config string '%s'",
G_STRFUNC, string);
goto error;
}
if (!parse_int (tokens[0], &params.window_position.x) ||
!parse_int (tokens[1], &params.window_position.y) ||
!parse_int (tokens[2], &params.window_position.width) ||
!parse_int (tokens[3], &params.window_position.height) ||
params.window_position.width < -1 || params.window_position.width > 100000 ||
params.window_position.height < -1 || params.window_position.height > 100000)
{
g_critical ("%s: invalid window position in pane config string '%s'",
G_STRFUNC, string);
goto error;
}
if (!parse_bool (tokens[4], &value[0]) ||
!parse_bool (tokens[5], &value[1]) ||
!parse_bool (tokens[6], &value[2]))
{
g_critical ("%s: invalid parameters in pane config string '%s'",
G_STRFUNC, string);
goto error;
}
params.detached = value[0];
params.maximized = value[1];
params.keep_on_top = value[2];
g_hash_table_insert (hash,
g_strndup (string, colon - string),
moo_pane_params_copy (&params));
g_strfreev (tokens);
return TRUE;
error:
g_strfreev (tokens);
return FALSE;
}
static void
pane_config_serialize (const char *id,
MooPaneParams *params,
GString *string)
{
g_string_append_printf (string, ";%s:%d,%d,%d,%d,%s,%s,%s",
id,
params->window_position.x,
params->window_position.y,
params->window_position.width,
params->window_position.height,
params->detached ? "1" : "0",
params->maximized ? "1" : "0",
params->keep_on_top ? "1" : "0");
}
static MooBigPanedConfig *
config_parse (const char *string)
{
MooBigPanedConfig *config = NULL;
char **tokens = NULL;
char **p;
int pos;
g_return_val_if_fail (string != NULL, NULL);
if (strncmp (string, CONFIG_STRING_MAGIC, CONFIG_STRING_MAGIC_LEN) != 0)
{
g_critical ("%s: invalid magic in config string '%s'", G_STRFUNC, string);
goto error;
}
string += CONFIG_STRING_MAGIC_LEN;
tokens = g_strsplit (string, ";", 0);
if (g_strv_length (tokens) < 4)
{
g_critical ("%s: invalid string '%s'", G_STRFUNC, string);
goto error;
}
config = config_new ();
for (pos = 0; pos < 4; ++pos)
if (!parse_paned_config (tokens[pos], &config->paned[pos]))
goto error;
for (p = tokens + 4; *p; ++p)
if (!parse_pane_config (*p, config->panes))
goto error;
g_strfreev (tokens);
return config;
error:
g_strfreev (tokens);
config_free (config);
return NULL;
}
static char *
config_serialize (MooBigPanedConfig *config)
{
GString *string;
int pos;
g_return_val_if_fail (config != NULL, NULL);
string = g_string_new (CONFIG_STRING_MAGIC);
for (pos = 0; pos < 4; ++pos)
{
if (pos != 0)
g_string_append (string, ";");
paned_config_serialize (&config->paned[pos], string);
}
g_hash_table_foreach (config->panes, (GHFunc) pane_config_serialize, string);
return g_string_free (string, FALSE);
}