Commit e086ae99 authored by Tristan Van Berkom's avatar Tristan Van Berkom
Browse files

* plugins/gtk+/Makefile.am, plugins/gtk+/glade-string-list.[ch]: Added Boxed type

	  and GladeEditorProperty to manage/edit a list of strings that can be translatable.
	  This property editor uses a treeview and supports reordering of rows with Drag'n'Drop
	  as well as deletion of rows using the 'Delete' key. Every row can have individual
	  i18n information set via the common i18n dialog.

	* plugins/gtk+/glade-gtk.c, plugins/gtk+/gtk+.xml.in: Add GtkComboBoxText support
	  to Glade and use the GladeStringList boxed type to edit the list of translatable
	  items.
parent 5dbbd4eb
2011-01-26 Tristan Van Berkom <tristanvb@openismus.com>
* plugins/gtk+/Makefile.am, plugins/gtk+/glade-string-list.[ch]: Added Boxed type
and GladeEditorProperty to manage/edit a list of strings that can be translatable.
This property editor uses a treeview and supports reordering of rows with Drag'n'Drop
as well as deletion of rows using the 'Delete' key. Every row can have individual
i18n information set via the common i18n dialog.
* plugins/gtk+/glade-gtk.c, plugins/gtk+/gtk+.xml.in: Add GtkComboBoxText support
to Glade and use the GladeStringList boxed type to edit the list of translatable
items.
2011-01-25 Juan Pablo Ugarte <juanpablougarte@gmail.com>
* gladeui/glade-design-layout.[ch]:
o Made GladeDesignLayout an offscreen container, all its children are redirected offscreen
and composited later on. This allow us drawing "pretty" selections over widgets.
o added new function derived from glade_design_layout_widget_event(),
glade_design_layout_do_event() to marshal events from the override class handler
o added glade_design_layout_selection_set()
o reworked cursors pointer in an array to ease setting them.
o replaced glade_design_layout_get_pointer_region () with gdl_get_activity_from_pointer()
o removed call to gtk_widget_size_allocate() in glade_design_layout_update_child()
fixes rendering glith while resizing
* gladeui/glade-design-view.c: set layout selection on project's selection-changed signal
* gladeui/glade-app.[ch]:
o added glade_app_do_event()
o set custom gdk event handler to marshal event to GladeDesignLayouts using
glade_design_layout_do_event()
* gladeui/glade-utils.[ch]: removed unused function glade_util_draw_selection_nodes()
* gladeui/glade-widget.[ch]:
o moved IS_GLADE_WIDGET_EVENT macro to header.
o replaced glade_widget_connect_signal_handlers() with glade_widget_add_events()
* gladeui/glade-design-layout.[ch]:
o Made GladeDesignLayout an offscreen container, all its children are redirected offscreen
and composited later on. This allow us drawing "pretty" selections over widgets.
o added new function derived from glade_design_layout_widget_event(),
glade_design_layout_do_event() to marshal events from the override class handler
o added glade_design_layout_selection_set()
o reworked cursors pointer in an array to ease setting them.
o replaced glade_design_layout_get_pointer_region () with gdl_get_activity_from_pointer()
o removed call to gtk_widget_size_allocate() in glade_design_layout_update_child()
fixes rendering glith while resizing
* gladeui/glade-design-view.c: set layout selection on project's selection-changed signal
* gladeui/glade-app.[ch]:
o added glade_app_do_event()
o set custom gdk event handler to marshal event to GladeDesignLayouts using
glade_design_layout_do_event()
* gladeui/glade-utils.[ch]: removed unused function glade_util_draw_selection_nodes()
* gladeui/glade-widget.[ch]:
o moved IS_GLADE_WIDGET_EVENT macro to header.
o replaced glade_widget_connect_signal_handlers() with glade_widget_add_events()
2011-01-24 Tristan Van Berkom <tristanvb@openismus.com>
......
......@@ -25,7 +25,7 @@ libgladegtk_la_SOURCES = glade-gtk.c glade-fixed.c glade-accels.c glade-attr
glade-icon-sources.c glade-button-editor.c glade-tool-button-editor.c glade-image-editor.c \
glade-image-item-editor.c glade-icon-factory-editor.c glade-store-editor.c glade-label-editor.c \
glade-cell-renderer-editor.c glade-treeview-editor.c glade-entry-editor.c glade-activatable-editor.c \
glade-tool-item-group-editor.c
glade-tool-item-group-editor.c glade-string-list.c
libgladegtk_la_LDFLAGS = -module -avoid-version $(AM_LDFLAGS)
libgladegtk_la_LIBADD = $(libgladeui) $(GTK_LIBS)
......@@ -35,7 +35,8 @@ libgladegtkinclude_HEADERS = glade-gtk.h glade-accels.h glade-attributes.h glade
glade-text-button.h glade-icon-sources.h glade-button-editor.h \
glade-tool-button-editor.h glade-image-editor.h glade-image-item-editor.h glade-icon-factory-editor.h \
glade-store-editor.h glade-label-editor.h glade-cell-renderer-editor.h glade-treeview-editor.h \
glade-entry-editor.h glade-activatable-editor.h glade-fixed.h glade-tool-item-group-editor.h
glade-entry-editor.h glade-activatable-editor.h glade-fixed.h glade-tool-item-group-editor.h \
glade-string-list.h
if PLATFORM_WIN32
......
......@@ -42,6 +42,7 @@
#include "glade-entry-editor.h"
#include "glade-activatable-editor.h"
#include "glade-tool-item-group-editor.h"
#include "glade-string-list.h"
#include "glade-fixed.h"
#include <gladeui/glade-editor-property.h>
......@@ -8672,6 +8673,11 @@ glade_gtk_combo_box_post_create (GladeWidgetAdaptor * adaptor,
glade_widget_adaptor_create_internal
(widget, G_OBJECT (gtk_bin_get_child (GTK_BIN (object))),
"entry", "comboboxentry", FALSE, reason);
/* For some reason combos need a kick in the but in order to show
* up properly after loading. */
gtk_widget_hide (GTK_WIDGET (object));
gtk_widget_show (GTK_WIDGET (object));
}
void
......@@ -8733,6 +8739,216 @@ glade_gtk_combo_box_get_internal_child (GladeWidgetAdaptor * adaptor,
return child;
}
/* ----------------------------- GtkComboBoxText ------------------------------ */
#define GLADE_TAG_ITEMS "items"
#define GLADE_TAG_ITEM "item"
void
glade_gtk_combo_box_text_post_create (GladeWidgetAdaptor *adaptor,
GObject *object,
GladeCreateReason reason)
{
GladeWidget *gwidget;
/* Chain Up */
GWA_GET_CLASS (GTK_TYPE_COMBO_BOX)->post_create (adaptor, object, reason);
/* No editor, no model, no cells on a GtkComboBoxText, just the items. */
gwidget = glade_widget_get_from_gobject (object);
glade_widget_set_action_visible (gwidget, "launch_editor", FALSE);
}
GladeEditorProperty *
glade_gtk_combo_box_text_create_eprop (GladeWidgetAdaptor * adaptor,
GladePropertyClass * klass,
gboolean use_command)
{
GladeEditorProperty *eprop;
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (klass);
if (pspec->value_type == GLADE_TYPE_STRING_LIST)
{
eprop = glade_eprop_string_list_new (klass, use_command, TRUE);
}
else
eprop = GWA_GET_CLASS
(GTK_TYPE_WIDGET)->create_eprop (adaptor, klass, use_command);
return eprop;
}
gchar *
glade_gtk_combo_box_text_string_from_value (GladeWidgetAdaptor * adaptor,
GladePropertyClass * klass,
const GValue * value)
{
GParamSpec *pspec;
pspec = glade_property_class_get_pspec (klass);
if (pspec->value_type == GLADE_TYPE_STRING_LIST)
{
GList *list = g_value_get_boxed (value);
return glade_string_list_to_string (list);
}
else
return GWA_GET_CLASS
(GTK_TYPE_COMBO_BOX)->string_from_value (adaptor, klass, value);
}
void
glade_gtk_combo_box_text_set_property (GladeWidgetAdaptor * adaptor,
GObject * object,
const gchar * id, const GValue * value)
{
if (!strcmp (id, "glade-items"))
{
GList *string_list, *l;
GladeString *string;
gint active;
string_list = g_value_get_boxed (value);
active = gtk_combo_box_get_active (GTK_COMBO_BOX (object));
/* Update comboboxtext items */
gtk_combo_box_text_remove_all (GTK_COMBO_BOX_TEXT (object));
for (l = string_list; l; l = l->next)
{
string = l->data;
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (object), string->string);
}
gtk_combo_box_set_active (GTK_COMBO_BOX (object),
CLAMP (active, 0, g_list_length (string_list) - 1));
}
else
GWA_GET_CLASS (GTK_TYPE_COMBO_BOX)->set_property (adaptor, object, id, value);
}
static void
glade_gtk_combo_box_text_read_items (GladeWidget * widget, GladeXmlNode * node)
{
GladeXmlNode *items_node;
GladeXmlNode *item_node;
GList *string_list = NULL;
if ((items_node =
glade_xml_search_child (node, GLADE_TAG_ITEMS)) != NULL)
{
for (item_node = glade_xml_node_get_children (items_node);
item_node; item_node = glade_xml_node_next (item_node))
{
gchar *str, *comment, *context;
gboolean translatable;
if (!glade_xml_node_verify (item_node, GLADE_TAG_ITEM))
continue;
if ((str = glade_xml_get_content (item_node)) == NULL)
continue;
context = glade_xml_get_property_string (item_node, GLADE_TAG_CONTEXT);
comment = glade_xml_get_property_string (item_node, GLADE_TAG_COMMENT);
translatable = glade_xml_get_property_boolean (item_node, GLADE_TAG_TRANSLATABLE, FALSE);
string_list =
glade_string_list_append (string_list,
str, comment, context, translatable);
g_free (str);
g_free (context);
g_free (comment);
}
glade_widget_property_set (widget, "glade-items", string_list);
glade_string_list_free (string_list);
}
}
void
glade_gtk_combo_box_text_read_widget (GladeWidgetAdaptor * adaptor,
GladeWidget * widget, GladeXmlNode * node)
{
if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
return;
/* First chain up and read in all the normal properties.. */
GWA_GET_CLASS (GTK_TYPE_COMBO_BOX)->read_widget (adaptor, widget, node);
glade_gtk_combo_box_text_read_items (widget, node);
}
static void
glade_gtk_combo_box_text_write_items (GladeWidget * widget,
GladeXmlContext * context,
GladeXmlNode * node)
{
GladeXmlNode *item_node;
GList *string_list = NULL, *l;
GladeString *string;
if (!glade_widget_property_get (widget, "glade-items", &string_list) || !string_list)
return;
for (l = string_list; l; l = l->next)
{
string = l->data;
item_node = glade_xml_node_new (context, GLADE_TAG_ITEM);
glade_xml_node_append_child (node, item_node);
glade_xml_set_content (item_node, string->string);
if (string->translatable)
glade_xml_node_set_property_string (item_node,
GLADE_TAG_TRANSLATABLE,
GLADE_XML_TAG_I18N_TRUE);
if (string->comment)
glade_xml_node_set_property_string (item_node,
GLADE_TAG_COMMENT,
string->comment);
if (string->context)
glade_xml_node_set_property_string (item_node,
GLADE_TAG_CONTEXT,
string->context);
}
}
void
glade_gtk_combo_box_text_write_widget (GladeWidgetAdaptor * adaptor,
GladeWidget * widget,
GladeXmlContext * context, GladeXmlNode * node)
{
GladeXmlNode *attrs_node;
if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
return;
/* First chain up and read in all the normal properties.. */
GWA_GET_CLASS (GTK_TYPE_WIDGET)->write_widget (adaptor, widget, context,
node);
attrs_node = glade_xml_node_new (context, GLADE_TAG_ITEMS);
glade_gtk_combo_box_text_write_items (widget, context, attrs_node);
if (!glade_xml_node_get_children (attrs_node))
glade_xml_node_delete (attrs_node);
else
glade_xml_node_append_child (node, attrs_node);
}
/* ----------------------------- GtkSpinButton ------------------------------ */
static void
glade_gtk_spin_button_set_adjustment (GObject * object, const GValue * value)
......
/*
* glade-string-list.c - Editing support for lists of translatable strings
*
* Copyright (C) 2010 Openismus GmbH
*
* Author(s):
* Tristan Van Berkom <tvb@gnome.org>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*/
#include <config.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gladeui/glade.h>
#include <glib/gi18n-lib.h>
#include "glade-string-list.h"
/**************************************************************
* GladeStringList boxed type stuff here *
**************************************************************/
static GladeString *
glade_string_new (const gchar *string,
const gchar *comment,
const gchar *context,
gboolean translatable)
{
GladeString *gstring = g_slice_new0 (GladeString);
gstring->string = g_strdup (string);
gstring->comment = g_strdup (comment);
gstring->context = g_strdup (context);
gstring->translatable = translatable;
return gstring;
}
static GladeString *
glade_string_copy (GladeString *string)
{
return glade_string_new (string->string,
string->comment,
string->context,
string->translatable);
}
static void
glade_string_free (GladeString *string)
{
g_free (string->string);
g_free (string->comment);
g_free (string->context);
g_slice_free (GladeString, string);
}
GList *
glade_string_list_append (GList *list,
gchar *string,
gchar *comment,
gchar *context,
gboolean translatable)
{
GladeString *gstring;
gstring = glade_string_new (string, comment, context, translatable);
return g_list_append (list, gstring);
}
GList *
glade_string_list_copy (GList *string_list)
{
GList *ret = NULL, *list;
GladeString *string, *copy;
for (list = string_list; list; list = list->next)
{
string = list->data;
copy = glade_string_copy (string);
ret = g_list_prepend (ret, copy);
}
return g_list_reverse (ret);
}
void
glade_string_list_free (GList * string_list)
{
g_list_foreach (string_list, (GFunc)glade_string_free, NULL);
g_list_free (string_list);
}
GType
glade_string_list_get_type (void)
{
static GType type_id = 0;
if (!type_id)
type_id = g_boxed_type_register_static
("GladeStringList",
(GBoxedCopyFunc) glade_string_list_copy,
(GBoxedFreeFunc) glade_string_list_free);
return type_id;
}
gchar *
glade_string_list_to_string (GList *list)
{
GString *string = g_string_new ("");
GList *l;
for (l = list; l; l = l->next)
{
GladeString *str = l->data;
if (l != list)
g_string_append_c (string, ',');
g_string_append_printf (string, "%s:%s:%s:%d",
str->string,
str->comment ? str->comment : "",
str->context ? str->context : "",
str->translatable);
}
return g_string_free (string, FALSE);
}
/**************************************************************
* GladeEditorProperty stuff here
**************************************************************/
typedef struct
{
GladeEditorProperty parent_instance;
GtkTreeModel *model;
GtkWidget *view;
guint translatable : 1;
guint want_focus : 1;
guint editing_index;
guint changed_id;
guint update_id;
GList *pending_string_list;
} GladeEPropStringList;
enum
{
COLUMN_STRING,
COLUMN_INDEX,
COLUMN_DUMMY,
NUM_COLUMNS
};
GLADE_MAKE_EPROP (GladeEPropStringList, glade_eprop_string_list)
#define GLADE_EPROP_STRING_LIST(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_EPROP_STRING_LIST, GladeEPropStringList))
static void
glade_eprop_string_list_finalize (GObject * object)
{
GladeEPropStringList *eprop_string_list = GLADE_EPROP_STRING_LIST (object);
GObjectClass *parent_class =
g_type_class_peek_parent (G_OBJECT_GET_CLASS (object));
if (eprop_string_list->update_id)
{
g_source_remove (eprop_string_list->update_id);
eprop_string_list->update_id = 0;
}
if (eprop_string_list->changed_id)
{
g_source_remove (eprop_string_list->changed_id);
eprop_string_list->changed_id = 0;
}
if (eprop_string_list->pending_string_list)
{
glade_string_list_free (eprop_string_list->pending_string_list);
eprop_string_list->pending_string_list = NULL;
}
/* Chain up */
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
update_string_list_idle (GladeEditorProperty * eprop)
{
GladeEPropStringList *eprop_string_list = GLADE_EPROP_STRING_LIST (eprop);
GValue value = { 0, };
eprop_string_list->want_focus = TRUE;
g_value_init (&value, GLADE_TYPE_STRING_LIST);
g_value_take_boxed (&value, eprop_string_list->pending_string_list);
glade_editor_property_commit (eprop, &value);
g_value_unset (&value);
eprop_string_list->want_focus = FALSE;
eprop_string_list->pending_string_list = NULL;
eprop_string_list->update_id = 0;
return FALSE;
}
static gboolean
data_changed_idle (GladeEditorProperty *eprop)
{
GladeEPropStringList *eprop_string_list = GLADE_EPROP_STRING_LIST (eprop);
GladeProperty *property = glade_editor_property_get_property (eprop);
GladeString *string, *copy;
GList *string_list = NULL;
GList *new_list = NULL;
GtkTreeIter iter;
guint index;
/* Create a new list based on the order and contents
* of the model state */
glade_property_get (property, &string_list);
if (gtk_tree_model_get_iter_first (eprop_string_list->model, &iter))
{
do
{
gtk_tree_model_get (eprop_string_list->model, &iter,
COLUMN_INDEX, &index, -1);
if ((string = g_list_nth_data (string_list, index)) != NULL)
{
copy = glade_string_copy (string);
new_list = g_list_prepend (new_list, copy);
}
}
while (gtk_tree_model_iter_next (eprop_string_list->model, &iter));
}
new_list = g_list_reverse (new_list);
if (eprop_string_list->pending_string_list)
glade_string_list_free (eprop_string_list->pending_string_list);
eprop_string_list->pending_string_list = new_list;
/* We're already in an idle, just call it directly here */
update_string_list_idle (eprop);
eprop_string_list->changed_id = 0;
return FALSE;
}
static void
row_deleted (GtkTreeModel * tree_model,
GtkTreePath * path, GladeEditorProperty * eprop)
{
GladeEPropStringList *eprop_string_list = GLADE_EPROP_STRING_LIST (eprop);
if (glade_editor_property_loading (eprop))
return;
eprop_string_list->editing_index = 0;
if (eprop_string_list->changed_id == 0)
eprop_string_list->changed_id =
g_idle_add ((GSourceFunc) data_changed_idle, eprop);
}
static void
glade_eprop_string_list_load (GladeEditorProperty * eprop, GladeProperty * property)
{
GladeEPropStringList *eprop_string_list = GLADE_EPROP_STRING_LIST (eprop);
GladeEditorPropertyClass *parent_class =
g_type_class_peek_parent (G_OBJECT_GET_CLASS (eprop));
GList *string_list, *list;