glade/gladeui/glade-editor-property.c
Tristan Van Berkom 094588c1af * gladeui/glade-editor-property.[ch]: Privatized members of GladeEditorProperty.
* plugins/gtk+/*.c: updated all custom editors to used eprop accessors now
2011-01-04 17:34:37 +09:00

3391 lines
115 KiB
C

/*
* Copyright (C) 2001 Ximian, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors:
* Tristan Van Berkom <tvb@gnome.org>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
/**
* SECTION:glade-editor-property
* @Short_Description: A generic widget to edit a #GladeProperty.
*
* The #GladeEditorProperty is a factory that will create the correct
* control for the #GladePropertyClass it was created for and provides
* a simple unified api to them.
*/
#include <stdio.h>
#include <string.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "glade.h"
#include "glade-widget.h"
#include "glade-editor-property.h"
#include "glade-property.h"
#include "glade-command.h"
#include "glade-project.h"
#include "glade-popup.h"
#include "glade-builtins.h"
#include "glade-marshallers.h"
#include "glade-displayable-values.h"
#include "glade-named-icon-chooser-dialog.h"
enum
{
PROP_0,
PROP_PROPERTY_CLASS,
PROP_USE_COMMAND
};
enum
{
CHANGED,
COMMIT,
LAST_SIGNAL
};
static GtkTableClass *table_class;
static GladeEditorPropertyClass *editor_property_class;
static guint glade_eprop_signals[LAST_SIGNAL] = { 0, };
#define GLADE_PROPERTY_TABLE_ROW_SPACING 2
#define FLAGS_COLUMN_SETTING 0
#define FLAGS_COLUMN_SYMBOL 1
struct _GladeEditorPropertyPrivate
{
GladePropertyClass *klass; /* The property class this GladeEditorProperty was created for */
GladeProperty *property; /* The currently loaded property */
GtkWidget *item_label; /* The property name portion of the eprop */
GtkWidget *label; /* The actual property name label */
GtkWidget *warning; /* Icon to show warnings */
GtkWidget *input; /* Input part of property (need to set sensitivity seperately) */
GtkWidget *check; /* Check button for optional properties. */
gulong tooltip_id; /* signal connection id for tooltip changes */
gulong sensitive_id; /* signal connection id for sensitivity changes */
gulong changed_id; /* signal connection id for value changes */
gulong enabled_id; /* signal connection id for enable/disable changes */
gulong state_id; /* signal connection id for state changes */
gboolean loading; /* True during glade_editor_property_load calls, this
* is used to avoid feedback from input widgets.
*/
guint committing : 1; /* True while the editor property itself is applying
* the property with glade_editor_property_commit_no_callback ().
*/
guint use_command : 1; /* Whether we should use the glade command interface
* or skip directly to GladeProperty interface.
* (used for query dialogs).
*/
};
/*******************************************************************************
GladeEditorPropertyClass
*******************************************************************************/
/* declare this forwardly for the finalize routine */
static void glade_editor_property_load_common (GladeEditorProperty * eprop,
GladeProperty * property);
static void
glade_editor_property_commit_common (GladeEditorProperty * eprop,
GValue * value)
{
if (eprop->priv->use_command == FALSE)
glade_property_set_value (eprop->priv->property, value);
else
glade_command_set_property_value (eprop->priv->property, value);
/* If the value was denied by a verify function, we'll have to
* reload the real value.
*/
if (glade_property_equals_value (eprop->priv->property, value))
GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->load (eprop, eprop->priv->property);
else
/* publish a value change to those interested */
g_signal_emit (G_OBJECT (eprop), glade_eprop_signals[CHANGED], 0,
eprop->priv->property);
}
void
glade_editor_property_commit_no_callback (GladeEditorProperty * eprop,
GValue * value)
{
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
if (eprop->priv->committing)
return;
g_signal_handler_block (G_OBJECT (eprop->priv->property), eprop->priv->changed_id);
eprop->priv->committing = TRUE;
glade_editor_property_commit (eprop, value);
eprop->priv->committing = FALSE;
g_signal_handler_unblock (G_OBJECT (eprop->priv->property), eprop->priv->changed_id);
}
GtkWidget *
glade_editor_property_get_item_label (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
return eprop->priv->item_label;
}
GladePropertyClass *
glade_editor_property_get_pclass (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
return eprop->priv->klass;
}
GladeProperty *
glade_editor_property_get_property (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
return eprop->priv->property;
}
gboolean
glade_editor_property_loading (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), FALSE);
return eprop->priv->loading;
}
static void
glade_editor_property_tooltip_cb (GladeProperty * property,
const gchar * tooltip,
const gchar * insensitive,
const gchar * support,
GladeEditorProperty * eprop)
{
const gchar *choice_tooltip;
if (glade_property_get_sensitive (property))
choice_tooltip = tooltip;
else
choice_tooltip = insensitive;
gtk_widget_set_tooltip_text (eprop->priv->input, choice_tooltip);
gtk_widget_set_tooltip_text (eprop->priv->label, choice_tooltip);
gtk_widget_set_tooltip_text (eprop->priv->warning, support);
}
static void
glade_editor_property_sensitivity_cb (GladeProperty * property,
GParamSpec * pspec,
GladeEditorProperty * eprop)
{
gboolean sensitive = glade_property_get_sensitive (eprop->priv->property);
gboolean support_sensitive =
(glade_property_get_state (eprop->priv->property) & GLADE_STATE_SUPPORT_DISABLED) == 0;
gtk_widget_set_sensitive (eprop->priv->input, sensitive && support_sensitive &&
glade_property_get_enabled (property));
if (eprop->priv->check)
gtk_widget_set_sensitive (eprop->priv->check, sensitive && support_sensitive);
}
static void
glade_editor_property_value_changed_cb (GladeProperty * property,
GValue * old_value,
GValue * value,
GladeEditorProperty * eprop)
{
g_assert (eprop->priv->property == property);
glade_editor_property_load (eprop, eprop->priv->property);
}
static void
glade_editor_property_fix_label (GladeEditorProperty * eprop)
{
gchar *text = NULL;
if (!eprop->priv->property)
return;
/* refresh label */
if ((glade_property_get_state (eprop->priv->property) & GLADE_STATE_CHANGED) != 0)
text = g_strdup_printf ("<b>%s:</b>", glade_property_class_get_name (eprop->priv->klass));
else
text = g_strdup_printf ("%s:", glade_property_class_get_name (eprop->priv->klass));
gtk_label_set_markup (GTK_LABEL (eprop->priv->label), text);
g_free (text);
/* refresh icon */
if ((glade_property_get_state (eprop->priv->property) & GLADE_STATE_UNSUPPORTED) != 0)
gtk_widget_show (eprop->priv->warning);
else
gtk_widget_hide (eprop->priv->warning);
/* check sensitivity */
glade_editor_property_sensitivity_cb (eprop->priv->property, NULL, eprop);
}
static void
glade_editor_property_state_cb (GladeProperty * property,
GParamSpec * pspec, GladeEditorProperty * eprop)
{
glade_editor_property_fix_label (eprop);
}
static void
glade_editor_property_enabled_cb (GladeProperty * property,
GParamSpec * pspec,
GladeEditorProperty * eprop)
{
gboolean enabled;
g_assert (eprop->priv->property == property);
if (glade_property_class_optional (eprop->priv->klass))
{
enabled = glade_property_get_enabled (property);
/* sensitive = enabled && support enabled && sensitive */
if (enabled == FALSE)
gtk_widget_set_sensitive (eprop->priv->input, FALSE);
else if (glade_property_get_sensitive (property) ||
(glade_property_get_state (property) & GLADE_STATE_SUPPORT_DISABLED) != 0)
gtk_widget_set_sensitive (eprop->priv->input, TRUE);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop->priv->check), enabled);
}
}
static void
glade_editor_property_enabled_toggled_cb (GtkWidget * check,
GladeEditorProperty * eprop)
{
glade_property_set_enabled (eprop->priv->property,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(check)));
}
static gboolean
glade_editor_property_button_pressed (GtkWidget * widget,
GdkEventButton * event,
GladeEditorProperty * eprop)
{
if (glade_popup_is_popup_event (event))
{
glade_popup_property_pop (eprop->priv->property, event);
return TRUE;
}
return FALSE;
}
static GObject *
glade_editor_property_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam * construct_properties)
{
GObject *obj;
GladeEditorProperty *eprop;
GtkWidget *hbox;
/* Invoke parent constructor (eprop->priv->klass should be resolved by this point) . */
obj = G_OBJECT_CLASS (table_class)->constructor
(type, n_construct_properties, construct_properties);
eprop = GLADE_EDITOR_PROPERTY (obj);
/* Create hbox and possibly check button
*/
if (glade_property_class_optional (eprop->priv->klass))
{
eprop->priv->check = gtk_check_button_new ();
gtk_widget_show (eprop->priv->check);
gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->check, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (eprop->priv->check), "toggled",
G_CALLBACK (glade_editor_property_enabled_toggled_cb),
eprop);
}
/* Create the class specific input widget and add it */
eprop->priv->input = GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->create_input (eprop);
gtk_widget_set_hexpand (eprop->priv->input, TRUE);
gtk_widget_show (eprop->priv->input);
/* Create the warning icon */
eprop->priv->warning = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
GTK_ICON_SIZE_MENU);
gtk_widget_set_no_show_all (eprop->priv->warning, TRUE);
/* Create & setup label */
eprop->priv->item_label = gtk_event_box_new ();
eprop->priv->label = gtk_label_new (NULL);
gtk_event_box_set_visible_window (GTK_EVENT_BOX (eprop->priv->item_label), FALSE);
hbox = gtk_hbox_new (FALSE, 4);
gtk_label_set_line_wrap (GTK_LABEL (eprop->priv->label), TRUE);
gtk_label_set_width_chars (GTK_LABEL (eprop->priv->label), 10);
gtk_label_set_line_wrap_mode (GTK_LABEL (eprop->priv->label), PANGO_WRAP_WORD_CHAR);
gtk_misc_set_alignment (GTK_MISC (eprop->priv->label), 0.0, 0.5);
gtk_box_pack_start (GTK_BOX (hbox), eprop->priv->label, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), eprop->priv->warning, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (eprop->priv->item_label), hbox);
gtk_widget_show_all (eprop->priv->item_label);
glade_editor_property_fix_label (eprop);
gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->input, TRUE, TRUE, 0);
g_signal_connect (G_OBJECT (eprop->priv->item_label), "button-press-event",
G_CALLBACK (glade_editor_property_button_pressed), eprop);
g_signal_connect (G_OBJECT (eprop->priv->input), "button-press-event",
G_CALLBACK (glade_editor_property_button_pressed), eprop);
return obj;
}
static void
glade_editor_property_finalize (GObject * object)
{
GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object);
/* detatch from loaded property */
glade_editor_property_load_common (eprop, NULL);
G_OBJECT_CLASS (table_class)->finalize (object);
}
static void
glade_editor_property_set_property (GObject * object,
guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object);
switch (prop_id)
{
case PROP_PROPERTY_CLASS:
eprop->priv->klass = g_value_get_pointer (value);
break;
case PROP_USE_COMMAND:
eprop->priv->use_command = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_editor_property_real_get_property (GObject * object,
guint prop_id,
GValue * value, GParamSpec * pspec)
{
GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object);
switch (prop_id)
{
case PROP_PROPERTY_CLASS:
g_value_set_pointer (value, eprop->priv->klass);
break;
case PROP_USE_COMMAND:
g_value_set_boolean (value, eprop->priv->use_command);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_eprop_property_finalized (GladeEditorProperty * eprop,
GladeProperty * where_property_was)
{
eprop->priv->tooltip_id = 0;
eprop->priv->sensitive_id = 0;
eprop->priv->changed_id = 0;
eprop->priv->enabled_id = 0;
eprop->priv->state_id = 0;
eprop->priv->property = NULL;
glade_editor_property_load (eprop, NULL);
}
static void
glade_editor_property_load_common (GladeEditorProperty * eprop,
GladeProperty * property)
{
/* NOTE THIS CODE IS FINALIZE SAFE */
/* disconnect anything from previously loaded property */
if (eprop->priv->property != property && eprop->priv->property != NULL)
{
if (eprop->priv->tooltip_id > 0)
g_signal_handler_disconnect (eprop->priv->property, eprop->priv->tooltip_id);
if (eprop->priv->sensitive_id > 0)
g_signal_handler_disconnect (eprop->priv->property, eprop->priv->sensitive_id);
if (eprop->priv->changed_id > 0)
g_signal_handler_disconnect (eprop->priv->property, eprop->priv->changed_id);
if (eprop->priv->state_id > 0)
g_signal_handler_disconnect (eprop->priv->property, eprop->priv->state_id);
if (eprop->priv->enabled_id > 0)
g_signal_handler_disconnect (eprop->priv->property, eprop->priv->enabled_id);
eprop->priv->tooltip_id = 0;
eprop->priv->sensitive_id = 0;
eprop->priv->changed_id = 0;
eprop->priv->enabled_id = 0;
eprop->priv->state_id = 0;
/* Unref it here */
g_object_weak_unref (G_OBJECT (eprop->priv->property),
(GWeakNotify) glade_eprop_property_finalized, eprop);
/* For a reason I cant quite tell yet, this is the only
* safe way to nullify the property member of the eprop
* without leeking signal connections to properties :-/
*/
if (property == NULL)
{
eprop->priv->property = NULL;
}
}
/* Connect new stuff, deal with tooltip
*/
if (eprop->priv->property != property && property != NULL)
{
GladePropertyClass *pclass = glade_property_get_class (property);
eprop->priv->property = property;
eprop->priv->tooltip_id =
g_signal_connect (G_OBJECT (eprop->priv->property),
"tooltip-changed",
G_CALLBACK (glade_editor_property_tooltip_cb),
eprop);
eprop->priv->sensitive_id =
g_signal_connect (G_OBJECT (eprop->priv->property),
"notify::sensitive",
G_CALLBACK (glade_editor_property_sensitivity_cb),
eprop);
eprop->priv->changed_id =
g_signal_connect (G_OBJECT (eprop->priv->property),
"value-changed",
G_CALLBACK (glade_editor_property_value_changed_cb),
eprop);
eprop->priv->enabled_id =
g_signal_connect (G_OBJECT (eprop->priv->property),
"notify::enabled",
G_CALLBACK (glade_editor_property_enabled_cb),
eprop);
eprop->priv->state_id =
g_signal_connect (G_OBJECT (eprop->priv->property),
"notify::state",
G_CALLBACK (glade_editor_property_state_cb), eprop);
/* In query dialogs when the user hits cancel,
* these babies go away (so better stay protected).
*/
g_object_weak_ref (G_OBJECT (eprop->priv->property),
(GWeakNotify) glade_eprop_property_finalized, eprop);
/* Load initial tooltips
*/
glade_editor_property_tooltip_cb
(property, glade_property_class_get_tooltip (pclass),
glade_propert_get_insensitive_tooltip (property),
glade_property_get_support_warning (property), eprop);
/* Load initial enabled state
*/
glade_editor_property_enabled_cb (property, NULL, eprop);
/* Load initial sensitive state.
*/
glade_editor_property_sensitivity_cb (property, NULL, eprop);
/* Load intial label state
*/
glade_editor_property_state_cb (property, NULL, eprop);
}
}
static void
glade_editor_property_init (GladeEditorProperty * eprop)
{
eprop->priv =
G_TYPE_INSTANCE_GET_PRIVATE ((eprop),
GLADE_TYPE_EDITOR_PROPERTY,
GladeEditorPropertyPrivate);
}
static void
glade_editor_property_class_init (GladeEditorPropertyClass * eprop_class)
{
GObjectClass *object_class;
g_return_if_fail (eprop_class != NULL);
/* Both parent classes assigned here.
*/
editor_property_class = eprop_class;
table_class = g_type_class_peek_parent (eprop_class);
object_class = G_OBJECT_CLASS (eprop_class);
/* GObjectClass */
object_class->constructor = glade_editor_property_constructor;
object_class->finalize = glade_editor_property_finalize;
object_class->get_property = glade_editor_property_real_get_property;
object_class->set_property = glade_editor_property_set_property;
/* Class methods */
eprop_class->load = glade_editor_property_load_common;
eprop_class->commit = glade_editor_property_commit_common;
eprop_class->create_input = NULL;
/**
* GladeEditorProperty::value-changed:
* @gladeeditorproperty: the #GladeEditorProperty which changed value
* @arg1: the #GladeProperty that's value changed.
*
* Emitted when a contained property changes value
*/
glade_eprop_signals[CHANGED] =
g_signal_new ("value-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GladeEditorPropertyClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GLADE_TYPE_PROPERTY);
/**
* GladeEditorProperty::commit:
* @gladeeditorproperty: the #GladeEditorProperty which changed value
* @arg1: the new #GValue to commit.
*
* Emitted when a property's value is committed, can be useful to serialize
* commands before and after the property's commit command from custom editors.
*/
glade_eprop_signals[COMMIT] =
g_signal_new ("commit",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GladeEditorPropertyClass, commit),
NULL, NULL,
glade_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
/* Properties */
g_object_class_install_property
(object_class, PROP_PROPERTY_CLASS,
g_param_spec_pointer
("property-class", _("Property Class"),
_("The GladePropertyClass this GladeEditorProperty was created for"),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_USE_COMMAND,
g_param_spec_boolean
("use-command", _("Use Command"),
_("Whether we should use the command API for the undo/redo stack"),
FALSE, G_PARAM_READWRITE));
g_type_class_add_private (eprop_class, sizeof (GladeEditorPropertyPrivate));
}
GType
glade_editor_property_get_type (void)
{
static GType property_type = 0;
if (!property_type)
{
static const GTypeInfo property_info = {
sizeof (GladeEditorPropertyClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) glade_editor_property_class_init,
(GClassFinalizeFunc) NULL,
NULL, /* class_data */
sizeof (GladeEditorProperty),
0, /* n_preallocs */
(GInstanceInitFunc) glade_editor_property_init,
};
property_type =
g_type_register_static (GTK_TYPE_HBOX,
"GladeEditorProperty", &property_info, 0);
}
return property_type;
}
/*******************************************************************************
GladeEditorPropertyNumericClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *spin;
} GladeEPropNumeric;
GLADE_MAKE_EPROP (GladeEPropNumeric, glade_eprop_numeric)
#define GLADE_EPROP_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_NUMERIC, GladeEPropNumeric))
#define GLADE_EPROP_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_NUMERIC, GladeEPropNumericClass))
#define GLADE_IS_EPROP_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_NUMERIC))
#define GLADE_IS_EPROP_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_NUMERIC))
#define GLADE_EPROP_NUMERIC_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_NUMERIC, GladeEPropNumericClass))
static void glade_eprop_numeric_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_numeric_load (GladeEditorProperty * eprop, GladeProperty * property)
{
gfloat val = 0.0F;
GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop);
GParamSpec *pspec;
GValue *value;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
value = glade_property_inline_value (property);
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (G_IS_PARAM_SPEC_INT (pspec))
val = (gfloat) g_value_get_int (value);
else if (G_IS_PARAM_SPEC_UINT (pspec))
val = (gfloat) g_value_get_uint (value);
else if (G_IS_PARAM_SPEC_LONG (pspec))
val = (gfloat) g_value_get_long (value);
else if (G_IS_PARAM_SPEC_ULONG (pspec))
val = (gfloat) g_value_get_ulong (value);
else if (G_IS_PARAM_SPEC_INT64 (pspec))
val = (gfloat) g_value_get_int64 (value);
else if (G_IS_PARAM_SPEC_UINT64 (pspec))
val = (gfloat) g_value_get_uint64 (value);
else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
val = (gfloat) g_value_get_double (value);
else if (G_IS_PARAM_SPEC_FLOAT (pspec))
val = g_value_get_float (value);
else
g_warning ("Unsupported type %s\n",
g_type_name (G_PARAM_SPEC_TYPE (pspec)));
gtk_spin_button_set_value (GTK_SPIN_BUTTON (eprop_numeric->spin), val);
}
}
static void
glade_eprop_numeric_changed (GtkWidget * spin, GladeEditorProperty * eprop)
{
GValue val = { 0, };
GParamSpec *pspec;
if (eprop->priv->loading)
return;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
g_value_init (&val, pspec->value_type);
if (G_IS_PARAM_SPEC_INT (pspec))
g_value_set_int (&val, gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_UINT (pspec))
g_value_set_uint (&val, gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_LONG (pspec))
g_value_set_long (&val, (glong) gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_ULONG (pspec))
g_value_set_ulong (&val, (gulong) gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_INT64 (pspec))
g_value_set_int64 (&val, (gint64) gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_UINT64 (pspec))
g_value_set_uint64 (&val, (guint64) gtk_spin_button_get_value_as_int
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_FLOAT (pspec))
g_value_set_float (&val, (gfloat) gtk_spin_button_get_value
(GTK_SPIN_BUTTON (spin)));
else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
g_value_set_double (&val, gtk_spin_button_get_value
(GTK_SPIN_BUTTON (spin)));
else
g_warning ("Unsupported type %s\n",
g_type_name (G_PARAM_SPEC_TYPE (pspec)));
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
static GtkWidget *
glade_eprop_numeric_create_input (GladeEditorProperty * eprop)
{
GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop);
GtkAdjustment *adjustment;
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
adjustment = glade_property_class_make_adjustment (eprop->priv->klass);
eprop_numeric->spin =
gtk_spin_button_new (adjustment, 4,
G_IS_PARAM_SPEC_FLOAT (pspec) ||
G_IS_PARAM_SPEC_DOUBLE (pspec) ? 2 : 0);
gtk_widget_show (eprop_numeric->spin);
g_signal_connect (G_OBJECT (eprop_numeric->spin), "value_changed",
G_CALLBACK (glade_eprop_numeric_changed), eprop);
return eprop_numeric->spin;
}
/*******************************************************************************
GladeEditorPropertyEnumClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *combo_box;
} GladeEPropEnum;
GLADE_MAKE_EPROP (GladeEPropEnum, glade_eprop_enum)
#define GLADE_EPROP_ENUM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_ENUM, GladeEPropEnum))
#define GLADE_EPROP_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_ENUM, GladeEPropEnumClass))
#define GLADE_IS_EPROP_ENUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_ENUM))
#define GLADE_IS_EPROP_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_ENUM))
#define GLADE_EPROP_ENUM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_ENUM, GladeEPropEnumClass))
static void glade_eprop_enum_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_enum_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropEnum *eprop_enum = GLADE_EPROP_ENUM (eprop);
GParamSpec *pspec;
GEnumClass *eclass;
guint i;
gint value;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
pspec = glade_property_class_get_pspec (eprop->priv->klass);
eclass = g_type_class_ref (pspec->value_type);
value = g_value_get_enum (glade_property_inline_value (property));
for (i = 0; i < eclass->n_values; i++)
if (eclass->values[i].value == value)
break;
gtk_combo_box_set_active (GTK_COMBO_BOX (eprop_enum->combo_box),
i < eclass->n_values ? i : 0);
g_type_class_unref (eclass);
}
}
static void
glade_eprop_enum_changed (GtkWidget * combo_box, GladeEditorProperty * eprop)
{
gint ival;
GValue val = { 0, };
GParamSpec *pspec;
GladeProperty *property;
GtkTreeModel *tree_model;
GtkTreeIter iter;
if (eprop->priv->loading)
return;
tree_model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo_box));
gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo_box), &iter);
gtk_tree_model_get (tree_model, &iter, 1, &ival, -1);
property = eprop->priv->property;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
g_value_init (&val, pspec->value_type);
g_value_set_enum (&val, ival);
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
static GtkWidget *
glade_eprop_enum_create_input (GladeEditorProperty * eprop)
{
GladeEPropEnum *eprop_enum = GLADE_EPROP_ENUM (eprop);
GladePropertyClass *klass;
GParamSpec *pspec;
GEnumClass *eclass;
GtkListStore *list_store;
GtkTreeIter iter;
GtkCellRenderer *cell_renderer;
guint i;
klass = eprop->priv->klass;
pspec = glade_property_class_get_pspec (klass);
eclass = g_type_class_ref (pspec->value_type);
list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter);
for (i = 0; i < eclass->n_values; i++)
{
const gchar *value_name =
glade_get_displayable_value (pspec->value_type,
eclass->values[i].value_nick);
if (value_name == NULL)
value_name = eclass->values[i].value_nick;
gtk_list_store_append (list_store, &iter);
gtk_list_store_set (list_store, &iter, 0, value_name, 1,
eclass->values[i].value, -1);
}
eprop_enum->combo_box = gtk_combo_box_new_with_model
(GTK_TREE_MODEL (list_store));
cell_renderer = gtk_cell_renderer_text_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (eprop_enum->combo_box),
cell_renderer, TRUE);
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (eprop_enum->combo_box),
cell_renderer, "text", 0);
g_signal_connect (G_OBJECT (eprop_enum->combo_box), "changed",
G_CALLBACK (glade_eprop_enum_changed), eprop);
gtk_widget_show_all (eprop_enum->combo_box);
g_type_class_unref (eclass);
return eprop_enum->combo_box;
}
/*******************************************************************************
GladeEditorPropertyFlagsClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkTreeModel *model;
GtkWidget *entry;
} GladeEPropFlags;
GLADE_MAKE_EPROP (GladeEPropFlags, glade_eprop_flags)
#define GLADE_EPROP_FLAGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_FLAGS, GladeEPropFlags))
#define GLADE_EPROP_FLAGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_FLAGS, GladeEPropFlagsClass))
#define GLADE_IS_EPROP_FLAGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_FLAGS))
#define GLADE_IS_EPROP_FLAGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_FLAGS))
#define GLADE_EPROP_FLAGS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_FLAGS, GladeEPropFlagsClass))
static void glade_eprop_flags_finalize (GObject * object)
{
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (object);
g_object_unref (G_OBJECT (eprop_flags->model));
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_flags_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
GFlagsClass *klass;
GParamSpec *pspec;
guint flag_num, value;
GString *string = g_string_new (NULL);
/* Chain up first */
editor_property_class->load (eprop, property);
gtk_list_store_clear (GTK_LIST_STORE (eprop_flags->model));
if (property)
{
/* Populate the model with the flags. */
klass = g_type_class_ref (G_VALUE_TYPE (glade_property_inline_value (property)));
value = g_value_get_flags (glade_property_inline_value (property));
pspec = glade_property_class_get_pspec (eprop->priv->klass);
/* Step through each of the flags in the class. */
for (flag_num = 0; flag_num < klass->n_values; flag_num++)
{
GtkTreeIter iter;
guint mask;
gboolean setting;
const gchar *value_name;
mask = klass->values[flag_num].value;
setting = ((value & mask) == mask) ? TRUE : FALSE;
value_name = glade_get_displayable_value
(pspec->value_type, klass->values[flag_num].value_nick);
if (value_name == NULL)
value_name = klass->values[flag_num].value_name;
/* Setup string for property label */
if (setting)
{
if (string->len > 0)
g_string_append (string, " | ");
g_string_append (string, value_name);
}
/* Add a row to represent the flag. */
gtk_list_store_append (GTK_LIST_STORE (eprop_flags->model), &iter);
gtk_list_store_set (GTK_LIST_STORE (eprop_flags->model), &iter,
FLAGS_COLUMN_SETTING,
setting, FLAGS_COLUMN_SYMBOL, value_name, -1);
}
g_type_class_unref (klass);
}
gtk_entry_set_text (GTK_ENTRY (eprop_flags->entry), string->str);
g_string_free (string, TRUE);
}
static void
flag_toggled_direct (GtkCellRendererToggle * cell,
gchar * path_string, GladeEditorProperty * eprop)
{
GtkTreeIter iter;
guint new_value = 0;
gboolean selected;
guint value = 0;
gint flag_num = 0;
GFlagsClass *klass;
GValue *gvalue;
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
if (!eprop->priv->property)
return;
gvalue = glade_property_inline_value (eprop->priv->property);
klass = g_type_class_ref (G_VALUE_TYPE (gvalue));
value = g_value_get_flags (gvalue);
gtk_tree_model_get_iter_from_string (eprop_flags->model, &iter, path_string);
gtk_tree_model_get (eprop_flags->model, &iter,
FLAGS_COLUMN_SETTING, &selected, -1);
selected = selected ? FALSE : TRUE;
gtk_list_store_set (GTK_LIST_STORE (eprop_flags->model), &iter,
FLAGS_COLUMN_SETTING, selected, -1);
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (eprop_flags->model), &iter);
/* Step through each of the flags in the class, checking if
the corresponding toggle in the dialog is selected, If it
is, OR the flags' mask with the new value. */
for (flag_num = 0; flag_num < klass->n_values; flag_num++)
{
gboolean setting;
gtk_tree_model_get (GTK_TREE_MODEL (eprop_flags->model), &iter,
FLAGS_COLUMN_SETTING, &setting, -1);
if (setting)
new_value |= klass->values[flag_num].value;
gtk_tree_model_iter_next (GTK_TREE_MODEL (eprop_flags->model), &iter);
}
/* If the new_value is different from the old value, we need
to update the property. */
if (new_value != value)
{
GValue val = { 0, };
g_value_init (&val, G_VALUE_TYPE (gvalue));
g_value_set_flags (&val, new_value);
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
}
static GtkWidget *
glade_eprop_flags_create_treeview (GladeEditorProperty * eprop)
{
GtkWidget *scrolled_window;
GtkWidget *tree_view;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
if (!eprop_flags->model)
eprop_flags->model = GTK_TREE_MODEL (gtk_list_store_new (2, G_TYPE_BOOLEAN,
G_TYPE_STRING));
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_widget_show (scrolled_window);
tree_view =
gtk_tree_view_new_with_model (GTK_TREE_MODEL (eprop_flags->model));
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
gtk_widget_show (tree_view);
gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
column = gtk_tree_view_column_new ();
renderer = gtk_cell_renderer_toggle_new ();
gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer,
"active", FLAGS_COLUMN_SETTING, NULL);
g_signal_connect (renderer, "toggled",
G_CALLBACK (flag_toggled_direct), eprop);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_attributes (column, renderer,
"text", FLAGS_COLUMN_SYMBOL, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
return scrolled_window;
}
static void
glade_eprop_flags_show_dialog (GtkWidget * button, GladeEditorProperty * eprop)
{
GtkWidget *dialog;
GtkWidget *view;
GtkWidget *label;
GtkWidget *vbox;
GtkWidget *content_area;
GtkWidget *action_area;
dialog = gtk_dialog_new_with_buttons (_("Select Fields"),
GTK_WINDOW (gtk_widget_get_toplevel
(button)), GTK_DIALOG_MODAL,
GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
NULL);
gtk_window_set_default_size (GTK_WINDOW (dialog), 300, 400);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
/* HIG spacings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
gtk_box_set_spacing (GTK_BOX (action_area), 6);
vbox = gtk_vbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
view = glade_eprop_flags_create_treeview (eprop);
label = gtk_label_new_with_mnemonic (_("_Select individual fields:"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_label_set_mnemonic_widget (GTK_LABEL (label),
gtk_bin_get_child (GTK_BIN (view)));
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
gtk_widget_show (label);
gtk_widget_show (view);
gtk_widget_show (vbox);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
static GtkWidget *
glade_eprop_flags_create_input (GladeEditorProperty * eprop)
{
GtkWidget *vbox, *hbox, *button, *widget;
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
hbox = gtk_hbox_new (FALSE, 0);
vbox = gtk_vbox_new (FALSE, 0);
widget = glade_eprop_flags_create_treeview (eprop);
eprop_flags->entry = gtk_entry_new ();
gtk_editable_set_editable (GTK_EDITABLE (eprop_flags->entry), FALSE);
gtk_box_pack_start (GTK_BOX (vbox), eprop_flags->entry, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
button = gtk_button_new_with_label ("...");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show_all (hbox);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_eprop_flags_show_dialog), eprop);
return hbox;
}
/*******************************************************************************
GladeEditorPropertyColorClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *cbutton;
GtkWidget *entry;
} GladeEPropColor;
GLADE_MAKE_EPROP (GladeEPropColor, glade_eprop_color)
#define GLADE_EPROP_COLOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_COLOR, GladeEPropColor))
#define GLADE_EPROP_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_COLOR, GladeEPropColorClass))
#define GLADE_IS_EPROP_COLOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_COLOR))
#define GLADE_IS_EPROP_COLOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_COLOR))
#define GLADE_EPROP_COLOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_COLOR, GladeEPropColorClass))
static void glade_eprop_color_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_color_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropColor *eprop_color = GLADE_EPROP_COLOR (eprop);
GdkColor *color;
gchar *text;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
if ((text = glade_property_make_string (property)) != NULL)
{
gtk_entry_set_text (GTK_ENTRY (eprop_color->entry), text);
g_free (text);
}
else
gtk_entry_set_text (GTK_ENTRY (eprop_color->entry), "");
if ((color = g_value_get_boxed (glade_property_inline_value (property))) != NULL)
gtk_color_button_set_color (GTK_COLOR_BUTTON (eprop_color->cbutton), color);
else
{
GdkColor black = { 0, };
/* Manually fill it with black for an NULL value.
*/
if (gdk_color_parse ("Black", &black))
gtk_color_button_set_color
(GTK_COLOR_BUTTON (eprop_color->cbutton), &black);
}
}
}
static void
glade_eprop_color_changed (GtkWidget * button, GladeEditorProperty * eprop)
{
GdkColor color = { 0, };
GValue value = { 0, };
if (eprop->priv->loading)
return;
gtk_color_button_get_color (GTK_COLOR_BUTTON (button), &color);
g_value_init (&value, GDK_TYPE_COLOR);
g_value_set_boxed (&value, &color);
glade_editor_property_commit_no_callback (eprop, &value);
g_value_unset (&value);
}
static GtkWidget *
glade_eprop_color_create_input (GladeEditorProperty * eprop)
{
GladeEPropColor *eprop_color = GLADE_EPROP_COLOR (eprop);
GtkWidget *hbox;
hbox = gtk_hbox_new (FALSE, 0);
eprop_color->entry = gtk_entry_new ();
gtk_editable_set_editable (GTK_EDITABLE (eprop_color->entry), FALSE);
gtk_widget_show (eprop_color->entry);
gtk_box_pack_start (GTK_BOX (hbox), eprop_color->entry, TRUE, TRUE, 0);
eprop_color->cbutton = gtk_color_button_new ();
gtk_widget_show (eprop_color->cbutton);
gtk_box_pack_start (GTK_BOX (hbox), eprop_color->cbutton, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (eprop_color->cbutton), "color-set",
G_CALLBACK (glade_eprop_color_changed), eprop);
return hbox;
}
/*******************************************************************************
GladeEditorPropertyNamedIconClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *entry;
gchar *current_context;
} GladeEPropNamedIcon;
GLADE_MAKE_EPROP (GladeEPropNamedIcon, glade_eprop_named_icon)
#define GLADE_EPROP_NAMED_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_NAMED_ICON, GladeEPropNamedIcon))
#define GLADE_EPROP_NAMED_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_NAMED_ICON, GladeEPropNamedIconClass))
#define GLADE_IS_EPROP_NAMED_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_NAMED_ICON))
#define GLADE_IS_EPROP_NAMED_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_NAMED_ICON))
#define GLADE_EPROP_NAMED_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_NAMED_ICON, GladeEPropNamedIconClass))
static void glade_eprop_named_icon_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_named_icon_load (GladeEditorProperty * eprop,
GladeProperty * property)
{
GladeEPropNamedIcon *eprop_named_icon = GLADE_EPROP_NAMED_ICON (eprop);
GtkEntry *entry;
const gchar *text;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property == NULL)
return;
entry = GTK_ENTRY (eprop_named_icon->entry);
text = glade_property_make_string (property);
gtk_entry_set_text (entry, text ? text : "");
}
static void
glade_eprop_named_icon_changed_common (GladeEditorProperty * eprop,
const gchar * text, gboolean use_command)
{
GValue *val;
gchar *prop_text;
val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_STRING);
glade_property_get (eprop->priv->property, &prop_text);
/* Here we try not to modify the project state by not
* modifying a null value for an unchanged property.
*/
if (prop_text == NULL && text && text[0] == '\0')
g_value_set_string (val, NULL);
else if (text == NULL && prop_text && prop_text == '\0')
g_value_set_string (val, "");
else
g_value_set_string (val, text);
glade_editor_property_commit (eprop, val);
g_value_unset (val);
g_free (val);
}
static void
glade_eprop_named_icon_changed (GtkWidget * entry, GladeEditorProperty * eprop)
{
gchar *text;
if (eprop->priv->loading)
return;
text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
glade_eprop_named_icon_changed_common (eprop, text, eprop->priv->use_command);
g_free (text);
}
static gboolean
glade_eprop_named_icon_focus_out (GtkWidget * entry,
GdkEventFocus * event,
GladeEditorProperty * eprop)
{
glade_eprop_named_icon_changed (entry, eprop);
return FALSE;
}
static void
glade_eprop_named_icon_activate (GtkEntry * entry, GladeEPropNamedIcon * eprop)
{
glade_eprop_named_icon_changed (GTK_WIDGET (entry),
GLADE_EDITOR_PROPERTY (eprop));
}
static void
chooser_response (GladeNamedIconChooserDialog * dialog,
gint response_id, GladeEPropNamedIcon * eprop)
{
gchar *icon_name;
switch (response_id)
{
case GTK_RESPONSE_OK:
g_free (eprop->current_context);
eprop->current_context =
glade_named_icon_chooser_dialog_get_context (dialog);
icon_name = glade_named_icon_chooser_dialog_get_icon_name (dialog);
gtk_entry_set_text (GTK_ENTRY (eprop->entry), icon_name);
gtk_widget_destroy (GTK_WIDGET (dialog));
g_free (icon_name);
glade_eprop_named_icon_changed (eprop->entry,
GLADE_EDITOR_PROPERTY (eprop));
break;
case GTK_RESPONSE_CANCEL:
gtk_widget_destroy (GTK_WIDGET (dialog));
break;
case GTK_RESPONSE_HELP:
break;
case GTK_RESPONSE_DELETE_EVENT:
gtk_widget_destroy (GTK_WIDGET (dialog));
}
}
static void
glade_eprop_named_icon_show_chooser_dialog (GtkWidget * button,
GladeEditorProperty * eprop)
{
GtkWidget *dialog;
dialog = glade_named_icon_chooser_dialog_new (_("Select Named Icon"),
GTK_WINDOW
(gtk_widget_get_toplevel
(GTK_WIDGET (eprop))),
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
glade_named_icon_chooser_dialog_set_context (GLADE_NAMED_ICON_CHOOSER_DIALOG
(dialog),
GLADE_EPROP_NAMED_ICON (eprop)->
current_context);
glade_named_icon_chooser_dialog_set_icon_name (GLADE_NAMED_ICON_CHOOSER_DIALOG
(dialog),
gtk_entry_get_text (GTK_ENTRY
(GLADE_EPROP_NAMED_ICON
(eprop)->
entry)));
g_signal_connect (dialog, "response", G_CALLBACK (chooser_response), eprop);
gtk_widget_show (dialog);
}
static GtkWidget *
glade_eprop_named_icon_create_input (GladeEditorProperty * eprop)
{
GladeEPropNamedIcon *eprop_named_icon = GLADE_EPROP_NAMED_ICON (eprop);
GtkWidget *hbox;
GtkWidget *button;
hbox = gtk_hbox_new (FALSE, 0);
eprop_named_icon->entry = gtk_entry_new ();
gtk_widget_show (eprop_named_icon->entry);
eprop_named_icon->current_context = NULL;
gtk_box_pack_start (GTK_BOX (hbox), eprop_named_icon->entry, TRUE, TRUE, 0);
g_signal_connect (G_OBJECT (eprop_named_icon->entry), "activate",
G_CALLBACK (glade_eprop_named_icon_activate), eprop);
g_signal_connect (G_OBJECT (eprop_named_icon->entry), "focus-out-event",
G_CALLBACK (glade_eprop_named_icon_focus_out), eprop);
button = gtk_button_new_with_label ("\342\200\246");
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (glade_eprop_named_icon_show_chooser_dialog),
eprop);
return hbox;
}
/*******************************************************************************
GladeEditorPropertyTextClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *text_entry;
GtkTreeModel *store;
} GladeEPropText;
GLADE_MAKE_EPROP (GladeEPropText, glade_eprop_text)
#define GLADE_EPROP_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_TEXT, GladeEPropText))
#define GLADE_EPROP_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_TEXT, GladeEPropTextClass))
#define GLADE_IS_EPROP_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_TEXT))
#define GLADE_IS_EPROP_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_TEXT))
#define GLADE_EPROP_TEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_TEXT, GladeEPropTextClass))
static void glade_eprop_text_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_text_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop);
GParamSpec *pspec;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property == NULL)
return;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (GTK_IS_COMBO_BOX (eprop_text->text_entry))
{
if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (eprop_text->text_entry)))
{
const gchar *text = glade_property_make_string (property);
if (!text)
text = "";
gtk_entry_set_text (GTK_ENTRY
(gtk_bin_get_child
(GTK_BIN (eprop_text->text_entry))), text);
}
else
{
const gchar *text = glade_property_make_string (property);
gint value = text ?
glade_utils_enum_value_from_string (GLADE_TYPE_STOCK, text) : 0;
/* Set active iter... */
gtk_combo_box_set_active (GTK_COMBO_BOX (eprop_text->text_entry),
value);
}
}
else if (GTK_IS_ENTRY (eprop_text->text_entry))
{
GtkEntry *entry = GTK_ENTRY (eprop_text->text_entry);
const gchar *text = NULL;
if (pspec->value_type == G_TYPE_STRING)
text = glade_property_make_string (property);
else if (pspec->value_type == GDK_TYPE_PIXBUF)
{
GObject *object = g_value_get_object (glade_property_inline_value (property));
if (object)
text = g_object_get_data (object, "GladeFileName");
}
gtk_entry_set_text (entry, text ? text : "");
}
else if (GTK_IS_TEXT_VIEW (eprop_text->text_entry))
{
GtkTextBuffer *buffer;
buffer =
gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry));
if (pspec->value_type == G_TYPE_STRV ||
pspec->value_type == G_TYPE_VALUE_ARRAY)
{
GladePropertyClass *pclass = glade_property_get_class (property);
gchar *text = glade_widget_adaptor_string_from_value
(glade_property_class_get_adaptor (pclass),
pclass, glade_property_inline_value (property));
gtk_text_buffer_set_text (buffer, text ? text : "", -1);
g_free (text);
}
else
{
const gchar *text = glade_property_make_string (property);
gtk_text_buffer_set_text (buffer, text ? text : "", -1);
}
}
else
{
g_warning ("BUG! Invalid Text Widget type.");
}
}
static void
glade_eprop_text_changed_common (GladeEditorProperty * eprop,
const gchar * text, gboolean use_command)
{
GValue *val;
GParamSpec *pspec;
gchar *prop_text;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (pspec->value_type == G_TYPE_STRV ||
pspec->value_type == G_TYPE_VALUE_ARRAY ||
pspec->value_type == GDK_TYPE_PIXBUF)
{
val = glade_property_class_make_gvalue_from_string
(eprop->priv->klass, text,
glade_widget_get_project (glade_property_get_widget (eprop->priv->property)),
glade_property_get_widget (eprop->priv->property));
}
else
{
val = g_new0 (GValue, 1);
g_value_init (val, G_TYPE_STRING);
glade_property_get (eprop->priv->property, &prop_text);
/* Here we try not to modify the project state by not
* modifying a null value for an unchanged property.
*/
if (prop_text == NULL && text && text[0] == '\0')
g_value_set_string (val, NULL);
else if (text == NULL && prop_text && prop_text == '\0')
g_value_set_string (val, "");
else
g_value_set_string (val, text);
}
glade_editor_property_commit_no_callback (eprop, val);
g_value_unset (val);
g_free (val);
}
static void
glade_eprop_text_changed (GtkWidget * entry, GladeEditorProperty * eprop)
{
gchar *text;
if (eprop->priv->loading)
return;
text = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command);
g_free (text);
}
static void
glade_eprop_text_buffer_changed (GtkTextBuffer * buffer,
GladeEditorProperty * eprop)
{
gchar *text;
GtkTextIter start, end;
if (eprop->priv->loading)
return;
gtk_text_buffer_get_start_iter (buffer, &start);
gtk_text_buffer_get_end_iter (buffer, &end);
text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command);
g_free (text);
}
/**
* glade_editor_property_show_i18n_dialog:
* @parent: The parent widget for the dialog.
* @text: A read/write pointer to the text property
* @context: A read/write pointer to the translation context
* @comment: A read/write pointer to the translator comment
* @translatable: A read/write pointer to the translatable setting]
*
* Runs a dialog and updates the provided values.
*
* Returns: %TRUE if OK was selected.
*/
gboolean
glade_editor_property_show_i18n_dialog (GtkWidget * parent,
gchar ** text,
gchar ** context,
gchar ** comment,
gboolean * translatable)
{
GtkWidget *dialog;
GtkWidget *vbox, *hbox;
GtkWidget *label;
GtkWidget *sw;
GtkWidget *alignment;
GtkWidget *text_view, *comment_view, *context_view;
GtkTextBuffer *text_buffer, *comment_buffer, *context_buffer = NULL;
GtkWidget *translatable_button;
GtkWidget *content_area, *action_area;
gint res;
g_return_val_if_fail (text && context && comment && translatable, FALSE);
dialog = gtk_dialog_new_with_buttons (_("Edit Text"),
parent ?
GTK_WINDOW (gtk_widget_get_toplevel
(parent)) : NULL,
GTK_DIALOG_MODAL, GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL, GTK_STOCK_OK,
GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL, -1);
/* HIG spacings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
gtk_box_set_spacing (GTK_BOX (action_area), 6);
vbox = gtk_vbox_new (FALSE, 6);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
gtk_widget_show (vbox);
gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
/* Text */
label = gtk_label_new_with_mnemonic (_("_Text:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 200);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
text_view = gtk_text_view_new ();
gtk_widget_show (text_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), text_view);
gtk_container_add (GTK_CONTAINER (sw), text_view);
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
if (*text)
{
gtk_text_buffer_set_text (text_buffer, *text, -1);
}
/* Translatable and context prefix. */
hbox = gtk_hbox_new (FALSE, 12);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
/* Translatable */
translatable_button = gtk_check_button_new_with_mnemonic (_("T_ranslatable"));
gtk_widget_show (translatable_button);
gtk_box_pack_start (GTK_BOX (hbox), translatable_button, FALSE, FALSE, 0);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (translatable_button),
*translatable);
gtk_widget_set_tooltip_text (translatable_button,
_("Whether this property is translatable"));
/* Context. */
alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0);
gtk_widget_show (alignment);
label = gtk_label_new_with_mnemonic (_("Conte_xt for translation:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_container_add (GTK_CONTAINER (alignment), label);
gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
gtk_widget_set_tooltip_text (alignment,
"XXX Some explanation about translation context please ???");
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
context_view = gtk_text_view_new ();
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (context_view), GTK_WRAP_WORD);
gtk_widget_show (context_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), context_view);
gtk_container_add (GTK_CONTAINER (sw), context_view);
context_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (context_view));
if (*context)
{
gtk_text_buffer_set_text (context_buffer, *context, -1);
}
/* Comments. */
alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 12, 0, 0, 0);
gtk_widget_show (alignment);
label = gtk_label_new_with_mnemonic (_("Co_mments for translators:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_container_add (GTK_CONTAINER (alignment), label);
gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
comment_view = gtk_text_view_new ();
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (comment_view), GTK_WRAP_WORD);
gtk_widget_show (comment_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), comment_view);
gtk_container_add (GTK_CONTAINER (sw), comment_view);
comment_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (comment_view));
if (*comment)
{
gtk_text_buffer_set_text (comment_buffer, *comment, -1);
}
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_OK)
{
GtkTextIter start, end;
g_free ((gpointer) * text);
g_free ((gpointer) * context);
g_free ((gpointer) * comment);
/* get the new values for translatable, has_context, and comment */
*translatable =
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(translatable_button));
/* Comment */
gtk_text_buffer_get_bounds (comment_buffer, &start, &end);
*comment = gtk_text_buffer_get_text (comment_buffer, &start, &end, TRUE);
if (*comment[0] == '\0')
{
g_free (*comment);
*comment = NULL;
}
/* Text */
gtk_text_buffer_get_bounds (text_buffer, &start, &end);
*text = gtk_text_buffer_get_text (text_buffer, &start, &end, TRUE);
if (*text[0] == '\0')
{
g_free (*text);
*text = NULL;
}
/* Context */
gtk_text_buffer_get_bounds (context_buffer, &start, &end);
*context = gtk_text_buffer_get_text (context_buffer, &start, &end, TRUE);
if (*context[0] == '\0')
{
g_free (*context);
*context = NULL;
}
gtk_widget_destroy (dialog);
return TRUE;
}
gtk_widget_destroy (dialog);
return FALSE;
}
static void
glade_eprop_text_show_i18n_dialog (GtkWidget * entry,
GladeEditorProperty * eprop)
{
gchar *text = glade_property_make_string (eprop->priv->property);
gchar *context = g_strdup (glade_property_i18n_get_context (eprop->priv->property));
gchar *comment = g_strdup (glade_property_i18n_get_comment (eprop->priv->property));
gboolean translatable =
glade_property_i18n_get_translatable (eprop->priv->property);
if (glade_editor_property_show_i18n_dialog
(entry, &text, &context, &comment, &translatable))
{
glade_command_set_i18n (eprop->priv->property, translatable, context, comment);
glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command);
glade_editor_property_load (eprop, eprop->priv->property);
g_free (text);
g_free (context);
g_free (comment);
}
}
gboolean
glade_editor_property_show_resource_dialog (GladeProject * project,
GtkWidget * parent,
gchar ** filename)
{
GtkWidget *dialog;
GtkWidget *action_area;
gchar *folder;
g_return_val_if_fail (filename != NULL, FALSE);
dialog =
gtk_file_chooser_dialog_new (_
("Select a file from the project resource directory"),
parent ?
GTK_WINDOW (gtk_widget_get_toplevel (parent))
: NULL, GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL, -1);
/* HIG spacings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2); /* 2 * 5 + 2 = 12 */
action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
gtk_box_set_spacing (GTK_BOX (action_area), 6);
folder = glade_project_resource_fullpath (project, ".");
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), folder);
g_free (folder);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
gchar *name;
name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
*filename = name ? g_path_get_basename (name) : NULL;
g_free (name);
gtk_widget_destroy (dialog);
return TRUE;
}
gtk_widget_destroy (dialog);
return FALSE;
}
static void
glade_eprop_text_show_resource_dialog (GtkWidget * entry,
GladeEditorProperty * eprop)
{
GladeWidget *widget = glade_property_get_widget (eprop->priv->property);
GladeProject *project = glade_widget_get_project (widget);
gchar *text = NULL;
if (glade_editor_property_show_resource_dialog (project, entry, &text))
{
glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command);
glade_editor_property_load (eprop, eprop->priv->property);
g_free (text);
}
}
enum
{
COMBO_COLUMN_TEXT = 0,
COMBO_COLUMN_PIXBUF,
COMBO_LAST_COLUMN
};
static GtkListStore *
glade_eprop_text_create_store (GType enum_type)
{
GtkListStore *store;
GtkTreeIter iter;
GEnumClass *eclass;
guint i;
eclass = g_type_class_ref (enum_type);
store = gtk_list_store_new (COMBO_LAST_COLUMN, G_TYPE_STRING, G_TYPE_STRING);
for (i = 0; i < eclass->n_values; i++)
{
const gchar *displayable =
glade_get_displayable_value (enum_type, eclass->values[i].value_nick);
if (!displayable)
displayable = eclass->values[i].value_nick;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
COMBO_COLUMN_TEXT, displayable,
COMBO_COLUMN_PIXBUF, eclass->values[i].value_nick,
-1);
}
g_type_class_unref (eclass);
return store;
}
static void
eprop_text_stock_changed (GtkComboBox * combo, GladeEditorProperty * eprop)
{
GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop);
GtkTreeIter iter;
gchar *text = NULL;
const gchar *str;
if (eprop->priv->loading)
return;
if (gtk_combo_box_get_active_iter (combo, &iter))
{
gtk_tree_model_get (GTK_TREE_MODEL (eprop_text->store), &iter,
COMBO_COLUMN_PIXBUF, &text, -1);
glade_eprop_text_changed_common (eprop, text, eprop->priv->use_command);
g_free (text);
}
else if (gtk_combo_box_get_has_entry (combo))
{
str =
gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo))));
glade_eprop_text_changed_common (eprop, str, eprop->priv->use_command);
}
}
static GtkWidget *
glade_eprop_text_create_input (GladeEditorProperty * eprop)
{
GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop);
GladePropertyClass *klass;
GParamSpec *pspec;
GtkWidget *hbox;
klass = eprop->priv->klass;
pspec = glade_property_class_get_pspec (klass);
hbox = gtk_hbox_new (FALSE, 0);
if (glade_property_class_stock (klass) ||
glade_property_class_stock_icon (klass))
{
GtkCellRenderer *renderer;
GtkWidget *child;
GtkWidget *combo = gtk_combo_box_new_with_entry ();
eprop_text->store = (GtkTreeModel *)
glade_eprop_text_create_store (glade_property_class_stock (klass) ?
GLADE_TYPE_STOCK : GLADE_TYPE_STOCK_IMAGE);
gtk_combo_box_set_model (GTK_COMBO_BOX (combo),
GTK_TREE_MODEL (eprop_text->store));
/* let the comboboxentry prepend its intrusive cell first... */
gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo),
COMBO_COLUMN_TEXT);
renderer = gtk_cell_renderer_pixbuf_new ();
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo), renderer, 0);
gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
"stock-id", COMBO_COLUMN_PIXBUF, NULL);
/* Dont allow custom items where an actual GTK+ stock item is expected
* (i.e. real items come with labels) */
child = gtk_bin_get_child (GTK_BIN (combo));
if (glade_property_class_stock (klass))
gtk_editable_set_editable (GTK_EDITABLE (child), FALSE);
else
gtk_editable_set_editable (GTK_EDITABLE (child), TRUE);
gtk_widget_show (combo);
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (combo), "changed",
G_CALLBACK (eprop_text_stock_changed), eprop);
eprop_text->text_entry = combo;
}
else if (glade_property_class_multiline (klass) ||
pspec->value_type == G_TYPE_STRV ||
pspec->value_type == G_TYPE_VALUE_ARRAY)
{
GtkWidget *swindow;
GtkTextBuffer *buffer;
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
GTK_SHADOW_IN);
eprop_text->text_entry = gtk_text_view_new ();
buffer =
gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry));
gtk_container_add (GTK_CONTAINER (swindow), eprop_text->text_entry);
gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (swindow), TRUE, TRUE, 0);
gtk_widget_show_all (swindow);
g_signal_connect (G_OBJECT (buffer), "changed",
G_CALLBACK (glade_eprop_text_buffer_changed), eprop);
}
else
{
eprop_text->text_entry = gtk_entry_new ();
gtk_widget_show (eprop_text->text_entry);
gtk_box_pack_start (GTK_BOX (hbox), eprop_text->text_entry, TRUE, TRUE,
0);
g_signal_connect (G_OBJECT (eprop_text->text_entry), "changed",
G_CALLBACK (glade_eprop_text_changed), eprop);
if (pspec->value_type == GDK_TYPE_PIXBUF)
{
GtkWidget *image =
gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
GtkWidget *button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (button), image);
g_signal_connect (button, "clicked",
G_CALLBACK (glade_eprop_text_show_resource_dialog),
eprop);
gtk_widget_show_all (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
}
}
if (glade_property_class_translatable (klass))
{
GtkWidget *button = gtk_button_new_with_label ("\342\200\246");
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (button, "clicked",
G_CALLBACK (glade_eprop_text_show_i18n_dialog), eprop);
}
return hbox;
}
/*******************************************************************************
GladeEditorPropertyBoolClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *toggle;
} GladeEPropBool;
GLADE_MAKE_EPROP (GladeEPropBool, glade_eprop_bool)
#define GLADE_EPROP_BOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_BOOL, GladeEPropBool))
#define GLADE_EPROP_BOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_BOOL, GladeEPropBoolClass))
#define GLADE_IS_EPROP_BOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_BOOL))
#define GLADE_IS_EPROP_BOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_BOOL))
#define GLADE_EPROP_BOOL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_BOOL, GladeEPropBoolClass))
static void glade_eprop_bool_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_bool_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop);
GtkWidget *label;
gboolean state;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
state = g_value_get_boolean (glade_property_inline_value (property));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop_bool->toggle),
state);
label = gtk_bin_get_child (GTK_BIN (eprop_bool->toggle));
gtk_label_set_text (GTK_LABEL (label), state ? _("Yes") : _("No"));
}
}
static void
glade_eprop_bool_changed (GtkWidget * button, GladeEditorProperty * eprop)
{
GtkWidget *label;
gboolean state;
GValue val = { 0, };
if (eprop->priv->loading)
return;
state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
label = gtk_bin_get_child (GTK_BIN (button));
gtk_label_set_text (GTK_LABEL (label), state ? _("Yes") : _("No"));
g_value_init (&val, G_TYPE_BOOLEAN);
g_value_set_boolean (&val, state);
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
static GtkWidget *
glade_eprop_bool_create_input (GladeEditorProperty * eprop)
{
GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop);
eprop_bool->toggle = gtk_toggle_button_new_with_label (_("No"));
g_signal_connect (G_OBJECT (eprop_bool->toggle), "toggled",
G_CALLBACK (glade_eprop_bool_changed), eprop);
return eprop_bool->toggle;
}
/*******************************************************************************
GladeEditorPropertyUnicharClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *entry;
} GladeEPropUnichar;
GLADE_MAKE_EPROP (GladeEPropUnichar, glade_eprop_unichar)
#define GLADE_EPROP_UNICHAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_UNICHAR, GladeEPropUnichar))
#define GLADE_EPROP_UNICHAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_UNICHAR, GladeEPropUnicharClass))
#define GLADE_IS_EPROP_UNICHAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_UNICHAR))
#define GLADE_IS_EPROP_UNICHAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_UNICHAR))
#define GLADE_EPROP_UNICHAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_UNICHAR, GladeEPropUnicharClass))
static void glade_eprop_unichar_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_unichar_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropUnichar *eprop_unichar = GLADE_EPROP_UNICHAR (eprop);
/* Chain up first */
editor_property_class->load (eprop, property);
if (property && GTK_IS_ENTRY (eprop_unichar->entry))
{
GtkEntry *entry = GTK_ENTRY (eprop_unichar->entry);
gchar utf8st[8];
gint n;
if ((n = g_unichar_to_utf8 (g_value_get_uint (glade_property_inline_value (property)), utf8st)))
{
utf8st[n] = '\0';
gtk_entry_set_text (entry, utf8st);
}
}
}
static void
glade_eprop_unichar_changed (GtkWidget * entry, GladeEditorProperty * eprop)
{
const gchar *text;
if (eprop->priv->loading)
return;
if ((text = gtk_entry_get_text (GTK_ENTRY (entry))) != NULL)
{
gunichar unich = g_utf8_get_char (text);
GValue val = { 0, };
g_value_init (&val, G_TYPE_UINT);
g_value_set_uint (&val, unich);
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
}
static void
glade_eprop_unichar_delete (GtkEditable * editable,
gint start_pos,
gint end_pos, GladeEditorProperty * eprop)
{
if (eprop->priv->loading)
return;
gtk_editable_select_region (editable, 0, -1);
g_signal_stop_emission_by_name (G_OBJECT (editable), "delete_text");
}
static void
glade_eprop_unichar_insert (GtkWidget * entry,
const gchar * text,
gint length,
gint * position, GladeEditorProperty * eprop)
{
if (eprop->priv->loading)
return;
g_signal_handlers_block_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_changed), eprop);
g_signal_handlers_block_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_insert), eprop);
g_signal_handlers_block_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_delete), eprop);
gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
*position = 0;
gtk_editable_insert_text (GTK_EDITABLE (entry), text, 1, position);
g_signal_handlers_unblock_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_changed), eprop);
g_signal_handlers_unblock_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_insert), eprop);
g_signal_handlers_unblock_by_func
(G_OBJECT (entry), G_CALLBACK (glade_eprop_unichar_delete), eprop);
g_signal_stop_emission_by_name (G_OBJECT (entry), "insert_text");
glade_eprop_unichar_changed (entry, eprop);
}
static GtkWidget *
glade_eprop_unichar_create_input (GladeEditorProperty * eprop)
{
GladeEPropUnichar *eprop_unichar = GLADE_EPROP_UNICHAR (eprop);
eprop_unichar->entry = gtk_entry_new ();
/* it's 2 to prevent spirious beeps... */
gtk_entry_set_max_length (GTK_ENTRY (eprop_unichar->entry), 2);
g_signal_connect (G_OBJECT (eprop_unichar->entry), "changed",
G_CALLBACK (glade_eprop_unichar_changed), eprop);
g_signal_connect (G_OBJECT (eprop_unichar->entry), "insert_text",
G_CALLBACK (glade_eprop_unichar_insert), eprop);
g_signal_connect (G_OBJECT (eprop_unichar->entry), "delete_text",
G_CALLBACK (glade_eprop_unichar_delete), eprop);
return eprop_unichar->entry;
}
/*******************************************************************************
GladeEditorPropertyObjectClass
*******************************************************************************/
enum
{
OBJ_COLUMN_WIDGET = 0,
OBJ_COLUMN_WIDGET_NAME,
OBJ_COLUMN_WIDGET_CLASS,
OBJ_COLUMN_SELECTED,
OBJ_COLUMN_SELECTABLE,
OBJ_NUM_COLUMNS
};
#define GLADE_RESPONSE_CLEAR 42
#define GLADE_RESPONSE_CREATE 43
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *entry;
} GladeEPropObject;
GLADE_MAKE_EPROP (GladeEPropObject, glade_eprop_object)
#define GLADE_EPROP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_OBJECT, GladeEPropObject))
#define GLADE_EPROP_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_OBJECT, GladeEPropObjectClass))
#define GLADE_IS_EPROP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_OBJECT))
#define GLADE_IS_EPROP_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_OBJECT))
#define GLADE_EPROP_OBJECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_OBJECT, GladeEPropObjectClass))
static void glade_eprop_object_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static gchar *
glade_eprop_object_name (const gchar * name,
GtkTreeStore * model, GtkTreeIter * parent_iter)
{
GtkTreePath *path;
GString *string;
gint i;
string = g_string_new (name);
if (parent_iter)
{
path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), parent_iter);
for (i = 0; i < gtk_tree_path_get_depth (path); i++)
g_string_prepend (string, " ");
}
return g_string_free (string, FALSE);
}
static gboolean
search_list (GList * list, gpointer data)
{
return g_list_find (list, data) != NULL;
}
/*
* Note that widgets is a list of GtkWidgets, while what we store
* in the model are the associated GladeWidgets.
*/
static void
glade_eprop_object_populate_view_real (GtkTreeStore * model,
GtkTreeIter * parent_iter,
GList * widgets,
GList * selected_widgets,
GList * exception_widgets,
GType object_type, gboolean parentless)
{
GList *children, *list;
GtkTreeIter iter;
gboolean good_type, has_decendant;
for (list = widgets; list; list = list->next)
{
GladeWidget *widget;
GladeWidgetAdaptor *adaptor;
if ((widget = glade_widget_get_from_gobject (list->data)) != NULL)
{
adaptor = glade_widget_get_adaptor (widget);
has_decendant = !parentless && glade_widget_has_decendant
(widget, object_type);
good_type = (glade_widget_adaptor_get_object_type (adaptor) == object_type ||
g_type_is_a (glade_widget_adaptor_get_object_type (adaptor), object_type) ||
glade_util_class_implements_interface (glade_widget_adaptor_get_object_type (adaptor),
object_type));
if (parentless)
good_type = good_type && !GWA_IS_TOPLEVEL (adaptor);
if (good_type || has_decendant)
{
gtk_tree_store_append (model, &iter, parent_iter);
gtk_tree_store_set
(model, &iter,
OBJ_COLUMN_WIDGET, widget,
OBJ_COLUMN_WIDGET_NAME,
glade_eprop_object_name (glade_widget_get_name (widget), model, parent_iter),
OBJ_COLUMN_WIDGET_CLASS, glade_widget_adaptor_get_title (adaptor),
/* Selectable if its a compatible type and
* its not itself.
*/
OBJ_COLUMN_SELECTABLE,
good_type && !search_list (exception_widgets, widget),
OBJ_COLUMN_SELECTED,
good_type && search_list (selected_widgets, widget), -1);
}
if (has_decendant &&
(children = glade_widget_adaptor_get_children
(adaptor, glade_widget_get_object (widget))) != NULL)
{
GtkTreeIter *copy = NULL;
copy = gtk_tree_iter_copy (&iter);
glade_eprop_object_populate_view_real (model, copy, children,
selected_widgets,
exception_widgets,
object_type, parentless);
gtk_tree_iter_free (copy);
g_list_free (children);
}
}
}
}
static void
glade_eprop_object_populate_view (GladeProject * project,
GtkTreeView * view,
GList * selected,
GList * exceptions,
GType object_type, gboolean parentless)
{
GtkTreeStore *model = (GtkTreeStore *) gtk_tree_view_get_model (view);
GList *list, *toplevels = NULL;
/* Make a list of only the toplevel widgets */
for (list = (GList *) glade_project_get_objects (project); list;
list = list->next)
{
GObject *object = G_OBJECT (list->data);
GladeWidget *gwidget = glade_widget_get_from_gobject (object);
g_assert (gwidget);
if (glade_widget_get_parent (gwidget) == NULL)
toplevels = g_list_append (toplevels, object);
}
/* add the widgets and recurse */
glade_eprop_object_populate_view_real (model, NULL, toplevels, selected,
exceptions, object_type, parentless);
g_list_free (toplevels);
}
static gboolean
glade_eprop_object_clear_iter (GtkTreeModel * model,
GtkTreePath * path,
GtkTreeIter * iter, gpointer data)
{
gtk_tree_store_set (GTK_TREE_STORE (model), iter,
OBJ_COLUMN_SELECTED, FALSE, -1);
return FALSE;
}
static gboolean
glade_eprop_object_selected_widget (GtkTreeModel * model,
GtkTreePath * path,
GtkTreeIter * iter, GladeWidget ** ret)
{
gboolean selected;
GladeWidget *widget;
gtk_tree_model_get (model, iter,
OBJ_COLUMN_SELECTED, &selected,
OBJ_COLUMN_WIDGET, &widget, -1);
if (selected)
{
*ret = widget;
return TRUE;
}
return FALSE;
}
static void
glade_eprop_object_selected (GtkCellRendererToggle * cell,
gchar * path_str, GtkTreeModel * model)
{
GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
GtkTreeIter iter;
gboolean enabled, radio;
radio = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model), "radio-list"));
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_model_get (model, &iter, OBJ_COLUMN_SELECTED, &enabled, -1);
/* Clear the rest of the view first
*/
if (radio)
gtk_tree_model_foreach (model, glade_eprop_object_clear_iter, NULL);
gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
OBJ_COLUMN_SELECTED, radio ? TRUE : !enabled, -1);
gtk_tree_path_free (path);
}
static GtkWidget *
glade_eprop_object_view (gboolean radio)
{
GtkWidget *view_widget;
GtkTreeModel *model;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
model = (GtkTreeModel *) gtk_tree_store_new (OBJ_NUM_COLUMNS, G_TYPE_OBJECT, /* The GladeWidget */
G_TYPE_STRING, /* The GladeWidget's name */
G_TYPE_STRING, /* The GladeWidgetClass title */
G_TYPE_BOOLEAN, /* Whether this row is selected or not */
G_TYPE_BOOLEAN); /* Whether this GladeWidget is
* of an acceptable type and
* therefore can be selected.
*/
g_object_set_data (G_OBJECT (model), "radio-list", GINT_TO_POINTER (radio));
view_widget = gtk_tree_view_new_with_model (model);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view_widget), FALSE);
/* Pass ownership to the view */
g_object_unref (G_OBJECT (model));
g_object_set (G_OBJECT (view_widget), "enable-search", FALSE, NULL);
/********************* fake invisible column *********************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "editable", FALSE, "visible", FALSE, NULL);
column = gtk_tree_view_column_new_with_attributes (NULL, renderer, NULL);
gtk_tree_view_append_column (GTK_TREE_VIEW (view_widget), column);
gtk_tree_view_column_set_visible (column, FALSE);
gtk_tree_view_set_expander_column (GTK_TREE_VIEW (view_widget), column);
/************************ selected column ************************/
renderer = gtk_cell_renderer_toggle_new ();
g_object_set (G_OBJECT (renderer),
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
"activatable", TRUE, "radio", radio, NULL);
g_signal_connect (renderer, "toggled",
G_CALLBACK (glade_eprop_object_selected), model);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), 0,
NULL, renderer,
"visible", OBJ_COLUMN_SELECTABLE,
"sensitive", OBJ_COLUMN_SELECTABLE, "active", OBJ_COLUMN_SELECTED, NULL);
/********************* widget name column *********************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), 1,
_("Name"), renderer, "text", OBJ_COLUMN_WIDGET_NAME, NULL);
/***************** widget class title column ******************/
renderer = gtk_cell_renderer_text_new ();
g_object_set (G_OBJECT (renderer),
"editable", FALSE,
"style", PANGO_STYLE_ITALIC, "foreground", "Gray", NULL);
gtk_tree_view_insert_column_with_attributes
(GTK_TREE_VIEW (view_widget), 2,
_("Class"), renderer, "text", OBJ_COLUMN_WIDGET_CLASS, NULL);
return view_widget;
}
static gchar *
glade_eprop_object_dialog_title (GladeEditorProperty * eprop)
{
GladeWidgetAdaptor *adaptor;
GParamSpec *pspec;
const gchar *format;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (glade_property_class_parentless_widget (eprop->priv->klass))
format = GLADE_IS_PARAM_SPEC_OBJECTS (pspec) ?
_("Choose parentless %s type objects in this project") :
_("Choose a parentless %s in this project");
else
format = GLADE_IS_PARAM_SPEC_OBJECTS (pspec) ?
_("Choose %s type objects in this project") :
_("Choose a %s in this project");
if (GLADE_IS_PARAM_SPEC_OBJECTS (pspec))
return g_strdup_printf (format, g_type_name
(glade_param_spec_objects_get_type
(GLADE_PARAM_SPEC_OBJECTS (pspec))));
else if ((adaptor =
glade_widget_adaptor_get_by_type (pspec->value_type)) != NULL)
return g_strdup_printf (format, glade_widget_adaptor_get_title (adaptor));
/* Fallback on type name (which would look like "GtkButton"
* instead of "Button" and maybe not translated).
*/
return g_strdup_printf (format, g_type_name (pspec->value_type));
}
gboolean
glade_editor_property_show_object_dialog (GladeProject * project,
const gchar * title,
GtkWidget * parent,
GType object_type,
GladeWidget * exception,
GladeWidget ** object)
{
GtkWidget *dialog;
GtkWidget *vbox, *label, *sw;
GtkWidget *tree_view;
GtkWidget *content_area;
GtkWidget *action_area;
GList *selected_list = NULL, *exception_list = NULL;
gint res;
g_return_val_if_fail (object != NULL, -1);
if (!parent)
parent = glade_app_get_window ();
dialog = gtk_dialog_new_with_buttons (title,
GTK_WINDOW (parent),
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
GLADE_RESPONSE_CLEAR, -1);
gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
/* HIG settings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
gtk_box_set_spacing (GTK_BOX (action_area), 6);
vbox = gtk_vbox_new (FALSE, 6);
gtk_widget_show (vbox);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
/* Checklist */
label = gtk_label_new_with_mnemonic (_("O_bjects:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 200);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
if (*object)
selected_list = g_list_prepend (selected_list, *object);
if (exception)
exception_list = g_list_prepend (exception_list, exception);
tree_view = glade_eprop_object_view (TRUE);
glade_eprop_object_populate_view (project,
GTK_TREE_VIEW (tree_view),
selected_list, exception_list,
object_type, FALSE);
g_list_free (selected_list);
g_list_free (exception_list);
gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
gtk_widget_show (tree_view);
gtk_container_add (GTK_CONTAINER (sw), tree_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view);
/* Run the dialog */
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_OK)
{
GladeWidget *selected = NULL;
gtk_tree_model_foreach
(gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)),
(GtkTreeModelForeachFunc)
glade_eprop_object_selected_widget, &selected);
*object = selected;
}
else if (res == GLADE_RESPONSE_CLEAR)
*object = NULL;
gtk_widget_destroy (dialog);
return (res == GTK_RESPONSE_OK || res == GLADE_RESPONSE_CLEAR);
}
static void
glade_eprop_object_show_dialog (GtkWidget * dialog_button,
GladeEditorProperty * eprop)
{
GtkWidget *dialog, *parent;
GtkWidget *vbox, *label, *sw;
GtkWidget *tree_view;
GtkWidget *content_area;
GtkWidget *action_area;
GladeProject *project;
GladeWidget *widget;
GParamSpec *pspec;
gchar *title = glade_eprop_object_dialog_title (eprop);
gint res;
GladeWidgetAdaptor *create_adaptor = NULL;
GList *selected_list = NULL, *exception_list = NULL;
widget = glade_property_get_widget (eprop->priv->property);
project = glade_widget_get_project (widget);
parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop));
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (glade_property_class_create_type (eprop->priv->klass))
create_adaptor =
glade_widget_adaptor_get_by_name (glade_property_class_create_type (eprop->priv->klass));
if (!create_adaptor &&
G_TYPE_IS_INSTANTIATABLE (pspec->value_type) && !G_TYPE_IS_ABSTRACT (pspec->value_type))
create_adaptor = glade_widget_adaptor_get_by_type (pspec->value_type);
if (create_adaptor)
{
dialog = gtk_dialog_new_with_buttons (title,
GTK_WINDOW (parent),
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_CLEAR,
GLADE_RESPONSE_CLEAR, _("_New"),
GLADE_RESPONSE_CREATE, GTK_STOCK_OK,
GTK_RESPONSE_OK, NULL);
g_free (title);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GLADE_RESPONSE_CREATE,
GTK_RESPONSE_CANCEL,
GLADE_RESPONSE_CLEAR, -1);
}
else
{
dialog = gtk_dialog_new_with_buttons (title,
GTK_WINDOW (parent),
GTK_DIALOG_MODAL,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_CLEAR,
GLADE_RESPONSE_CLEAR, GTK_STOCK_OK,
GTK_RESPONSE_OK, NULL);
g_free (title);
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
GTK_RESPONSE_OK,
GTK_RESPONSE_CANCEL,
GLADE_RESPONSE_CLEAR, -1);
}
gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
/* HIG settings */
gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
gtk_box_set_spacing (GTK_BOX (action_area), 6);
vbox = gtk_vbox_new (FALSE, 6);
gtk_widget_show (vbox);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
/* Checklist */
label = gtk_label_new_with_mnemonic (_("O_bjects:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 200);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
exception_list = g_list_prepend (exception_list, widget);
if (g_value_get_object (glade_property_inline_value (eprop->priv->property)))
selected_list = g_list_prepend (selected_list,
glade_widget_get_from_gobject
(g_value_get_object
(glade_property_inline_value (eprop->priv->property))));
tree_view = glade_eprop_object_view (TRUE);
glade_eprop_object_populate_view (project, GTK_TREE_VIEW (tree_view),
selected_list, exception_list,
pspec->value_type,
glade_property_class_parentless_widget (eprop->priv->klass));
g_list_free (selected_list);
g_list_free (exception_list);
gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
gtk_widget_show (tree_view);
gtk_container_add (GTK_CONTAINER (sw), tree_view);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), tree_view);
/* Run the dialog */
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_OK)
{
GladeWidget *selected = NULL;
gtk_tree_model_foreach
(gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)),
(GtkTreeModelForeachFunc)
glade_eprop_object_selected_widget, &selected);
if (selected)
{
GValue *value;
glade_project_selection_set (project,
glade_widget_get_object (widget),
TRUE);
value = glade_property_class_make_gvalue_from_string
(eprop->priv->klass, glade_widget_get_name (selected), project, widget);
/* Unparent the widget so we can reuse it for this property */
if (glade_property_class_parentless_widget (eprop->priv->klass))
{
GObject *new_object, *old_object = NULL;
GladeWidget *new_widget;
GladeProperty *old_ref;
if (!G_IS_PARAM_SPEC_OBJECT (pspec))
g_warning
("Parentless widget property should be of object type");
else
{
glade_property_get (eprop->priv->property, &old_object);
new_object = g_value_get_object (value);
new_widget = glade_widget_get_from_gobject (new_object);
if (new_object && old_object != new_object)
{
if ((old_ref =
glade_widget_get_parentless_widget_ref (new_widget)))
{
glade_command_push_group (_("Setting %s of %s to %s"),
glade_property_class_get_name (eprop->priv->klass),
glade_widget_get_name (widget),
glade_widget_get_name (new_widget));
glade_command_set_property (old_ref, NULL);
glade_editor_property_commit (eprop, value);
glade_command_pop_group ();
}
else
glade_editor_property_commit (eprop, value);
}
}
}
else
glade_editor_property_commit (eprop, value);
g_value_unset (value);
g_free (value);
}
}
else if (res == GLADE_RESPONSE_CREATE)
{
GValue *value;
GladeWidget *new_widget;
/* translators: Creating 'a widget' for 'a property' of 'a widget' */
glade_command_push_group (_("Creating %s for %s of %s"),
glade_widget_adaptor_get_name (create_adaptor),
glade_property_class_get_name (eprop->priv->klass),
glade_widget_get_name (widget));
/* Dont bother if the user canceled the widget */
if ((new_widget =
glade_command_create (create_adaptor, NULL, NULL, project)) != NULL)
{
glade_project_selection_set (project, glade_widget_get_object (widget), TRUE);
value = glade_property_class_make_gvalue_from_string
(eprop->priv->klass, glade_widget_get_name (new_widget), project, NULL);
glade_editor_property_commit (eprop, value);
g_value_unset (value);
g_free (value);
}
glade_command_pop_group ();
}
else if (res == GLADE_RESPONSE_CLEAR)
{
GValue *value = glade_property_class_make_gvalue_from_string
(eprop->priv->klass, NULL, project, glade_property_get_widget (eprop->priv->property));
glade_editor_property_commit (eprop, value);
g_value_unset (value);
g_free (value);
}
gtk_widget_destroy (dialog);
}
static void
glade_eprop_object_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropObject *eprop_object = GLADE_EPROP_OBJECT (eprop);
gchar *obj_name;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property == NULL)
return;
if ((obj_name = glade_widget_adaptor_string_from_value
(glade_property_class_get_adaptor (eprop->priv->klass),
eprop->priv->klass, glade_property_inline_value (property))) != NULL)
{
gtk_entry_set_text (GTK_ENTRY (eprop_object->entry), obj_name);
g_free (obj_name);
}
else
gtk_entry_set_text (GTK_ENTRY (eprop_object->entry), "");
}
static GtkWidget *
glade_eprop_object_create_input (GladeEditorProperty * eprop)
{
GladeEPropObject *eprop_object = GLADE_EPROP_OBJECT (eprop);
GtkWidget *hbox;
GtkWidget *button;
hbox = gtk_hbox_new (FALSE, 0);
eprop_object->entry = gtk_entry_new ();
gtk_editable_set_editable (GTK_EDITABLE (eprop_object->entry), FALSE);
gtk_widget_show (eprop_object->entry);
gtk_box_pack_start (GTK_BOX (hbox), eprop_object->entry, TRUE, TRUE, 0);
button = gtk_button_new_with_label ("...");
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_eprop_object_show_dialog), eprop);
return hbox;
}
/*******************************************************************************
GladeEditorPropertyObjectsClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *entry;
} GladeEPropObjects;
GLADE_MAKE_EPROP (GladeEPropObjects, glade_eprop_objects)
#define GLADE_EPROP_OBJECTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_OBJECTS, GladeEPropObjects))
#define GLADE_EPROP_OBJECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_OBJECTS, GladeEPropObjectsClass))
#define GLADE_IS_EPROP_OBJECTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_OBJECTS))
#define GLADE_IS_EPROP_OBJECTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_OBJECTS))
#define GLADE_EPROP_OBJECTS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_OBJECTS, GladeEPropObjectsClass))
static void glade_eprop_objects_finalize (GObject * object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_objects_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropObjects *eprop_objects = GLADE_EPROP_OBJECTS (eprop);
gchar *obj_name;
/* Chain up first */
editor_property_class->load (eprop, property);
if (property == NULL)
return;
if ((obj_name = glade_widget_adaptor_string_from_value
(glade_property_class_get_adaptor (eprop->priv->klass),
eprop->priv->klass, glade_property_inline_value (property))) != NULL)
{
gtk_entry_set_text (GTK_ENTRY (eprop_objects->entry), obj_name);
g_free (obj_name);
}
else
gtk_entry_set_text (GTK_ENTRY (eprop_objects->entry), "");
}
static gboolean
glade_eprop_objects_selected_widget (GtkTreeModel * model,
GtkTreePath * path,
GtkTreeIter * iter, GList ** ret)
{
gboolean selected;
GladeWidget *widget;
gtk_tree_model_get (model, iter,
OBJ_COLUMN_SELECTED, &selected,
OBJ_COLUMN_WIDGET, &widget, -1);
if (selected)
{
*ret = g_list_append (*ret, glade_widget_get_object (widget));
g_object_unref (widget);
}
return FALSE;
}
static void
glade_eprop_objects_show_dialog (GtkWidget * dialog_button,
GladeEditorProperty * eprop)
{
GtkWidget *dialog, *parent;
GtkWidget *vbox, *label, *sw;
GtkWidget *tree_view;
GladeWidget *widget;
GladeProject *project;
GParamSpec *pspec;
gchar *title = glade_eprop_object_dialog_title (eprop);
gint res;
GList *selected_list = NULL, *exception_list = NULL, *selected_objects = NULL;
widget = glade_property_get_widget (eprop->priv->property);
project = glade_widget_get_project (widget);
parent = gtk_widget_get_toplevel (GTK_WIDGET (eprop));
pspec = glade_property_class_get_pspec (eprop->priv->klass);
dialog = gtk_dialog_new_with_buttons (title,
GTK_WINDOW (parent),
GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CLEAR, GLADE_RESPONSE_CLEAR,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
g_free (title);
gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500);
vbox = gtk_vbox_new (FALSE, 6);
gtk_widget_show (vbox);
gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
gtk_box_pack_start (GTK_BOX
(gtk_dialog_get_content_area (GTK_DIALOG (dialog))), vbox,
TRUE, TRUE, 0);
/* Checklist */
label = gtk_label_new (_("Objects:"));
gtk_widget_show (label);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
sw = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (sw);
gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
gtk_widget_set_size_request (sw, 400, 200);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
tree_view = glade_eprop_object_view (FALSE);
exception_list = g_list_prepend (exception_list, widget);
/* XXX This looks broken, do "object list" properties hold an object ? I doubt it... */
if (g_value_get_object (glade_property_inline_value (eprop->priv->property)))
{
GList *l;
glade_property_get (eprop->priv->property, &selected_objects);
for (l = selected_objects; l; l = l->next)
selected_list = g_list_prepend (selected_list,
glade_widget_get_from_gobject (l->data));
}
glade_eprop_object_populate_view (project, GTK_TREE_VIEW (tree_view),
selected_list, exception_list,
pspec->value_type,
glade_property_class_parentless_widget (eprop->priv->klass));
g_list_free (selected_list);
g_list_free (exception_list);
gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view));
gtk_widget_show (tree_view);
gtk_container_add (GTK_CONTAINER (sw), tree_view);
/* Run the dialog */
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_OK)
{
GValue *value;
GList *selected = NULL;
gtk_tree_model_foreach
(gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)),
(GtkTreeModelForeachFunc)
glade_eprop_objects_selected_widget, &selected);
value = glade_property_class_make_gvalue (eprop->priv->klass, selected);
glade_editor_property_commit (eprop, value);
g_value_unset (value);
g_free (value);
}
else if (res == GLADE_RESPONSE_CLEAR)
{
GValue *value = glade_property_class_make_gvalue (eprop->priv->klass, NULL);
glade_editor_property_commit (eprop, value);
g_value_unset (value);
g_free (value);
}
gtk_widget_destroy (dialog);
}
static GtkWidget *
glade_eprop_objects_create_input (GladeEditorProperty * eprop)
{
GladeEPropObjects *eprop_objects = GLADE_EPROP_OBJECTS (eprop);
GtkWidget *hbox;
GtkWidget *button;
hbox = gtk_hbox_new (FALSE, 0);
eprop_objects->entry = gtk_entry_new ();
gtk_editable_set_editable (GTK_EDITABLE (eprop_objects->entry), FALSE);
gtk_widget_show (eprop_objects->entry);
gtk_box_pack_start (GTK_BOX (hbox), eprop_objects->entry, TRUE, TRUE, 0);
button = gtk_button_new_with_label ("...");
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (glade_eprop_objects_show_dialog), eprop);
return hbox;
}
/*******************************************************************************
API
*******************************************************************************/
/**
* glade_editor_property_commit:
* @eprop: A #GladeEditorProperty
* @value: The #GValue to commit
*
* Commits @value to the property currently being edited by @eprop.
*
*/
void
glade_editor_property_commit (GladeEditorProperty * eprop, GValue * value)
{
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
g_return_if_fail (G_IS_VALUE (value));
g_signal_emit (G_OBJECT (eprop), glade_eprop_signals[COMMIT], 0, value);
}
/**
* glade_editor_property_load:
* @eprop: A #GladeEditorProperty
* @property: A #GladeProperty
*
* Loads @property values into @eprop and connects.
* (the editor property will watch the property's value
* until its loaded with another property or %NULL)
*/
void
glade_editor_property_load (GladeEditorProperty * eprop,
GladeProperty * property)
{
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
g_return_if_fail (property == NULL || GLADE_IS_PROPERTY (property));
if (eprop->priv->committing)
return;
eprop->priv->loading = TRUE;
GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)->load (eprop, property);
eprop->priv->loading = FALSE;
}
/**
* glade_editor_property_load_by_widget:
* @eprop: A #GladeEditorProperty
* @widget: A #GladeWidget
*
* Convenience function to load the appropriate #GladeProperty into
* @eprop from @widget
*/
void
glade_editor_property_load_by_widget (GladeEditorProperty * eprop,
GladeWidget * widget)
{
GladeProperty *property = NULL;
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
g_return_if_fail (widget == NULL || GLADE_IS_WIDGET (widget));
if (widget)
{
/* properties are allowed to be missing on some internal widgets */
if (glade_property_class_get_is_packing (eprop->priv->klass))
property = glade_widget_get_pack_property (widget, glade_property_class_id (eprop->priv->klass));
else
property = glade_widget_get_property (widget, glade_property_class_id (eprop->priv->klass));
glade_editor_property_load (eprop, property);
if (property)
{
g_assert (eprop->priv->klass == glade_property_get_class (property));
gtk_widget_show (GTK_WIDGET (eprop));
gtk_widget_show (GTK_WIDGET (eprop->priv->item_label));
}
else
{
gtk_widget_hide (GTK_WIDGET (eprop));
gtk_widget_hide (GTK_WIDGET (eprop->priv->item_label));
}
}
else
glade_editor_property_load (eprop, NULL);
}