From 4f1e473e0809ddd33e7e58de401f56b8b27516b1 Mon Sep 17 00:00:00 2001 From: Tristan Van Berkom Date: Thu, 16 Oct 2008 14:31:42 +0000 Subject: Encapsulated name tracking mechanism * gladeui/Makefile.am, gladeui/glade-name-context.[ch]: Encapsulated name tracking mechanism * gladeui/glade-project.c, gladeui/glade-command.c: Now added a naming policy to the project with prefs and load/save support + a glade command to set it - also revamped the prefs dialog, it also pops up automatically for new projects. * gladeui/glade-editor.c, gladeui/glade-editor-property.c, gladeui/glade-property-class.c, gladeui/glade-property.c, gladeui/glade-widget.c: All effected since now glade_property_class_make_gvalue_from_string () needs a GladeWidget argument to do hierachic context sensitive searches... that and naming is much cleaner now. * src/glade-window.c: remember to pass ownership of the project to the app. * plugins/gtk+/glade-gtk.c, plugins/gtk+/glade-column-types.c, plugins/gtk+/glade-model-data.c: BEWARE: Dangerous and still a work in progress. svn path=/trunk/; revision=1972 --- ChangeLog | 28 + gladeui/Makefile.am | 8 +- gladeui/glade-app.c | 13 +- gladeui/glade-command.c | 153 +- gladeui/glade-command.h | 3 + gladeui/glade-editor-property.c | 20 +- gladeui/glade-editor.c | 4 +- gladeui/glade-name-context.c | 241 +++ gladeui/glade-name-context.h | 34 + gladeui/glade-project.c | 3017 ++++++++++++++++++++++--------------- gladeui/glade-project.h | 39 +- gladeui/glade-property-class.c | 31 +- gladeui/glade-property-class.h | 9 +- gladeui/glade-property.c | 5 +- gladeui/glade-utils.c | 9 +- gladeui/glade-utils.h | 6 +- gladeui/glade-widget.c | 19 +- gladeui/glade-xml-utils.h | 7 + plugins/gnome/glade-gnome.c | 2 +- plugins/gtk+/glade-column-types.c | 166 +- plugins/gtk+/glade-gtk.c | 14 +- plugins/gtk+/glade-model-data.c | 84 +- plugins/gtk+/gtk+.xml.in | 6 +- src/glade-window.c | 10 +- 24 files changed, 2473 insertions(+), 1455 deletions(-) create mode 100644 gladeui/glade-name-context.c create mode 100644 gladeui/glade-name-context.h diff --git a/ChangeLog b/ChangeLog index 88fca9c5..e6aa841a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,31 @@ +2008-10-16 Tristan Van Berkom + + * gladeui/Makefile.am, gladeui/glade-name-context.[ch]: Encapsulated name tracking mechanism + + * gladeui/glade-project.c, gladeui/glade-command.c: Now added a naming policy to the project + with prefs and load/save support + a glade command to set it - also revamped the prefs dialog, + it also pops up automatically for new projects. + + * gladeui/glade-editor.c, gladeui/glade-editor-property.c, gladeui/glade-property-class.c, + gladeui/glade-property.c, gladeui/glade-widget.c: All effected since now + glade_property_class_make_gvalue_from_string () needs a GladeWidget argument to do + hierachic context sensitive searches... that and naming is much cleaner now. + + * src/glade-window.c: remember to pass ownership of the project to the app. + + * plugins/gtk+/glade-gtk.c, plugins/gtk+/glade-column-types.c, plugins/gtk+/glade-model-data.c: + BEWARE: Dangerous and still a work in progress. + +2008-10-10 Tristan Van Berkom + + * gladeui/glade-xml-utils.c, gladeui/glade-property-class.[ch]: Added + new "needs-sync" property + + * gladeui/glade-widget.c: glade_widget_sync_custom_props(): also sync + props marked as needs-sync. + + * plugins/gtk+/gtk+.xml.in: GtkTable:n-rows/n-columns marked as "needs-sync" + 2008-09-30 Tristan Van Berkom * gladeui/glade-property.[ch], gladeui/glade-command.[ch], diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am index cd53741d..0e96567c 100644 --- a/gladeui/Makefile.am +++ b/gladeui/Makefile.am @@ -62,7 +62,8 @@ libgladeui_1_la_SOURCES = \ glade-catalog.h \ glade-marshallers.h \ glade-accumulators.h \ - glade-widget-action.c + glade-widget-action.c \ + glade-name-context.c libgladeui_1_la_CPPFLAGS = \ $(common_defines) \ @@ -110,8 +111,9 @@ libgladeuiinclude_HEADERS = \ glade-xml-utils.h \ glade-signal.h \ glade-cursor.h \ - glade-catalog.h \ - glade-widget-action.h + glade-catalog.h \ + glade-widget-action.h \ + glade-name-context.h if PLATFORM_WIN32 diff --git a/gladeui/glade-app.c b/gladeui/glade-app.c index ae342bb9..78c283dc 100644 --- a/gladeui/glade-app.c +++ b/gladeui/glade-app.c @@ -936,19 +936,19 @@ glade_app_add_project (GladeProject *project) g_return_if_fail (GLADE_IS_PROJECT (project)); + app = glade_app_get (); + /* If the project was previously loaded, don't re-load */ - if (glade_app_is_project_loaded (glade_project_get_path (project))) + if (g_list_find (app->priv->projects, project) != NULL) { glade_app_set_project (project); return; } glade_app_update_instance_count (project); - - app = glade_app_get (); - - g_object_ref (project); - app->priv->projects = g_list_append (app->priv->projects, project); + /* Take a reference for GladeApp here... */ + app->priv->projects = g_list_append (app->priv->projects, + g_object_ref (project)); /* connect to the project signals so that the editor can be updated */ g_signal_connect (G_OBJECT (project), "selection_changed", @@ -1022,7 +1022,6 @@ glade_app_remove_project (GladeProject *project) * that point. */ g_object_unref (project); - } diff --git a/gladeui/glade-command.c b/gladeui/glade-command.c index 880244b2..8d085000 100644 --- a/gladeui/glade-command.c +++ b/gladeui/glade-command.c @@ -49,6 +49,7 @@ #include "glade-signal.h" #include "glade-app.h" #include "glade-fixed.h" +#include "glade-name-context.h" /* Concerning placeholders: we do not hold any reference to placeholders, * placeholders that are supplied by the backend are not reffed, placeholders @@ -771,7 +772,8 @@ glade_command_set_name_execute (GladeCommand *cmd) g_return_val_if_fail (me->widget != NULL, TRUE); g_return_val_if_fail (me->name != NULL, TRUE); - glade_widget_set_name (me->widget, me->name); + glade_project_set_widget_name (me->widget->project, + me->widget, me->name); tmp = me->old_name; me->old_name = me->name; @@ -1621,7 +1623,10 @@ glade_command_create(GladeWidgetAdaptor *adaptor, GladeWidget *parent, GladePlac g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); /* attempt to create the widget -- widget may be null, e.g. the user clicked cancel on a query */ - widget = glade_widget_adaptor_create_widget(adaptor, TRUE, "parent", parent, "project", project, NULL); + widget = glade_widget_adaptor_create_widget(adaptor, TRUE, + "parent", parent, + "project", project, + NULL); if (widget == NULL) { return NULL; @@ -2354,3 +2359,147 @@ glade_command_set_project_format (GladeProject *project, } + +/****************************************************************************** + * + * set project naming policy + * + * This command sets the naming policy on the project. + * + *****************************************************************************/ + +typedef struct { + GladeCommand parent; + GladeProject *project; + GladeNamingPolicy policy; + GladeNamingPolicy old_policy; + gboolean run_once; +} GladeCommandSetPolicy; + + +GLADE_MAKE_COMMAND (GladeCommandSetPolicy, glade_command_set_policy); +#define GLADE_COMMAND_SET_POLICY_TYPE (glade_command_set_policy_get_type ()) +#define GLADE_COMMAND_SET_POLICY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_POLICY_TYPE, GladeCommandSetPolicy)) +#define GLADE_COMMAND_SET_POLICY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_POLICY_TYPE, GladeCommandSetPolicyClass)) +#define GLADE_IS_COMMAND_SET_POLICY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_POLICY_TYPE)) +#define GLADE_IS_COMMAND_SET_POLICY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_POLICY_TYPE)) + +static gboolean +glade_command_set_policy_execute(GladeCommand *cmd) +{ + GladeCommandSetPolicy *me = (GladeCommandSetPolicy *)cmd; + GladeNamingPolicy policy; + + /* sanity check */ + g_return_val_if_fail (me != NULL, TRUE); + g_return_val_if_fail (me->project != NULL, TRUE); + + /* set the new policy */ + glade_project_set_naming_policy (me->project, me->policy, me->run_once == FALSE); + + /* swap the current values with the old values to prepare for undo */ + policy = me->policy; + me->policy = me->old_policy; + me->old_policy = policy; + + me->run_once = TRUE; + + return TRUE; +} + +static gboolean +glade_command_set_policy_undo(GladeCommand *cmd) +{ + return glade_command_set_policy_execute(cmd); +} + +static void +glade_command_set_policy_finalize(GObject *obj) +{ +/* GladeCommandSetPolicy *me; */ + + g_return_if_fail(GLADE_IS_COMMAND_SET_POLICY(obj)); + + glade_command_finalize(obj); +} + +static gboolean +glade_command_set_policy_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd) +{ +/* GladeCommandSetPolicy *cmd1; */ +/* GladeCommandSetPolicy *cmd2; */ + + return FALSE; +} + +static void +glade_command_set_policy_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd) +{ + /* this command is the one that will be used for an undo of the sequence of like commands */ + //GladeCommandSetPolicy *this = GLADE_COMMAND_SET_POLICY (this_cmd); + + /* the other command contains the values that will be used for a redo */ + //GladeCommandSetPolicy *other = GLADE_COMMAND_SET_POLICY (other_cmd); + + g_return_if_fail (GLADE_IS_COMMAND_SET_POLICY (this_cmd) && GLADE_IS_COMMAND_SET_POLICY (other_cmd)); + + /* no unify/collapse */ +} + +/** + * glade_command_set_project_naming_policy: + * @project: a #GladeProject + * @policy: the #GladeNamingPolicy + * + * Sets the naming policy of a project + */ +void +glade_command_set_project_naming_policy (GladeProject *project, + GladeNamingPolicy policy) + +{ + GladeCommandSetPolicy *me; + + g_return_if_fail (GLADE_IS_PROJECT (project)); + + if (glade_project_get_naming_policy (project) != policy) + { + gchar *prj_name = glade_project_get_name (project); + glade_command_push_group (_("Setting %s to use a %s naming policy"), + prj_name, + policy == GLADE_POLICY_PROJECT_WIDE ? + "project wide" : "toplevel contextual"); + g_free (prj_name); + + /* load up the command */ + me = g_object_new(GLADE_COMMAND_SET_POLICY_TYPE, NULL); + me->project = project; + me->policy = policy; + me->old_policy = glade_project_get_naming_policy (project); + + me->run_once = FALSE; + + GLADE_COMMAND(me)->description = g_strdup_printf("dummy string"); + + glade_command_check_group(GLADE_COMMAND(me)); + + /* execute the command and push it on the stack if successful + * this sets the actual policy + */ + if (glade_command_set_policy_execute(GLADE_COMMAND(me))) + { + glade_project_push_undo(glade_app_get_project(), GLADE_COMMAND(me)); + } + else + { + g_object_unref(G_OBJECT(me)); + } + + glade_command_pop_group (); + + glade_editor_refresh (glade_app_get_editor ()); + } +} + + + diff --git a/gladeui/glade-command.h b/gladeui/glade-command.h index b4d3cf06..51948024 100644 --- a/gladeui/glade-command.h +++ b/gladeui/glade-command.h @@ -90,6 +90,9 @@ void glade_command_collapse (GladeCommand *command, void glade_command_set_project_format (GladeProject *project, GladeProjectFormat fmt); +void glade_command_set_project_naming_policy (GladeProject *project, + GladeNamingPolicy policy); + /************************** properties *********************************/ void glade_command_set_property (GladeProperty *property, diff --git a/gladeui/glade-editor-property.c b/gladeui/glade-editor-property.c index 71f55ae5..a1ad9723 100644 --- a/gladeui/glade-editor-property.c +++ b/gladeui/glade-editor-property.c @@ -113,8 +113,9 @@ glade_editor_property_commit (GladeEditorProperty *eprop, } -void glade_editor_property_commit_no_callback (GladeEditorProperty *eprop, - GValue *value) +void +glade_editor_property_commit_no_callback (GladeEditorProperty *eprop, + GValue *value) { g_return_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop)); @@ -1601,7 +1602,7 @@ glade_eprop_text_changed_common (GladeEditorProperty *eprop, eprop->property->klass->pspec->value_type == G_TYPE_VALUE_ARRAY) { val = glade_property_class_make_gvalue_from_string - (eprop->property->klass, text, NULL); + (eprop->property->klass, text, NULL, NULL); } else { @@ -2228,7 +2229,7 @@ glade_eprop_resource_entry_activate (GtkEntry *entry, GladeEditorProperty *eprop { GladeProject *project = glade_widget_get_project (eprop->property->widget); GValue *value = glade_property_class_make_gvalue_from_string - (eprop->klass, gtk_entry_get_text(entry), project); + (eprop->klass, gtk_entry_get_text(entry), project, eprop->property->widget); /* Set project resource here where we still have the fullpath. */ @@ -2287,7 +2288,7 @@ glade_eprop_resource_select_file (GtkButton *button, GladeEditorProperty *eprop) basename = g_path_get_basename (file); value = glade_property_class_make_gvalue_from_string - (eprop->klass, basename, project); + (eprop->klass, basename, project, eprop->property->widget); glade_editor_property_commit (eprop, value); @@ -2824,7 +2825,7 @@ glade_eprop_object_show_dialog (GtkWidget *dialog_button, if (selected) { GValue *value = glade_property_class_make_gvalue_from_string - (eprop->klass, selected->name, project); + (eprop->klass, selected->name, project, eprop->property->widget); /* Unparent the widget so we can reuse it for this property */ if (eprop->klass->parentless_widget) @@ -2880,9 +2881,12 @@ glade_eprop_object_show_dialog (GtkWidget *dialog_button, if ((new_widget = glade_command_create (create_adaptor, NULL, NULL, project)) != NULL) { value = glade_property_class_make_gvalue_from_string - (eprop->klass, new_widget->name, project); + (eprop->klass, new_widget->name, project, NULL); glade_editor_property_commit (eprop, value); + + g_value_unset (value); + g_free (value); } glade_command_pop_group (); @@ -2890,7 +2894,7 @@ glade_eprop_object_show_dialog (GtkWidget *dialog_button, else if (res == GLADE_RESPONSE_CLEAR) { GValue *value = glade_property_class_make_gvalue_from_string - (eprop->klass, NULL, project); + (eprop->klass, NULL, project, eprop->property->widget); glade_editor_property_commit (eprop, value); diff --git a/gladeui/glade-editor.c b/gladeui/glade-editor.c index ed7f3aa3..4c2713c5 100644 --- a/gladeui/glade-editor.c +++ b/gladeui/glade-editor.c @@ -18,6 +18,7 @@ * * Authors: * Chema Celorio + * Tristan Van Berkom */ @@ -503,7 +504,8 @@ glade_editor_widget_name_changed (GtkWidget *editable, GladeEditor *editor) widget = editor->loaded_widget; new_name = gtk_editable_get_chars (GTK_EDITABLE (editable), 0, -1); - if (!glade_project_get_widget_by_name (widget->project, new_name)) + + if (glade_project_available_widget_name (widget->project, widget, new_name)) glade_command_set_name (widget, new_name); g_free (new_name); } diff --git a/gladeui/glade-name-context.c b/gladeui/glade-name-context.c new file mode 100644 index 00000000..d4b52918 --- /dev/null +++ b/gladeui/glade-name-context.c @@ -0,0 +1,241 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * glade-name-context.c + * + * Copyright (C) 2008 Tristan Van Berkom. + * + * Authors: + * Tristan Van Berkom + * + * 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. + * + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "glade-id-allocator.h" +#include "glade-name-context.h" + +struct _GladeNameContext { + GHashTable *name_allocators; + + GHashTable *names; +}; + + + +GladeNameContext * +glade_name_context_new (void) +{ + GladeNameContext *context = g_new0 (GladeNameContext, 1); + + context->name_allocators = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) glade_id_allocator_destroy); + + context->names = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + return context; +} + +void +glade_name_context_destroy (GladeNameContext *context) +{ + g_return_if_fail (context != NULL); + + g_hash_table_destroy (context->name_allocators); + g_hash_table_destroy (context->names); + g_free (context); +} + +gchar * +glade_name_context_new_name (GladeNameContext *context, + const gchar *base_name) +{ + GladeIDAllocator *id_allocator; + const gchar *number; + gchar *name = NULL, *freeme = NULL; + guint i = 1; + + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (base_name && base_name[0], NULL); + + number = base_name + strlen (base_name); + while (number > base_name && g_ascii_isdigit (number[-1])) + --number; + + if (*number) + { + freeme = g_strndup (base_name, number - base_name); + base_name = freeme; + } + + id_allocator = g_hash_table_lookup (context->name_allocators, base_name); + + if (id_allocator == NULL) + { + id_allocator = glade_id_allocator_new (); + g_hash_table_insert (context->name_allocators, + g_strdup (base_name), id_allocator); + } + + do + { + g_free (name); + i = glade_id_allocator_allocate (id_allocator); + name = g_strdup_printf ("%s%u", base_name, i); + } + while (glade_name_context_has_name (context, name)); + + g_free (freeme); + return name; +} + +gchar * +glade_name_context_dual_new_name (GladeNameContext *context, + GladeNameContext *another_context, + const gchar *base_name) +{ + GladeIDAllocator *id_allocator; + const gchar *number; + gchar *name = NULL, *freeme = NULL; + guint i = 1; + + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (another_context != NULL, NULL); + g_return_val_if_fail (base_name && base_name[0], NULL); + + number = base_name + strlen (base_name); + while (number > base_name && g_ascii_isdigit (number[-1])) + --number; + + if (*number) + { + freeme = g_strndup (base_name, number - base_name); + base_name = freeme; + } + + id_allocator = g_hash_table_lookup (context->name_allocators, base_name); + + if (id_allocator == NULL) + { + id_allocator = glade_id_allocator_new (); + g_hash_table_insert (context->name_allocators, + g_strdup (base_name), id_allocator); + } + + do + { + g_free (name); + i = glade_id_allocator_allocate (id_allocator); + name = g_strdup_printf ("%s%u", base_name, i); + } + while (glade_name_context_has_name (context, name) || + glade_name_context_has_name (another_context, name)); + + g_free (freeme); + return name; +} + +guint +glade_name_context_n_names (GladeNameContext *context) +{ + g_return_val_if_fail (context != NULL, FALSE); + + return g_hash_table_size (context->names); +} + +gboolean +glade_name_context_has_name (GladeNameContext *context, + const gchar *name) +{ + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (name && name[0], FALSE); + + return (g_hash_table_lookup (context->names, name) != NULL); +} + +gboolean +glade_name_context_add_name (GladeNameContext *context, + const gchar *name) +{ + gboolean ret = FALSE; + + g_return_val_if_fail (context != NULL, FALSE); + g_return_val_if_fail (name && name[0], FALSE); + + if (!glade_name_context_has_name (context, name)) + { + g_hash_table_insert (context->names, g_strdup (name), GINT_TO_POINTER (TRUE)); + ret = TRUE; + } + + return ret; +} + +void +glade_name_context_release_name (GladeNameContext *context, + const gchar *name) +{ + + const gchar *first_number = name; + gchar *end_number, *base_name; + GladeIDAllocator *id_allocator; + gunichar ch; + gint id; + + g_return_if_fail (context != NULL); + g_return_if_fail (name && name[0]); + + /* Remove from name hash first... */ + g_hash_table_remove (context->names, name); + + do + { + ch = g_utf8_get_char (first_number); + + if (ch == 0 || g_unichar_isdigit (ch)) + break; + + first_number = g_utf8_next_char (first_number); + } + while (TRUE); + + /* if there is a number - then we have to unallocate it... */ + if (ch == 0) return; + + + base_name = g_strdup (name); + *(base_name + (first_number - name)) = 0; + + if ((id_allocator = + g_hash_table_lookup (context->name_allocators, base_name)) != NULL) + { + + id = (int) strtol (first_number, &end_number, 10); + if (*end_number == 0) + glade_id_allocator_release (id_allocator, id); + } + + g_free (base_name); +} diff --git a/gladeui/glade-name-context.h b/gladeui/glade-name-context.h new file mode 100644 index 00000000..088e5023 --- /dev/null +++ b/gladeui/glade-name-context.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +#ifndef __GLADE_NAME_CONTEXT_H__ +#define __GLADE_NAME_CONTEXT_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _GladeNameContext GladeNameContext; + +GladeNameContext *glade_name_context_new (void); +void glade_name_context_destroy (GladeNameContext *context); + +gchar *glade_name_context_new_name (GladeNameContext *context, + const gchar *base_name); + +gchar *glade_name_context_dual_new_name (GladeNameContext *context, + GladeNameContext *another_context, + const gchar *base_name); + +guint glade_name_context_n_names (GladeNameContext *context); + +gboolean glade_name_context_has_name (GladeNameContext *context, + const gchar *name); + +gboolean glade_name_context_add_name (GladeNameContext *context, + const gchar *name); + +void glade_name_context_release_name (GladeNameContext *context, + const gchar *name); + +G_END_DECLS + +#endif /* __GLADE_NAME_CONTEXT_H__ */ diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c index c2c9ed4c..fd94cc7f 100644 --- a/gladeui/glade-project.c +++ b/gladeui/glade-project.c @@ -48,6 +48,7 @@ #include "glade-project.h" #include "glade-command.h" +#include "glade-name-context.h" #define GLADE_PROJECT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GLADE_TYPE_PROJECT, GladeProjectPrivate)) @@ -105,12 +106,14 @@ struct _GladeProjectPrivate * of #GtkWidget items. */ + GladeNameContext *toplevel_names; /* Context for uniqueness of names at the toplevel */ + GList *toplevels; /* List of toplevels with thier own naming contexts */ + + gboolean has_selection; /* Whether the project has a selection */ GList *undo_stack; /* A stack with the last executed commands */ GList *prev_redo_item; /* Points to the item previous to the redo items */ - GHashTable *widget_names_allocator; /* hash table with the used widget names */ - GHashTable *widget_old_names; /* widget -> old name of the widget */ gboolean first_modification_is_na; /* the flag indicates that the first_modification item has been lost */ @@ -135,14 +138,83 @@ struct _GladeProjectPrivate GHashTable *target_versions_minor; /* target versions by catalog */ GList *loaded_factory_files; + + GladeNamingPolicy naming_policy; /* What rules apply to widget names */ +/* Control on the preferences dialog to update buttons etc when properties change */ + GtkWidget *prefs_dialog; + GtkWidget *glade_radio; + GtkWidget *builder_radio; + GtkWidget *project_wide_radio; + GtkWidget *toplevel_contextual_radio; + GHashTable *target_radios; + }; +typedef struct { + GladeWidget *toplevel; + GladeNameContext *names; +} TopLevelInfo; + typedef struct { gchar *stock; gchar *filename; } StockFilePair; +static void glade_project_set_target_version (GladeProject *project, + const gchar *catalog, + gint major, + gint minor); +static void glade_project_get_target_version (GladeProject *project, + const gchar *catalog, + gint *major, + gint *minor); + +static void glade_project_target_version_for_adaptor (GladeProject *project, + GladeWidgetAdaptor *adaptor, + gint *major, + gint *minor); + +static void glade_project_set_readonly (GladeProject *project, + gboolean readonly); + + +static gboolean glade_project_verify (GladeProject *project, + gboolean saving); + +static void glade_project_verify_adaptor (GladeProject *project, + GladeWidgetAdaptor *adaptor, + const gchar *path_name, + GString *string, + gboolean saving, + gboolean forwidget, + GladeSupportMask *mask); + +static void glade_project_move_resources (GladeProject *project, + const gchar *old_dir, + const gchar *new_dir); + +static void glade_project_sync_resources_for_widget (GladeProject *project, + GladeProject *prev_project, + GladeWidget *gwidget, + gboolean remove); + +GladeWidget *search_ancestry_by_name (GladeWidget *toplevel, + const gchar *name); + +static GtkWidget *glade_project_build_prefs_dialog (GladeProject *project); + +static void format_libglade_button_clicked (GtkWidget *widget, + GladeProject *project); +static void format_builder_button_clicked (GtkWidget *widget, + GladeProject *project); +static void policy_project_wide_button_clicked (GtkWidget *widget, + GladeProject *project); +static void policy_toplevel_contextual_button_clicked (GtkWidget *widget, + GladeProject *project); +static void target_button_clicked (GtkWidget *widget, + GladeProject *project); + static guint glade_project_signals[LAST_SIGNAL] = {0}; static GladeIDAllocator *unsaved_number_allocator = NULL; @@ -228,6 +300,10 @@ static void glade_project_finalize (GObject *object) { GladeProject *project = GLADE_PROJECT (object); + GList *list; + TopLevelInfo *tinfo; + + gtk_widget_destroy (project->priv->prefs_dialog); g_free (project->priv->path); g_free (project->priv->comment); @@ -235,11 +311,20 @@ glade_project_finalize (GObject *object) if (project->priv->unsaved_number > 0) glade_id_allocator_release (get_unsaved_number_allocator (), project->priv->unsaved_number); - g_hash_table_destroy (project->priv->widget_names_allocator); - g_hash_table_destroy (project->priv->widget_old_names); g_hash_table_destroy (project->priv->resources); g_hash_table_destroy (project->priv->target_versions_major); g_hash_table_destroy (project->priv->target_versions_minor); + g_hash_table_destroy (project->priv->target_radios); + + glade_name_context_destroy (project->priv->toplevel_names); + + for (list = project->priv->toplevels; list; list = list->next) + { + tinfo = list->data; + glade_name_context_destroy (tinfo->names); + g_free (tinfo); + } + g_list_free (project->priv->toplevels); G_OBJECT_CLASS (glade_project_parent_class)->finalize (object); } @@ -500,251 +585,10 @@ glade_project_changed_impl (GladeProject *project, glade_app_update_ui (); } + /******************************************************************* - Initializers + Class Initializers *******************************************************************/ - -static void -glade_project_get_target_version (GladeProject *project, - const gchar *catalog, - gint *major, - gint *minor) -{ - *major = GPOINTER_TO_INT - (g_hash_table_lookup (project->priv->target_versions_major, - catalog)); - *minor = GPOINTER_TO_INT - (g_hash_table_lookup (project->priv->target_versions_minor, - catalog)); -} - -static void -glade_project_target_version_for_adaptor (GladeProject *project, - GladeWidgetAdaptor *adaptor, - gint *major, - gint *minor) -{ - gchar *catalog = NULL; - - g_object_get (adaptor, "catalog", &catalog, NULL); - glade_project_get_target_version (project, catalog, major, minor); - g_free (catalog); -} - -static void -glade_project_verify_adaptor (GladeProject *project, - GladeWidgetAdaptor *adaptor, - const gchar *path_name, - GString *string, - gboolean saving, - gboolean forwidget, - GladeSupportMask *mask) -{ - GladeSupportMask support_mask = GLADE_SUPPORT_OK; - GladeWidgetAdaptor *adaptor_iter; - gint target_major, target_minor; - gchar *catalog = NULL; - - for (adaptor_iter = adaptor; adaptor_iter; - adaptor_iter = glade_widget_adaptor_get_parent_adaptor (adaptor_iter)) - { - - g_object_get (adaptor_iter, "catalog", &catalog, NULL); - glade_project_target_version_for_adaptor (project, adaptor_iter, - &target_major, - &target_minor); - - if (target_major < GWA_VERSION_SINCE_MAJOR (adaptor_iter) || - (target_major == GWA_VERSION_SINCE_MAJOR (adaptor_iter) && - target_minor < GWA_VERSION_SINCE_MINOR (adaptor_iter))) - { - if (forwidget) - { - /* translators: reffers to a widget - * introduced in toolkit version '%s %d.%d', - * and a project targeting toolkit verion '%s %d.%d' */ - g_string_append_printf - (string, - _("This widget was introduced in %s %d.%d " - "project targets %s %d.%d"), - catalog, - GWA_VERSION_SINCE_MAJOR (adaptor_iter), - GWA_VERSION_SINCE_MINOR (adaptor_iter), - catalog, target_major, target_minor); - } - else - /* translators: reffers to a widget '[%s]' - * introduced in toolkit version '%s %d.%d' */ - g_string_append_printf - (string, - _("[%s] Object class '%s' was introduced in %s %d.%d\n"), - path_name, adaptor_iter->title, catalog, - GWA_VERSION_SINCE_MAJOR (adaptor_iter), - GWA_VERSION_SINCE_MINOR (adaptor_iter)); - - support_mask |= GLADE_SUPPORT_MISMATCH; - } - - if (project->priv->format == GLADE_PROJECT_FORMAT_GTKBUILDER && - GWA_LIBGLADE_ONLY (adaptor_iter)) - { - if (forwidget) - { - if (string->len) - g_string_append (string, "\n"); - g_string_append_printf - (string, - _("This widget is only supported in libglade format")); - } - else - /* translators: reffers to a widget '[%s]' - * loaded from toolkit version '%s %d.%d' */ - g_string_append_printf - (string, - _("[%s] Object class '%s' from %s %d.%d " - "is only supported in libglade format\n"), - path_name, adaptor_iter->title, catalog, - target_major, target_minor); - - support_mask |= GLADE_SUPPORT_LIBGLADE_ONLY; - } - else if (project->priv->format == GLADE_PROJECT_FORMAT_LIBGLADE && - GWA_LIBGLADE_UNSUPPORTED (adaptor_iter)) - { - if (forwidget) - { - if (string->len) - g_string_append (string, "\n"); - g_string_append_printf - (string, - _("This widget is not supported in libglade format")); - } - else - /* translators: reffers to a widget '[%s]' - * loaded from toolkit version '%s %d.%d' */ - g_string_append_printf - (string, - _("[%s] Object class '%s' from %s %d.%d " - "is not supported in libglade format\n"), - path_name, adaptor_iter->title, catalog, - target_major, target_minor); - - support_mask |= GLADE_SUPPORT_LIBGLADE_UNSUPPORTED; - } - - if (!saving && GWA_DEPRECATED (adaptor_iter)) - { - if (forwidget) - { - if (string->len) - g_string_append (string, "\n"); - g_string_append_printf - (string, _("This widget is deprecated")); - } - else - /* translators: reffers to a widget '[%s]' - * loaded from toolkit version '%s %d.%d' */ - g_string_append_printf - (string, - _("[%s] Object class '%s' from %s %d.%d " - "is deprecated\n"), - path_name, adaptor_iter->title, catalog, - target_major, target_minor); - - support_mask |= GLADE_SUPPORT_DEPRECATED; - } - g_free (catalog); - } - if (mask) - *mask = support_mask; -} - -/** - * glade_project_verify_widget_adaptor: - * @project: A #GladeProject - * @adaptor: the #GladeWidgetAdaptor to verify - * @mask: a return location for a #GladeSupportMask - * - * Checks the supported state of this widget adaptor - * and generates a string to show in the UI describing why. - * - * Returns: A newly allocated string - */ -gchar * -glade_project_verify_widget_adaptor (GladeProject *project, - GladeWidgetAdaptor *adaptor, - GladeSupportMask *mask) -{ - GString *string = g_string_new (NULL); - gchar *ret = NULL; - - glade_project_verify_adaptor (project, adaptor, NULL, - string, FALSE, TRUE, mask); - - /* there was a '\0' byte... */ - if (string->len > 0) - { - ret = string->str; - g_string_free (string, FALSE); - } - else - g_string_free (string, TRUE); - - - return ret; -} - - -/** - * glade_project_verify_project_for_ui: - * @project: A #GladeProject - * - * Checks the project and updates warning strings in the UI - */ -void -glade_project_verify_project_for_ui (GladeProject *project) -{ - GList *list; - GladeWidget *widget; - gchar *warning; - - /* Sync displayable info here */ - for (list = project->priv->objects; list; list = list->next) - { - widget = glade_widget_get_from_gobject (list->data); - - warning = glade_project_verify_widget_adaptor (project, widget->adaptor, NULL); - glade_widget_set_support_warning (widget, warning); - - if (warning) - g_free (warning); - - glade_project_verify_properties (widget); - } - - /* refresh palette if this is the active project */ - if (project == glade_app_get_project ()) - glade_palette_refresh (glade_app_get_palette ()); -} - -static void -glade_project_set_target_version (GladeProject *project, - const gchar *catalog, - gint major, - gint minor) -{ - - g_hash_table_insert (project->priv->target_versions_major, - g_strdup (catalog), - GINT_TO_POINTER (major)); - g_hash_table_insert (project->priv->target_versions_minor, - g_strdup (catalog), - GINT_TO_POINTER (minor)); - - glade_project_verify_project_for_ui (project); - -} - static void glade_project_init (GladeProject *project) { @@ -764,12 +608,8 @@ glade_project_init (GladeProject *project) priv->first_modification = NULL; priv->first_modification_is_na = FALSE; - priv->widget_names_allocator = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - (GDestroyNotify) glade_id_allocator_destroy); - - priv->widget_old_names = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free); + priv->toplevel_names = glade_name_context_new (); + priv->naming_policy = GLADE_POLICY_PROJECT_WIDE; priv->accel_group = NULL; @@ -801,6 +641,11 @@ glade_project_init (GladeProject *project) glade_catalog_get_major_version (catalog), glade_catalog_get_minor_version (catalog)); } + + priv->target_radios = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + priv->prefs_dialog = glade_project_build_prefs_dialog (project); + } static void @@ -1060,37 +905,9 @@ glade_project_class_init (GladeProjectClass *klass) } /******************************************************************* - API + Loading project code here *******************************************************************/ -static void -glade_project_set_readonly (GladeProject *project, gboolean readonly) -{ - g_assert (GLADE_IS_PROJECT (project)); - - if (project->priv->readonly != readonly) - { - project->priv->readonly = readonly; - g_object_notify (G_OBJECT (project), "read-only"); - } -} - -/** - * glade_project_get_readonly: - * @project: a #GladeProject - * - * Gets whether the project is read only or not - * - * Returns: TRUE if project is read only - */ -gboolean -glade_project_get_readonly (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - - return project->priv->readonly; -} - /** * glade_project_new: * @@ -1101,145 +918,9 @@ glade_project_get_readonly (GladeProject *project) GladeProject * glade_project_new (void) { - return g_object_new (GLADE_TYPE_PROJECT, NULL); -} - - -static void -glade_project_fix_object_props (GladeProject *project) -{ - GList *l, *ll; - GValue *value; - GladeWidget *gwidget; - GladeProperty *property; - gchar *txt; - - for (l = project->priv->objects; l; l = l->next) - { - gwidget = glade_widget_get_from_gobject (l->data); - - for (ll = gwidget->properties; ll; ll = ll->next) - { - property = GLADE_PROPERTY (ll->data); - - if (glade_property_class_is_object (property->klass, - project->priv->format) && - (txt = g_object_get_data (G_OBJECT (property), - "glade-loaded-object")) != NULL) - { - /* Parse the object list and set the property to it - * (this magicly works for both objects & object lists) - */ - value = glade_property_class_make_gvalue_from_string - (property->klass, txt, project); - - glade_property_set_value (property, value); - - g_value_unset (value); - g_free (value); - - g_object_set_data (G_OBJECT (property), - "glade-loaded-object", NULL); - } - } - } -} - - -static gchar * -glade_project_read_requires_from_comment (GladeXmlNode *comment, - gint *major, - gint *minor) -{ - gint maj, min; - gchar *value, buffer[256]; - gchar *required_lib = NULL; - - if (!glade_xml_node_is_comment (comment)) - return FALSE; - - value = glade_xml_get_content (comment); - if (value && !strncmp ("interface-requires", value, strlen ("interface-requires"))) - { - if (sscanf (value, "interface-requires %s %d.%d", buffer, &maj, &min) == 3) - { - if (major) *major = maj; - if (minor) *minor = min; - required_lib = g_strdup (buffer); - } - } - g_free (value); - - return required_lib; -} - - -static gboolean -glade_project_read_requires (GladeProject *project, - GladeXmlNode *root_node, - const gchar *path) -{ - - GString *string = g_string_new (NULL); - GladeXmlNode *node; - gchar *required_lib = NULL; - gboolean loadable = TRUE; - gint major, minor; - - for (node = glade_xml_node_get_children_with_comments (root_node); - node; node = glade_xml_node_next_with_comments (node)) - { - /* Skip non "requires" tags */ - if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_REQUIRES) || - (required_lib = - glade_project_read_requires_from_comment (node, &major, &minor)))) - continue; - - if (!required_lib) - { - required_lib = - glade_xml_get_property_string_required (node, GLADE_XML_TAG_LIB, - NULL); - glade_xml_get_property_version (node, GLADE_XML_TAG_VERSION, - &major, &minor); - } - - if (!required_lib) continue; - - /* Dont mention gtk+ as a required lib in - * the generated glade file - */ - if (!glade_catalog_is_loaded (required_lib)) - { - if (!loadable) - g_string_append (string, ", "); - - g_string_append (string, required_lib); - loadable = FALSE; - } - else - glade_project_set_target_version - (project, required_lib, major, minor); - - g_free (required_lib); - } - - if (!loadable) - glade_util_ui_message (glade_app_get_window(), - GLADE_UI_ERROR, NULL, - _("Failed to load %s.\n" - "The following required catalogs are unavailable: %s"), - path, string->str); - g_string_free (string, TRUE); - return loadable; -} - -static void -glade_project_read_comment (GladeProject *project, GladeXmlDoc *doc) -{ - /* TODO Write me !! Find out how to extract root level comments - * with libxml2 !!! - */ + GladeProject *project = g_object_new (GLADE_TYPE_PROJECT, NULL); + glade_project_preferences (project); + return project; } static GList * @@ -1444,57 +1125,252 @@ glade_project_is_generated_node (GladeProject *project, return generated; } -gboolean -glade_project_load_from_file (GladeProject *project, const gchar *path) + +/* Called when finishing loading a glade file to resolve object type properties + */ +static void +glade_project_fix_object_props (GladeProject *project) { - GladeXmlContext *context; - GladeXmlDoc *doc; - GladeXmlNode *root; - GladeXmlNode *node; - GladeWidget *widget; + GList *l, *ll; + GValue *value; + GladeWidget *gwidget; + GladeProperty *property; + gchar *txt; - project->priv->selection = NULL; - project->priv->objects = NULL; - project->priv->loading = TRUE; + for (l = project->priv->objects; l; l = l->next) + { + gwidget = glade_widget_get_from_gobject (l->data); - project->priv->path = glade_util_canonical_path (path); + for (ll = gwidget->properties; ll; ll = ll->next) + { + property = GLADE_PROPERTY (ll->data); - /* get the context & root node of the catalog file */ - if (!(context = - glade_xml_context_new_from_path (path, - NULL, - NULL))) - { - g_warning ("Couldn't open glade file [%s].", path); - return FALSE; + if (glade_property_class_is_object (property->klass, + project->priv->format) && + (txt = g_object_get_data (G_OBJECT (property), + "glade-loaded-object")) != NULL) + { + /* Parse the object list and set the property to it + * (this magicly works for both objects & object lists) + */ + value = glade_property_class_make_gvalue_from_string + (property->klass, txt, gwidget->project, gwidget); + + glade_property_set_value (property, value); + + g_value_unset (value); + g_free (value); + + g_object_set_data (G_OBJECT (property), + "glade-loaded-object", NULL); + } + } } +} - doc = glade_xml_context_get_doc (context); - root = glade_xml_doc_get_root (doc); +static gchar * +glade_project_read_requires_from_comment (GladeXmlNode *comment, + gint *major, + gint *minor) +{ + gint maj, min; + gchar *value, buffer[256]; + gchar *required_lib = NULL; - if (glade_xml_node_verify_silent (root, GLADE_XML_TAG_LIBGLADE_PROJECT)) - project->priv->format = GLADE_PROJECT_FORMAT_LIBGLADE; - else if (glade_xml_node_verify_silent (root, GLADE_XML_TAG_BUILDER_PROJECT)) - project->priv->format = GLADE_PROJECT_FORMAT_GTKBUILDER; - else - { - g_warning ("Couldnt determine project format, skipping %s", path); - glade_xml_context_free (context); - return FALSE; - } + if (!glade_xml_node_is_comment (comment)) + return NULL; - /* XXX Need to load project->priv->comment ! */ - glade_project_read_comment (project, doc); + value = glade_xml_get_content (comment); - if (glade_project_read_requires (project, root, path) == FALSE) + if (value && !strncmp (" interface-requires", value, strlen (" interface-requires"))) { - glade_xml_context_free (context); - return FALSE; + if (sscanf (value, " interface-requires %s %d.%d", buffer, &maj, &min) == 3) + { + if (major) *major = maj; + if (minor) *minor = min; + required_lib = g_strdup (buffer); + } } + g_free (value); - for (node = glade_xml_node_get_children (root); - node; node = glade_xml_node_next (node)) - { + return required_lib; +} + + +static gboolean +glade_project_read_requires (GladeProject *project, + GladeXmlNode *root_node, + const gchar *path) +{ + + GString *string = g_string_new (NULL); + GladeXmlNode *node; + gchar *required_lib = NULL; + gboolean loadable = TRUE; + gint major, minor; + + for (node = glade_xml_node_get_children_with_comments (root_node); + node; node = glade_xml_node_next_with_comments (node)) + { + /* Skip non "requires" tags */ + if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_REQUIRES) || + (required_lib = + glade_project_read_requires_from_comment (node, &major, &minor)))) + continue; + + if (!required_lib) + { + required_lib = + glade_xml_get_property_string_required (node, GLADE_XML_TAG_LIB, + NULL); + glade_xml_get_property_version (node, GLADE_XML_TAG_VERSION, + &major, &minor); + } + + if (!required_lib) continue; + + /* Dont mention gtk+ as a required lib in + * the generated glade file + */ + if (!glade_catalog_is_loaded (required_lib)) + { + if (!loadable) + g_string_append (string, ", "); + + g_string_append (string, required_lib); + loadable = FALSE; + } + else + glade_project_set_target_version + (project, required_lib, major, minor); + + g_free (required_lib); + } + + if (!loadable) + glade_util_ui_message (glade_app_get_window(), + GLADE_UI_ERROR, NULL, + _("Failed to load %s.\n" + "The following required catalogs are unavailable: %s"), + path, string->str); + g_string_free (string, TRUE); + return loadable; +} + + +static gboolean +glade_project_read_policy_from_comment (GladeXmlNode *comment, + GladeNamingPolicy *policy) +{ + gchar *value, buffer[256]; + gboolean loaded = FALSE; + + if (!glade_xml_node_is_comment (comment)) + return FALSE; + + value = glade_xml_get_content (comment); + if (value && !strncmp (" interface-naming-policy", value, strlen (" interface-naming-policy"))) + { + if (sscanf (value, " interface-naming-policy %s", buffer) == 1) + { + if (strcmp (buffer, "project-wide") == 0) + *policy = GLADE_POLICY_PROJECT_WIDE; + else + *policy = GLADE_POLICY_TOPLEVEL_CONTEXTUAL; + + loaded = TRUE; + } + } + g_free (value); + + return loaded; +} + + +static void +glade_project_read_naming_policy (GladeProject *project, + GladeXmlNode *root_node) +{ + /* A project file with no mention of a policy needs more lax rules + * (a new project has a project wide policy by default) + */ + GladeNamingPolicy policy = GLADE_POLICY_TOPLEVEL_CONTEXTUAL; + GladeXmlNode *node; + + for (node = glade_xml_node_get_children_with_comments (root_node); + node; node = glade_xml_node_next_with_comments (node)) + { + /* Skip non "requires" tags */ + if (!glade_project_read_policy_from_comment (node, &policy)) + continue; + } + + glade_project_set_naming_policy (project, policy, FALSE); +} + + +static void +glade_project_read_comment (GladeProject *project, GladeXmlDoc *doc) +{ + /* TODO Write me !! Find out how to extract root level comments + * with libxml2 !!! + */ +} + +gboolean +glade_project_load_from_file (GladeProject *project, const gchar *path) +{ + GladeXmlContext *context; + GladeXmlDoc *doc; + GladeXmlNode *root; + GladeXmlNode *node; + GladeWidget *widget; + + project->priv->selection = NULL; + project->priv->objects = NULL; + project->priv->loading = TRUE; + + project->priv->path = glade_util_canonical_path (path); + + /* get the context & root node of the catalog file */ + if (!(context = + glade_xml_context_new_from_path (path, + NULL, + NULL))) + { + g_warning ("Couldn't open glade file [%s].", path); + return FALSE; + } + + doc = glade_xml_context_get_doc (context); + root = glade_xml_doc_get_root (doc); + + if (glade_xml_node_verify_silent (root, GLADE_XML_TAG_LIBGLADE_PROJECT)) + project->priv->format = GLADE_PROJECT_FORMAT_LIBGLADE; + else if (glade_xml_node_verify_silent (root, GLADE_XML_TAG_BUILDER_PROJECT)) + project->priv->format = GLADE_PROJECT_FORMAT_GTKBUILDER; + else + { + g_warning ("Couldnt determine project format, skipping %s", path); + glade_xml_context_free (context); + return FALSE; + } + + /* XXX Need to load project->priv->comment ! */ + glade_project_read_comment (project, doc); + + + if (glade_project_read_requires (project, root, path) == FALSE) + { + glade_xml_context_free (context); + return FALSE; + } + + glade_project_read_naming_policy (project, root); + + for (node = glade_xml_node_get_children (root); + node; node = glade_xml_node_next (node)) + { /* Skip "requires" tags */ if (!glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET (project->priv->format))) @@ -1541,55 +1417,351 @@ glade_project_load_from_file (GladeProject *project, const gchar *path) } +/** + * glade_project_load: + * @path: + * + * Opens a project at the given path. + * + * Returns: a new #GladeProject for the opened project on success, %NULL on + * failure + */ +GladeProject * +glade_project_load (const gchar *path) +{ + GladeProject *project; + gboolean retval; + + g_return_val_if_fail (path != NULL, NULL); + + project = g_object_new (GLADE_TYPE_PROJECT, NULL); + + retval = glade_project_load_from_file (project, path); + + if (retval) + { + gchar *name, *title; + /* Update prefs dialogs here... */ + name = glade_project_get_name (project); + title = g_strdup_printf (_("%s preferences"), name); + gtk_window_set_title (GTK_WINDOW (project->priv->prefs_dialog), title); + g_free (title); + g_free (name); -static void -glade_project_verify_property (GladeProject *project, - GladeProperty *property, - const gchar *path_name, - GString *string, - gboolean forwidget) -{ - GladeWidgetAdaptor *adaptor; - gint target_major, target_minor; - gchar *catalog, *tooltip; + return project; + } + else + { + g_object_unref (project); + return NULL; + } +} - if (glade_property_original_default (property) && !forwidget) - return; +/******************************************************************* + Writing project code here + *******************************************************************/ - adaptor = GLADE_WIDGET_ADAPTOR (property->klass->origin_handle); +#define GLADE_XML_COMMENT "Generated with "PACKAGE_NAME + +static gchar * +glade_project_make_comment () +{ + time_t now = time (NULL); + gchar *comment; + comment = g_strdup_printf (GLADE_XML_COMMENT" "PACKAGE_VERSION" on %s", + ctime (&now)); + glade_util_replace (comment, '\n', ' '); - g_object_get (adaptor, "catalog", &catalog, NULL); - glade_project_target_version_for_adaptor (property->widget->project, adaptor, - &target_major, - &target_minor); + return comment; +} + +static void +glade_project_update_comment (GladeProject *project) +{ + gchar **lines, **l, *comment = NULL; - if (project->priv->format == GLADE_PROJECT_FORMAT_LIBGLADE && - property->klass->libglade_unsupported) + /* If project has no comment -> add the new one */ + if (project->priv->comment == NULL) { - if (forwidget) - glade_property_set_support_warning - (property, TRUE, _("This property is not supported in libglade format")); - else - /* translators: reffers to a property '%s' of widget '[%s]' - * introduced in toolkit version '%s %d.%d' */ - g_string_append_printf - (string, - property->klass->packing ? - _("[%s] Packing property '%s' of object class '%s' is not " - "supported in libglade format\n") : - _("[%s] Property '%s' of object class '%s' is not " - "supported in libglade format\n"), - path_name, - property->klass->name, - adaptor->title); + project->priv->comment = glade_project_make_comment (); + return; } - else if (project->priv->format == GLADE_PROJECT_FORMAT_GTKBUILDER && - property->klass->libglade_only) + + for (lines = l = g_strsplit (project->priv->comment, "\n", 0); *l; l++) { - if (forwidget) - glade_property_set_support_warning - (property, TRUE, _("This property is only supported in libglade format")); + gchar *start; + + /* Ignore leading spaces */ + for (start = *l; *start && g_ascii_isspace (*start); start++); + + if (g_str_has_prefix (start, GLADE_XML_COMMENT)) + { + /* This line was generated by glade -> updating... */ + g_free (*l); + *l = comment = glade_project_make_comment (); + } + } + + if (comment) + { + g_free (project->priv->comment); + project->priv->comment = g_strjoinv ("\n", lines); + } + + g_strfreev (lines); +} + +static void +glade_project_write_required_libs (GladeProject *project, + GladeXmlContext *context, + GladeXmlNode *root) +{ + GladeProjectFormat fmt; + GladeXmlNode *req_node; + GList *required, *list; + gint major, minor; + gchar *version; + + fmt = glade_project_get_format (project); + + if ((required = glade_project_required_libs (project)) != NULL) + { + for (list = required; list; list = list->next) + { + glade_project_get_target_version (project, (gchar *)list->data, + &major, &minor); + + version = g_strdup_printf ("%d.%d", major, minor); + + /* Write the standard requires tag */ + if (fmt == GLADE_PROJECT_FORMAT_GTKBUILDER || + (fmt == GLADE_PROJECT_FORMAT_LIBGLADE && + strcmp ("gtk+", (gchar *)list->data))) + { + if (GLADE_GTKBUILDER_HAS_VERSIONING (major, minor)) + { + req_node = glade_xml_node_new (context, GLADE_XML_TAG_REQUIRES); + glade_xml_node_append_child (root, req_node); + glade_xml_node_set_property_string (req_node, + GLADE_XML_TAG_LIB, + (gchar *)list->data); + } + else + { + gchar *comment = + g_strdup_printf (" interface-requires %s %s ", + (gchar *)list->data, version); + req_node = glade_xml_node_new_comment (context, comment); + glade_xml_node_append_child (root, req_node); + g_free (comment); + } + + if (fmt != GLADE_PROJECT_FORMAT_LIBGLADE) + glade_xml_node_set_property_string + (req_node, GLADE_XML_TAG_VERSION, version); + } + + /* Add extra metadata for libglade */ + if (fmt == GLADE_PROJECT_FORMAT_LIBGLADE) + { + gchar *comment = g_strdup_printf (" interface-requires %s %s ", + (gchar *)list->data, version); + req_node = glade_xml_node_new_comment (context, comment); + glade_xml_node_append_child (root, req_node); + g_free (comment); + } + g_free (version); + + } + g_list_foreach (required, (GFunc)g_free, NULL); + g_list_free (required); + } + +} + +static void +glade_project_write_naming_policy (GladeProject *project, + GladeXmlContext *context, + GladeXmlNode *root) +{ + GladeXmlNode *policy_node; + gchar *comment = g_strdup_printf (" interface-naming-policy %s ", + project->priv->naming_policy == GLADE_POLICY_PROJECT_WIDE ? + "project-wide" : "toplevel-contextual"); + + policy_node = glade_xml_node_new_comment (context, comment); + glade_xml_node_append_child (root, policy_node); + g_free (comment); +} + +static GladeXmlContext * +glade_project_write (GladeProject *project) +{ + GladeXmlContext *context; + GladeXmlDoc *doc; + GladeXmlNode *root, *comment_node; + GList *list; + + doc = glade_xml_doc_new (); + context = glade_xml_context_new (doc, NULL); + root = glade_xml_node_new (context, GLADE_XML_TAG_PROJECT (project->priv->format)); + glade_xml_doc_set_root (doc, root); + + glade_project_update_comment (project); +/* comment_node = glade_xml_node_new_comment (context, project->priv->comment); */ + + /* XXX Need to append this to the doc ! not the ROOT ! + glade_xml_node_append_child (root, comment_node); */ + + glade_project_write_required_libs (project, context, root); + + glade_project_write_naming_policy (project, context, root); + + /* Any automatically generated stuff goes here */ + glade_project_generate_nodes (project, context, root); + + for (list = project->priv->objects; list; list = list->next) + { + GladeWidget *widget; + + widget = glade_widget_get_from_gobject (list->data); + + /* + * Append toplevel widgets. Each widget then takes + * care of appending its children. + */ + if (widget->parent == NULL) + glade_widget_write (widget, context, root); + } + + return context; +} + +/** + * glade_project_save: + * @project: a #GladeProject + * @path: location to save glade file + * @error: an error from the G_FILE_ERROR domain. + * + * Saves @project to the given path. + * + * Returns: %TRUE on success, %FALSE on failure + */ +gboolean +glade_project_save (GladeProject *project, const gchar *path, GError **error) +{ + GladeXmlContext *context; + GladeXmlDoc *doc; + gchar *canonical_path; + gint ret; + + if (!glade_project_verify (project, TRUE)) + return FALSE; + + context = glade_project_write (project); + doc = glade_xml_context_get_doc (context); + ret = glade_xml_doc_save (doc, path); + glade_xml_context_destroy (context); + + canonical_path = glade_util_canonical_path (path); + g_assert (canonical_path); + + if (project->priv->path == NULL || + strcmp (canonical_path, project->priv->path)) + { + gchar *old_dir, *new_dir, *name, *title; + + if (project->priv->path) + { + old_dir = g_path_get_dirname (project->priv->path); + new_dir = g_path_get_dirname (canonical_path); + + glade_project_move_resources (project, + old_dir, + new_dir); + g_free (old_dir); + g_free (new_dir); + } + project->priv->path = (g_free (project->priv->path), + g_strdup (canonical_path)); + + /* Update prefs dialogs here... */ + name = glade_project_get_name (project); + title = g_strdup_printf (_("%s preferences"), name); + gtk_window_set_title (GTK_WINDOW (project->priv->prefs_dialog), title); + g_free (title); + g_free (name); + } + + glade_project_set_readonly (project, + !glade_util_file_is_writeable (project->priv->path)); + + project->priv->mtime = glade_util_get_file_mtime (project->priv->path, NULL); + + glade_project_set_modified (project, FALSE); + + if (project->priv->unsaved_number > 0) + { + glade_id_allocator_release (get_unsaved_number_allocator (), project->priv->unsaved_number); + project->priv->unsaved_number = 0; + } + + g_free (canonical_path); + + return ret > 0; +} + +/******************************************************************* + Verify code here (versioning, incompatability checks) + *******************************************************************/ +static void +glade_project_verify_property (GladeProject *project, + GladeProperty *property, + const gchar *path_name, + GString *string, + gboolean forwidget) +{ + GladeWidgetAdaptor *adaptor; + gint target_major, target_minor; + gchar *catalog, *tooltip; + + if (glade_property_original_default (property) && !forwidget) + return; + + adaptor = GLADE_WIDGET_ADAPTOR (property->klass->origin_handle); + + g_object_get (adaptor, "catalog", &catalog, NULL); + glade_project_target_version_for_adaptor (property->widget->project, adaptor, + &target_major, + &target_minor); + + if (project->priv->format == GLADE_PROJECT_FORMAT_LIBGLADE && + property->klass->libglade_unsupported) + { + if (forwidget) + glade_property_set_support_warning + (property, TRUE, _("This property is not supported in libglade format")); + else + /* translators: reffers to a property '%s' of widget '[%s]' + * introduced in toolkit version '%s %d.%d' */ + g_string_append_printf + (string, + property->klass->packing ? + _("[%s] Packing property '%s' of object class '%s' is not " + "supported in libglade format\n") : + _("[%s] Property '%s' of object class '%s' is not " + "supported in libglade format\n"), + path_name, + property->klass->name, + adaptor->title); + } + else if (project->priv->format == GLADE_PROJECT_FORMAT_GTKBUILDER && + property->klass->libglade_only) + { + if (forwidget) + glade_property_set_support_warning + (property, TRUE, _("This property is only supported in libglade format")); else /* translators: reffers to a property '%s' of widget '[%s]' * introduced in toolkit version '%s %d.%d' */ @@ -1829,147 +2001,500 @@ glade_project_verify (GladeProject *project, return ret; } - -/** - * glade_project_selection_changed: - * @project: a #GladeProject - * - * Causes @project to emit a "selection_changed" signal. - */ -void -glade_project_selection_changed (GladeProject *project) +static void +glade_project_target_version_for_adaptor (GladeProject *project, + GladeWidgetAdaptor *adaptor, + gint *major, + gint *minor) { - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_signal_emit (G_OBJECT (project), - glade_project_signals [SELECTION_CHANGED], - 0); + gchar *catalog = NULL; + + g_object_get (adaptor, "catalog", &catalog, NULL); + glade_project_get_target_version (project, catalog, major, minor); + g_free (catalog); } static void -glade_project_on_widget_notify (GladeWidget *widget, GParamSpec *arg, GladeProject *project) +glade_project_verify_adaptor (GladeProject *project, + GladeWidgetAdaptor *adaptor, + const gchar *path_name, + GString *string, + gboolean saving, + gboolean forwidget, + GladeSupportMask *mask) { - g_return_if_fail (GLADE_IS_WIDGET (widget)); - g_return_if_fail (GLADE_IS_PROJECT (project)); + GladeSupportMask support_mask = GLADE_SUPPORT_OK; + GladeWidgetAdaptor *adaptor_iter; + gint target_major, target_minor; + gchar *catalog = NULL; - switch (arg->name[0]) + for (adaptor_iter = adaptor; adaptor_iter; + adaptor_iter = glade_widget_adaptor_get_parent_adaptor (adaptor_iter)) { - case 'n': - if (strcmp (arg->name, "name") == 0) - { - const char *old_name = g_hash_table_lookup (project->priv->widget_old_names, widget); - glade_project_widget_name_changed (project, widget, old_name); - g_hash_table_insert (project->priv->widget_old_names, widget, g_strdup (glade_widget_get_name (widget))); - } - - case 'p': - if (strcmp (arg->name, "project") == 0) - glade_project_remove_object (project, glade_widget_get_object (widget)); - } -} + g_object_get (adaptor_iter, "catalog", &catalog, NULL); + glade_project_target_version_for_adaptor (project, adaptor_iter, + &target_major, + &target_minor); -static void -gp_sync_resources (GladeProject *project, - GladeProject *prev_project, - GladeWidget *gwidget, - gboolean remove) -{ - GList *prop_list, *l; - GladeProperty *property; - gchar *resource, *full_resource; + if (target_major < GWA_VERSION_SINCE_MAJOR (adaptor_iter) || + (target_major == GWA_VERSION_SINCE_MAJOR (adaptor_iter) && + target_minor < GWA_VERSION_SINCE_MINOR (adaptor_iter))) + { + if (forwidget) + { + /* translators: reffers to a widget + * introduced in toolkit version '%s %d.%d', + * and a project targeting toolkit verion '%s %d.%d' */ + g_string_append_printf + (string, + _("This widget was introduced in %s %d.%d " + "project targets %s %d.%d"), + catalog, + GWA_VERSION_SINCE_MAJOR (adaptor_iter), + GWA_VERSION_SINCE_MINOR (adaptor_iter), + catalog, target_major, target_minor); + } + else + /* translators: reffers to a widget '[%s]' + * introduced in toolkit version '%s %d.%d' */ + g_string_append_printf + (string, + _("[%s] Object class '%s' was introduced in %s %d.%d\n"), + path_name, adaptor_iter->title, catalog, + GWA_VERSION_SINCE_MAJOR (adaptor_iter), + GWA_VERSION_SINCE_MINOR (adaptor_iter)); - prop_list = g_list_copy (gwidget->properties); - prop_list = g_list_concat - (prop_list, g_list_copy (gwidget->packing_properties)); + support_mask |= GLADE_SUPPORT_MISMATCH; + } - for (l = prop_list; l; l = l->next) - { - property = l->data; - if (property->klass->resource) + if (project->priv->format == GLADE_PROJECT_FORMAT_GTKBUILDER && + GWA_LIBGLADE_ONLY (adaptor_iter)) { - GValue value = { 0, }; + if (forwidget) + { + if (string->len) + g_string_append (string, "\n"); + g_string_append_printf + (string, + _("This widget is only supported in libglade format")); + } + else + /* translators: reffers to a widget '[%s]' + * loaded from toolkit version '%s %d.%d' */ + g_string_append_printf + (string, + _("[%s] Object class '%s' from %s %d.%d " + "is only supported in libglade format\n"), + path_name, adaptor_iter->title, catalog, + target_major, target_minor); - if (remove) + support_mask |= GLADE_SUPPORT_LIBGLADE_ONLY; + } + else if (project->priv->format == GLADE_PROJECT_FORMAT_LIBGLADE && + GWA_LIBGLADE_UNSUPPORTED (adaptor_iter)) + { + if (forwidget) { - glade_project_set_resource (project, property, NULL); - continue; + if (string->len) + g_string_append (string, "\n"); + g_string_append_printf + (string, + _("This widget is not supported in libglade format")); } + else + /* translators: reffers to a widget '[%s]' + * loaded from toolkit version '%s %d.%d' */ + g_string_append_printf + (string, + _("[%s] Object class '%s' from %s %d.%d " + "is not supported in libglade format\n"), + path_name, adaptor_iter->title, catalog, + target_major, target_minor); - glade_property_get_value (property, &value); - - if ((resource = glade_widget_adaptor_string_from_value - (GLADE_WIDGET_ADAPTOR (property->klass->handle), - property->klass, &value, project->priv->format)) != NULL) + support_mask |= GLADE_SUPPORT_LIBGLADE_UNSUPPORTED; + } + + if (!saving && GWA_DEPRECATED (adaptor_iter)) + { + if (forwidget) { - full_resource = glade_project_resource_fullpath - (prev_project ? prev_project : project, resource); - - /* Use a full path here so that the current - * working directory isnt used. - */ - glade_project_set_resource (project, - property, - full_resource); - - g_free (full_resource); - g_free (resource); + if (string->len) + g_string_append (string, "\n"); + g_string_append_printf + (string, _("This widget is deprecated")); } - g_value_unset (&value); + else + /* translators: reffers to a widget '[%s]' + * loaded from toolkit version '%s %d.%d' */ + g_string_append_printf + (string, + _("[%s] Object class '%s' from %s %d.%d " + "is deprecated\n"), + path_name, adaptor_iter->title, catalog, + target_major, target_minor); + + support_mask |= GLADE_SUPPORT_DEPRECATED; } + g_free (catalog); } - g_list_free (prop_list); + if (mask) + *mask = support_mask; } -static void -glade_project_sync_resources_for_widget (GladeProject *project, - GladeProject *prev_project, - GladeWidget *gwidget, - gboolean remove) +/** + * glade_project_verify_widget_adaptor: + * @project: A #GladeProject + * @adaptor: the #GladeWidgetAdaptor to verify + * @mask: a return location for a #GladeSupportMask + * + * Checks the supported state of this widget adaptor + * and generates a string to show in the UI describing why. + * + * Returns: A newly allocated string + */ +gchar * +glade_project_verify_widget_adaptor (GladeProject *project, + GladeWidgetAdaptor *adaptor, + GladeSupportMask *mask) { - GList *children, *l; - GladeWidget *gchild; + GString *string = g_string_new (NULL); + gchar *ret = NULL; - children = glade_widget_adaptor_get_children - (gwidget->adaptor, gwidget->object); + glade_project_verify_adaptor (project, adaptor, NULL, + string, FALSE, TRUE, mask); - for (l = children; l; l = l->next) - if ((gchild = - glade_widget_get_from_gobject (l->data)) != NULL) - glade_project_sync_resources_for_widget - (project, prev_project, gchild, remove); - if (children) - g_list_free (children); + /* there was a '\0' byte... */ + if (string->len > 0) + { + ret = string->str; + g_string_free (string, FALSE); + } + else + g_string_free (string, TRUE); - gp_sync_resources (project, prev_project, gwidget, remove); + + return ret; } + /** - * glade_project_add_object: - * @project: the #GladeProject the widget is added to - * @old_project: the #GladeProject the widget was previously in - * (or %NULL for the clipboard) - * @object: the #GObject to add - * - * Adds an object to the project. + * glade_project_verify_project_for_ui: + * @project: A #GladeProject + * + * Checks the project and updates warning strings in the UI */ void -glade_project_add_object (GladeProject *project, - GladeProject *old_project, - GObject *object) +glade_project_verify_project_for_ui (GladeProject *project) { - GladeWidget *gwidget; - GList *list, *children; - static gint reentrancy_count = 0; + GList *list; + GladeWidget *widget; + gchar *warning; - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); + /* Sync displayable info here */ + for (list = project->priv->objects; list; list = list->next) + { + widget = glade_widget_get_from_gobject (list->data); - /* We don't list placeholders */ - if (GLADE_IS_PLACEHOLDER (object)) - return; + warning = glade_project_verify_widget_adaptor (project, widget->adaptor, NULL); + glade_widget_set_support_warning (widget, warning); - /* Only widgets accounted for in the catalog or widgets declared - * in the plugin with glade_widget_new_for_internal_child () are + if (warning) + g_free (warning); + + glade_project_verify_properties (widget); + } + + /* refresh palette if this is the active project */ + if (project == glade_app_get_project ()) + glade_palette_refresh (glade_app_get_palette ()); +} + +/******************************************************************* + Project object tracking code, name exclusivity, resources etc... + *******************************************************************/ +static GladeNameContext * +name_context_by_widget (GladeProject *project, + GladeWidget *gwidget) +{ + TopLevelInfo *tinfo; + GladeWidget *iter; + GList *list; + + iter = gwidget; + while (iter->parent) iter = iter->parent; + + for (list = project->priv->toplevels; list; list = list->next) + { + tinfo = list->data; + if (tinfo->toplevel == iter) + return tinfo->names; + } + return NULL; +} + +GladeWidget * +search_ancestry_by_name (GladeWidget *toplevel, const gchar *name) +{ + GladeWidget *widget = NULL, *iter; + GList *list, *children; + + if ((children = glade_widget_adaptor_get_children (toplevel->adaptor, + toplevel->object)) != NULL) + { + for (list = children; list; list = list->next) + if ((iter = glade_widget_get_from_gobject (list->data)) != NULL) + { + if (iter->name && strcmp (iter->name, name) == 0) + { + widget = iter; + break; + } + else if ((widget = search_ancestry_by_name (iter, name)) != NULL) + break; + } + + g_list_free (children); + } + return widget; +} + +/** + * glade_project_get_widget_by_name: + * @project: a #GladeProject + * @ancestor: The toplevel project object to search under + * @name: The user visible name of the widget we are looking for + * + * Searches under @ancestor in @project looking for a #GladeWidget named @name. + * + * Returns: a pointer to the widget, %NULL if the widget does not exist + */ +GladeWidget * +glade_project_get_widget_by_name (GladeProject *project, GladeWidget *ancestor, const gchar *name) +{ + GladeWidget *toplevel, *widget = NULL; + GList *list; + + g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); + g_return_val_if_fail (name != NULL, NULL); + + if (ancestor) + { + toplevel = glade_widget_get_toplevel (ancestor); + if ((widget = search_ancestry_by_name (toplevel, name)) != NULL) + return widget; + } + + /* Now try searching in only toplevel objects... */ + for (list = project->priv->objects; list; list = list->next) { + GladeWidget *widget; + + widget = glade_widget_get_from_gobject (list->data); + g_assert (widget->name); + if (widget->parent == NULL && strcmp (widget->name, name) == 0) + return widget; + } + + /* Finally resort to a project wide search. */ + for (list = project->priv->objects; list; list = list->next) { + GladeWidget *widget; + + widget = glade_widget_get_from_gobject (list->data); + g_return_val_if_fail (widget->name != NULL, NULL); + if (strcmp (widget->name, name) == 0) + return widget; + } + + return NULL; +} + +static void +glade_project_release_widget_name (GladeProject *project, GladeWidget *gwidget, const char *widget_name) +{ + GladeNameContext *context = NULL; + TopLevelInfo *tinfo = NULL; + GladeWidget *iter; + GList *list; + + /* Search by hand here since we need the tinfo to free... */ + iter = gwidget; + while (iter->parent) iter = iter->parent; + + for (list = project->priv->toplevels; list; list = list->next) + { + tinfo = list->data; + if (tinfo->toplevel == iter) + { + context = tinfo->names; + break; + } + } + + if (context) + glade_name_context_release_name (context, widget_name); + + if (!gwidget->parent) + { + glade_name_context_release_name (project->priv->toplevel_names, widget_name); + + if (context && glade_name_context_n_names (context) == 0) + { + glade_name_context_destroy (context); + g_free (tinfo); + project->priv->toplevels = g_list_remove (project->priv->toplevels, tinfo); + } + } +} + +/** + * glade_project_available_widget_name: + * @project: a #GladeProject + * @widget: the #GladeWidget intended to recieve a new name + * @name: base name of the widget to create + * + * Checks whether @name is an appropriate name for @widget. + * + * Returns: whether the name is available or not. + */ +gboolean +glade_project_available_widget_name (GladeProject *project, + GladeWidget *widget, + const gchar *name) +{ + GladeNameContext *context; + + g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); + g_return_val_if_fail (GLADE_IS_WIDGET (widget), FALSE); + g_return_val_if_fail (widget->project == project, FALSE); + + if (!name || !name[0]) + return FALSE; + + context = name_context_by_widget (project, widget); + g_assert (context); + + if (!widget->parent) + return (!glade_name_context_has_name (context, name) && + !glade_name_context_has_name (project->priv->toplevel_names, name)); + + return !glade_name_context_has_name (context, name); +} + +/** + * glade_project_new_widget_name: + * @project: a #GladeProject + * @widget: the #GladeWidget intended to recieve a new name + * @base_name: base name of the widget to create + * + * Creates a new name for a widget that doesn't collide with any of the names + * already in @project. This name will start with @base_name. + * + * Returns: a string containing the new name, %NULL if there is not enough + * memory for this string + */ +gchar * +glade_project_new_widget_name (GladeProject *project, + GladeWidget *widget, + const gchar *base_name) +{ + GladeNameContext *context; + gchar *name; + + g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); + g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL); + g_return_val_if_fail (widget->project == project, NULL); + g_return_val_if_fail (base_name && base_name[0], NULL); + + context = name_context_by_widget (project, widget); + + /* should use dual here to encourage unique names across the file... */ + if (context && widget->parent) + name = glade_name_context_new_name (context, base_name); + else if (context) + name = glade_name_context_dual_new_name (context, project->priv->toplevel_names, base_name); + else + name = glade_name_context_new_name (project->priv->toplevel_names, base_name); + + return name; +} + +/** + * glade_project_set_widget_name: + * @project: a #GladeProject + * @widget: the #GladeWidget to set a name on + * @name: the name to set. + * + * Sets @name on @widget in @project, if @name is not + * available then a new name will be used. + */ +void +glade_project_set_widget_name (GladeProject *project, + GladeWidget *widget, + const gchar *name) +{ + GladeNameContext *context = NULL; + gchar *new_name; + + g_return_if_fail (GLADE_IS_PROJECT (project)); + g_return_if_fail (GLADE_IS_WIDGET (widget)); + g_return_if_fail (widget->project == project); + g_return_if_fail (name && name[0]); + + if (strcmp (name, widget->name) == 0) + return; + + /* Police the widget name */ + if (!glade_project_available_widget_name (project, widget, name)) + new_name = glade_project_new_widget_name (project, widget, name); + else + new_name = g_strdup (name); + + + /* Add to name context(s) */ + context = name_context_by_widget (project, widget); + g_assert (context); + glade_name_context_add_name (context, new_name); + if (!widget->parent) + glade_name_context_add_name (project->priv->toplevel_names, new_name); + + /* Release old name and set new widget name */ + glade_project_release_widget_name (project, widget, widget->name); + glade_widget_set_name (widget, new_name); + + g_free (new_name); +} + + + +/** + * glade_project_add_object: + * @project: the #GladeProject the widget is added to + * @old_project: the #GladeProject the widget was previously in + * (or %NULL for the clipboard) + * @object: the #GObject to add + * + * Adds an object to the project. + */ +void +glade_project_add_object (GladeProject *project, + GladeProject *old_project, + GObject *object) +{ + GladeNameContext *context; + GladeWidget *gwidget; + GList *list, *children; + static gint reentrancy_count = 0; + gchar *name; + + g_return_if_fail (GLADE_IS_PROJECT (project)); + g_return_if_fail (G_IS_OBJECT (object)); + + /* We don't list placeholders */ + if (GLADE_IS_PLACEHOLDER (object)) + return; + + /* Only widgets accounted for in the catalog or widgets declared + * in the plugin with glade_widget_new_for_internal_child () are * usefull in the project. */ if ((gwidget = glade_widget_get_from_gobject (object)) == NULL) @@ -1979,14 +2504,29 @@ glade_project_add_object (GladeProject *project, if (glade_project_has_object (project, object)) return; - /* Police widget names here (just rename them on the way in the project) - */ - if (glade_project_get_widget_by_name (project, gwidget->name) != NULL) - { - gchar *name = glade_project_new_widget_name (project, gwidget->name); + /* Create a name context for newly added toplevels... */ + if (!gwidget->parent) + { + TopLevelInfo *tinfo = g_new0 (TopLevelInfo, 1); + tinfo->toplevel = gwidget; + tinfo->names = glade_name_context_new (); + project->priv->toplevels = g_list_prepend (project->priv->toplevels, tinfo); + } + + /* Make sure we have an exclusive name first... */ + if (!glade_project_available_widget_name (project, gwidget, gwidget->name)) + { + name = glade_project_new_widget_name (project, gwidget, gwidget->name); glade_widget_set_name (gwidget, name); g_free (name); } + + /* Now lock down the widget name. */ + context = name_context_by_widget (project, gwidget); + g_assert (context); + glade_name_context_add_name (context, gwidget->name); + if (!gwidget->parent) + glade_name_context_add_name (project->priv->toplevel_names, gwidget->name); /* Code body starts here */ reentrancy_count++; @@ -2001,11 +2541,6 @@ glade_project_add_object (GladeProject *project, } glade_widget_set_project (gwidget, (gpointer)project); - g_hash_table_insert (project->priv->widget_old_names, - gwidget, g_strdup (glade_widget_get_name (gwidget))); - - g_signal_connect (G_OBJECT (gwidget), "notify", - (GCallback) glade_project_on_widget_notify, project); project->priv->objects = g_list_append (project->priv->objects, g_object_ref (object)); @@ -2039,58 +2574,6 @@ glade_project_has_object (GladeProject *project, GObject *object) return (g_list_find (project->priv->objects, object)) != NULL; } -/** - * glade_project_release_widget_name: - * @project: a #GladeProject - * @glade_widget: - * @widget_name: - * - * TODO: Write me - */ -static void -glade_project_release_widget_name (GladeProject *project, GladeWidget *glade_widget, const char *widget_name) -{ - const char *first_number = widget_name; - char *end_number; - char *base_widget_name; - GladeIDAllocator *id_allocator; - gunichar ch; - int id; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_WIDGET (glade_widget)); - - do - { - ch = g_utf8_get_char (first_number); - - if (ch == 0 || g_unichar_isdigit (ch)) - break; - - first_number = g_utf8_next_char (first_number); - } - while (TRUE); - - if (ch == 0) - return; - - base_widget_name = g_strdup (widget_name); - *(base_widget_name + (first_number - widget_name)) = 0; - - id_allocator = g_hash_table_lookup (project->priv->widget_names_allocator, base_widget_name); - if (id_allocator == NULL) - goto lblend; - - id = (int) strtol (first_number, &end_number, 10); - if (*end_number != 0) - goto lblend; - - glade_id_allocator_release (id_allocator, id); - - lblend: - g_hash_table_remove (project->priv->widget_old_names, glade_widget); - g_free (base_widget_name); -} /** * glade_project_remove_object: @@ -2151,133 +2634,192 @@ glade_project_remove_object (GladeProject *project, GObject *object) glade_project_sync_resources_for_widget (project, NULL, gwidget, TRUE); } -/** - * glade_project_widget_name_changed: - * @project: a #GladeProject - * @widget: a #GladeWidget - * @old_name: - * - * TODO: write me - */ -void -glade_project_widget_name_changed (GladeProject *project, GladeWidget *widget, const char *old_name) +static void +adjust_naming_policy (GladeProject *project, gboolean use_command) { - GladeWidget *iter; - GList *l; - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (GLADE_IS_WIDGET (widget)); - - glade_project_release_widget_name (project, widget, old_name); + GList *list; + GladeWidget *widget; + TopLevelInfo *tinfo; + GladeNameContext *context; - /* Police widget names here (just rename them on the way in the project) - */ - for (l = project->priv->objects; l; l = l->next) + if (project->priv->naming_policy == GLADE_POLICY_PROJECT_WIDE) { - iter = glade_widget_get_from_gobject (l->data); - - if (widget != iter && - !strcmp (widget->name, iter->name)) - { - gchar *name = glade_project_new_widget_name (project, widget->name); - glade_widget_set_name (widget, name); - g_free (name); - } - - } - - g_signal_emit (G_OBJECT (project), - glade_project_signals [WIDGET_NAME_CHANGED], - 0, - widget); -} + for (list = project->priv->objects; list; list = list->next) + { + widget = glade_widget_get_from_gobject (list->data); + + if (!widget->parent) + continue; -/** - * glade_project_get_widget_by_name: - * @project: a #GladeProject - * @name: The user visible name of the widget we are looking for - * - * Searches through @project looking for a #GladeWidget named @name. - * - * Returns: a pointer to the widget, %NULL if the widget does not exist - */ -GladeWidget * -glade_project_get_widget_by_name (GladeProject *project, const gchar *name) -{ - GList *list; + if (!glade_name_context_has_name (project->priv->toplevel_names, widget->name)) + glade_name_context_add_name (project->priv->toplevel_names, widget->name); + else + { + gchar *new_name = glade_name_context_new_name (project->priv->toplevel_names, + widget->name); - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - g_return_val_if_fail (name != NULL, NULL); + if (use_command) + glade_command_set_name (widget, new_name); + else + glade_widget_set_name (widget, new_name); - for (list = project->priv->objects; list; list = list->next) { - GladeWidget *widget; + glade_name_context_add_name (project->priv->toplevel_names, new_name); + g_free (new_name); + } + } - widget = glade_widget_get_from_gobject (list->data); - g_return_val_if_fail (widget->name != NULL, NULL); - if (strcmp (widget->name, name) == 0) - return widget; + for (list = project->priv->toplevels; list; list = list->next) + { + tinfo = list->data; + glade_name_context_destroy (tinfo->names); + g_free (tinfo); + } + project->priv->toplevels = + (g_list_free (project->priv->toplevels), NULL); + } + else + { + /* First add toplevel names */ + for (list = project->priv->objects; list; list = list->next) + { + widget = glade_widget_get_from_gobject (list->data); + + if (!widget->parent) + { + TopLevelInfo *tinfo = g_new0 (TopLevelInfo, 1); + tinfo->toplevel = widget; + tinfo->names = glade_name_context_new (); + project->priv->toplevels = g_list_prepend (project->priv->toplevels, tinfo); + + glade_name_context_add_name (tinfo->names, widget->name); + } + } + + /* Now add child names */ + for (list = project->priv->objects; list; list = list->next) + { + widget = glade_widget_get_from_gobject (list->data); + + if (widget->parent) + { + context = name_context_by_widget (project, widget); + glade_name_context_add_name (context, widget->name); + glade_name_context_release_name (project->priv->toplevel_names, widget->name); + } + } } - return NULL; } -/** - * glade_project_new_widget_name: - * @project: a #GladeProject - * @base_name: base name of the widget to create - * - * Creates a new name for a widget that doesn't collide with any of the names - * already in @project. This name will start with @base_name. - * - * Returns: a string containing the new name, %NULL if there is not enough - * memory for this string - */ -char * -glade_project_new_widget_name (GladeProject *project, const char *base_name) + +/******************************************************************* + Remaining stubs and api + *******************************************************************/ +static void +glade_project_get_target_version (GladeProject *project, + const gchar *catalog, + gint *major, + gint *minor) { - GladeIDAllocator *id_allocator; - const gchar *number; - gchar *name = NULL, *freeme = NULL; - guint i = 1; - - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); + *major = GPOINTER_TO_INT + (g_hash_table_lookup (project->priv->target_versions_major, + catalog)); + *minor = GPOINTER_TO_INT + (g_hash_table_lookup (project->priv->target_versions_minor, + catalog)); +} - number = base_name + strlen (base_name); +static void +glade_project_set_target_version (GladeProject *project, + const gchar *catalog, + gint major, + gint minor) +{ + GladeTargetableVersion *version; + GSList *radios, *list; + GtkWidget *radio; - while (number > base_name && g_ascii_isdigit (number[-1])) - --number; + g_hash_table_insert (project->priv->target_versions_major, + g_strdup (catalog), + GINT_TO_POINTER (major)); + g_hash_table_insert (project->priv->target_versions_minor, + g_strdup (catalog), + GINT_TO_POINTER (minor)); - if (*number) - { - freeme = g_strndup (base_name, number - base_name); - base_name = freeme; - } - - id_allocator = g_hash_table_lookup (project->priv->widget_names_allocator, base_name); - if (id_allocator == NULL) + /* Update prefs dialog from here... */ + if (project->priv->target_radios && + (radios = g_hash_table_lookup (project->priv->target_radios, catalog)) != NULL) { - id_allocator = glade_id_allocator_new (); - g_hash_table_insert (project->priv->widget_names_allocator, - g_strdup (base_name), id_allocator); + for (list = radios; list; list = list->next) + g_signal_handlers_block_by_func (G_OBJECT (list->data), + G_CALLBACK (target_button_clicked), + project); + + for (list = radios; list; list = list->next) + { + radio = list->data; + + version = g_object_get_data (G_OBJECT (radio), "version"); + if (version->major == major && + version->minor == minor) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); + break; + } + } + + for (list = radios; list; list = list->next) + g_signal_handlers_unblock_by_func (G_OBJECT (list->data), + G_CALLBACK (target_button_clicked), + project); } - while (TRUE) + glade_project_verify_project_for_ui (project); +} + +static void +glade_project_set_readonly (GladeProject *project, gboolean readonly) +{ + g_assert (GLADE_IS_PROJECT (project)); + + if (project->priv->readonly != readonly) { - i = glade_id_allocator_allocate (id_allocator); - name = g_strdup_printf ("%s%u", base_name, i); + project->priv->readonly = readonly; + g_object_notify (G_OBJECT (project), "read-only"); + } +} + +/** + * glade_project_get_readonly: + * @project: a #GladeProject + * + * Gets whether the project is read only or not + * + * Returns: TRUE if project is read only + */ +gboolean +glade_project_get_readonly (GladeProject *project) +{ + g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE); - /* ok, there is no widget with this name, so return the name */ - if (glade_project_get_widget_by_name (project, name) == NULL) - return name; + return project->priv->readonly; +} - /* we already have a widget with this name, so free the name and - * try again with another number */ - g_free (name); - i++; - } - g_free (freeme); - return name; +/** + * glade_project_selection_changed: + * @project: a #GladeProject + * + * Causes @project to emit a "selection_changed" signal. + */ +void +glade_project_selection_changed (GladeProject *project) +{ + g_return_if_fail (GLADE_IS_PROJECT (project)); + g_signal_emit (G_OBJECT (project), + glade_project_signals [SELECTION_CHANGED], + 0); } static void @@ -2386,406 +2928,118 @@ glade_project_selection_remove (GladeProject *project, * a selection change * * Adds @object to the selection chain of @project - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_add (GladeProject *project, - GObject *object, - gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - g_return_if_fail (g_list_find (project->priv->objects, object) != NULL); - - if (glade_project_is_selected (project, object) == FALSE) - { - if (GTK_IS_WIDGET (object)) - glade_util_add_selection (GTK_WIDGET (object)); - if (project->priv->selection == NULL) - glade_project_set_has_selection (project, TRUE); - project->priv->selection = g_list_prepend (project->priv->selection, object); - if (emit_signal) - glade_project_selection_changed (project); - } -} - -/** - * glade_project_selection_set: - * @project: a #GladeProject - * @object: a #GObject in @project - * @emit_signal: whether or not to emit a signal - * indicating a selection change - * - * Set the selection in @project to @object - * - * If @emit_signal is %TRUE, calls glade_project_selection_changed(). - */ -void -glade_project_selection_set (GladeProject *project, - GObject *object, - gboolean emit_signal) -{ - g_return_if_fail (GLADE_IS_PROJECT (project)); - g_return_if_fail (G_IS_OBJECT (object)); - - if (g_list_find (project->priv->objects, object) == NULL) - return; - - if (project->priv->selection == NULL) - glade_project_set_has_selection (project, TRUE); - - if (glade_project_is_selected (project, object) == FALSE || - g_list_length (project->priv->selection) != 1) - { - glade_project_selection_clear (project, FALSE); - glade_project_selection_add (project, object, emit_signal); - } -} - -/** - * glade_project_selection_get: - * @project: a #GladeProject - * - * Returns: a #GList containing the #GtkWidget items currently selected in - * @project - */ -GList * -glade_project_selection_get (GladeProject *project) -{ - g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - - return project->priv->selection; -} - -/** - * glade_project_required_libs: - * @project: a #GladeProject - * - * Returns: a #GList of allocated strings which are the names - * of the required catalogs for this project - */ -GList * -glade_project_required_libs (GladeProject *project) -{ - GList *required = NULL, *l, *ll; - GladeWidget *gwidget; - gboolean listed; - - for (l = project->priv->objects; l; l = l->next) - { - gchar *catalog = NULL; - - gwidget = glade_widget_get_from_gobject (l->data); - g_assert (gwidget); - - g_object_get (gwidget->adaptor, "catalog", &catalog, NULL); - - if (catalog) - { - listed = FALSE; - for (ll = required; ll; ll = ll->next) - if (!strcmp ((gchar *)ll->data, catalog)) - { - listed = TRUE; - break; - } - - if (!listed) - required = g_list_prepend (required, catalog); - } - } - return g_list_reverse (required); -} - -#define GLADE_XML_COMMENT "Generated with "PACKAGE_NAME - -static gchar * -glade_project_make_comment () -{ - time_t now = time (NULL); - gchar *comment; - comment = g_strdup_printf (GLADE_XML_COMMENT" "PACKAGE_VERSION" on %s", - ctime (&now)); - glade_util_replace (comment, '\n', ' '); - - return comment; -} - -static void -glade_project_update_comment (GladeProject *project) -{ - gchar **lines, **l, *comment = NULL; - - /* If project has no comment -> add the new one */ - if (project->priv->comment == NULL) - { - project->priv->comment = glade_project_make_comment (); - return; - } - - for (lines = l = g_strsplit (project->priv->comment, "\n", 0); *l; l++) - { - gchar *start; - - /* Ignore leading spaces */ - for (start = *l; *start && g_ascii_isspace (*start); start++); - - if (g_str_has_prefix (start, GLADE_XML_COMMENT)) - { - /* This line was generated by glade -> updating... */ - g_free (*l); - *l = comment = glade_project_make_comment (); - } - } - - if (comment) - { - g_free (project->priv->comment); - project->priv->comment = g_strjoinv ("\n", lines); - } - - g_strfreev (lines); -} - -static void -glade_project_write_required_libs (GladeProject *project, - GladeXmlContext *context, - GladeXmlNode *root) -{ - GladeProjectFormat fmt; - GladeXmlNode *req_node; - GList *required, *list; - gint major, minor; - gchar *version; - - fmt = glade_project_get_format (project); - - if ((required = glade_project_required_libs (project)) != NULL) - { - for (list = required; list; list = list->next) - { - glade_project_get_target_version (project, (gchar *)list->data, - &major, &minor); - - version = g_strdup_printf ("%d.%d", major, minor); - - /* Write the standard requires tag */ - if (fmt == GLADE_PROJECT_FORMAT_GTKBUILDER || - (fmt == GLADE_PROJECT_FORMAT_LIBGLADE && - strcmp ("gtk+", (gchar *)list->data))) - { - if (GLADE_GTKBUILDER_HAS_VERSIONING (major, minor)) - { - req_node = glade_xml_node_new (context, GLADE_XML_TAG_REQUIRES); - glade_xml_node_append_child (root, req_node); - glade_xml_node_set_property_string (req_node, - GLADE_XML_TAG_LIB, - (gchar *)list->data); - } - else - { - gchar *comment = - g_strdup_printf ("interface-requires %s %s", - (gchar *)list->data, version); - req_node = glade_xml_node_new_comment (context, comment); - glade_xml_node_append_child (root, req_node); - g_free (comment); - } - - if (fmt != GLADE_PROJECT_FORMAT_LIBGLADE) - glade_xml_node_set_property_string - (req_node, GLADE_XML_TAG_VERSION, version); - } - - /* Add extra metadata for libglade */ - if (fmt == GLADE_PROJECT_FORMAT_LIBGLADE) - { - gchar *comment = g_strdup_printf ("interface-requires %s %s", - (gchar *)list->data, version); - req_node = glade_xml_node_new_comment (context, comment); - glade_xml_node_append_child (root, req_node); - g_free (comment); - } - g_free (version); - - } - g_list_foreach (required, (GFunc)g_free, NULL); - g_list_free (required); - } - -} - -static GladeXmlContext * -glade_project_write (GladeProject *project) -{ - GladeXmlContext *context; - GladeXmlDoc *doc; - GladeXmlNode *root, *comment_node; - GList *list; - - doc = glade_xml_doc_new (); - context = glade_xml_context_new (doc, NULL); - root = glade_xml_node_new (context, GLADE_XML_TAG_PROJECT (project->priv->format)); - glade_xml_doc_set_root (doc, root); - - glade_project_update_comment (project); -/* comment_node = glade_xml_node_new_comment (context, project->priv->comment); */ - - /* XXX Need to append this to the doc ! not the ROOT ! - glade_xml_node_append_child (root, comment_node); */ - - glade_project_write_required_libs (project, context, root); - - /* Any automatically generated stuff goes here */ - glade_project_generate_nodes (project, context, root); - - for (list = project->priv->objects; list; list = list->next) - { - GladeWidget *widget; - - widget = glade_widget_get_from_gobject (list->data); - - /* - * Append toplevel widgets. Each widget then takes - * care of appending its children. - */ - if (widget->parent == NULL) - glade_widget_write (widget, context, root); - } - - return context; -} - -/** - * glade_project_load: - * @path: - * - * Opens a project at the given path. - * - * Returns: a new #GladeProject for the opened project on success, %NULL on - * failure + * + * If @emit_signal is %TRUE, calls glade_project_selection_changed(). */ -GladeProject * -glade_project_load (const gchar *path) +void +glade_project_selection_add (GladeProject *project, + GObject *object, + gboolean emit_signal) { - GladeProject *project; - gboolean retval; - - g_return_val_if_fail (path != NULL, NULL); - - project = glade_project_new (); - - retval = glade_project_load_from_file (project, path); - - if (retval) - { - return project; - } - else + g_return_if_fail (GLADE_IS_PROJECT (project)); + g_return_if_fail (G_IS_OBJECT (object)); + g_return_if_fail (g_list_find (project->priv->objects, object) != NULL); + + if (glade_project_is_selected (project, object) == FALSE) { - g_object_unref (project); - return NULL; + if (GTK_IS_WIDGET (object)) + glade_util_add_selection (GTK_WIDGET (object)); + if (project->priv->selection == NULL) + glade_project_set_has_selection (project, TRUE); + project->priv->selection = g_list_prepend (project->priv->selection, object); + if (emit_signal) + glade_project_selection_changed (project); } } -static void -glade_project_move_resources (GladeProject *project, - const gchar *old_dir, - const gchar *new_dir) +/** + * glade_project_selection_set: + * @project: a #GladeProject + * @object: a #GObject in @project + * @emit_signal: whether or not to emit a signal + * indicating a selection change + * + * Set the selection in @project to @object + * + * If @emit_signal is %TRUE, calls glade_project_selection_changed(). + */ +void +glade_project_selection_set (GladeProject *project, + GObject *object, + gboolean emit_signal) { - GList *list, *resources; - gchar *old_name, *new_name; + g_return_if_fail (GLADE_IS_PROJECT (project)); + g_return_if_fail (G_IS_OBJECT (object)); - if (old_dir == NULL || /* <-- Cant help you :( */ - new_dir == NULL) /* <-- Unlikely */ + if (g_list_find (project->priv->objects, object) == NULL) return; - if ((resources = /* Nothing to do here */ - glade_project_list_resources (project)) == NULL) - return; - - for (list = resources; list; list = list->next) + if (project->priv->selection == NULL) + glade_project_set_has_selection (project, TRUE); + + if (glade_project_is_selected (project, object) == FALSE || + g_list_length (project->priv->selection) != 1) { - old_name = g_build_filename - (old_dir, (gchar *)list->data, NULL); - new_name = g_build_filename - (new_dir, (gchar *)list->data, NULL); - glade_util_copy_file (old_name, new_name); - g_free (old_name); - g_free (new_name); + glade_project_selection_clear (project, FALSE); + glade_project_selection_add (project, object, emit_signal); } - g_list_free (resources); -} +} /** - * glade_project_save: + * glade_project_selection_get: * @project: a #GladeProject - * @path: location to save glade file - * @error: an error from the G_FILE_ERROR domain. - * - * Saves @project to the given path. * - * Returns: %TRUE on success, %FALSE on failure + * Returns: a #GList containing the #GtkWidget items currently selected in + * @project */ -gboolean -glade_project_save (GladeProject *project, const gchar *path, GError **error) +GList * +glade_project_selection_get (GladeProject *project) { - GladeXmlContext *context; - GladeXmlDoc *doc; - gchar *canonical_path; - gint ret; + g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL); - if (!glade_project_verify (project, TRUE)) - return FALSE; + return project->priv->selection; +} - context = glade_project_write (project); - doc = glade_xml_context_get_doc (context); - ret = glade_xml_doc_save (doc, path); - glade_xml_context_destroy (context); +/** + * glade_project_required_libs: + * @project: a #GladeProject + * + * Returns: a #GList of allocated strings which are the names + * of the required catalogs for this project + */ +GList * +glade_project_required_libs (GladeProject *project) +{ + GList *required = NULL, *l, *ll; + GladeWidget *gwidget; + gboolean listed; - canonical_path = glade_util_canonical_path (path); - g_assert (canonical_path); + for (l = project->priv->objects; l; l = l->next) + { + gchar *catalog = NULL; - if (project->priv->path == NULL || - strcmp (canonical_path, project->priv->path)) - { - gchar *old_dir, *new_dir; + gwidget = glade_widget_get_from_gobject (l->data); + g_assert (gwidget); - if (project->priv->path) + g_object_get (gwidget->adaptor, "catalog", &catalog, NULL); + + if (catalog) { - old_dir = g_path_get_dirname (project->priv->path); - new_dir = g_path_get_dirname (canonical_path); + listed = FALSE; + for (ll = required; ll; ll = ll->next) + if (!strcmp ((gchar *)ll->data, catalog)) + { + listed = TRUE; + break; + } - glade_project_move_resources (project, - old_dir, - new_dir); - g_free (old_dir); - g_free (new_dir); + if (!listed) + required = g_list_prepend (required, catalog); } - project->priv->path = (g_free (project->priv->path), - g_strdup (canonical_path)); } - - glade_project_set_readonly (project, - !glade_util_file_is_writeable (project->priv->path)); - - project->priv->mtime = glade_util_get_file_mtime (project->priv->path, NULL); - - glade_project_set_modified (project, FALSE); - - if (project->priv->unsaved_number > 0) - { - glade_id_allocator_release (get_unsaved_number_allocator (), project->priv->unsaved_number); - project->priv->unsaved_number = 0; - } - - g_free (canonical_path); - - return ret > 0; + return g_list_reverse (required); } - /** * glade_project_undo: * @project: a #GladeProject @@ -3005,47 +3259,154 @@ glade_project_redo_items (GladeProject *project) G_CALLBACK (redo_item_activated), project); } - - return menu; + + return menu; +} + + +void +glade_project_reset_path (GladeProject *project) +{ + g_return_if_fail (GLADE_IS_PROJECT (project)); + project->priv->path = (g_free (project->priv->path), NULL); +} + +/** + * glade_project_set_accel_group: + * @project: A #GladeProject + * @accel_group: The @GtkAccelGroup + * + * Set @accel_group to every top level widget in @project. + */ +void +glade_project_set_accel_group (GladeProject *project, GtkAccelGroup *accel_group) +{ + GList *objects; + + g_return_if_fail (GLADE_IS_PROJECT (project) && GTK_IS_ACCEL_GROUP (accel_group)); + + objects = project->priv->objects; + while (objects) + { + if(GTK_IS_WINDOW (objects->data)) + { + if (project->priv->accel_group) + gtk_window_remove_accel_group (GTK_WINDOW (objects->data), project->priv->accel_group); + + gtk_window_add_accel_group (GTK_WINDOW (objects->data), accel_group); + } + + objects = objects->next; + } + + project->priv->accel_group = accel_group; +} + + + + +static void +gp_sync_resources (GladeProject *project, + GladeProject *prev_project, + GladeWidget *gwidget, + gboolean remove) +{ + GList *prop_list, *l; + GladeProperty *property; + gchar *resource, *full_resource; + + prop_list = g_list_copy (gwidget->properties); + prop_list = g_list_concat + (prop_list, g_list_copy (gwidget->packing_properties)); + + for (l = prop_list; l; l = l->next) + { + property = l->data; + if (property->klass->resource) + { + GValue value = { 0, }; + + if (remove) + { + glade_project_set_resource (project, property, NULL); + continue; + } + + glade_property_get_value (property, &value); + + if ((resource = glade_widget_adaptor_string_from_value + (GLADE_WIDGET_ADAPTOR (property->klass->handle), + property->klass, &value, project->priv->format)) != NULL) + { + full_resource = glade_project_resource_fullpath + (prev_project ? prev_project : project, resource); + + /* Use a full path here so that the current + * working directory isnt used. + */ + glade_project_set_resource (project, + property, + full_resource); + + g_free (full_resource); + g_free (resource); + } + g_value_unset (&value); + } + } + g_list_free (prop_list); } - -void -glade_project_reset_path (GladeProject *project) +static void +glade_project_sync_resources_for_widget (GladeProject *project, + GladeProject *prev_project, + GladeWidget *gwidget, + gboolean remove) { - g_return_if_fail (GLADE_IS_PROJECT (project)); - project->priv->path = (g_free (project->priv->path), NULL); + GList *children, *l; + GladeWidget *gchild; + + children = glade_widget_adaptor_get_children + (gwidget->adaptor, gwidget->object); + + for (l = children; l; l = l->next) + if ((gchild = + glade_widget_get_from_gobject (l->data)) != NULL) + glade_project_sync_resources_for_widget + (project, prev_project, gchild, remove); + if (children) + g_list_free (children); + + gp_sync_resources (project, prev_project, gwidget, remove); } -/** - * glade_project_set_accel_group: - * @project: A #GladeProject - * @accel_group: The @GtkAccelGroup - * - * Set @accel_group to every top level widget in @project. - */ -void -glade_project_set_accel_group (GladeProject *project, GtkAccelGroup *accel_group) +static void +glade_project_move_resources (GladeProject *project, + const gchar *old_dir, + const gchar *new_dir) { - GList *objects; + GList *list, *resources; + gchar *old_name, *new_name; - g_return_if_fail (GLADE_IS_PROJECT (project) && GTK_IS_ACCEL_GROUP (accel_group)); - - objects = project->priv->objects; - while (objects) - { - if(GTK_IS_WINDOW (objects->data)) - { - if (project->priv->accel_group) - gtk_window_remove_accel_group (GTK_WINDOW (objects->data), project->priv->accel_group); - - gtk_window_add_accel_group (GTK_WINDOW (objects->data), accel_group); - } + if (old_dir == NULL || /* <-- Cant help you :( */ + new_dir == NULL) /* <-- Unlikely */ + return; - objects = objects->next; - } + if ((resources = /* Nothing to do here */ + glade_project_list_resources (project)) == NULL) + return; - project->priv->accel_group = accel_group; + for (list = resources; list; list = list->next) + { + old_name = g_build_filename + (old_dir, (gchar *)list->data, NULL); + new_name = g_build_filename + (new_dir, (gchar *)list->data, NULL); + glade_util_copy_file (old_name, new_name); + g_free (old_name); + g_free (new_name); + } + g_list_free (resources); } /** @@ -3288,6 +3649,48 @@ glade_project_get_modified (GladeProject *project) return project->priv->modified; } +void +glade_project_set_naming_policy (GladeProject *project, + GladeNamingPolicy policy, + gboolean use_command) +{ + g_return_if_fail (GLADE_IS_PROJECT (project)); + + if (project->priv->naming_policy != policy) + { + project->priv->naming_policy = policy; + + adjust_naming_policy (project, use_command); + + /* Update the toggle button in the prefs dialog here: */ + g_signal_handlers_block_by_func (project->priv->project_wide_radio, + G_CALLBACK (policy_project_wide_button_clicked), project); + g_signal_handlers_block_by_func (project->priv->toplevel_contextual_radio, + G_CALLBACK (policy_toplevel_contextual_button_clicked), project); + + if (policy == GLADE_POLICY_PROJECT_WIDE) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->project_wide_radio), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->toplevel_contextual_radio), TRUE); + + g_signal_handlers_unblock_by_func (project->priv->project_wide_radio, + G_CALLBACK (policy_project_wide_button_clicked), project); + g_signal_handlers_unblock_by_func (project->priv->toplevel_contextual_radio, + G_CALLBACK (policy_toplevel_contextual_button_clicked), project); + + } + + +} + +GladeNamingPolicy +glade_project_get_naming_policy (GladeProject *project) +{ + g_return_val_if_fail (GLADE_IS_PROJECT (project), -1); + + return project->priv->naming_policy; +} + /** * glade_project_set_format: @@ -3307,6 +3710,23 @@ glade_project_set_format (GladeProject *project, GladeProjectFormat format) project->priv->format = format; g_object_notify (G_OBJECT (project), "format"); glade_project_verify_project_for_ui (project); + + /* Update the toggle button in the prefs dialog here: */ + g_signal_handlers_block_by_func (project->priv->glade_radio, + G_CALLBACK (format_libglade_button_clicked), project); + g_signal_handlers_block_by_func (project->priv->builder_radio, + G_CALLBACK (format_builder_button_clicked), project); + + if (format == GLADE_PROJECT_FORMAT_GTKBUILDER) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->builder_radio), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->glade_radio), TRUE); + + g_signal_handlers_unblock_by_func (project->priv->glade_radio, + G_CALLBACK (format_libglade_button_clicked), project); + g_signal_handlers_unblock_by_func (project->priv->builder_radio, + G_CALLBACK (format_builder_button_clicked), project); + } } @@ -3333,6 +3753,20 @@ format_builder_button_clicked (GtkWidget *widget, glade_command_set_project_format (project, GLADE_PROJECT_FORMAT_GTKBUILDER); } +static void +policy_project_wide_button_clicked (GtkWidget *widget, + GladeProject *project) +{ + glade_command_set_project_naming_policy (project, GLADE_POLICY_PROJECT_WIDE); +} + +static void +policy_toplevel_contextual_button_clicked (GtkWidget *widget, + GladeProject *project) +{ + glade_command_set_project_naming_policy (project, GLADE_POLICY_TOPLEVEL_CONTEXTUAL); +} + static void target_button_clicked (GtkWidget *widget, GladeProject *project) @@ -3369,20 +3803,123 @@ glade_project_build_prefs_box (GladeProject *project) { GtkWidget *main_box, *button; GtkWidget *vbox, *hbox, *frame; - GtkWidget *glade_radio, *builder_radio, *target_radio, *active_radio; + GtkWidget *target_radio, *active_radio; GtkWidget *label, *alignment; GList *list, *targets; - gchar *string = g_strdup_printf ("%s", _("File format")); + gchar *string; + GtkWidget *main_frame, *main_alignment; + GtkSizeGroup *sizegroup1 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL), + *sizegroup2 = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + string = g_strdup_printf ("%s", _("Set options in your project")); + main_frame = gtk_frame_new (NULL); + main_alignment = gtk_alignment_new (0.5F, 0.5F, 0.8F, 0.8F); main_box = gtk_vbox_new (FALSE, 0); + gtk_alignment_set_padding (GTK_ALIGNMENT (main_alignment), 12, 0, 4, 0); + + label = gtk_label_new (string); + g_free (string); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + + gtk_frame_set_label_widget (GTK_FRAME (main_frame), label); + gtk_container_add (GTK_CONTAINER (main_alignment), main_box); + gtk_container_add (GTK_CONTAINER (main_frame), main_alignment); + + + /* Project format */ + string = g_strdup_printf ("%s", _("File format")); + frame = gtk_frame_new (NULL); + hbox = gtk_hbox_new (FALSE, 0); + alignment = gtk_alignment_new (0.5F, 0.5F, 0.8F, 0.8F); + + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 8, 0, 12, 0); + + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + + label = gtk_label_new (string); + g_free (string); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + + project->priv->glade_radio = gtk_radio_button_new_with_label (NULL, "Libglade"); + project->priv->builder_radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (project->priv->glade_radio), "GtkBuilder"); + + gtk_size_group_add_widget (sizegroup1, project->priv->builder_radio); + gtk_size_group_add_widget (sizegroup2, project->priv->glade_radio); + + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_container_add (GTK_CONTAINER (alignment), hbox); + gtk_container_add (GTK_CONTAINER (frame), alignment); + + gtk_box_pack_start (GTK_BOX (hbox), project->priv->builder_radio, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (hbox), project->priv->glade_radio, TRUE, TRUE, 2); + + gtk_box_pack_start (GTK_BOX (main_box), frame, TRUE, TRUE, 2); + + + if (glade_project_get_format (project) == GLADE_PROJECT_FORMAT_GTKBUILDER) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->builder_radio), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->glade_radio), TRUE); + + g_signal_connect (G_OBJECT (project->priv->glade_radio), "clicked", + G_CALLBACK (format_libglade_button_clicked), project); + + g_signal_connect (G_OBJECT (project->priv->builder_radio), "clicked", + G_CALLBACK (format_builder_button_clicked), project); + + + /* Naming policy format */ + string = g_strdup_printf ("%s", _("Object names are unique:")); + frame = gtk_frame_new (NULL); + hbox = gtk_hbox_new (FALSE, 0); + alignment = gtk_alignment_new (0.5F, 0.5F, 0.8F, 0.8F); + + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 8, 0, 12, 0); + + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + + label = gtk_label_new (string); + g_free (string); + gtk_label_set_use_markup (GTK_LABEL (label), TRUE); + + project->priv->project_wide_radio = gtk_radio_button_new_with_label (NULL, _("within the project")); + project->priv->toplevel_contextual_radio = gtk_radio_button_new_with_label_from_widget + (GTK_RADIO_BUTTON (project->priv->project_wide_radio), _("inside toplevels")); + + gtk_size_group_add_widget (sizegroup1, project->priv->project_wide_radio); + gtk_size_group_add_widget (sizegroup2, project->priv->toplevel_contextual_radio); + + gtk_box_pack_start (GTK_BOX (hbox), project->priv->project_wide_radio, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (hbox), project->priv->toplevel_contextual_radio, TRUE, TRUE, 2); + + gtk_frame_set_label_widget (GTK_FRAME (frame), label); + gtk_container_add (GTK_CONTAINER (alignment), hbox); + gtk_container_add (GTK_CONTAINER (frame), alignment); + + gtk_box_pack_start (GTK_BOX (main_box), frame, TRUE, TRUE, 6); + + if (project->priv->naming_policy == GLADE_POLICY_PROJECT_WIDE) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->project_wide_radio), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (project->priv->toplevel_contextual_radio), TRUE); + + g_signal_connect (G_OBJECT (project->priv->project_wide_radio), "clicked", + G_CALLBACK (policy_project_wide_button_clicked), project); + + g_signal_connect (G_OBJECT (project->priv->toplevel_contextual_radio), "clicked", + G_CALLBACK (policy_toplevel_contextual_button_clicked), project); + + /* Target versions */ - string = g_strdup_printf ("%s", _("Target Versions:")); + string = g_strdup_printf ("%s", _("Target versions:")); frame = gtk_frame_new (NULL); vbox = gtk_vbox_new (FALSE, 0); alignment = gtk_alignment_new (0.5F, 0.5F, 1.0F, 1.0F); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 2, 0, 12, 0); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 8, 0, 12, 0); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); @@ -3394,7 +3931,7 @@ glade_project_build_prefs_box (GladeProject *project) gtk_container_add (GTK_CONTAINER (alignment), vbox); gtk_container_add (GTK_CONTAINER (frame), alignment); - gtk_box_pack_start (GTK_BOX (main_box), frame, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (main_box), frame, TRUE, TRUE, 6); /* Add stuff to vbox */ for (list = glade_app_get_catalogs (); list; list = list->next) @@ -3455,105 +3992,50 @@ glade_project_build_prefs_box (GladeProject *project) } if (active_radio) + { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (active_radio), TRUE); + g_hash_table_insert (project->priv->target_radios, + g_strdup (glade_catalog_get_name (catalog)), + gtk_radio_button_get_group (GTK_RADIO_BUTTON (active_radio))); + } else g_warning ("Corrupt catalog versions"); - gtk_box_pack_end (GTK_BOX (vbox), hbox, TRUE, TRUE, 2); - + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 2); } - /* Project format */ - string = g_strdup_printf ("%s", _("File format")); - frame = gtk_frame_new (NULL); - hbox = gtk_hbox_new (FALSE, 0); - alignment = gtk_alignment_new (0.5F, 0.5F, 0.8F, 0.8F); - - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 2, 0, 12, 0); - - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); - - label = gtk_label_new (string); - g_free (string); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - - glade_radio = gtk_radio_button_new_with_label (NULL, "Libglade"); - builder_radio = gtk_radio_button_new_with_label_from_widget - (GTK_RADIO_BUTTON (glade_radio), "GtkBuilder"); - - if (glade_project_get_format (project) == GLADE_PROJECT_FORMAT_GTKBUILDER) - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (builder_radio), TRUE); - else - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (glade_radio), TRUE); - - g_signal_connect (G_OBJECT (glade_radio), "clicked", - G_CALLBACK (format_libglade_button_clicked), project); - - g_signal_connect (G_OBJECT (builder_radio), "clicked", - G_CALLBACK (format_builder_button_clicked), project); - - gtk_box_pack_start (GTK_BOX (hbox), builder_radio, TRUE, TRUE, 2); - gtk_box_pack_start (GTK_BOX (hbox), glade_radio, TRUE, TRUE, 2); - - gtk_frame_set_label_widget (GTK_FRAME (frame), label); - gtk_container_add (GTK_CONTAINER (alignment), hbox); - gtk_container_add (GTK_CONTAINER (frame), alignment); - - gtk_box_pack_start (GTK_BOX (main_box), frame, TRUE, TRUE, 2); - - /* Run verify */ - string = g_strdup_printf ("%s", _("Verify versions and deprecations:")); - frame = gtk_frame_new (NULL); - alignment = gtk_alignment_new (0.0F, 0.5F, 0.0F, 0.0F); - gtk_container_set_border_width (GTK_CONTAINER (alignment), 4); - + hbox = gtk_hbox_new (FALSE, 2); button = gtk_button_new_from_stock (GTK_STOCK_EXECUTE); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (verify_clicked), project); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 2, 4, 12, 0); - - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + label = gtk_label_new (_("Verify versions and deprecations:")); - label = gtk_label_new (string); - g_free (string); - gtk_label_set_use_markup (GTK_LABEL (label), TRUE); - - gtk_frame_set_label_widget (GTK_FRAME (frame), label); - gtk_container_add (GTK_CONTAINER (alignment), button); - gtk_container_add (GTK_CONTAINER (frame), alignment); + gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 4); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 4); - gtk_box_pack_start (GTK_BOX (main_box), frame, FALSE, FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 4); + gtk_widget_show_all (main_frame); - gtk_widget_show_all (main_box); - - return main_box; + return main_frame; } -/** - * glade_project_preferences: - * @project: A #GladeProject - * - * Runs a preferences dialog for @project. - */ -void -glade_project_preferences (GladeProject *project) +static GtkWidget * +glade_project_build_prefs_dialog (GladeProject *project) { - GtkWidget *dialog = NULL; - GtkWidget *widget; + GtkWidget *widget, *dialog; gchar *title, *name; - - g_return_if_fail (GLADE_IS_PROJECT (project)); - name = glade_project_get_name (project); - title = g_strdup_printf ("%s preferences", name); + title = g_strdup_printf (_("%s preferences"), name); dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (glade_app_get_window ()), GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, + GTK_RESPONSE_ACCEPT, NULL); g_free (title); g_free (name); @@ -3567,13 +4049,34 @@ glade_project_preferences (GladeProject *project) gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + /* HIG spacings */ + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); /* 2 * 5 + 2 = 12 */ + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->action_area), 6); + /* Were explicitly destroying it anyway */ g_signal_connect (G_OBJECT (dialog), "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL); - gtk_window_present (GTK_WINDOW (dialog)); + /* Only one action, used to "close" the dialog */ + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (gtk_widget_hide), NULL); + + return dialog; +} + +/** + * glade_project_preferences: + * @project: A #GladeProject + * + * Runs a preferences dialog for @project. + */ +void +glade_project_preferences (GladeProject *project) +{ + g_return_if_fail (GLADE_IS_PROJECT (project)); - gtk_dialog_run (GTK_DIALOG (dialog)); - gtk_widget_destroy (dialog); + gtk_window_present (GTK_WINDOW (project->priv->prefs_dialog)); } diff --git a/gladeui/glade-project.h b/gladeui/glade-project.h index afbffd36..904d1562 100644 --- a/gladeui/glade-project.h +++ b/gladeui/glade-project.h @@ -22,11 +22,11 @@ typedef struct _GladeProjectClass GladeProjectClass; typedef enum { - GLADE_SUPPORT_OK = 0x00, - GLADE_SUPPORT_DEPRECATED = 0x01, - GLADE_SUPPORT_MISMATCH = 0x02, - GLADE_SUPPORT_LIBGLADE_UNSUPPORTED = 0x04, - GLADE_SUPPORT_LIBGLADE_ONLY = 0x08 + GLADE_SUPPORT_OK = 0, + GLADE_SUPPORT_DEPRECATED = (0x01 << 0), + GLADE_SUPPORT_MISMATCH = (0x01 << 1), + GLADE_SUPPORT_LIBGLADE_UNSUPPORTED = (0x01 << 2), + GLADE_SUPPORT_LIBGLADE_ONLY = (0x01 << 3) } GladeSupportMask; struct _GladeProject @@ -111,16 +111,26 @@ void glade_project_add_object (GladeProject *project, GladeProject *old_project, GObject *object); -void glade_project_remove_object (GladeProject *project, GObject *object); +void glade_project_remove_object (GladeProject *project, + GObject *object); + +gboolean glade_project_has_object (GladeProject *project, + GObject *object); -gboolean glade_project_has_object (GladeProject *project, GObject *object); +GladeWidget *glade_project_get_widget_by_name (GladeProject *project, + GladeWidget *ancestor, + const gchar *name); -GladeWidget *glade_project_get_widget_by_name (GladeProject *project, const char *name); +void glade_project_set_widget_name (GladeProject *project, + GladeWidget *widget, + const gchar *name); -char *glade_project_new_widget_name (GladeProject *project, const char *base_name); +gchar *glade_project_new_widget_name (GladeProject *project, + GladeWidget *widget, + const gchar *base_name); -void glade_project_widget_name_changed (GladeProject *project, GladeWidget *widget, - const char *old_name); +gboolean glade_project_available_widget_name (GladeProject *project, GladeWidget *widget, + const gchar *name); /* Selection */ @@ -187,8 +197,13 @@ void glade_project_verify_project_for_ui (GladeProject *project); gboolean glade_project_is_loaded_factory_file (GladeProject *project, const gchar *stock_id); -GList *glade_project_required_libs (GladeProject *project); +GList *glade_project_required_libs (GladeProject *project); + +void glade_project_set_naming_policy (GladeProject *project, + GladeNamingPolicy policy, + gboolean use_command); +GladeNamingPolicy glade_project_get_naming_policy (GladeProject *project); G_END_DECLS diff --git a/gladeui/glade-property-class.c b/gladeui/glade-property-class.c index 2463ebbe..90002370 100644 --- a/gladeui/glade-property-class.c +++ b/gladeui/glade-property-class.c @@ -85,6 +85,7 @@ glade_property_class_new (gpointer handle) property_class->save = TRUE; property_class->save_always = FALSE; property_class->ignore = FALSE; + property_class->needs_sync = FALSE; property_class->resource = FALSE; property_class->themed_icon = FALSE; property_class->translatable = FALSE; @@ -628,7 +629,8 @@ glade_property_class_make_enum_from_string (GType type, const char *string) static GObject * glade_property_class_make_object_from_string (GladePropertyClass *property_class, const gchar *string, - GladeProject *project) + GladeProject *project, + GladeWidget *widget) { GObject *object = NULL; gchar *fullpath; @@ -668,7 +670,7 @@ glade_property_class_make_object_from_string (GladePropertyClass *property_class g_free (fullpath); } - if (glade_project_get_format (project) == GLADE_PROJECT_FORMAT_LIBGLADE && + if (project && glade_project_get_format (project) == GLADE_PROJECT_FORMAT_LIBGLADE && property_class->pspec->value_type == GTK_TYPE_ADJUSTMENT) { gdouble value, lower, upper, step_increment, page_increment, page_size; @@ -687,7 +689,7 @@ glade_property_class_make_object_from_string (GladePropertyClass *property_class { GladeWidget *gwidget; if ((gwidget = glade_project_get_widget_by_name - (project, string)) != NULL) + (project, widget, string)) != NULL) object = gwidget->object; } @@ -697,7 +699,8 @@ glade_property_class_make_object_from_string (GladePropertyClass *property_class static GList * glade_property_class_make_objects_from_string (GladePropertyClass *property_class, const gchar *string, - GladeProject *project) + GladeProject *project, + GladeWidget *widget) { GList *objects = NULL; GObject *object; @@ -709,7 +712,7 @@ glade_property_class_make_objects_from_string (GladePropertyClass *property_clas for (i = 0; split[i]; i++) { if ((object = glade_property_class_make_object_from_string - (property_class, split[i], project)) != NULL) + (property_class, split[i], project, widget)) != NULL) objects = g_list_prepend (objects, object); } g_strfreev (split); @@ -721,8 +724,7 @@ glade_property_class_make_objects_from_string (GladePropertyClass *property_clas * glade_property_class_make_gvalue_from_string: * @property_class: A #GladePropertyClass * @string: a string representation of this property - * @project: the glade project that the associated property - * belongs to. + * @widget: the #GladeWidget that the associated property belongs to. * * Returns: A #GValue created based on the @property_class * and @string criteria. @@ -730,7 +732,8 @@ glade_property_class_make_objects_from_string (GladePropertyClass *property_clas GValue * glade_property_class_make_gvalue_from_string (GladePropertyClass *property_class, const gchar *string, - GladeProject *project) + GladeProject *project, + GladeWidget *widget) { GValue *value = g_new0 (GValue, 1); gchar **strv, *fullpath; @@ -831,13 +834,13 @@ glade_property_class_make_gvalue_from_string (GladePropertyClass *property_class else if (G_IS_PARAM_SPEC_OBJECT(property_class->pspec)) { GObject *object = glade_property_class_make_object_from_string - (property_class, string, project); + (property_class, string, project, widget); g_value_set_object (value, object); } else if (GLADE_IS_PARAM_SPEC_OBJECTS (property_class->pspec)) { GList *objects = glade_property_class_make_objects_from_string - (property_class, string, project); + (property_class, string, project, widget); g_value_set_boxed (value, objects); } else @@ -1459,7 +1462,7 @@ glade_property_class_update_from_node (GladeXmlNode *node, g_value_unset (klass->def); g_free (klass->def); } - klass->def = glade_property_class_make_gvalue_from_string (klass, buf, NULL); + klass->def = glade_property_class_make_gvalue_from_string (klass, buf, NULL, NULL); g_free (buf); } @@ -1519,6 +1522,7 @@ glade_property_class_update_from_node (GladeXmlNode *node, klass->save = glade_xml_get_property_boolean (node, GLADE_TAG_SAVE, klass->save); klass->visible = glade_xml_get_property_boolean (node, GLADE_TAG_VISIBLE, klass->visible); klass->ignore = glade_xml_get_property_boolean (node, GLADE_TAG_IGNORE, klass->ignore); + klass->needs_sync = glade_xml_get_property_boolean (node, GLADE_TAG_NEEDS_SYNC, klass->needs_sync); klass->resource = glade_xml_get_property_boolean (node, GLADE_TAG_RESOURCE, klass->resource); klass->themed_icon = glade_xml_get_property_boolean (node, GLADE_TAG_THEMED_ICON, klass->themed_icon); klass->weight = glade_xml_get_property_double (node, GLADE_TAG_WEIGHT, klass->weight); @@ -1651,6 +1655,8 @@ glade_property_class_compare (GladePropertyClass *klass, if (G_VALUE_HOLDS_BOXED (value1)) { gchar *val1, *val2; + + /* This has got to change... */ val1 = glade_widget_adaptor_string_from_value (klass->handle, klass, value1, fmt); val2 = glade_widget_adaptor_string_from_value (klass->handle, klass, value2, fmt); @@ -1662,6 +1668,9 @@ glade_property_class_compare (GladePropertyClass *klass, g_free (val1); g_free (val2); + + /* boxed always changed... XXX */ + return -1; } else { diff --git a/gladeui/glade-property-class.h b/gladeui/glade-property-class.h index fd3c6a06..73903a52 100644 --- a/gladeui/glade-property-class.h +++ b/gladeui/glade-property-class.h @@ -133,6 +133,12 @@ struct _GladePropertyClass * changes, or load values from the object. */ + gboolean needs_sync; /* Virtual properties need to be synchronized after object + * creation, some properties that are not virtual also need + * handling from the backend, if "needs-sync" is true then + * this property will by synced with virtual properties. + */ + gboolean is_modified; /* If true, this property_class has been "modified" from the * the standard property by a xml file. */ @@ -197,7 +203,8 @@ gboolean glade_property_class_is_object (GladePropertyC GValue *glade_property_class_make_gvalue_from_string (GladePropertyClass *property_class, const gchar *string, - GladeProject *project); + GladeProject *project, + GladeWidget *widget); gchar *glade_property_class_make_string_from_gvalue (GladePropertyClass *property_class, const GValue *value, diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c index be24fc52..ec4a4467 100644 --- a/gladeui/glade-property.c +++ b/gladeui/glade-property.c @@ -1038,8 +1038,7 @@ glade_property_read (GladeProperty *property, if (fmt == GLADE_PROJECT_FORMAT_GTKBUILDER) { gboolean is_loaded_value = - glade_project_is_loaded_factory_file - (property->widget->project, value); + glade_project_is_loaded_factory_file (project, value); if (property->klass->factory_stock_id && is_loaded_value) search_name = property->klass->factory_stock_id; @@ -1085,7 +1084,7 @@ glade_property_read (GladeProperty *property, } gvalue = glade_property_class_make_gvalue_from_string - (property->klass, value, project); + (property->klass, value, project, property->widget); if (property) { diff --git a/gladeui/glade-utils.c b/gladeui/glade-utils.c index f67e14e2..e82461c0 100644 --- a/gladeui/glade-utils.c +++ b/gladeui/glade-utils.c @@ -1943,7 +1943,7 @@ glade_utils_enum_value_from_string (GType enum_type, const gchar *strval) gint value = 0; GValue *gvalue; - if ((gvalue = glade_utils_value_from_string (enum_type, strval, NULL)) != NULL) + if ((gvalue = glade_utils_value_from_string (enum_type, strval, NULL, NULL)) != NULL) { value = g_value_get_enum (gvalue); g_value_unset (gvalue); @@ -2075,6 +2075,8 @@ pclass_from_gtype (GType type) * @type: a #GType to convert with * @string: the string to convert * @project: the #GladeProject to look for formats of object names when needed + * @widget: if the value is a gobject, this #GladeWidget will be used to look + * for an object in the same widget tree. * * Allocates and sets a #GValue of type @type * set to @string (using glade conversion routines) @@ -2084,7 +2086,8 @@ pclass_from_gtype (GType type) GValue * glade_utils_value_from_string (GType type, const gchar *string, - GladeProject *project) + GladeProject *project, + GladeWidget *widget) { GladePropertyClass *pclass; @@ -2092,7 +2095,7 @@ glade_utils_value_from_string (GType type, g_return_val_if_fail (string != NULL, NULL); if ((pclass = pclass_from_gtype (type)) != NULL) - return glade_property_class_make_gvalue_from_string (pclass, string, project); + return glade_property_class_make_gvalue_from_string (pclass, string, project, widget); return NULL; } diff --git a/gladeui/glade-utils.h b/gladeui/glade-utils.h index cf47bd85..fe05abd3 100644 --- a/gladeui/glade-utils.h +++ b/gladeui/glade-utils.h @@ -139,12 +139,16 @@ gchar *glade_utils_enum_string_from_value (GType enum_type, gint valu GValue *glade_utils_value_from_string (GType type, const gchar *string, - GladeProject *project); + GladeProject *project, + GladeWidget *widget); + gchar *glade_utils_string_from_value (const GValue *value, GladeProject *project); GtkListStore *glade_utils_liststore_from_enum_type (GType enum_type, gboolean include_empty); + + G_END_DECLS #endif /* __GLADE_UTILS_H__ */ diff --git a/gladeui/glade-widget.c b/gladeui/glade-widget.c index 84b8c820..dead8c2a 100644 --- a/gladeui/glade-widget.c +++ b/gladeui/glade-widget.c @@ -619,7 +619,7 @@ glade_widget_sync_custom_props (GladeWidget *widget) { GladeProperty *prop = GLADE_PROPERTY(l->data); - if (prop->klass->virt) + if (prop->klass->virt || prop->klass->needs_sync) glade_property_sync (prop); } @@ -662,6 +662,7 @@ glade_widget_constructor (GType type, { gwidget->name = glade_project_new_widget_name (gwidget->project, + gwidget, name_base); g_free (name_base); } @@ -671,7 +672,7 @@ glade_widget_constructor (GType type, } else if (gwidget->project) gwidget->name = glade_project_new_widget_name - (GLADE_PROJECT (gwidget->project), + (gwidget->project, gwidget, gwidget->adaptor->generic_name); else gwidget->name = @@ -1228,7 +1229,8 @@ glade_widget_set_default_packing_properties (GladeWidget *container, value = glade_property_class_make_gvalue_from_string (property_class, def, - child->project); + child->project, + child); glade_widget_child_set_property (container, child, property_class->id, value); @@ -2377,15 +2379,8 @@ void glade_widget_set_name (GladeWidget *widget, const gchar *name) { g_return_if_fail (GLADE_IS_WIDGET (widget)); - if (widget->name != name) { - - if (widget->project && - glade_project_get_widget_by_name (widget->project, name)) - { - /* print a warning ? */ - return; - } - + if (widget->name != name) + { if (widget->name) g_free (widget->name); diff --git a/gladeui/glade-xml-utils.h b/gladeui/glade-xml-utils.h index 2633100a..228839d8 100644 --- a/gladeui/glade-xml-utils.h +++ b/gladeui/glade-xml-utils.h @@ -36,6 +36,12 @@ typedef enum } GladeProjectFormat; +typedef enum { + GLADE_POLICY_PROJECT_WIDE = 0, /* widget names are unique throughout the project */ + GLADE_POLICY_TOPLEVEL_CONTEXTUAL /* toplevel names are unique, and widgets inside a toplevel */ +} GladeNamingPolicy; + + #define GLADE_XML_TAG_PROJECT(type) \ ((type == GLADE_PROJECT_FORMAT_LIBGLADE) ? \ GLADE_XML_TAG_LIBGLADE_PROJECT : GLADE_XML_TAG_BUILDER_PROJECT) @@ -108,6 +114,7 @@ typedef enum #define GLADE_TAG_PARENTLESS_WIDGET "parentless-widget" #define GLADE_TAG_DISABLED "disabled" #define GLADE_TAG_CONSTRUCT_ONLY "construct-only" +#define GLADE_TAG_NEEDS_SYNC "needs-sync" #define GLADE_TAG_DEFAULT_PALETTE_STATE "default-palette-state" #define GLADE_TAG_PROJECT_CONVERT_FUNCTION "project-convert-function" #define GLADE_TAG_REPLACE_CHILD_FUNCTION "replace-child-function" diff --git a/plugins/gnome/glade-gnome.c b/plugins/gnome/glade-gnome.c index 66b0e3b5..0aa68629 100644 --- a/plugins/gnome/glade-gnome.c +++ b/plugins/gnome/glade-gnome.c @@ -597,7 +597,7 @@ glade_gnome_dps_set_color_common (GObject *object, property_name); color = glade_property_class_make_gvalue_from_string (prop->klass, - color_str, NULL); + color_str, NULL, NULL); if (color) glade_property_set_value (prop, color); } diff --git a/plugins/gtk+/glade-column-types.c b/plugins/gtk+/glade-column-types.c index 15c2d974..ba62f2ed 100644 --- a/plugins/gtk+/glade-column-types.c +++ b/plugins/gtk+/glade-column-types.c @@ -223,106 +223,31 @@ glade_eprop_column_types_finalize (GObject *object) } static void -eprop_reload_value (GladeEPropColumnTypes *eprop_types) +eprop_column_append (GladeEditorProperty *eprop, + GType type, + const gchar *column_name) { - GladeEditorProperty *eprop = GLADE_EDITOR_PROPERTY (eprop_types); - GtkTreeModel *model = GTK_TREE_MODEL (eprop_types->store); - GValue value = {0, }; - GtkTreeIter iter; - GList *list = NULL, *l; - GNode *data_tree = NULL; - - if (gtk_tree_model_get_iter_first (model, &iter)) - { - do - { - GladeColumnType *data = g_new0(GladeColumnType, 1); - - gtk_tree_model_get (model, &iter, - COLUMN_COLUMN_NAME, &data->column_name, - COLUMN_GTYPE, &data->type, - -1); - - list = g_list_append (list, data); - } while (gtk_tree_model_iter_next (model, &iter)); - } - - glade_widget_property_get (eprop->property->widget, "data", &data_tree); - if (data_tree) - { - GNode *row, *iter; - gint colnum; - data_tree = glade_model_data_tree_copy (data_tree); - - glade_command_push_group (_("Setting columns of %s"), eprop->property->widget->name); - - /* Remove extra columns */ - for (row = data_tree->children; row; row = row->next) - { - - for (colnum = 0, iter = row->children; iter; - colnum++, iter = iter->next) - { + GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); + GList *columns = NULL; + GladeColumnType *data; + GValue value = { 0, }; - } - } + glade_property_get (eprop->property, &columns); + if (columns) + columns = glade_column_list_copy (columns); - g_value_init (&value, GLADE_TYPE_MODEL_DATA_TREE); - g_value_take_boxed (&value, data_tree); - glade_editor_property_commit (eprop, &value); - g_value_unset (&value); + data = g_new0 (GladeColumnType, 1); + data->column_name = g_strdup (column_name); + data->type = type; - } + columns = g_list_append (columns, data); g_value_init (&value, GLADE_TYPE_COLUMN_TYPE_LIST); - g_value_take_boxed (&value, list); + g_value_take_boxed (&value, columns); glade_editor_property_commit (eprop, &value); g_value_unset (&value); - - if (data_tree) - { - glade_model_data_tree_free (data_tree); - glade_command_pop_group (); - } } -static void -eprop_column_append (GladeEPropColumnTypes *eprop_types, - GType type, - const gchar *name, - const gchar *column_name) -{ - gtk_list_store_insert_with_values (eprop_types->store, NULL, -1, - COLUMN_NAME, name ? name : g_type_name (type), - COLUMN_GTYPE, type, - COLUMN_COLUMN_NAME, column_name, - -1); -} - -static void -glade_eprop_column_types_load (GladeEditorProperty *eprop, GladeProperty *property) -{ - GladeEditorPropertyClass *parent_class = - g_type_class_peek_parent (GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)); - GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); - GList *l, *list; - - /* Chain up first */ - parent_class->load (eprop, property); - - /* Clear Store */ - gtk_list_store_clear (eprop_types->store); - /* We could set the combo to the first item */ - - list = g_value_get_boxed (property->value); - - for (l = list; l; l = g_list_next (l)) - { - GladeColumnType *data = l->data; - - eprop_column_append (eprop_types, data->type, NULL, data->column_name); - } -} static void glade_eprop_column_types_add_clicked (GtkWidget *button, @@ -340,36 +265,77 @@ glade_eprop_column_types_add_clicked (GtkWidget *button, COLUMN_GTYPE, &type2add, -1); - eprop_column_append (eprop_types, type2add, name, NULL); - eprop_reload_value (eprop_types); + eprop_column_append (GLADE_EDITOR_PROPERTY (eprop_types), type2add, NULL); } static void glade_eprop_column_types_delete_clicked (GtkWidget *button, GladeEPropColumnTypes *eprop_types) { + /* Remove from list and commit value, dont touch the liststore except in load() */ + GtkTreeIter iter; if (gtk_tree_selection_get_selected (eprop_types->selection, NULL, &iter)) gtk_list_store_remove (GTK_LIST_STORE (eprop_types->store), &iter); } -static gboolean -eprop_reload_value_idle (gpointer data) -{ - eprop_reload_value (GLADE_EPROP_COLUMN_TYPES (data)); - return FALSE; -} - static void eprop_treeview_row_deleted (GtkTreeModel *tree_model, GtkTreePath *path, GladeEditorProperty *eprop) { + GtkTreeIter iter; if (eprop->loading) return; - eprop_reload_value_idle (eprop); + + /* Find the deleted row and remove that column... */ + if (!gtk_tree_model_get_iter (tree_model, &iter, path)) + return; + + /* Get it by name... */ + } + +static void +eprop_column_load (GladeEPropColumnTypes *eprop_types, + GType type, + const gchar *name, + const gchar *column_name) +{ + gtk_list_store_insert_with_values (eprop_types->store, NULL, -1, + COLUMN_NAME, name ? name : g_type_name (type), + COLUMN_GTYPE, type, + COLUMN_COLUMN_NAME, column_name, + -1); +} + +static void +glade_eprop_column_types_load (GladeEditorProperty *eprop, GladeProperty *property) +{ + GladeEditorPropertyClass *parent_class = + g_type_class_peek_parent (GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)); + GladeEPropColumnTypes *eprop_types = GLADE_EPROP_COLUMN_TYPES (eprop); + GList *l, *list; + + /* Chain up first */ + parent_class->load (eprop, property); + + /* Clear Store */ + gtk_list_store_clear (eprop_types->store); + /* We could set the combo to the first item */ + + list = g_value_get_boxed (property->value); + + for (l = list; l; l = g_list_next (l)) + { + GladeColumnType *data = l->data; + + eprop_column_load (eprop_types, data->type, NULL, data->column_name); + } +} + + static GtkWidget * glade_eprop_column_types_create_input (GladeEditorProperty *eprop) { diff --git a/plugins/gtk+/glade-gtk.c b/plugins/gtk+/glade-gtk.c index b5065a8a..781a8fc9 100644 --- a/plugins/gtk+/glade-gtk.c +++ b/plugins/gtk+/glade-gtk.c @@ -479,7 +479,7 @@ glade_gtk_parse_atk_props (GladeWidget *widget, /* Set the parsed value on the property ... */ gvalue = glade_property_class_make_gvalue_from_string - (property->klass, value, widget->project); + (property->klass, value, widget->project, widget); glade_property_set_value (property, gvalue); g_value_unset (gvalue); g_free (gvalue); @@ -8777,10 +8777,14 @@ glade_gtk_store_write_columns (GladeWidget *widget, for (l = g_value_get_boxed (prop->value); l; l = g_list_next (l)) { GladeColumnType *data = l->data; - GladeXmlNode *column_node; - + GladeXmlNode *column_node, *comment_node; + /* Write column names in comments... */ - + gchar *comment = g_strdup_printf ("column-name %s", data->column_name); + comment_node = glade_xml_node_new_comment (context, comment); + glade_xml_node_append_child (columns_node, comment_node); + g_free (comment); + column_node = glade_xml_node_new (context, GLADE_TAG_COLUMN); glade_xml_node_append_child (columns_node, column_node); glade_xml_node_set_property_string (column_node, GLADE_TAG_TYPE, @@ -8989,7 +8993,7 @@ glade_gtk_store_read_data (GladeWidget *widget, GladeXmlNode *node) * should we be doing this part in "finished" ? ... todo thinkso... */ value_str = glade_xml_get_content (col_node); - value = glade_utils_value_from_string (column_type->type, value_str, widget->project); + value = glade_utils_value_from_string (column_type->type, value_str, widget->project, widget); g_free (value_str); data = glade_model_data_new (column_type->type); diff --git a/plugins/gtk+/glade-model-data.c b/plugins/gtk+/glade-model-data.c index 64740c97..dc99b669 100644 --- a/plugins/gtk+/glade-model-data.c +++ b/plugins/gtk+/glade-model-data.c @@ -286,6 +286,20 @@ append_row (GNode *node, GList *columns) } } +static void +clear_view (GladeEditorProperty *eprop) +{ + GladeEPropModelData *eprop_data = GLADE_EPROP_MODEL_DATA (eprop); + GtkTreeViewColumn *column; + + /* Clear columns ... */ + while ((column = gtk_tree_view_get_column (eprop_data->view, 0)) != NULL) + gtk_tree_view_remove_column (eprop_data->view, column); + + /* Clear store ... (this will unref the old store) */ + gtk_tree_view_set_model (eprop_data->view, NULL); + +} /* User pressed add: append row and commit values */ static void @@ -302,6 +316,8 @@ glade_eprop_model_data_add_clicked (GtkWidget *button, if (!columns) return; + clear_view (eprop); + if (!node) node = g_node_new (NULL); else @@ -313,6 +329,7 @@ glade_eprop_model_data_add_clicked (GtkWidget *button, g_value_take_boxed (&value, node); glade_editor_property_commit (eprop, &value); g_value_unset (&value); + } /* User pressed delete: remove selected row and commit values */ @@ -394,7 +411,7 @@ eprop_model_data_generate_store (GladeEditorProperty *eprop) iter_data = iter_node->data; g_array_append_val (gtypes, G_VALUE_TYPE (&iter_data->value)); } - store = gtk_list_store_newv (NUM_COLUMNS + gtypes->len, (GType *)gtypes->data); + store = gtk_list_store_newv (gtypes->len, (GType *)gtypes->data); g_array_free (gtypes, TRUE); /* Now populate the store with data */ @@ -406,11 +423,11 @@ eprop_model_data_generate_store (GladeEditorProperty *eprop) gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COLUMN_ROW, row_num, -1); - for (column_num = 0, iter_node = row_node->children; iter_node; + for (column_num = NUM_COLUMNS, iter_node = row_node->children; iter_node; column_num++, iter_node = iter_node->next) { iter_data = iter_node->data; - gtk_list_store_set_value (store, &iter, NUM_COLUMNS + column_num, &iter_data->value); + gtk_list_store_set_value (store, &iter, column_num, &iter_data->value); } } return store; @@ -448,13 +465,48 @@ value_text_edited (GtkCellRendererText *cell, GladeEPropModelData *eprop_data = GLADE_EPROP_MODEL_DATA (eprop); GtkTreeIter iter; gint colnum = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cell), "column-number")); + gint row; + GNode *data_tree = NULL, *node; + GValue value = { 0, }; + GladeModelData *data; if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (eprop_data->store), &iter, path)) return; - gtk_list_store_set (eprop_data->store, &iter, - NUM_COLUMNS + colnum, new_text, + + gtk_tree_model_get (eprop_data->store, &iter, + COLUMN_ROW, &row, -1); + + glade_property_get (eprop->property, &data_tree); + + /* if we are editing, then there is data in the datatree */ + g_assert (data_tree); + + data_tree = glade_model_data_tree_copy (data_tree); + + if ((node = g_node_nth_child (data_tree, row)) != NULL) + { + if ((node = g_node_nth_child (node, colnum)) != NULL) + { + data = node->data; + + g_value_set_string (&(data->value), new_text); + } + } + + g_value_init (&value, GLADE_TYPE_MODEL_DATA_TREE); + g_value_take_boxed (&value, data_tree); + glade_editor_property_commit (eprop, &value); + g_value_unset (&value); + + /* No need to update store, it will be reloaded in load() */ +/* gtk_list_store_set (eprop_data->store, &iter, */ +/* NUM_COLUMNS + colnum, new_text, */ +/* -1); */ + + /* XXX Set string in data here and commit ! */ + } static GtkTreeViewColumn * @@ -479,7 +531,6 @@ eprop_model_generate_column (GladeEditorProperty *eprop, renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "editable", TRUE, - "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_attributes (column, renderer, @@ -529,7 +580,6 @@ eprop_model_generate_column (GladeEditorProperty *eprop, g_object_set (G_OBJECT (renderer), "editable", TRUE, "adjustment", adjustment, - "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_tree_view_column_pack_start (column, renderer, TRUE); @@ -589,10 +639,6 @@ eprop_model_data_generate_columns (GladeEditorProperty *eprop) glade_property_get (eprop->property, &data_tree); - /* Clear columns ... */ - while ((column = gtk_tree_view_get_column (eprop_data->view, 0)) != NULL) - gtk_tree_view_remove_column (eprop_data->view, column); - if (!data_tree || !data_tree->children || !data_tree->children->children) return; @@ -614,19 +660,17 @@ glade_eprop_model_data_load (GladeEditorProperty *eprop, GladeEditorPropertyClass *parent_class = g_type_class_peek_parent (GLADE_EDITOR_PROPERTY_GET_CLASS (eprop)); GladeEPropModelData *eprop_data = GLADE_EPROP_MODEL_DATA (eprop); - - /* Chain up first */ + + clear_view (eprop); + + /* Chain up in a clean state... */ parent_class->load (eprop, property); - - /* Recreate and populate store */ - if (eprop_data->store) - g_object_unref (G_OBJECT (eprop_data->store)); - + if ((eprop_data->store = eprop_model_data_generate_store (eprop)) != NULL) { eprop_data->selection = gtk_tree_view_get_selection (eprop_data->view); - /* Pass ownership of the store to the model */ + /* Pass ownership of the store to the view... */ gtk_tree_view_set_model (eprop_data->view, GTK_TREE_MODEL (eprop_data->store)); g_object_unref (G_OBJECT (eprop_data->store)); @@ -635,7 +679,7 @@ glade_eprop_model_data_load (GladeEditorProperty *eprop, eprop); } - /* Clear and create new columns with renderers */ + /* Create new columns with renderers */ eprop_model_data_generate_columns (eprop); } diff --git a/plugins/gtk+/gtk+.xml.in b/plugins/gtk+/gtk+.xml.in index d14ad24a..17ef4f60 100644 --- a/plugins/gtk+/gtk+.xml.in +++ b/plugins/gtk+/gtk+.xml.in @@ -901,7 +901,7 @@ embedded in another object - + @@ -1068,8 +1068,8 @@ embedded in another object - - + + diff --git a/src/glade-window.c b/src/glade-window.c index a4868aa2..59626f37 100644 --- a/src/glade-window.c +++ b/src/glade-window.c @@ -1705,7 +1705,6 @@ notebook_tab_removed_cb (GtkNotebook *notebook, GladeWindow *window) { GladeProject *project; - GladeInspector *inspector; --window->priv->num_tabs; @@ -1721,13 +1720,12 @@ notebook_tab_removed_cb (GtkNotebook *notebook, G_CALLBACK (project_selection_changed_cb), window); - /* FIXME: this function needs to be preferably called somewhere else */ - glade_app_remove_project (project); - - inspector = (GladeInspector *) gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), page_num); gtk_notebook_remove_page (GTK_NOTEBOOK (window->priv->inspectors_notebook), page_num); + /* FIXME: this function needs to be preferably called somewhere else */ + glade_app_remove_project (project); + refresh_projects_list_menu (window); refresh_title (window); @@ -2501,7 +2499,9 @@ add_project (GladeWindow *window, GladeProject *project) view = glade_design_view_new (project); gtk_widget_show (view); + /* Pass ownership of the project to the app */ glade_app_add_project (project); + g_object_unref (project); gtk_notebook_append_page (GTK_NOTEBOOK (window->priv->notebook), GTK_WIDGET (view), NULL); gtk_notebook_set_current_page (GTK_NOTEBOOK (window->priv->notebook), -1); -- cgit v1.2.1