Files
gnucash/lib/goffice-0.0.4/goffice/graph/gog-object.c
Andreas Köhler 35f43c0a1a Raise dependencies: GLib 2.6, Pango 1.8, GTK+ 2.6.
Ask for GLib 2.6 and GTK+ 2.6. Let the included GOffice 0.0.4 and LibGSF
1.12.3 use their native base dependencies. Remove all #ifdefs and
compatibility code that were necessary to make GnuCash, GOffice and
LibGSF compile on an older setup (GLib 2.4, Pango 1.6, GTK+ 2.4).


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@15400 57a11ea4-9604-0410-9ed3-97b8803252fd
2007-01-20 15:21:35 +00:00

1660 lines
44 KiB
C

/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* gog-object.c :
*
* Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include <goffice/goffice-config.h>
#include <goffice/graph/gog-object.h>
#include <goffice/graph/gog-graph-impl.h> /* for gog_graph_request_update */
#include <goffice/graph/gog-data-set.h>
#include <goffice/data/go-data.h>
#include <goffice/gtk/goffice-gtk.h>
#include <gsf/gsf-impl-utils.h>
#include <glib/gi18n.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtkcombobox.h>
#include <gtk/gtklabel.h>
#include <gtk/gtknotebook.h>
#include <gtk/gtksizegroup.h>
#include <gtk/gtkspinbutton.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkwidget.h>
GogEditor *
gog_editor_new (void)
{
GogEditor *editor = g_new (GogEditor, 1);
editor->store_page = NULL;
editor->pages = NULL;
return editor;
}
void
gog_editor_add_page (GogEditor *editor, gpointer widget, char const *label)
{
GogEditorPage *page;
g_return_if_fail (editor != NULL);
page = g_new (GogEditorPage, 1);
page->widget = widget;
page->label = label;
editor->pages = g_slist_prepend (editor->pages, page);
}
void
gog_editor_set_store_page (GogEditor *editor, unsigned *store_page)
{
g_return_if_fail (editor != NULL);
editor->store_page = store_page;
}
static void
cb_switch_page (G_GNUC_UNUSED GtkNotebook *n, G_GNUC_UNUSED GtkNotebookPage *p,
guint page_num, guint *store_page)
{
*store_page = page_num;
}
gpointer
gog_editor_get_notebook (GogEditor *editor)
{
GtkWidget *notebook;
GogEditorPage *page;
GSList *ptr;
unsigned page_count = 0;
notebook = gtk_notebook_new ();
if (editor->pages != NULL) {
for (ptr = editor->pages; ptr != NULL; ptr = ptr->next) {
page = (GogEditorPage *) ptr->data;
gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook),
GTK_WIDGET (page->widget),
gtk_label_new (page->label));
gtk_widget_show (page->widget);
page_count ++;
}
} else {
/* Display a blank page */
GtkWidget *label = gtk_label_new (NULL);
gtk_notebook_prepend_page (GTK_NOTEBOOK (notebook),
label, NULL);
gtk_widget_show (label);
page_count = 1;
}
if (page_count == 1)
gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
if (editor->store_page != NULL) {
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), *editor->store_page);
g_signal_connect (G_OBJECT (notebook),
"switch_page",
G_CALLBACK (cb_switch_page), editor->store_page);
} else
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0);
return notebook;
}
void
gog_editor_free (GogEditor *editor)
{
g_slist_foreach (editor->pages, (GFunc) g_free, NULL);
g_slist_free (editor->pages);
g_free (editor);
}
typedef struct {
char const *label;
char const *value;
unsigned const flags;
} GogPositionFlagDesc;
static GogPositionFlagDesc const position_compass[] = {
{N_("Top"), "top", GOG_POSITION_N},
{N_("Top right"), "top-righ", GOG_POSITION_N|GOG_POSITION_E},
{N_("Right"), "right", GOG_POSITION_E},
{N_("Bottom right"), "bottom-righ", GOG_POSITION_E|GOG_POSITION_S},
{N_("Bottom"), "bottom", GOG_POSITION_S},
{N_("Bottom left"), "bottom-left", GOG_POSITION_S|GOG_POSITION_W},
{N_("Left"), "left", GOG_POSITION_W},
{N_("Top left"), "top-left", GOG_POSITION_W|GOG_POSITION_N}
};
static GogPositionFlagDesc const position_alignment[] = {
{N_("Fill"), "fill", GOG_POSITION_ALIGN_FILL},
{N_("Start"), "start", GOG_POSITION_ALIGN_START},
{N_("End"), "end", GOG_POSITION_ALIGN_END},
{N_("Center"), "center", GOG_POSITION_ALIGN_CENTER}
};
static GogPositionFlagDesc const position_anchor[] = {
{N_("Top left"), "top-left", GOG_POSITION_ANCHOR_NW},
{N_("Top"), "top", GOG_POSITION_ANCHOR_N},
{N_("Top right"), "top-right", GOG_POSITION_ANCHOR_NE},
{N_("Left"), "left", GOG_POSITION_ANCHOR_W},
{N_("Center"), "center", GOG_POSITION_ANCHOR_CENTER},
{N_("Right"), "right", GOG_POSITION_ANCHOR_E},
{N_("Bottom left"), "bottom-left", GOG_POSITION_ANCHOR_SW},
{N_("Bottom"), "bottom", GOG_POSITION_ANCHOR_S},
{N_("Bottom right"), "bottom-right", GOG_POSITION_ANCHOR_SE}
};
enum {
OBJECT_PROP_0,
OBJECT_PROP_ID,
OBJECT_PROP_POSITION,
OBJECT_PROP_POSITION_COMPASS,
OBJECT_PROP_POSITION_ALIGNMENT,
OBJECT_PROP_POSITION_IS_MANUAL,
OBJECT_PROP_POSITION_ANCHOR,
};
enum {
CHILD_ADDED,
CHILD_REMOVED,
CHILD_NAME_CHANGED,
CHILDREN_REORDERED,
NAME_CHANGED,
CHANGED,
LAST_SIGNAL
};
static gulong gog_object_signals [LAST_SIGNAL] = { 0, };
static GObjectClass *parent_klass;
static void gog_object_set_id (GogObject *obj, unsigned id);
static void
gog_object_finalize (GObject *gobj)
{
GogObject *obj = GOG_OBJECT (gobj);
g_free (obj->user_name); obj->user_name = NULL;
g_free (obj->auto_name); obj->auto_name = NULL;
g_slist_foreach (obj->children, (GFunc) g_object_unref, NULL);
g_slist_free (obj->children);
obj->children = NULL;
(parent_klass->finalize) (gobj);
}
static void
gog_object_parent_changed (GogObject *child, gboolean was_set)
{
GSList *ptr = child->children;
for (; ptr != NULL ; ptr = ptr->next) {
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (ptr->data);
(*klass->parent_changed) (ptr->data, was_set);
}
if (IS_GOG_DATASET (child))
gog_dataset_parent_changed (GOG_DATASET (child), was_set);
}
static void
gog_object_set_property (GObject *obj, guint param_id,
GValue const *value, GParamSpec *pspec)
{
GogObject *gobj = GOG_OBJECT (obj);
char const *str;
char **str_doubles;
unsigned id;
switch (param_id) {
case OBJECT_PROP_ID:
id = g_value_get_uint (value);
gog_object_set_id (gobj, id);
break;
case OBJECT_PROP_POSITION:
str = g_value_get_string (value);
str_doubles = g_strsplit (str, " ", 4);
if (g_strv_length (str_doubles) != 4) {
g_strfreev (str_doubles);
break;
}
gobj->manual_position.x = g_ascii_strtod (str_doubles[0], NULL);
gobj->manual_position.y = g_ascii_strtod (str_doubles[1], NULL);
gobj->manual_position.w = g_ascii_strtod (str_doubles[2], NULL);
gobj->manual_position.h = g_ascii_strtod (str_doubles[3], NULL);
g_strfreev (str_doubles);
break;
case OBJECT_PROP_POSITION_COMPASS:
str = g_value_get_string (value);
if (str == NULL)
break;
for (id = 0; id < G_N_ELEMENTS (position_compass); id++)
if (strcmp (str, position_compass[id].value) == 0)
break;
if (id < G_N_ELEMENTS (position_compass))
gog_object_set_position_flags (gobj,
position_compass[id].flags,
GOG_POSITION_COMPASS);
break;
case OBJECT_PROP_POSITION_ALIGNMENT:
str = g_value_get_string (value);
if (str == NULL)
break;
for (id = 0; id < G_N_ELEMENTS (position_alignment); id++)
if (strcmp (str, position_alignment[id].value) == 0)
break;
if (id < G_N_ELEMENTS (position_alignment))
gog_object_set_position_flags (gobj,
position_alignment[id].flags,
GOG_POSITION_ALIGNMENT);
break;
case OBJECT_PROP_POSITION_IS_MANUAL:
gog_object_set_position_flags (gobj,
g_value_get_boolean (value) ? GOG_POSITION_MANUAL : 0,
GOG_POSITION_MANUAL);
break;
case OBJECT_PROP_POSITION_ANCHOR:
str = g_value_get_string (value);
if (str == NULL)
break;
for (id = 0; id < G_N_ELEMENTS (position_anchor); id++)
if (strcmp (str, position_anchor[id].value) == 0)
break;
if (id < G_N_ELEMENTS (position_anchor))
gog_object_set_position_flags (gobj,
position_anchor[id].flags,
GOG_POSITION_ANCHOR);
break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
return; /* NOTE : RETURN */
}
}
static void
gog_object_get_property (GObject *obj, guint param_id,
GValue *value, GParamSpec *pspec)
{
GogObject *gobj = GOG_OBJECT (obj);
GogObjectPosition flags;
GString *string;
char buffer[G_ASCII_DTOSTR_BUF_SIZE];
unsigned i;
switch (param_id) {
case OBJECT_PROP_ID:
g_value_set_uint (value, GOG_OBJECT (obj)->id);
break;
case OBJECT_PROP_POSITION:
string = g_string_new ("");
g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), gobj->manual_position.x));
g_string_append_c (string, ' ');
g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), gobj->manual_position.y));
g_string_append_c (string, ' ');
g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), gobj->manual_position.w));
g_string_append_c (string, ' ');
g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), gobj->manual_position.h));
g_value_set_string (value, string->str);
g_string_free (string, TRUE);
break;
case OBJECT_PROP_POSITION_COMPASS:
flags = gog_object_get_position_flags (GOG_OBJECT (obj), GOG_POSITION_COMPASS);
for (i = 0; i < G_N_ELEMENTS (position_compass); i++)
if (position_compass[i].flags == flags) {
g_value_set_string (value, position_compass[i].value);
break;
}
break;
case OBJECT_PROP_POSITION_ALIGNMENT:
flags = gog_object_get_position_flags (GOG_OBJECT (obj), GOG_POSITION_ALIGNMENT);
for (i = 0; i < G_N_ELEMENTS (position_alignment); i++)
if (position_alignment[i].flags == flags) {
g_value_set_string (value, position_alignment[i].value);
break;
}
break;
case OBJECT_PROP_POSITION_IS_MANUAL:
g_value_set_boolean (value, (gobj->position & GOG_POSITION_MANUAL) != 0);
break;
case OBJECT_PROP_POSITION_ANCHOR:
flags = gog_object_get_position_flags (GOG_OBJECT (obj), GOG_POSITION_ANCHOR);
for (i = 0; i < G_N_ELEMENTS (position_anchor); i++)
if (position_anchor[i].flags == flags) {
g_value_set_string (value, position_anchor[i].value);
break;
}
break;
default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
break;
}
}
typedef struct {
GtkWidget *x_spin, *y_spin, *w_spin, *h_spin;
GtkWidget *manual_toggle;
GogObject *gobj;
GladeXML *gui;
} ObjectPrefState;
static void
object_pref_state_free (ObjectPrefState *state)
{
g_object_unref (state->gobj);
g_object_unref (state->gui);
}
static void
cb_compass_changed (GtkComboBox *combo, ObjectPrefState *state)
{
GogObjectPosition position = position_compass[gtk_combo_box_get_active (combo)].flags;
gog_object_set_position_flags (state->gobj, position, GOG_POSITION_COMPASS);
}
static void
cb_alignment_changed (GtkComboBox *combo, ObjectPrefState *state)
{
GogObjectPosition position = position_alignment[gtk_combo_box_get_active (combo)].flags;
gog_object_set_position_flags (state->gobj, position, GOG_POSITION_ALIGNMENT);
}
static void
cb_position_changed (GtkWidget *spin, ObjectPrefState *state)
{
GogViewAllocation pos;
double value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin)) / 100.0;
gog_object_get_manual_position (state->gobj, &pos);
if (spin == state->x_spin)
pos.x = value;
else if (spin == state->y_spin)
pos.y = value;
else if (spin == state->w_spin)
pos.w = value;
else if (spin == state->h_spin)
pos.h = value;
gog_object_set_manual_position (state->gobj, &pos);
if (state->manual_toggle != NULL)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->manual_toggle), TRUE);
}
static void
cb_manual_position_changed (GtkToggleButton *button, ObjectPrefState *state)
{
gog_object_set_position_flags (state->gobj,
gtk_toggle_button_get_active (button) ? GOG_POSITION_MANUAL : 0,
GOG_POSITION_MANUAL);
}
static void
cb_anchor_changed (GtkComboBox *combo, ObjectPrefState *state)
{
GogObjectPosition position = position_anchor[gtk_combo_box_get_active (combo)].flags;
gog_object_set_position_flags (state->gobj, position, GOG_POSITION_ANCHOR);
if (state->manual_toggle != NULL)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->manual_toggle), TRUE);
}
static void
gog_object_populate_editor (GogObject *gobj,
GogEditor *editor,
G_GNUC_UNUSED GogDataAllocator *dalloc,
GOCmdContext *cc)
{
GtkWidget *w;
GtkSizeGroup *widget_size_group, *label_size_group;
GladeXML *gui;
GogObjectClass *gog_klass;
GogObjectPosition allowable_positions, flags;
ObjectPrefState *state;
unsigned i;
if (gobj->role == NULL)
return;
gog_klass = GOG_OBJECT_GET_CLASS (gobj);
allowable_positions = gobj->role->allowable_positions;
if (!(allowable_positions & (GOG_POSITION_MANUAL | GOG_POSITION_COMPASS)))
return;
gui = go_libglade_new ("gog-object-prefs.glade", "gog_object_prefs", NULL, cc);
if (gui == NULL)
return;
state = g_new (ObjectPrefState, 1);
state->gobj = gobj;
state->gui = gui;
state->manual_toggle = NULL;
g_object_ref (G_OBJECT (gobj));
widget_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
label_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
if (allowable_positions & GOG_POSITION_COMPASS) {
w = glade_xml_get_widget (gui, "position_combo");
gtk_size_group_add_widget (widget_size_group, w);
flags = gog_object_get_position_flags (gobj, GOG_POSITION_COMPASS);
for (i = 0; i < G_N_ELEMENTS (position_compass); i++) {
gtk_combo_box_append_text (GTK_COMBO_BOX (w), _(position_compass[i].label));
if (position_compass[i].flags == flags)
gtk_combo_box_set_active (GTK_COMBO_BOX (w), i);
}
g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_compass_changed), state);
w = glade_xml_get_widget (gui, "position_label");
gtk_size_group_add_widget (label_size_group, w);
} else {
w = glade_xml_get_widget (gui, "compass_position");
gtk_widget_hide (w);
}
if (allowable_positions & GOG_POSITION_COMPASS) {
w = glade_xml_get_widget (gui, "alignment_combo");
gtk_size_group_add_widget (widget_size_group, w);
flags = gog_object_get_position_flags (gobj, GOG_POSITION_ALIGNMENT);
for (i = 0; i < G_N_ELEMENTS (position_alignment); i++) {
gtk_combo_box_append_text (GTK_COMBO_BOX (w), _(position_alignment[i].label));
if (position_alignment[i].flags == flags)
gtk_combo_box_set_active (GTK_COMBO_BOX (w), i);
}
g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_alignment_changed), state);
w = glade_xml_get_widget (gui, "alignment_label");
gtk_size_group_add_widget (label_size_group, w);
} else {
w = glade_xml_get_widget (gui, "compass_alignment");
gtk_widget_hide (w);
}
if (!(allowable_positions & GOG_POSITION_COMPASS)) {
w =glade_xml_get_widget (gui, "automatic_position_box");
gtk_widget_hide (w);
}
g_object_unref (G_OBJECT (widget_size_group));
g_object_unref (G_OBJECT (label_size_group));
widget_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
label_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
if (allowable_positions & GOG_POSITION_MANUAL) {
w = glade_xml_get_widget (gui, "x_label");
gtk_size_group_add_widget (label_size_group, w);
w = glade_xml_get_widget (gui, "x_spin");
gtk_size_group_add_widget (widget_size_group, w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), gobj->manual_position.x * 100.0);
g_signal_connect (G_OBJECT (w), "value-changed", G_CALLBACK (cb_position_changed), state);
state->x_spin = w;
w = glade_xml_get_widget (gui, "y_label");
gtk_size_group_add_widget (label_size_group, w);
w = glade_xml_get_widget (gui, "y_spin");
gtk_size_group_add_widget (widget_size_group, w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), gobj->manual_position.y * 100.0);
g_signal_connect (G_OBJECT (w), "value-changed", G_CALLBACK (cb_position_changed), state);
state->y_spin = w;
w = glade_xml_get_widget (gui, "anchor_label");
gtk_size_group_add_widget (label_size_group, w);
w = glade_xml_get_widget (gui, "anchor_combo");
flags = gog_object_get_position_flags (gobj, GOG_POSITION_ANCHOR);
for (i = 0; i < G_N_ELEMENTS (position_anchor); i++) {
gtk_combo_box_append_text (GTK_COMBO_BOX (w), _(position_anchor[i].label));
if (i == 0 || position_anchor[i].flags == flags)
gtk_combo_box_set_active (GTK_COMBO_BOX (w), i);
}
g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_anchor_changed), state);
gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (w), 3);
if (gog_klass->can_manual_size) {
w = glade_xml_get_widget (gui, "width_label");
gtk_size_group_add_widget (label_size_group, w);
w = glade_xml_get_widget (gui, "width_spin");
gtk_size_group_add_widget (widget_size_group, w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), gobj->manual_position.w * 100.0);
g_signal_connect (G_OBJECT (w), "value-changed",
G_CALLBACK (cb_position_changed), state);
state->w_spin = w;
w = glade_xml_get_widget (gui, "height_label");
gtk_size_group_add_widget (label_size_group, w);
w = glade_xml_get_widget (gui, "height_spin");
gtk_size_group_add_widget (widget_size_group, w);
gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), gobj->manual_position.h * 100.0);
g_signal_connect (G_OBJECT (w), "value-changed",
G_CALLBACK (cb_position_changed), state);
state->h_spin = w;
} else {
w = glade_xml_get_widget (gui, "manual_sizes");
gtk_widget_hide (w);
}
}
g_object_unref (G_OBJECT (widget_size_group));
g_object_unref (G_OBJECT (label_size_group));
w = glade_xml_get_widget (gui, "manual_position_button");
if ((allowable_positions & GOG_POSITION_MANUAL) &&
((allowable_positions & (GOG_POSITION_COMPASS | GOG_POSITION_ALIGNMENT)) ||
(allowable_positions & GOG_POSITION_SPECIAL))) {
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
gog_object_get_position_flags (gobj, GOG_POSITION_MANUAL) != 0);
g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (cb_manual_position_changed), state);
state->manual_toggle = w;
} else {
gtk_widget_hide (w);
}
w = glade_xml_get_widget (gui, "gog_object_prefs");
g_object_set_data_full (G_OBJECT (w), "state", state,
(GDestroyNotify) object_pref_state_free);
gog_editor_add_page (editor, w, _("Position"));
}
static void
gog_object_base_init (GogObjectClass *klass)
{
klass->roles_allocated = FALSE;
/* klass->roles might be non-NULL; in that case, it points to
the roles hash of the superclass. */
}
static void
gog_object_base_finalize (GogObjectClass *klass)
{
if (klass->roles_allocated)
g_hash_table_destroy (klass->roles);
}
static void
gog_object_class_init (GObjectClass *klass)
{
GogObjectClass *gog_klass = (GogObjectClass *)klass;
parent_klass = g_type_class_peek_parent (klass);
klass->finalize = gog_object_finalize;
klass->set_property = gog_object_set_property;
klass->get_property = gog_object_get_property;
gog_klass->parent_changed = gog_object_parent_changed;
gog_klass->populate_editor = gog_object_populate_editor;
gog_klass->can_manual_size = FALSE;
gog_klass->use_parent_as_proxy = FALSE;
g_object_class_install_property (klass, OBJECT_PROP_ID,
g_param_spec_uint ("id", "id", "Object ID",
0, G_MAXINT, 0,
G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
g_object_class_install_property (klass, OBJECT_PROP_POSITION,
g_param_spec_string ("position", "Position",
"Position and size of object, in percentage of parent size",
"0 0 1 1", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
g_object_class_install_property (klass, OBJECT_PROP_POSITION_COMPASS,
g_param_spec_string ("compass", "Compass",
"Compass auto position flags",
"top", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
g_object_class_install_property (klass, OBJECT_PROP_POSITION_ALIGNMENT,
g_param_spec_string ("alignment", "Alignment",
"Alignment flag",
"fill", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
g_object_class_install_property (klass, OBJECT_PROP_POSITION_IS_MANUAL,
g_param_spec_boolean ("is-position-manual", "Is position manual",
"Is position manual",
FALSE, G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
g_object_class_install_property (klass, OBJECT_PROP_POSITION_ANCHOR,
g_param_spec_string ("anchor", "Anchor",
"Anchor for manual position",
"top-left", G_PARAM_READWRITE|GOG_PARAM_PERSISTENT));
gog_object_signals [CHILD_ADDED] = g_signal_new ("child-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, child_added),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
gog_object_signals [CHILD_REMOVED] = g_signal_new ("child-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, child_removed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
gog_object_signals [CHILD_NAME_CHANGED] = g_signal_new ("child-name-changed",
G_TYPE_FROM_CLASS (gog_klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, child_name_changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
gog_object_signals [CHILDREN_REORDERED] = g_signal_new ("children-reordered",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, children_reordered),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gog_object_signals [NAME_CHANGED] = g_signal_new ("name-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, name_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gog_object_signals [CHANGED] = g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GogObjectClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
}
static void
gog_object_init (GogObject *obj)
{
obj->children = NULL;
obj->user_name = NULL;
obj->auto_name = NULL;
obj->id = 0;
obj->needs_update = FALSE;
obj->being_updated = FALSE;
obj->manual_position.x =
obj->manual_position.y = 0.0;
obj->manual_position.w =
obj->manual_position.h = 1.0;
}
GSF_CLASS_FULL (GogObject, gog_object,
gog_object_base_init, gog_object_base_finalize,
gog_object_class_init, NULL, gog_object_init,
G_TYPE_OBJECT, 0, {})
static gboolean
gog_object_is_same_type (GogObject *obj_a, GogObject *obj_b)
{
g_return_val_if_fail (obj_a->role != NULL, FALSE);
g_return_val_if_fail (obj_b->role != NULL, FALSE);
if (obj_a->role->naming_conv != obj_b->role->naming_conv)
return FALSE;
if (obj_a->role->naming_conv == GOG_OBJECT_NAME_BY_ROLE)
return (obj_a->role == obj_b->role);
return (G_OBJECT_TYPE (obj_a) == G_OBJECT_TYPE (obj_b));
}
static void
gog_object_generate_name (GogObject *obj)
{
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
char const *type_name;
g_return_if_fail (klass != NULL);
g_return_if_fail (obj->role != NULL);
switch (obj->role->naming_conv) {
default :
case GOG_OBJECT_NAME_MANUALLY :
g_warning ("Role %s should not be autogenerating names",
obj->role->id);
case GOG_OBJECT_NAME_BY_ROLE :
g_return_if_fail (obj->role != NULL);
type_name = _(obj->role->id);
break;
case GOG_OBJECT_NAME_BY_TYPE :
g_return_if_fail (klass->type_name != NULL);
type_name = _((*klass->type_name) (obj));
break;
}
if (type_name == NULL)
type_name = "BROKEN";
g_free (obj->auto_name);
obj->auto_name = g_strdup_printf ("%s%d", type_name, obj->id);
}
unsigned
gog_object_get_id (GogObject const *obj)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, 0);
g_return_val_if_fail (obj != 0, 0);
return obj->id;
}
static void
gog_object_generate_id (GogObject *obj)
{
GSList *ptr;
unsigned id_max = 0;
GogObject *child;
obj->id = 0;
if (obj->parent == NULL)
return;
for (ptr = obj->parent->children; ptr != NULL ; ptr = ptr->next) {
child = GOG_OBJECT (ptr->data);
if (gog_object_is_same_type (obj, child))
id_max = MAX (child->id, id_max);
}
obj->id = id_max + 1;
gog_object_generate_name (obj);
}
static void
gog_object_set_id (GogObject *obj, unsigned id)
{
gboolean found = FALSE;
GSList *ptr;
GogObject *child;
g_return_if_fail (GOG_OBJECT (obj) != NULL);
if (id == 0)
return gog_object_generate_id (obj);
g_return_if_fail (GOG_OBJECT (obj)->parent != NULL);
for (ptr = obj->parent->children; ptr != NULL && !found; ptr = ptr->next) {
child = GOG_OBJECT (ptr->data);
found = child->id == id &&
gog_object_is_same_type (obj, child) &&
ptr->data != obj;
}
if (found) {
g_warning ("id %u already exists", id);
gog_object_generate_id (obj);
return;
}
if (id == obj->id)
return;
obj->id = id;
gog_object_generate_name (obj);
}
static void
dataset_dup (GogDataset const *src, GogDataset *dst)
{
gint n, last;
gog_dataset_dims (src, &n, &last);
for ( ; n <= last ; n++)
gog_dataset_set_dim (dst, n,
go_data_dup (gog_dataset_get_dim (src, n)),
NULL);
}
/**
* gog_object_dup :
* @src : #GogObject
* @new_parent : #GogObject the parent tree for the object (can be NULL)
* @datadup : a function to duplicate the data (a default one is used if NULL)
*
* Create a deep copy of @obj using @new_parent as its parent.
**/
GogObject *
gog_object_dup (GogObject const *src, GogObject *new_parent, GogDataDuplicator datadup)
{
gint n;
GParamSpec **props;
GogObject *dst = NULL;
GSList *ptr;
GValue val = { 0 };
if (src == NULL)
return NULL;
g_return_val_if_fail (GOG_OBJECT (src) != NULL, NULL);
if (src->role == NULL || src->explicitly_typed_role)
dst = g_object_new (G_OBJECT_TYPE (src), NULL);
if (new_parent)
dst = gog_object_add_by_role (new_parent, src->role, dst);
dst->position = src->position;
/* properties */
props = g_object_class_list_properties (G_OBJECT_GET_CLASS (src), &n);
while (n-- > 0)
if (props[n]->flags & GOG_PARAM_PERSISTENT) {
g_value_init (&val, props[n]->value_type);
g_object_get_property (G_OBJECT (src), props[n]->name, &val);
g_object_set_property (G_OBJECT (dst), props[n]->name, &val);
g_value_unset (&val);
}
g_free (props);
if (IS_GOG_DATASET (src)) { /* convenience to save data */
if (datadup)
datadup (GOG_DATASET (src), GOG_DATASET (dst));
else
dataset_dup (GOG_DATASET (src), GOG_DATASET (dst));
}
for (ptr = src->children; ptr != NULL ; ptr = ptr->next)
/* children added directly to new parent, no need to use the
* function result */
gog_object_dup (ptr->data, dst, datadup);
return dst;
}
/**
* gog_object_get_parent :
* @obj : a #GogObject
*
* Returns @obj's parent, potentially NULL if it has not been added to a
* heirarchy yet. does not change ref-count in any way.
**/
GogObject *
gog_object_get_parent (GogObject const *obj)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
return obj->parent;
}
/**
* gog_object_get_parent_typed :
* @obj : a #GogObject
* @type : a #GType
*
* Returns @obj's parent of type @type, potentially NULL if it has not been
* added to a heirarchy yet or none of the parents are of type @type.
**/
GogObject *
gog_object_get_parent_typed (GogObject const *obj, GType t)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
for (; obj != NULL ; obj = obj->parent)
if (G_TYPE_CHECK_INSTANCE_TYPE (obj, t))
return GOG_OBJECT (obj); /* const cast */
return NULL;
}
/**
* gog_object_get_graph :
* @obj : const * #GogObject
*
* Returns the parent graph.
**/
GogGraph *
gog_object_get_graph (GogObject const *obj)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
for (; obj != NULL ; obj = obj->parent)
if (IS_GOG_GRAPH (obj))
return GOG_GRAPH (obj);
return NULL;
}
GogTheme *
gog_object_get_theme (GogObject const *obj)
{
GogGraph *graph = gog_object_get_graph (obj);
return (graph != NULL) ? gog_graph_get_theme (graph) : NULL;
}
/**
* gog_object_get_name :
* @obj : a #GogObject
*
* No need to free the result
**/
char const *
gog_object_get_name (GogObject const *obj)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
return (obj->user_name != NULL && *obj->user_name != '\0') ? obj->user_name : obj->auto_name;
}
/**
* gog_object_set_name :
* @obj : #GogObject
* @name :
* @err : #GError
*
* Assign the new name and signals that it has changed.
* NOTE : it _absorbs_ @name rather than copying it, and generates a new name
* if @name == NULL
**/
void
gog_object_set_name (GogObject *obj, char *name, GError **err)
{
GogObject *tmp;
g_return_if_fail (GOG_OBJECT (obj) != NULL);
if (obj->user_name == name)
return;
g_free (obj->user_name);
obj->user_name = name;
g_signal_emit (G_OBJECT (obj),
gog_object_signals [NAME_CHANGED], 0);
for (tmp = obj; tmp != NULL ; tmp = tmp->parent)
g_signal_emit (G_OBJECT (tmp),
gog_object_signals [CHILD_NAME_CHANGED], 0, obj);
}
/**
* gog_object_get_children :
* @obj : a #GogObject
* @filter : an optional #GogObjectRole to use as a filter
*
* The list needs to be Freed
**/
GSList *
gog_object_get_children (GogObject const *obj, GogObjectRole const *filter)
{
GSList *ptr, *res = NULL;
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
if (filter == NULL)
return g_slist_copy (obj->children);
for (ptr = obj->children ; ptr != NULL ; ptr = ptr->next)
if (GOG_OBJECT (ptr->data)->role == filter)
res = g_slist_prepend (res, ptr->data);
return g_slist_reverse (res);
}
/**
* gog_object_get_child_by_role :
* @obj : a #GogObject
* @role : a #GogObjectRole to use as a filter
*
* A convenience routine to handle a unique child
* Returns NULL and spews an error if there is more than one.
**/
GogObject *
gog_object_get_child_by_role (GogObject const *obj, GogObjectRole const *role)
{
GogObject *res = NULL;
GSList *children = gog_object_get_children (obj, role);
if (children != NULL && children->next == NULL)
res = children->data;
g_slist_free (children);
return res;
}
/**
* gog_object_is_deletable :
* @obj : a #GogObject
*
* Can the specified @obj be deleted ?
**/
gboolean
gog_object_is_deletable (GogObject const *obj)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
if (IS_GOG_GRAPH (obj))
return FALSE;
return obj->role == NULL || obj->role->can_remove == NULL ||
(obj->role->can_remove) (obj);
}
struct possible_add_closure {
GSList *res;
GogObject const *parent;
};
static void
cb_collect_possible_additions (char const *name, GogObjectRole const *role,
struct possible_add_closure *data)
{
if (role->can_add == NULL || (role->can_add) (data->parent))
data->res = g_slist_prepend (data->res, (gpointer)role);
}
static int
gog_object_position_cmp (GogObjectPosition pos)
{
if (pos & GOG_POSITION_COMPASS)
return 0;
if (GOG_POSITION_IS_SPECIAL (pos) ||
GOG_POSITION_IS_PADDING (pos))
return 2;
return 1; /* GOG_POSITION_MANUAL */
}
static int
gog_role_cmp (GogObjectRole const *a, GogObjectRole const *b)
{
int index_a = gog_object_position_cmp (a->allowable_positions);
int index_b = gog_object_position_cmp (b->allowable_positions);
if (b->priority != a->priority)
return b->priority - a->priority;
/* intentionally reverse to put SPECIAL at the top */
if (index_a < index_b)
return 1;
else if (index_a > index_b)
return -1;
return 0;
}
static int
gog_role_cmp_full (GogObjectRole const *a, GogObjectRole const *b)
{
int res = gog_role_cmp (a, b);
if (res != 0)
return res;
return g_utf8_collate (a->id, b->id);
}
/**
* gog_object_possible_additions :
* @parent : a #GogObject
*
* returns a list of GogObjectRoles that could be added
*
* The resulting list needs to be freed
**/
GSList *
gog_object_possible_additions (GogObject const *parent)
{
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (parent);
g_return_val_if_fail (klass != NULL, NULL);
if (klass->roles != NULL) {
struct possible_add_closure data;
data.res = NULL;
data.parent = parent;
g_hash_table_foreach (klass->roles,
(GHFunc) cb_collect_possible_additions, &data);
return g_slist_sort (data.res, (GCompareFunc) gog_role_cmp_full);
}
return NULL;
}
/**
* gog_object_can_reorder :
* @obj : #GogObject
* @inc_ok : possibly NULL pointer.
* @dec_ok : possibly NULL pointer.
*
* If @obj can move forward or backward in its parents child list
**/
void
gog_object_can_reorder (GogObject const *obj, gboolean *inc_ok, gboolean *dec_ok)
{
GogObject const *parent;
GSList *ptr;
g_return_if_fail (GOG_OBJECT (obj) != NULL);
if (inc_ok != NULL)
*inc_ok = FALSE;
if (dec_ok != NULL)
*dec_ok = FALSE;
if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
return;
parent = obj->parent;
ptr = parent->children;
g_return_if_fail (ptr != NULL);
/* find a pointer to the previous sibling */
if (ptr->data != obj) {
while (ptr->next != NULL && ptr->next->data != obj)
ptr = ptr->next;
g_return_if_fail (ptr->next != NULL);
if (inc_ok != NULL &&
!gog_role_cmp (((GogObject *)ptr->data)->role, obj->role))
*inc_ok = TRUE;
ptr = ptr->next;
}
/* ptr now points at @obj */
if (dec_ok != NULL && ptr->next != NULL &&
!gog_role_cmp (obj->role, ((GogObject *)ptr->next->data)->role))
*dec_ok = TRUE;
}
/**
* gog_object_reorder :
* @obj : #GogObject
* @inc :
* @goto_max :
*
* Returns the object just before @obj in the new ordering.
**/
GogObject *
gog_object_reorder (GogObject const *obj, gboolean inc, gboolean goto_max)
{
GogObject *parent, *obj_follows;
GSList **ptr, *tmp;
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, NULL);
if (obj->parent == NULL || gog_object_get_graph (obj) == NULL)
return NULL;
parent = obj->parent;
if (inc)
parent->children = g_slist_reverse (parent->children);
for (ptr = &parent->children; *ptr != NULL && (*ptr)->data != obj ;)
ptr = &(*ptr)->next;
g_return_val_if_fail (*ptr != NULL, NULL);
g_return_val_if_fail ((*ptr)->next != NULL, NULL);
tmp = *ptr;
*ptr = tmp->next;
ptr = &(*ptr)->next;
while (goto_max && *ptr != NULL &&
!gog_role_cmp (obj->role, ((GogObject *)((*ptr)->data))->role))
ptr = &(*ptr)->next;
tmp->next = *ptr;
*ptr = tmp;
if (inc)
parent->children = g_slist_reverse (parent->children);
if (parent->children->data != obj) {
for (tmp = parent->children ; tmp->next->data != obj ; )
tmp = tmp->next;
obj_follows = tmp->data;
} else
obj_follows = NULL;
/* Pass the sibling that precedes obj, or NULL if is the head */
g_signal_emit (G_OBJECT (parent),
gog_object_signals [CHILDREN_REORDERED], 0);
gog_object_emit_changed (parent, TRUE);
return obj_follows;
}
/**
* gog_object_get_editor :
* @obj : #GogObject
* @dalloc : #GogDataAllocator
* @cc : #GOCmdContext
*
**/
gpointer
gog_object_get_editor (GogObject *obj, GogDataAllocator *dalloc,
GOCmdContext *cc)
{
GtkWidget *notebook;
GogEditor *editor;
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
g_return_val_if_fail (klass != NULL, NULL);
editor = gog_editor_new ();
if (klass->populate_editor) {
/* If there are pending updates do them before creating the editor
* to avoid expensive widget changes later */
gog_graph_force_update (gog_object_get_graph (obj));
(*klass->populate_editor) (obj, editor, dalloc, cc);
}
notebook = gog_editor_get_notebook (editor);
gog_editor_free (editor);
return notebook;
}
/**
* gog_object_new_view :
* @obj : a #GogObject
* @data :
**/
GogView *
gog_object_new_view (GogObject const *obj, GogView *parent)
{
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
g_return_val_if_fail (klass != NULL, NULL);
if (klass->view_type != 0)
/* set model before parent */
return g_object_new (klass->view_type,
"model", obj,
"parent", parent,
NULL);
return NULL;
}
void
gog_object_update (GogObject *obj)
{
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
GSList *ptr;
g_return_if_fail (klass != NULL);
ptr = obj->children; /* depth first */
for (; ptr != NULL ; ptr = ptr->next)
gog_object_update (ptr->data);
if (obj->needs_update) {
obj->needs_update = FALSE;
obj->being_updated = TRUE;
gog_debug (0, g_warning ("updating %s (%p)", G_OBJECT_TYPE_NAME (obj), obj););
if (klass->update != NULL)
(*klass->update) (obj);
obj->being_updated = FALSE;
}
}
gboolean
gog_object_request_update (GogObject *obj)
{
GogGraph *graph;
g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
g_return_val_if_fail (!obj->being_updated, FALSE);
if (obj->needs_update)
return FALSE;
graph = gog_object_get_graph (obj);
if (graph == NULL) /* we are not linked into a graph yet */
return FALSE;
gog_graph_request_update (graph);
obj->needs_update = TRUE;
return TRUE;
}
void
gog_object_emit_changed (GogObject *obj, gboolean resize)
{
GogObjectClass *gog_klass;
g_return_if_fail (GOG_OBJECT (obj));
gog_klass = GOG_OBJECT_GET_CLASS (obj);
if (gog_klass->use_parent_as_proxy) {
obj = obj->parent;
if (obj != NULL) {
g_return_if_fail (IS_GOG_OBJECT (obj));
gog_object_emit_changed (obj, resize);
}
return;
}
g_signal_emit (G_OBJECT (obj),
gog_object_signals [CHANGED], 0, resize);
}
/******************************************************************************/
/**
* gog_object_clear_parent :
* @obj : #GogObject
*
* Does _not_ unref the child, which in effect adds a ref by freeing up the ref
* previously associated with the parent.
**/
gboolean
gog_object_clear_parent (GogObject *obj)
{
GogObjectClass *klass;
GogObject *parent;
g_return_val_if_fail (GOG_OBJECT (obj), FALSE);
g_return_val_if_fail (obj->parent != NULL, FALSE);
g_return_val_if_fail (gog_object_is_deletable (obj), FALSE);
klass = GOG_OBJECT_GET_CLASS (obj);
parent = obj->parent;
g_signal_emit (G_OBJECT (parent),
gog_object_signals [CHILD_REMOVED], 0, obj);
(*klass->parent_changed) (obj, FALSE);
if (obj->role != NULL && obj->role->pre_remove != NULL)
(obj->role->pre_remove) (parent, obj);
parent->children = g_slist_remove (parent->children, obj);
obj->parent = NULL;
if (obj->role != NULL && obj->role->post_remove != NULL)
(obj->role->post_remove) (parent, obj);
obj->role = NULL;
return TRUE;
}
/**
* gog_object_set_parent :
* @child : #GogObject.
* @parent : #GogObject.
* @id : optionally %NULL.
* @role : a static string that can be sent to @parent::add
*
* Absorbs a ref to @child
**/
gboolean
gog_object_set_parent (GogObject *child, GogObject *parent,
GogObjectRole const *role, unsigned id)
{
GogObjectClass *klass;
GSList **step;
g_return_val_if_fail (GOG_OBJECT (child), FALSE);
g_return_val_if_fail (child->parent == NULL, FALSE);
g_return_val_if_fail (role != NULL, FALSE);
klass = GOG_OBJECT_GET_CLASS (child);
child->parent = parent;
child->role = role;
child->position = role->default_position;
/* Insert sorted based on hokey little ordering */
step = &parent->children;
while (*step != NULL &&
gog_role_cmp_full (GOG_OBJECT ((*step)->data)->role, role) >= 0)
step = &((*step)->next);
*step = g_slist_prepend (*step, child);
if (id != 0)
gog_object_set_id (child, id);
else
gog_object_generate_id (child);
if (role->post_add != NULL)
(role->post_add) (parent, child);
(*klass->parent_changed) (child, TRUE);
g_signal_emit (G_OBJECT (parent),
gog_object_signals [CHILD_ADDED], 0, child);
return TRUE;
}
GogObject *
gog_object_add_by_role (GogObject *parent, GogObjectRole const *role, GogObject *child)
{
GType is_a;
gboolean const explicitly_typed_role = (child != NULL);
g_return_val_if_fail (role != NULL, NULL);
g_return_val_if_fail (GOG_OBJECT (parent) != NULL, NULL);
is_a = g_type_from_name (role->is_a_typename);
g_return_val_if_fail (is_a != 0, NULL);
if (child == NULL)
child = (role->allocate)
? (role->allocate) (parent)
: g_object_new (is_a, NULL);
g_return_val_if_fail (G_TYPE_CHECK_INSTANCE_TYPE (child, is_a), NULL);
child->explicitly_typed_role = explicitly_typed_role;
if (gog_object_set_parent (child, parent, role, 0))
return child;
g_object_unref (child);
return NULL;
}
/**
* gog_object_add_by_name :
* @parent : #GogObject
* @role :
* @child : optionally null #GogObject
*
* Returns a newly created child of @parent in @role. If @child is provided,
* it is assumed to be an unaffiliated object that will be assigned in @role.
* On failure return NULL.
**/
GogObject *
gog_object_add_by_name (GogObject *parent,
char const *role, GogObject *child)
{
return gog_object_add_by_role (parent,
gog_object_find_role_by_name (parent, role), child);
}
/**
* gog_object_get_position_flags :
* @obj : #GogObject
* @mask : #GogObjectPosition
*
* Retrieve position flags of GogObject @obj, masked by @mask.
*/
GogObjectPosition
gog_object_get_position_flags (GogObject const *obj, GogObjectPosition mask)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, GOG_POSITION_SPECIAL & mask);
return obj->position & mask;
}
/**
* gog_object_set_position_flags :
* @obj : #GogObject
* @flags : #GogObjectPosition
* @mask : #GogObjectPosition
*
* Attempts to set the position flags of @obj to @flags.
* Returns TRUE the new flags are permitted.
**/
gboolean
gog_object_set_position_flags (GogObject *obj, GogObjectPosition flags, GogObjectPosition mask)
{
g_return_val_if_fail (GOG_OBJECT (obj) != NULL, FALSE);
if (obj->role == NULL)
return FALSE;
if ((obj->position & mask) == flags)
return TRUE;
if ((flags & obj->role->allowable_positions) !=
(flags & (GOG_POSITION_COMPASS | GOG_POSITION_ANY_MANUAL))) {
g_warning ("[GogObject::set_position_flags] Invalid flags (%s)",
gog_object_get_name (obj));
return FALSE;
}
obj->position = (obj->position & ~mask) | (flags & mask);
gog_object_emit_changed (obj, TRUE);
return TRUE;
}
/**
* gog_object_get_manual_position:
* @obj : #GogObject
*
* returns manual position of this object, in points.
**/
void
gog_object_get_manual_position (GogObject *gobj, GogViewAllocation *pos)
{
g_return_if_fail (GOG_OBJECT (gobj) != NULL);
if (pos != NULL)
*pos = gobj->manual_position;
}
/**
* gog_object_set_manual_position:
* @obj : #GogObject
* @pos : #GogViewAllocation
*
* set manual position of given object, in points.
**/
void
gog_object_set_manual_position (GogObject *gobj, GogViewAllocation const *pos)
{
g_return_if_fail (GOG_OBJECT (gobj) != NULL);
if (gobj->manual_position.x == pos->x &&
gobj->manual_position.y == pos->y &&
gobj->manual_position.w == pos->w &&
gobj->manual_position.h == pos->h)
return;
gobj->manual_position = *pos;
gog_object_emit_changed (gobj, TRUE);
}
/**
* gog_object_get_manual_allocation:
* @gobj : #GogObject
* @parent_allocation : #GogViewAllocation
* @requisition : #GogViewRequisition
*
* Returns manual allocation of a GogObject given its parent allocation
* and its size request.
**/
GogViewAllocation
gog_object_get_manual_allocation (GogObject *gobj,
GogViewAllocation const *parent_allocation,
GogViewRequisition const *requisition)
{
GogViewAllocation pos;
unsigned anchor;
pos.x = parent_allocation->x + gobj->manual_position.x * parent_allocation->w;
pos.y = parent_allocation->y + gobj->manual_position.y * parent_allocation->h;
if (GOG_OBJECT_GET_CLASS (gobj)->can_manual_size) {
pos.w = gobj->manual_position.w * parent_allocation->w;
pos.h = gobj->manual_position.h * parent_allocation->h;
} else {
pos.w = requisition->w;
pos.h = requisition->h;
}
anchor = gog_object_get_position_flags (gobj, GOG_POSITION_ANCHOR);
switch (anchor) {
case GOG_POSITION_ANCHOR_N:
case GOG_POSITION_ANCHOR_CENTER:
case GOG_POSITION_ANCHOR_S:
pos.x -= pos.w / 2.0;
break;
case GOG_POSITION_ANCHOR_SE:
case GOG_POSITION_ANCHOR_E:
case GOG_POSITION_ANCHOR_NE:
pos.x -= pos.w;
break;
default:
break;
}
switch (anchor) {
case GOG_POSITION_ANCHOR_E:
case GOG_POSITION_ANCHOR_CENTER:
case GOG_POSITION_ANCHOR_W:
pos.y -= pos.h / 2.0;
break;
case GOG_POSITION_ANCHOR_SE:
case GOG_POSITION_ANCHOR_S:
case GOG_POSITION_ANCHOR_SW:
pos.y -= pos.h;
break;
default:
break;
}
return pos;
}
GogObjectRole const *
gog_object_find_role_by_name (GogObject const *obj, char const *role)
{
GogObjectClass *klass = GOG_OBJECT_GET_CLASS (obj);
g_return_val_if_fail (klass != NULL, NULL);
return g_hash_table_lookup (klass->roles, role);
}
static void
cb_copy_hash_table (gpointer key, gpointer value, GHashTable *hash_table)
{
g_hash_table_insert (hash_table, key, value);
}
static void
gog_object_allocate_roles (GogObjectClass *klass)
{
GHashTable *roles = g_hash_table_new (g_str_hash, g_str_equal);
if (klass->roles != NULL)
g_hash_table_foreach (klass->roles,
(GHFunc) cb_copy_hash_table, roles);
klass->roles = roles;
klass->roles_allocated = TRUE;
}
void
gog_object_register_roles (GogObjectClass *klass,
GogObjectRole const *roles, unsigned n_roles)
{
unsigned i;
if (!klass->roles_allocated)
gog_object_allocate_roles (klass);
for (i = 0 ; i < n_roles ; i++) {
g_return_if_fail (g_hash_table_lookup (klass->roles,
(gpointer )roles[i].id) == NULL);
g_hash_table_replace (klass->roles,
(gpointer )roles[i].id, (gpointer) (roles + i));
}
}