/*
* 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 <math.h>
#include <glib/gi18n-lib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include "glade.h"
#include "glade-widget.h"
#include "glade-editable.h"
#include "glade-editor-property.h"
#include "glade-property-label.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"
#include "glade-private.h"
enum
{
PROP_0,
PROP_PROPERTY_CLASS,
PROP_USE_COMMAND,
PROP_DISABLE_CHECK,
PROP_CUSTOM_TEXT
};
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
#define FLAGS_COLUMN_VALUE 2
struct _GladeEditorPropertyPrivate
{
GladePropertyClass *klass; /* The property class this GladeEditorProperty was created for */
GladeProperty *property; /* The currently loaded property */
GtkWidget *item_label; /* A GladePropertyLabel, if one was constructed */
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 */
gchar *custom_text; /* Custom text to display in the property label */
guint loading : 1; /* 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).
*/
guint changed_blocked : 1; /* Whether the GladeProperty changed signal is currently blocked */
guint disable_check : 1; /* Whether to explicitly disable the optional check button */
};
static void glade_editor_property_editable_init (GladeEditableIface *iface);
static GladeEditableIface *parent_editable_iface;
G_DEFINE_TYPE_WITH_CODE (GladeEditorProperty, glade_editor_property, GTK_TYPE_BOX,
G_ADD_PRIVATE (GladeEditorProperty)
G_IMPLEMENT_INTERFACE (GLADE_TYPE_EDITABLE,
glade_editor_property_editable_init));
/*******************************************************************************
* GladeEditableIface *
*******************************************************************************/
static void
glade_editor_property_editable_load (GladeEditable *editable,
GladeWidget *widget)
{
/* Chain up to default implementation */
parent_editable_iface->load (editable, widget);
glade_editor_property_load_by_widget (GLADE_EDITOR_PROPERTY (editable), widget);
}
static void
glade_editor_property_set_show_name (GladeEditable *editable, gboolean show_name)
{
}
static void
glade_editor_property_editable_init (GladeEditableIface *iface)
{
parent_editable_iface = g_type_default_interface_peek (GLADE_TYPE_EDITABLE);
iface->load = glade_editor_property_editable_load;
iface->set_show_name = glade_editor_property_set_show_name;
}
/*******************************************************************************
GladeEditorPropertyClass
*******************************************************************************/
static void
deepest_child_grab_focus (GtkWidget *widget, gpointer data)
{
gboolean *focus_set = data;
if (*focus_set)
return;
if (GTK_IS_CONTAINER (widget))
gtk_container_foreach (GTK_CONTAINER (widget),
deepest_child_grab_focus,
data);
if (gtk_widget_get_can_focus (widget))
{
gtk_widget_grab_focus (widget);
*focus_set = TRUE;
}
}
/* 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_load (eprop, eprop->priv->property);
/* Restore input focus. If the property is construct-only, then
* glade_widget_rebuild() will be called which means the object will be
* removed from the project/selection and a new one will be added, which makes
* the eprop loose its focus.
*
* FIXME: find a better way to set focus?
* make gtk_widget_grab_focus(eprop->priv->input) work?
*/
if (glade_property_class_get_construct_only (eprop->priv->klass))
{
gboolean focus_set = FALSE;
gtk_container_foreach (GTK_CONTAINER (eprop->priv->input),
deepest_child_grab_focus,
&focus_set);
}
}
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->changed_blocked = TRUE;
eprop->priv->committing = TRUE;
glade_editor_property_commit (eprop, value);
eprop->priv->committing = FALSE;
/* When construct-only properties are set, we are disconnected and re-connected
* to the GladeWidget while it's rebuilding it's instance, in this case the
* signal handler is no longer blocked at this point.
*/
if (eprop->priv->changed_blocked)
g_signal_handler_unblock (G_OBJECT (eprop->priv->property), eprop->priv->changed_id);
}
void
glade_editor_property_set_custom_text (GladeEditorProperty *eprop,
const gchar *custom_text)
{
GladeEditorPropertyPrivate *priv;
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
priv = eprop->priv;
if (g_strcmp0 (priv->custom_text, custom_text) != 0)
{
g_free (priv->custom_text);
priv->custom_text = g_strdup (custom_text);
if (priv->item_label)
glade_property_label_set_custom_text (GLADE_PROPERTY_LABEL (priv->item_label),
custom_text);
g_object_notify (G_OBJECT (eprop), "custom-text");
}
}
const gchar *
glade_editor_property_get_custom_text (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
return eprop->priv->custom_text;
}
void
glade_editor_property_set_disable_check (GladeEditorProperty *eprop,
gboolean disable_check)
{
GladeEditorPropertyPrivate *priv;
g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop));
priv = eprop->priv;
if (priv->disable_check != disable_check)
{
priv->disable_check = disable_check;
gtk_widget_set_visible (priv->check, !disable_check);
g_object_notify (G_OBJECT (eprop), "disable-check");
}
}
gboolean
glade_editor_property_get_disable_check (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), FALSE);
return eprop->priv->disable_check;
}
GtkWidget *
glade_editor_property_get_item_label (GladeEditorProperty *eprop)
{
g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
if (!eprop->priv->item_label)
{
eprop->priv->item_label = glade_property_label_new();
g_object_ref_sink (eprop->priv->item_label);
if (eprop->priv->property)
glade_property_label_set_property (GLADE_PROPERTY_LABEL (eprop->priv->item_label),
eprop->priv->property);
}
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);
}
static void
glade_editor_property_sensitivity_cb (GladeProperty *property,
GParamSpec *pspec,
GladeEditorProperty *eprop)
{
GladeEditorPropertyPrivate *priv = eprop->priv;
gboolean property_enabled = glade_property_get_enabled (property);
gboolean sensitive = glade_property_get_sensitive (priv->property);
gboolean support_sensitive =
(glade_property_get_state (priv->property) & GLADE_STATE_SUPPORT_DISABLED) == 0;
gtk_widget_set_sensitive (priv->input,
sensitive && support_sensitive && property_enabled);
if (priv->check)
gtk_widget_set_sensitive (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_enabled_toggled_cb (GtkWidget *check,
GladeEditorProperty *eprop)
{
glade_command_set_property_enabled (eprop->priv->property,
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)));
}
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);
g_signal_handlers_block_by_func (eprop->priv->check, glade_editor_property_enabled_toggled_cb, eprop);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop->priv->check), enabled);
g_signal_handlers_unblock_by_func (eprop->priv->check, glade_editor_property_enabled_toggled_cb, eprop);
}
}
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 void
glade_editor_property_constructed (GObject *object)
{
GladeEditorProperty *eprop;
eprop = GLADE_EDITOR_PROPERTY (object);
G_OBJECT_CLASS (glade_editor_property_parent_class)->constructed (object);
/* Create hbox and possibly check button
*/
if (glade_property_class_optional (eprop->priv->klass))
{
eprop->priv->check = gtk_check_button_new ();
gtk_button_set_focus_on_click (GTK_BUTTON (eprop->priv->check), FALSE);
if (!eprop->priv->disable_check)
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_show (eprop->priv->input);
g_signal_connect (G_OBJECT (eprop->priv->input), "button-press-event",
G_CALLBACK (glade_editor_property_button_pressed), eprop);
if (gtk_widget_get_halign (eprop->priv->input) != GTK_ALIGN_FILL)
gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->input, FALSE, TRUE, 0);
else
gtk_box_pack_start (GTK_BOX (eprop), eprop->priv->input, TRUE, TRUE, 0);
}
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_free (eprop->priv->custom_text);
G_OBJECT_CLASS (table_class)->finalize (object);
}
static void
glade_editor_property_dispose (GObject *object)
{
GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (object);
if (eprop->priv->item_label)
{
g_object_unref (eprop->priv->item_label);
eprop->priv->item_label = NULL;
}
G_OBJECT_CLASS (table_class)->dispose (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;
case PROP_DISABLE_CHECK:
glade_editor_property_set_disable_check (eprop, g_value_get_boolean (value));
break;
case PROP_CUSTOM_TEXT:
glade_editor_property_set_custom_text (eprop, g_value_get_string (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;
case PROP_DISABLE_CHECK:
g_value_set_boolean (value, eprop->priv->disable_check);
break;
case PROP_CUSTOM_TEXT:
g_value_set_string (value, eprop->priv->custom_text);
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->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->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->changed_blocked = FALSE;
/* 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);
/* 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);
}
}
static void
glade_editor_property_init (GladeEditorProperty *eprop)
{
eprop->priv = glade_editor_property_get_instance_private (eprop);
gtk_box_set_spacing (GTK_BOX (eprop), 4);
gtk_orientable_set_orientation (GTK_ORIENTABLE (eprop),
GTK_ORIENTATION_HORIZONTAL);
}
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->constructed = glade_editor_property_constructed;
object_class->finalize = glade_editor_property_finalize;
object_class->dispose = glade_editor_property_dispose;
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_object_class_install_property
(object_class, PROP_DISABLE_CHECK,
g_param_spec_boolean
("disable-check", _("Disable Check"),
_("Whether to explicitly disable the check button"),
FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property
(object_class, PROP_CUSTOM_TEXT,
g_param_spec_string
("custom-text", _("Custom Text"),
_("Custom Text to display in the property label"),
NULL, G_PARAM_READWRITE));
}
/*******************************************************************************
GladeEditorPropertyNumericClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *spin;
gboolean refreshing;
} 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)
{
gdouble val = 0.0F;
GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop);
GParamSpec *pspec;
GValue *value;
if (eprop_numeric->refreshing)
return;
/* 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 = g_value_get_int (value);
else if (G_IS_PARAM_SPEC_UINT (pspec))
val = g_value_get_uint (value);
else if (G_IS_PARAM_SPEC_LONG (pspec))
val = g_value_get_long (value);
else if (G_IS_PARAM_SPEC_ULONG (pspec))
val = g_value_get_ulong (value);
else if (G_IS_PARAM_SPEC_INT64 (pspec))
val = g_value_get_int64 (value);
else if (G_IS_PARAM_SPEC_UINT64 (pspec))
val = g_value_get_uint64 (value);
else if (G_IS_PARAM_SPEC_DOUBLE (pspec))
val = 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);
}
}
#define NEAREST_INT_CAST(x) (((x - floor (x) < ceil (x) - x)) ? floor (x) : ceil (x))
static void
glade_eprop_numeric_value_set (GValue *val, gdouble value)
{
if (G_VALUE_HOLDS_INT (val))
g_value_set_int (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_UINT (val))
g_value_set_uint (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_LONG (val))
g_value_set_long (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_ULONG (val))
g_value_set_ulong (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_INT64 (val))
g_value_set_int64 (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_UINT64 (val))
g_value_set_uint64 (val, NEAREST_INT_CAST (value));
else if (G_VALUE_HOLDS_FLOAT (val))
g_value_set_float (val, value);
else if (G_VALUE_HOLDS_DOUBLE (val))
g_value_set_double (val, value);
else
g_warning ("Unsupported type %s\n", G_VALUE_TYPE_NAME (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);
glade_eprop_numeric_value_set (&val, gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin)));
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
static void
glade_eprop_numeric_force_update (GtkSpinButton *spin,
GladeEditorProperty *eprop)
{
GladeProperty *prop = glade_editor_property_get_property (eprop);
GladePropertyClass *klass = glade_property_get_class (prop);
GValue *val, newval = G_VALUE_INIT;
gdouble value;
gchar *text;
text = gtk_editable_get_chars (GTK_EDITABLE (spin), 0, -1);
/*
* Skip empty strings, otherwise if 0 is out of range the spin will get a
* bogus update.
*/
if (text && *text == '\0')
return;
val = glade_property_inline_value (prop);
g_value_init (&newval, G_VALUE_TYPE (val));
value = g_strtod (text, NULL);
glade_eprop_numeric_value_set (&newval, value);
/*
* If we unconditionally update the spin button whenever
* the entry changes we get bogus results (notably, the
* updating the spin button will insert 0 whenever text
* is removed, so selecting and inserting text will have
* an appended 0).
*/
if (glade_property_class_compare (klass, val, &newval))
{
gdouble min, max;
gtk_spin_button_get_range (spin, &min, &max);
if (value < min || value > max)
{
/* Special case, if the value is out of range, we force an update to
* change the value in the spin so the user knows about the range issue.
*/
gtk_spin_button_update (spin);
}
else
{
/* Here we commit the new property value but we make sure
* glade_eprop_numeric_load() is not called to prevent
* gtk_spin_button_set_value() changing the the value the
* user is trying to input.
*/
GladeEPropNumeric *eprop_numeric = GLADE_EPROP_NUMERIC (eprop);
eprop_numeric->refreshing = TRUE;
glade_editor_property_commit_no_callback (eprop, &newval);
eprop_numeric->refreshing = FALSE;
}
}
g_value_unset (&newval);
g_free (text);
}
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_set_hexpand (eprop_numeric->spin, TRUE);
gtk_widget_set_halign (eprop_numeric->spin, GTK_ALIGN_FILL);
gtk_widget_set_valign (eprop_numeric->spin, GTK_ALIGN_CENTER);
gtk_entry_set_activates_default (GTK_ENTRY (eprop_numeric->spin), TRUE);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (eprop_numeric->spin), TRUE);
glade_util_remove_scroll_events (eprop_numeric->spin);
gtk_widget_show (eprop_numeric->spin);
/* Limit the size of the spin if max allowed value is too big */
if (gtk_adjustment_get_upper (adjustment) > 9999999999999999.0)
gtk_entry_set_width_chars (GTK_ENTRY (eprop_numeric->spin), 16);
/* The force update callback is here to ensure that whenever the value
* is modified, it's committed immediately without requiring entry activation
* (this avoids lost modifications when modifying a value and navigating away)
*/
g_signal_connect (G_OBJECT (eprop_numeric->spin), "changed",
G_CALLBACK (glade_eprop_numeric_force_update), eprop);
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;
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);
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;
if (glade_displayable_value_is_disabled (pspec->value_type,
eclass->values[i].value_nick))
continue;
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));
gtk_widget_set_halign (eprop_enum->combo_box, GTK_ALIGN_FILL);
gtk_widget_set_valign (eprop_enum->combo_box, GTK_ALIGN_CENTER);
gtk_widget_set_hexpand (eprop_enum->combo_box, TRUE);
cell_renderer = gtk_cell_renderer_text_new ();
g_object_set (cell_renderer,
"wrap-mode", PANGO_WRAP_WORD,
"wrap-width", 1,
"width-chars", 8,
NULL);
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);
glade_util_remove_scroll_events (eprop_enum->combo_box);
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;
if (glade_displayable_value_is_disabled (pspec->value_type,
klass->values[flag_num].value_nick))
continue;
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,
FLAGS_COLUMN_VALUE, mask, -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;
GValue *gvalue;
gboolean valid;
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
if (!eprop->priv->property)
return;
gvalue = glade_property_inline_value (eprop->priv->property);
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);
valid = 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. */
while (valid)
{
gboolean setting;
guint value;
gtk_tree_model_get (GTK_TREE_MODEL (eprop_flags->model), &iter,
FLAGS_COLUMN_SETTING, &setting,
FLAGS_COLUMN_VALUE, &value, -1);
if (setting) new_value |= value;
valid = 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 != g_value_get_flags (gvalue))
{
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);
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 (GladeEditorProperty *eprop)
{
GtkWidget *window = gtk_widget_get_toplevel (GTK_WIDGET (eprop));
GtkWidget *dialog;
GtkWidget *view;
GtkWidget *label;
GtkWidget *vbox;
GtkWidget *content_area;
dialog = gtk_dialog_new_with_buttons (_("Select Fields"),
GTK_WINDOW (window),
GTK_DIALOG_MODAL,
_("_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);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_widget_set_halign (label, GTK_ALIGN_START);
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)
{
GladeEPropFlags *eprop_flags = GLADE_EPROP_FLAGS (eprop);
if (!eprop_flags->model)
eprop_flags->model = GTK_TREE_MODEL (gtk_list_store_new (3, G_TYPE_BOOLEAN,
G_TYPE_STRING,
G_TYPE_UINT));
eprop_flags->entry = gtk_entry_new ();
gtk_widget_set_hexpand (eprop_flags->entry, TRUE);
gtk_editable_set_editable (GTK_EDITABLE (eprop_flags->entry), FALSE);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_flags->entry),
GTK_ENTRY_ICON_SECONDARY,
"gtk-edit");
g_signal_connect_swapped (eprop_flags->entry, "icon-release",
G_CALLBACK (glade_eprop_flags_show_dialog),
eprop);
return eprop_flags->entry;
}
/*******************************************************************************
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);
GParamSpec *pspec;
GdkColor *color;
GdkRGBA *rgba;
gchar *text;
/* Chain up first */
editor_property_class->load (eprop, property);
pspec = glade_property_class_get_pspec (eprop->priv->klass);
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 (pspec->value_type == GDK_TYPE_COLOR)
{
if ((color = g_value_get_boxed (glade_property_inline_value (property))) != NULL)
{
GdkRGBA copy;
copy.red = color->red / 65535.0;
copy.green = color->green / 65535.0;
copy.blue = color->blue / 65535.0;
copy.alpha = 1.0;
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), ©);
}
else
{
GdkRGBA black = { 0, };
/* Manually fill it with black for an NULL value.
*/
if (gdk_rgba_parse (&black, "Black"))
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), &black);
}
}
else if (pspec->value_type == GDK_TYPE_RGBA)
{
if ((rgba = g_value_get_boxed (glade_property_inline_value (property))) != NULL)
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), rgba);
else
{
GdkRGBA black = { 0, };
/* Manually fill it with black for an NULL value.
*/
if (gdk_rgba_parse (&black, "Black"))
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (eprop_color->cbutton), &black);
}
}
}
}
static void
glade_eprop_color_changed (GtkWidget *button, GladeEditorProperty *eprop)
{
GdkRGBA rgba = { 0, };
GValue value = { 0, };
GParamSpec *pspec;
if (eprop->priv->loading)
return;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
g_value_init (&value, pspec->value_type);
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &rgba);
if (pspec->value_type == GDK_TYPE_COLOR)
{
GdkColor color = { 0, };
color.red = (gint16) (rgba.red * 65535);
color.green = (gint16) (rgba.green * 65535);
color.blue = (gint16) (rgba.blue * 65535);
g_value_set_boxed (&value, &color);
}
else if (pspec->value_type == GDK_TYPE_RGBA)
g_value_set_boxed (&value, &rgba);
glade_editor_property_commit (eprop, &value);
g_value_unset (&value);
}
static GtkWidget *
glade_eprop_color_create_input (GladeEditorProperty *eprop)
{
GladeEPropColor *eprop_color = GLADE_EPROP_COLOR (eprop);
GtkWidget *hbox;
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_halign (hbox, GTK_ALIGN_START);
gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER);
eprop_color->entry = gtk_entry_new ();
gtk_widget_set_hexpand (eprop_color->entry, TRUE);
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);
if (pspec->value_type == GDK_TYPE_RGBA)
gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (eprop_color->cbutton), TRUE);
else
gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (eprop_color->cbutton), FALSE);
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;
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 : "");
g_free (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 (GladeEditorProperty *eprop)
{
GtkWidget *dialog;
dialog = glade_named_icon_chooser_dialog_new (_("Select Named Icon"),
GTK_WINDOW
(gtk_widget_get_toplevel
(GTK_WIDGET (eprop))),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_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);
eprop_named_icon->entry = gtk_entry_new ();
gtk_widget_set_hexpand (eprop_named_icon->entry, TRUE);
gtk_widget_set_valign (eprop_named_icon->entry, GTK_ALIGN_CENTER);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_named_icon->entry),
GTK_ENTRY_ICON_SECONDARY,
"gtk-edit");
eprop_named_icon->current_context = NULL;
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);
g_signal_connect_swapped (eprop_named_icon->entry, "icon-release",
G_CALLBACK (glade_eprop_named_icon_show_chooser_dialog),
eprop);
return eprop_named_icon->entry;
}
/*******************************************************************************
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 gchar *
text_buffer_get_text (GtkTextBuffer *buffer)
{
GtkTextIter start, end;
gchar *retval;
gtk_text_buffer_get_bounds (buffer, &start, &end);
retval = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
if (retval && retval[0] == '\0')
{
g_free (retval);
return NULL;
}
return retval;
}
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)))
{
GtkWidget *entry = gtk_bin_get_child (GTK_BIN (eprop_text->text_entry));
gchar *text = glade_property_make_string (property);
gtk_entry_set_text (GTK_ENTRY (entry), text ? text : "");
g_free (text);
}
else
{
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);
g_free (text);
}
}
else if (GTK_IS_ENTRY (eprop_text->text_entry))
{
GtkEntry *entry = GTK_ENTRY (eprop_text->text_entry);
gchar *text = glade_property_make_string (property);
gtk_entry_set_text (entry, text ? text : "");
g_free (text);
}
else if (GTK_IS_TEXT_VIEW (eprop_text->text_entry))
{
GtkTextBuffer *buffer;
GType value_array_type;
/* Deprecated GValueArray */
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
value_array_type = G_TYPE_VALUE_ARRAY;
G_GNUC_END_IGNORE_DEPRECATIONS;
buffer =
gtk_text_view_get_buffer (GTK_TEXT_VIEW (eprop_text->text_entry));
if (pspec->value_type == G_TYPE_STRV ||
pspec->value_type == value_array_type)
{
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));
gchar *old_text = text_buffer_get_text (buffer);
/* Only update it if necessary, see notes bellow */
if (g_strcmp0 (text, old_text))
gtk_text_buffer_set_text (buffer, text ? text : "", -1);
g_free (text);
}
else
{
gchar *text = glade_property_make_string (property);
gchar *old_text = text_buffer_get_text (buffer);
/* NOTE: GtkTextBuffer does not like to be updated from a "changed"
* signal callback. It prints a iterator warning and moves the cursor
* to the end.
*/
if (g_strcmp0 (text, old_text))
gtk_text_buffer_set_text (buffer, text ? text : "", -1);
g_free (old_text);
g_free (text);
}
}
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;
GType value_array_type;
/* Deprecated GValueArray */
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
value_array_type = G_TYPE_VALUE_ARRAY;
G_GNUC_END_IGNORE_DEPRECATIONS;
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (pspec->value_type == G_TYPE_STRV ||
pspec->value_type == value_array_type ||
pspec->value_type == GDK_TYPE_PIXBUF)
{
GladeWidget *gwidget = glade_property_get_widget (eprop->priv->property);
val = glade_property_class_make_gvalue_from_string (eprop->priv->klass,
text,
glade_widget_get_project (gwidget));
}
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;
if (eprop->priv->loading)
return;
text = text_buffer_get_text (buffer);
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;
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,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_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);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_widget_set_halign (label, GTK_ALIGN_START);
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_scrollable_set_hscroll_policy (GTK_SCROLLABLE (text_view), GTK_SCROLL_MINIMUM);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
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_box_new (GTK_ORIENTATION_HORIZONTAL, 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_widget_set_halign (label, GTK_ALIGN_START);
gtk_container_add (GTK_CONTAINER (alignment), label);
gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, FALSE, 0);
gtk_widget_set_tooltip_text (alignment,
_("For short and ambiguous strings: type a word here to differentiate "
"the meaning of this string from the meaning of other occurrences of "
"the same string"));
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_scrollable_set_hscroll_policy (GTK_SCROLLABLE (context_view), GTK_SCROLL_MINIMUM);
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_widget_set_halign (label, GTK_ALIGN_START);
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_scrollable_set_hscroll_policy (GTK_SCROLLABLE (comment_view), GTK_SCROLL_MINIMUM);
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)
{
g_free ((gpointer) * text);
g_free ((gpointer) * context);
g_free ((gpointer) * comment);
/* Get the new values for translatable */
*translatable = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(translatable_button));
/* Comment, text and context */
*comment = text_buffer_get_text (comment_buffer);
*text = text_buffer_get_text (text_buffer);
*context = text_buffer_get_text (context_buffer);
gtk_widget_destroy (dialog);
return TRUE;
}
gtk_widget_destroy (dialog);
return FALSE;
}
static void
glade_eprop_text_show_i18n_dialog (GladeEditorProperty *eprop)
{
GladeEditorPropertyPrivate *priv = eprop->priv;
gchar *text = glade_property_make_string (priv->property);
gchar *context = g_strdup (glade_property_i18n_get_context (priv->property));
gchar *comment = g_strdup (glade_property_i18n_get_comment (priv->property));
gboolean translatable = glade_property_i18n_get_translatable (priv->property);
if (glade_editor_property_show_i18n_dialog
(GTK_WIDGET (eprop), &text, &context, &comment, &translatable))
{
glade_command_set_i18n (priv->property, translatable, context, comment);
glade_eprop_text_changed_common (eprop, text, priv->use_command);
glade_editor_property_load (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)
{
GFile *resource_folder;
GtkWidget *dialog;
gchar *folder;
g_return_val_if_fail (filename != NULL, FALSE);
*filename = NULL;
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,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_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);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
folder = glade_project_resource_fullpath (project, "");
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), folder);
resource_folder = g_file_new_for_path (folder);
g_free (folder);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{
GFile *file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
*filename = _glade_util_file_get_relative_path (resource_folder, file);
g_object_unref (file);
}
gtk_widget_destroy (dialog);
g_object_unref (resource_folder);
return *filename != NULL;
}
static void
glade_eprop_text_show_resource_dialog (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, GTK_WIDGET (eprop), &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 gint
get_text_view_height (void)
{
static gint height = -1;
if (height < 0)
{
GtkWidget *label = gtk_label_new (NULL);
PangoLayout *layout =
gtk_widget_create_pango_layout (label,
"The quick\n"
"brown fox\n"
"jumped over\n"
"the lazy dog");
pango_layout_get_pixel_size (layout, NULL, &height);
g_object_unref (layout);
g_object_ref_sink (label);
g_object_unref (label);
}
return height;
}
static GtkWidget *
glade_eprop_text_create_input (GladeEditorProperty *eprop)
{
GladeEPropText *eprop_text = GLADE_EPROP_TEXT (eprop);
GladePropertyClass *klass;
GParamSpec *pspec;
GtkWidget *hbox;
GType value_array_type;
/* Deprecated GValueArray */
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
value_array_type = G_TYPE_VALUE_ARRAY;
G_GNUC_END_IGNORE_DEPRECATIONS;
klass = eprop->priv->klass;
pspec = glade_property_class_get_pspec (klass);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 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 ();
gtk_widget_set_halign (hbox, GTK_ALIGN_START);
gtk_widget_set_valign (hbox, GTK_ALIGN_CENTER);
gtk_widget_set_hexpand (combo, TRUE);
glade_util_remove_scroll_events (combo);
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 == value_array_type)
{
GtkWidget *swindow;
GtkTextBuffer *buffer;
gint min_height = get_text_view_height ();
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (swindow), min_height);
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);
glade_util_remove_scroll_events (swindow);
eprop_text->text_entry = gtk_text_view_new ();
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (eprop_text->text_entry), GTK_SCROLL_MINIMUM);
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (eprop_text->text_entry), GTK_WRAP_WORD);
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);
gtk_widget_set_hexpand (swindow, TRUE);
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_set_hexpand (eprop_text->text_entry, TRUE);
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)
{
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_text->text_entry),
GTK_ENTRY_ICON_SECONDARY,
"document-open");
g_signal_connect_swapped (eprop_text->text_entry, "icon-release",
G_CALLBACK (glade_eprop_text_show_resource_dialog),
eprop);
}
}
if (glade_property_class_translatable (klass))
{
if (GTK_IS_ENTRY (eprop_text->text_entry))
{
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_text->text_entry),
GTK_ENTRY_ICON_SECONDARY,
"gtk-edit");
g_signal_connect_swapped (eprop_text->text_entry, "icon-release",
G_CALLBACK (glade_eprop_text_show_i18n_dialog),
eprop);
}
else
{
GtkWidget *button = gtk_button_new ();
gtk_button_set_image (GTK_BUTTON (button),
gtk_image_new_from_icon_name ("gtk-edit", GTK_ICON_SIZE_MENU));
gtk_widget_show (button);
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (glade_eprop_text_show_i18n_dialog),
eprop);
}
}
return hbox;
}
/*******************************************************************************
GladeEditorPropertyBoolClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *button;
} 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)
{
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
GladeEPropBool *eprop_bool = GLADE_EPROP_BOOL (eprop);
gboolean state = g_value_get_boolean (glade_property_inline_value (property));
gtk_switch_set_active (GTK_SWITCH (eprop_bool->button), state);
}
}
static void
glade_eprop_bool_active_notify (GObject *gobject,
GParamSpec *pspec,
GladeEditorProperty *eprop)
{
GValue val = { 0, };
if (eprop->priv->loading)
return;
g_value_init (&val, G_TYPE_BOOLEAN);
g_value_set_boolean (&val, gtk_switch_get_active (GTK_SWITCH (gobject)));
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->button = gtk_switch_new ();
gtk_widget_set_halign (eprop_bool->button, GTK_ALIGN_START);
gtk_widget_set_valign (eprop_bool->button, GTK_ALIGN_CENTER);
g_signal_connect (eprop_bool->button, "notify::active",
G_CALLBACK (glade_eprop_bool_active_notify), eprop);
return eprop_bool->button;
}
/*******************************************************************************
GladeEditorPropertyCheckClass
*******************************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkWidget *button;
} GladeEPropCheck;
GLADE_MAKE_EPROP (GladeEPropCheck, glade_eprop_check)
#define GLADE_EPROP_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_CHECK, GladeEPropCheck))
#define GLADE_EPROP_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_EPROP_CHECK, GladeEPropCheckClass))
#define GLADE_IS_EPROP_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_EPROP_CHECK))
#define GLADE_IS_EPROP_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_EPROP_CHECK))
#define GLADE_EPROP_CHECK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GLADE_EPROP_CHECK, GladeEPropCheckClass))
static void
glade_eprop_check_finalize (GObject *object)
{
/* Chain up */
G_OBJECT_CLASS (editor_property_class)->finalize (object);
}
static void
glade_eprop_check_load (GladeEditorProperty *eprop, GladeProperty *property)
{
/* Chain up first */
editor_property_class->load (eprop, property);
if (property)
{
GladeEPropCheck *eprop_check = GLADE_EPROP_CHECK (eprop);
gboolean state = g_value_get_boolean (glade_property_inline_value (property));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (eprop_check->button), state);
}
}
static void
glade_eprop_check_active_notify (GObject *gobject,
GParamSpec *pspec,
GladeEditorProperty *eprop)
{
GValue val = { 0, };
if (eprop->priv->loading)
return;
g_value_init (&val, G_TYPE_BOOLEAN);
g_value_set_boolean (&val, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (gobject)));
glade_editor_property_commit_no_callback (eprop, &val);
g_value_unset (&val);
}
static GtkWidget *
glade_eprop_check_create_input (GladeEditorProperty *eprop)
{
GladeEPropCheck *eprop_check = GLADE_EPROP_CHECK (eprop);
GladePropertyClass *pclass;
GtkWidget *label;
pclass = eprop->priv->klass;
/* Add the property label as the check button's child */
label = glade_editor_property_get_item_label (eprop);
glade_property_label_set_property_name (GLADE_PROPERTY_LABEL (label),
glade_property_class_id (pclass));
glade_property_label_set_packing (GLADE_PROPERTY_LABEL (label),
glade_property_class_get_is_packing (pclass));
glade_property_label_set_append_colon (GLADE_PROPERTY_LABEL (label), FALSE);
glade_property_label_set_custom_text (GLADE_PROPERTY_LABEL (label),
eprop->priv->custom_text);
gtk_widget_show (label);
eprop_check->button = gtk_check_button_new ();
gtk_button_set_focus_on_click (GTK_BUTTON (eprop_check->button), FALSE);
gtk_container_add (GTK_CONTAINER (eprop_check->button), label);
gtk_widget_set_halign (eprop_check->button, GTK_ALIGN_START);
gtk_widget_set_valign (eprop_check->button, GTK_ALIGN_CENTER);
g_signal_connect (eprop_check->button, "notify::active",
G_CALLBACK (glade_eprop_check_active_notify), eprop);
return eprop_check->button;
}
/*******************************************************************************
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 ();
gtk_widget_set_halign (eprop_unichar->entry, GTK_ALIGN_START);
gtk_widget_set_valign (eprop_unichar->entry, GTK_ALIGN_CENTER);
/* 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;
const gchar *widget_name;
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));
widget_name = glade_widget_get_display_name (widget);
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 (widget_name, 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)
{
gboolean parentless;
GParamSpec *pspec;
parentless = glade_property_class_parentless_widget (eprop->priv->klass);
pspec = glade_property_class_get_pspec (eprop->priv->klass);
if (GLADE_IS_PARAM_SPEC_OBJECTS (pspec))
{
const gchar *typename = g_type_name (glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS (pspec)));
if (parentless)
return g_strdup_printf (_("Choose parentless %s type objects in this project"), typename);
else
return g_strdup_printf (_("Choose %s type objects in this project"), typename);
}
else
{
GladeWidgetAdaptor *adaptor;
const gchar *title;
adaptor = glade_widget_adaptor_get_by_type (pspec->value_type);
if (adaptor != NULL)
title = glade_widget_adaptor_get_title (adaptor);
else
{
/* Fallback on type name (which would look like "GtkButton"
* instead of "Button" and maybe not translated).
*/
title = g_type_name (pspec->value_type);
}
if (parentless)
return g_strdup_printf (_("Choose a parentless %s in this project"), title);
else
return g_strdup_printf (_("Choose a %s in this project"), title);
}
}
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;
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,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("C_lear"), GLADE_RESPONSE_CLEAR,
_("_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);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_widget_set_halign (label, GTK_ALIGN_START);
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 (GladeEditorProperty *eprop)
{
GtkWidget *dialog, *parent;
GtkWidget *vbox, *label, *sw;
GtkWidget *tree_view;
GtkWidget *content_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,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("C_lear"), GLADE_RESPONSE_CLEAR,
_("_New"), GLADE_RESPONSE_CREATE,
_("_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,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("C_lear"), GLADE_RESPONSE_CLEAR,
_("_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);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_widget_set_halign (label, GTK_ALIGN_START);
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;
GObject *new_object, *old_object = NULL;
GladeWidget *new_widget;
const gchar *current_name;
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);
glade_property_get (eprop->priv->property, &old_object);
new_object = g_value_get_object (value);
new_widget = glade_widget_get_from_gobject (new_object);
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));
/* Unparent the widget so we can reuse it for this property */
if (glade_property_class_parentless_widget (eprop->priv->klass))
{
GladeProperty *old_ref;
if (!G_IS_PARAM_SPEC_OBJECT (pspec))
g_warning ("Parentless widget property should be of object type");
else if (new_object && old_object != new_object)
{
/* Steal parentless reference widget references, basically some references
* can only be referenced by one property, here we clear it if such a reference
* exists for the target object
*/
if ((old_ref = glade_widget_get_parentless_widget_ref (new_widget)))
glade_command_set_property (old_ref, NULL);
}
}
/* Ensure that the object we will now refer to has an ID, the NULL
* check is just paranoia, it *always* has a name.
*
* To refer to a widget, it needs to have a name.
*/
glade_widget_ensure_name (new_widget, project, TRUE);
glade_editor_property_commit (eprop, value);
glade_command_pop_group ();
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)
{
GValue *value;
glade_project_selection_set (project, glade_widget_get_object (widget), TRUE);
/* Give the newly created object a name */
glade_widget_ensure_name (new_widget, project, TRUE);
value = g_new0 (GValue, 1);
g_value_init (value, pspec->value_type);
g_value_set_object (value, glade_widget_get_object (new_widget));
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_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);
eprop_object->entry = gtk_entry_new ();
gtk_widget_set_hexpand (eprop_object->entry, TRUE);
gtk_widget_set_valign (eprop_object->entry, GTK_ALIGN_CENTER);
gtk_editable_set_editable (GTK_EDITABLE (eprop_object->entry), FALSE);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_object->entry),
GTK_ENTRY_ICON_SECONDARY,
"gtk-edit");
g_signal_connect_swapped (eprop_object->entry, "icon-release",
G_CALLBACK (glade_eprop_object_show_dialog), eprop);
return eprop_object->entry;
}
/*******************************************************************************
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 (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, *l;
/* It's improbable but possible the editor is visible with no
* property selected, in this case avoid crashes */
if (!eprop->priv->property)
return;
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,
_("C_lear"), GLADE_RESPONSE_CLEAR,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_OK, NULL);
g_free (title);
gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 500);
_glade_util_dialog_set_hig (GTK_DIALOG (dialog));
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 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_widget_set_halign (label, GTK_ALIGN_START);
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);
/* Dont allow selecting the widget owning this property (perhaps this is wrong) */
exception_list = g_list_prepend (exception_list, widget);
/* Build the list of already selected objects */
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,
glade_param_spec_objects_get_type (GLADE_PARAM_SPEC_OBJECTS (pspec)),
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, *l;
gtk_tree_model_foreach
(gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)),
(GtkTreeModelForeachFunc)
glade_eprop_objects_selected_widget, &selected);
if (selected)
{
glade_command_push_group (_("Setting %s of %s"),
glade_property_class_get_name (eprop->priv->klass),
glade_widget_get_name (widget));
/* Make sure the selected widgets have names now
*/
for (l = selected; l; l = l->next)
{
GObject *object = l->data;
GladeWidget *selected_widget = glade_widget_get_from_gobject (object);
glade_widget_ensure_name (selected_widget, project, TRUE);
}
}
value = glade_property_class_make_gvalue (eprop->priv->klass, selected);
glade_editor_property_commit (eprop, value);
if (selected)
glade_command_pop_group ();
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);
eprop_objects->entry = gtk_entry_new ();
gtk_widget_set_hexpand (eprop_objects->entry, TRUE);
gtk_widget_set_valign (eprop_objects->entry, GTK_ALIGN_CENTER);
gtk_editable_set_editable (GTK_EDITABLE (eprop_objects->entry), FALSE);
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (eprop_objects->entry),
GTK_ENTRY_ICON_SECONDARY,
"gtk-edit");
g_signal_connect_swapped (eprop_objects->entry, "icon-release",
G_CALLBACK (glade_eprop_objects_show_dialog), eprop);
return eprop_objects->entry;
}
/*******************************************************************************
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));
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 (eprop->priv->item_label)
glade_property_label_set_property (GLADE_PROPERTY_LABEL (eprop->priv->item_label), property);
if (property)
{
g_assert (eprop->priv->klass == glade_property_get_class (property));
gtk_widget_show (GTK_WIDGET (eprop));
if (eprop->priv->item_label)
gtk_widget_show (eprop->priv->item_label);
}
else
{
gtk_widget_hide (GTK_WIDGET (eprop));
if (eprop->priv->item_label)
gtk_widget_hide (eprop->priv->item_label);
}
}
else
glade_editor_property_load (eprop, NULL);
}