[glade/margins] Make GladeProject change gtk target to 3.0 and warn the user if there are any unknown (deprecated) o



commit 8a83e6ecc0a6d99bd1d2d3fd7c9adc0bb25b23dc
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Mon Sep 19 17:11:30 2011 -0300

    Make GladeProject change gtk target to 3.0 and warn the user if there are
    any unknown (deprecated) objects, it also replaces them with a stub to make it
    even more noticeable.
    
    Fixes bug 652673 "Project versions are ignored" and 376628 "Deletes unknown widget nodes."

 ChangeLog                   |   16 +++
 gladeui/Makefile.am         |    2 +
 gladeui/glade-object-stub.c |  224 +++++++++++++++++++++++++++++++++
 gladeui/glade-object-stub.h |   57 +++++++++
 gladeui/glade-project.c     |  287 +++++++++++++++++++++++++++++++++----------
 gladeui/glade-utils.c       |   17 ++-
 gladeui/glade-widget.c      |   28 ++++-
 gladeui/glade-xml-utils.c   |   11 ++
 gladeui/glade-xml-utils.h   |    1 +
 9 files changed, 566 insertions(+), 77 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index c100d92..7f58f19 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2011-09-19  Juan Pablo Ugarte <juanpablougarte gmail com>
+
+        * gladeui/glade-object-stub.[ch]: Added new object type used to remember
+          unknown objects in xml files.
+
+        * gladeui/glade-utils.c: made glade_util_ui_message () accept markup strings.
+        
+        * gladeui/glade-xml-utils.[ch]: added new function glade_xml_node_copy()
+
+        * gladeui/glade-widget.c:
+          o glade_widget_read() create object stub if class is unknown.
+          o glade_widget_write() used saved xml to dump stub objects.
+        
+        * glade/gladeui/glade-project.c: make GladeProject remember unknown catalogs
+          to avoid loosing requirements when loading/saving a file with unknown objects. 
+
 2011-09-19 Ignacio Casal Quinteiro <icq gnome org>
 
         * configure.ac, plugins/python/glade-python.c:
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 5860d03..5e35469 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -49,6 +49,7 @@ EXTRA_DIST = glade-marshallers.list gladeui.rc.in icon-naming-spec.c glade-previ
 # The glade core library
 libgladeui_2_la_SOURCES = \
 	glade-debug.c \
+	glade-object-stub.c \
 	glade-xml-utils.c \
 	glade-catalog.c \
 	glade-widget-adaptor.c \
@@ -144,6 +145,7 @@ libgladeuiinclude_HEADERS = \
 noinst_HEADERS = \
 	glade-marshallers.h \
 	glade-design-layout.h \
+	glade-object-stub.h \
 	glade-popup.h \
 	glade-accumulators.h \
 	glade-design-private.h \
diff --git a/gladeui/glade-object-stub.c b/gladeui/glade-object-stub.c
new file mode 100644
index 0000000..e15ce32
--- /dev/null
+++ b/gladeui/glade-object-stub.c
@@ -0,0 +1,224 @@
+/*
+ * glade-object-stub.c
+ *
+ * Copyright (C) 2011 Juan Pablo Ugarte
+   *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+   *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib/gi18n-lib.h>
+#include "glade-object-stub.h"
+#include "glade-project.h"
+
+struct _GladeObjectStubPrivate
+{
+  GtkLabel *label;
+  
+  gchar *type;
+  GladeXmlNode *node;
+};
+
+#define GLADE_OBJECT_STUB_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GLADE_TYPE_OBJECT_STUB, GladeObjectStubPrivate))
+
+enum
+{
+  PROP_0,
+
+  PROP_OBJECT_TYPE,
+  PROP_XML_NODE
+};
+
+
+G_DEFINE_TYPE (GladeObjectStub, glade_object_stub, GTK_TYPE_INFO_BAR);
+
+#define RESPONSE_DELETE 1
+#define RESPONSE_DELETE_ALL 2
+
+static void
+on_infobar_response (GladeObjectStub *stub, gint response_id)
+{
+  GladeWidget *gwidget = glade_widget_get_from_gobject (stub);
+  
+  if (response_id == RESPONSE_DELETE)
+    {
+      GList widgets = {0, };
+      widgets.data = gwidget;
+      glade_command_delete (&widgets);
+    }
+  else if (response_id == RESPONSE_DELETE_ALL)
+    {
+      GladeProject *project = glade_widget_get_project (gwidget);
+      GList *stubs = NULL;
+      const GList *l;
+
+      for (l = glade_project_get_objects (project); l; l = g_list_next (l))
+        {
+          if (GLADE_IS_OBJECT_STUB (l->data))
+            {
+              GladeWidget *gobj = glade_widget_get_from_gobject (l->data);
+              stubs = g_list_prepend (stubs, gobj);
+            }
+        }
+
+      glade_command_delete (stubs);
+      g_list_free (stubs);
+    }
+}
+
+static void
+glade_object_stub_init (GladeObjectStub *object)
+{
+  GladeObjectStubPrivate *priv = GLADE_OBJECT_STUB_PRIVATE (object);
+  GtkWidget *label = gtk_label_new (NULL);
+
+  object->priv = priv;
+  priv->type = NULL;
+  priv->node = NULL;
+  
+  priv->label = GTK_LABEL (label);
+  gtk_label_set_line_wrap (priv->label, TRUE);
+  
+  gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (object))), label);
+
+  gtk_info_bar_add_button (GTK_INFO_BAR (object),
+                           _("Delete"), RESPONSE_DELETE);
+  gtk_info_bar_add_button (GTK_INFO_BAR (object),
+                           _("Delete All"), RESPONSE_DELETE_ALL);
+  
+  g_signal_connect (object, "response", G_CALLBACK (on_infobar_response), NULL);
+}
+
+static void
+glade_object_stub_finalize (GObject *object)
+{
+  GladeObjectStubPrivate *priv = GLADE_OBJECT_STUB (object)->priv;
+
+  g_free (priv->type);
+  
+  if (priv->node) glade_xml_node_delete (priv->node);
+
+  G_OBJECT_CLASS (glade_object_stub_parent_class)->finalize (object);
+}
+
+static void
+glade_object_stub_refresh_text (GladeObjectStub *stub)
+{
+  GladeObjectStubPrivate *priv = stub->priv;
+  gchar *markup;
+
+  if (priv->type == NULL) return;
+
+  markup = g_markup_printf_escaped ("<b>FIXME:</b> Unable to create object with type %s", priv->type);
+
+  gtk_label_set_markup (priv->label, markup);
+  gtk_info_bar_set_message_type (GTK_INFO_BAR (stub), GTK_MESSAGE_WARNING);
+  g_free (markup);
+}
+
+static void
+glade_object_stub_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GladeObjectStubPrivate *priv;
+  GladeObjectStub *stub;
+  
+  g_return_if_fail (GLADE_IS_OBJECT_STUB (object));
+
+  stub = GLADE_OBJECT_STUB (object);
+  priv = stub->priv;
+  
+  switch (prop_id)
+    {
+      case PROP_OBJECT_TYPE:
+        g_free (priv->type);
+        priv->type = g_value_dup_string (value);
+        glade_object_stub_refresh_text (stub);
+        break;
+      case PROP_XML_NODE:
+        if (priv->node) glade_xml_node_delete (priv->node);
+        priv->node = g_value_dup_boxed (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+glade_object_stub_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GladeObjectStubPrivate *priv;
+  
+  g_return_if_fail (GLADE_IS_OBJECT_STUB (object));
+
+  priv = GLADE_OBJECT_STUB (object)->priv;
+  
+  switch (prop_id)
+    {
+      case PROP_OBJECT_TYPE:
+        g_value_set_string (value, priv->type);
+        break;
+      case PROP_XML_NODE:
+        g_value_set_boxed (value, priv->node);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static GType
+glade_xml_node_get_type (void)
+{
+  static GType type = 0;
+
+  if (type) return type;
+
+  type = g_boxed_type_register_static ("GladeXmlNode",
+                                       (GBoxedCopyFunc) glade_xml_node_copy,
+                                       (GBoxedFreeFunc) glade_xml_node_delete);
+  return type;
+}
+
+static void
+glade_object_stub_class_init (GladeObjectStubClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GladeObjectStubPrivate));
+
+  object_class->finalize = glade_object_stub_finalize;
+  object_class->set_property = glade_object_stub_set_property;
+  object_class->get_property = glade_object_stub_get_property;
+
+  g_object_class_install_property (object_class,
+                                   PROP_OBJECT_TYPE,
+                                   g_param_spec_string ("object-type",
+                                                        "Object Type",
+                                                        "The object type this stub replaces",
+                                                        NULL,
+                                                        G_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_XML_NODE,
+                                   g_param_spec_boxed ("xml-node",
+                                                       "XML node",
+                                                       "The XML representation of the original object this is replacing",
+                                                       glade_xml_node_get_type (),
+                                                       G_PARAM_READWRITE));
+}
diff --git a/gladeui/glade-object-stub.h b/gladeui/glade-object-stub.h
new file mode 100644
index 0000000..42369b4
--- /dev/null
+++ b/gladeui/glade-object-stub.h
@@ -0,0 +1,57 @@
+/*
+ * glade-object-stub.h
+ *
+ * Copyright (C) 2011 Juan Pablo Ugarte
+ *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _GLADE_OBJECT_STUB_H_
+#define _GLADE_OBJECT_STUB_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GLADE_TYPE_OBJECT_STUB             (glade_object_stub_get_type ())
+#define GLADE_OBJECT_STUB(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_OBJECT_STUB, GladeObjectStub))
+#define GLADE_OBJECT_STUB_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_OBJECT_STUB, GladeObjectStubClass))
+#define GLADE_IS_OBJECT_STUB(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_OBJECT_STUB))
+#define GLADE_IS_OBJECT_STUB_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_OBJECT_STUB))
+#define GLADE_OBJECT_STUB_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_TYPE_OBJECT_STUB, GladeObjectStubClass))
+
+typedef struct _GladeObjectStubClass GladeObjectStubClass;
+typedef struct _GladeObjectStub GladeObjectStub;
+typedef struct _GladeObjectStubPrivate GladeObjectStubPrivate;
+
+struct _GladeObjectStubClass
+{
+  GtkInfoBarClass parent_class;
+};
+
+struct _GladeObjectStub
+{
+  GtkInfoBar parent_instance;
+  GladeObjectStubPrivate *priv;
+};
+
+GType glade_object_stub_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* _GLADE_OBJECT_STUB_H_ */
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c
index 4239040..3dc2ec4 100644
--- a/gladeui/glade-project.c
+++ b/gladeui/glade-project.c
@@ -50,7 +50,7 @@
 #include "glade-project.h"
 #include "glade-command.h"
 #include "glade-name-context.h"
-
+#include "glade-object-stub.h"
 
 #define VALID_ITER(project, iter) ((iter)!= NULL && G_IS_OBJECT ((iter)->user_data) && ((GladeProject*)(project))->priv->stamp == (iter)->stamp)
 
@@ -131,6 +131,8 @@ struct _GladeProjectPrivate
                                  * (full or relative path, null means project directory).
                                  */
 
+  GList *unknown_catalogs; /* List of CatalogInfo catalogs */
+
   /* Control on the properties dialog to update buttons etc when properties change */
   GtkWidget *prefs_dialog;
   GtkWidget *project_wide_radio;
@@ -164,11 +166,11 @@ struct _GladeProjectPrivate
   guint pointer_mode : 3;        /* The currently effective GladePointerMode */
 };
 
-typedef struct
+typedef struct 
 {
-  gchar *stock;
-  gchar *filename;
-} StockFilePair;
+  gchar *catalog;
+  gint position;
+} CatalogInfo;
 
 GType
 glade_pointer_mode_get_type (void)
@@ -288,29 +290,30 @@ static void
 glade_project_dispose (GObject *object)
 {
   GladeProject *project = GLADE_PROJECT (object);
+  GladeProjectPrivate *priv = project->priv;
   GList *list, *tree;
 
   /* Emit close signal */
   g_signal_emit (object, glade_project_signals[CLOSE], 0);
 
   /* Destroy running previews */
-  if (project->priv->previews)
+  if (priv->previews)
     {
-      g_hash_table_destroy (project->priv->previews);
-      project->priv->previews = NULL;
+      g_hash_table_destroy (priv->previews);
+      priv->previews = NULL;
     }
 
-  if (project->priv->selection_changed_id > 0)
-    project->priv->selection_changed_id = 
-      (g_source_remove (project->priv->selection_changed_id), 0);
+  if (priv->selection_changed_id > 0)
+    priv->selection_changed_id = 
+      (g_source_remove (priv->selection_changed_id), 0);
 
   glade_project_selection_clear (project, TRUE);
 
-  glade_project_list_unref (project->priv->undo_stack);
-  project->priv->undo_stack = NULL;
+  glade_project_list_unref (priv->undo_stack);
+  priv->undo_stack = NULL;
 
   /* Remove objects from the project */
-  tree = g_list_copy (project->priv->tree);
+  tree = g_list_copy (priv->tree);
   for (list = tree; list; list = list->next)
     {
       GladeWidget *gwidget = glade_widget_get_from_gobject (list->data);
@@ -319,14 +322,29 @@ glade_project_dispose (GObject *object)
     }
   g_list_free (tree);
 
-  while (project->priv->tree)
-    glade_project_remove_object (project, project->priv->tree->data);
+  while (priv->tree)
+    glade_project_remove_object (project, priv->tree->data);
+
+  while (priv->objects)
+    glade_project_remove_object (project, priv->objects->data);
 
-  while (project->priv->objects)
-    glade_project_remove_object (project, project->priv->objects->data);
+  g_assert (priv->tree == NULL);
+  g_assert (priv->objects == NULL);
 
-  g_assert (project->priv->tree == NULL);
-  g_assert (project->priv->objects == NULL);
+  if (priv->unknown_catalogs)
+    {
+      GList *l;
+
+      for (l = priv->unknown_catalogs; l; l = g_list_next (l))
+        {
+          CatalogInfo *data = l->data;
+          g_free (data->catalog);
+          g_free (data);
+        }
+      
+      g_list_free (priv->unknown_catalogs);
+      priv->unknown_catalogs = NULL;
+    }
 
   G_OBJECT_CLASS (glade_project_parent_class)->dispose (object);
 }
@@ -717,6 +735,7 @@ glade_project_init (GladeProject *project)
   priv->prev_redo_item = NULL;
   priv->first_modification = NULL;
   priv->first_modification_is_na = FALSE;
+  priv->unknown_catalogs = NULL;
 
   priv->previews = g_hash_table_new_full (g_str_hash, 
                                           g_str_equal,
@@ -1106,6 +1125,7 @@ glade_project_read_requires (GladeProject *project,
   gchar *required_lib = NULL;
   gboolean loadable = TRUE;
   guint16 major, minor;
+  gint position = 0;
 
   for (node = glade_xml_node_get_children_with_comments (root_node);
        node; node = glade_xml_node_next_with_comments (node))
@@ -1133,6 +1153,17 @@ glade_project_read_requires (GladeProject *project,
        */
       if (!glade_catalog_is_loaded (required_lib))
         {
+          CatalogInfo *data = g_new0(CatalogInfo, 1);
+          
+          data->catalog = required_lib;
+          data->position = position;
+
+          /* Keep a list of unknown catalogs to avoid loosing the requirement */
+          project->priv->unknown_catalogs = g_list_append (project->priv->unknown_catalogs,
+                                                           data);
+          /* Also keep the version */
+          glade_project_set_target_version (project, required_lib, major, minor);
+
           if (!loadable)
             g_string_append (string, ", ");
 
@@ -1146,9 +1177,10 @@ glade_project_read_requires (GladeProject *project,
 
           glade_project_set_target_version
               (project, required_lib, major, minor);
+          g_free (required_lib);
         }
 
-      g_free (required_lib);
+      position++;
     }
 
   if (!loadable)
@@ -1435,9 +1467,94 @@ glade_project_push_progress (GladeProject *project)
                  project->priv->progress_full, project->priv->progress_step);
 }
 
+
+/* translators: reffers to project name '%s' that targets gtk version '%d.%d' */
+#define PROJECT_TARGET_DIALOG_TITLE_FMT _("%s targets Gtk+ %d.%d")
+
+static void
+glade_project_check_target_version (GladeProject *project)
+{
+  GladeProjectPrivate *priv;
+  GHashTable *unknown_classes;
+  gint unknown_objects;
+  gint major, minor;
+  GtkWidget *dialog;
+  GString *text;
+  GList *l;
+
+  glade_project_get_target_version (project, "gtk+", &major, &minor);
+    
+  /* Glade >= 3.10 only targets Gtk 3 */
+  if (major >= 3) return;
+
+  priv = project->priv;
+  unknown_classes = g_hash_table_new (g_str_hash, g_str_equal);
+  unknown_objects = 0;
+
+  for (l = priv->objects; l; l = g_list_next (l))
+    {
+      if (GLADE_IS_OBJECT_STUB (l->data))
+        {
+          gchar *type;
+          g_object_get (l->data, "object-type", &type, NULL);
+          g_hash_table_insert (unknown_classes, type, NULL);
+          unknown_objects++;
+        }
+    }
+
+  if (unknown_objects)
+    {
+      GList *classes = g_hash_table_get_keys (unknown_classes);
+
+      if (unknown_objects == 1)
+        {
+          text = g_string_new (_("Specially because there is an object that can not be build with type "));
+        }
+      else
+        {
+          text = g_string_new ("");
+          g_string_printf (text, _("Specially because there are %d objects that can not be build with types "),
+                           unknown_objects);
+        }
+
+      for (l = classes; l; l = g_list_next (l))
+        {
+          if (g_list_previous (l))
+            g_string_append (text, (g_list_next (l)) ? ", " : _(" and "));
+          
+          g_string_append (text, l->data);
+        }
+
+      g_list_free (classes);
+    }
+  else 
+    text = NULL;
+
+  dialog = gtk_message_dialog_new (GTK_WINDOW (glade_app_get_window ()),
+                                   GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
+                                   PROJECT_TARGET_DIALOG_TITLE_FMT,
+                                   glade_project_get_name (project),
+                                   major, minor);
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            _("But this version of Glade is for GTK+ 3 only.\n"
+                                              "Make sure you can run this project with Glade 3.8 with no deprecated widgets first.\n"
+                                              "%s"), (text) ? text->str : "");
+
+  gtk_dialog_run (GTK_DIALOG (dialog));
+  gtk_widget_destroy (dialog);
+
+  glade_project_set_target_version (project, "gtk+", 3, 0);
+
+  g_hash_table_destroy (unknown_classes);
+  if (text) g_string_free (text, TRUE);
+}
+
 static gboolean
 glade_project_load_internal (GladeProject *project)
 {
+  GladeProjectPrivate *priv = project->priv;
   GladeXmlContext *context;
   GladeXmlDoc *doc;
   GladeXmlNode *root;
@@ -1446,20 +1563,20 @@ glade_project_load_internal (GladeProject *project)
   gboolean has_gtk_dep = FALSE;
   gint count;
 
-  project->priv->selection = NULL;
-  project->priv->objects = NULL;
-  project->priv->loading = TRUE;
+  priv->selection = NULL;
+  priv->objects = NULL;
+  priv->loading = TRUE;
 
   /* get the context & root node of the catalog file */
   if (!(context =
-        glade_xml_context_new_from_path (project->priv->path, NULL, NULL)))
+        glade_xml_context_new_from_path (priv->path, NULL, NULL)))
     {
-      g_warning ("Couldn't open glade file [%s].", project->priv->path);
-      project->priv->loading = FALSE;
+      g_warning ("Couldn't open glade file [%s].", priv->path);
+      priv->loading = FALSE;
       return FALSE;
     }
 
-  project->priv->mtime = glade_util_get_file_mtime (project->priv->path, NULL);
+  priv->mtime = glade_util_get_file_mtime (priv->path, NULL);
 
   doc = glade_xml_context_get_doc (context);
   root = glade_xml_doc_get_root (doc);
@@ -1467,9 +1584,9 @@ glade_project_load_internal (GladeProject *project)
   if (!glade_xml_node_verify_silent (root, GLADE_XML_TAG_PROJECT))
     {
       g_warning ("Couldnt recognize GtkBuilder xml, skipping %s",
-                 project->priv->path);
+                 priv->path);
       glade_xml_context_free (context);
-      project->priv->loading = FALSE;
+      priv->loading = FALSE;
       return FALSE;
     }
 
@@ -1479,21 +1596,18 @@ glade_project_load_internal (GladeProject *project)
   /* XXX Need to load project->priv->comment ! */
   glade_project_read_comment (project, doc);
 
-  if (glade_project_read_requires
-      (project, root, project->priv->path, &has_gtk_dep) == FALSE)
-    {
-      project->priv->loading = FALSE;
-      glade_xml_context_free (context);
-      return FALSE;
-    }
+  /* Read requieres, and do not abort load if there are missing catalog since
+   * GladeObjectStub is created to keep the original xml for unknown object classes
+   */
+  glade_project_read_requires (project, root, priv->path, &has_gtk_dep);
 
   glade_project_read_resource_path (project, root);
 
   /* Launch a dialog if it's going to take enough time to be
    * worth showing at all */
   count = glade_project_count_xml_objects (project, root, 0);
-  project->priv->progress_full = count;
-  project->priv->progress_step = 0;
+  priv->progress_full = count;
+  priv->progress_step = 0;
 
   for (node = glade_xml_node_get_children (root);
        node; node = glade_xml_node_next (node))
@@ -1505,23 +1619,23 @@ glade_project_load_internal (GladeProject *project)
       if ((widget = glade_widget_read (project, NULL, node, NULL)) != NULL)
         glade_project_add_object (project, glade_widget_get_object (widget));
 
-      if (project->priv->load_cancel)
+      if (priv->load_cancel)
         break;
     }
 
   /* Finished with the xml context */
   glade_xml_context_free (context);
 
-  if (project->priv->load_cancel)
+  if (priv->load_cancel)
     {
-      project->priv->loading = FALSE;
+      priv->loading = FALSE;
       return FALSE;
     }
 
   if (!has_gtk_dep)
     glade_project_introspect_gtk_version (project);
 
-  if (glade_util_file_is_writeable (project->priv->path) == FALSE)
+  if (glade_util_file_is_writeable (priv->path) == FALSE)
     glade_project_set_readonly (project, TRUE);
 
   /* Now we have to loop over all the object properties
@@ -1532,8 +1646,8 @@ glade_project_load_internal (GladeProject *project)
   /* Reset project status here too so that you get a clean
    * slate after calling glade_project_open().
    */
-  project->priv->modified = FALSE;
-  project->priv->loading = FALSE;
+  priv->modified = FALSE;
+  priv->loading = FALSE;
 
   /* Emit "parse-finished" signal */
   g_signal_emit (project, glade_project_signals[PARSE_FINISHED], 0);
@@ -1542,6 +1656,8 @@ glade_project_load_internal (GladeProject *project)
    */
   glade_project_verify_project_for_ui (project);
 
+  glade_project_check_target_version (project);
+  
   return TRUE;
 
 }
@@ -2223,24 +2339,35 @@ static gboolean
 glade_project_verify (GladeProject *project, gboolean saving)
 {
   GString *string = g_string_new (NULL);
-  GladeWidget *widget;
   GList *list;
   gboolean ret = TRUE;
-  gchar *path_name;
-
+  
   for (list = project->priv->objects; list; list = list->next)
     {
-      widget = glade_widget_get_from_gobject (list->data);
-
-      path_name = glade_widget_generate_path_name (widget);
+      GladeWidget *widget = glade_widget_get_from_gobject (list->data);
+      
+      if (GLADE_IS_OBJECT_STUB (list->data))
+        {
+          gchar *type;
+          g_object_get (list->data, "object-type", &type, NULL);
+          
+          /* translators: reffers to an unknow object named '%s' of type '%s' */
+          g_string_append_printf (string, _("Unknow object %s with type %s\n"), 
+                                  glade_widget_get_name (widget), type);
+          g_free (type);
+        }
+      else
+        {
+          gchar *path_name = glade_widget_generate_path_name (widget);
 
-      glade_project_verify_adaptor (project, glade_widget_get_adaptor (widget),
-                                    path_name, string, saving, FALSE, NULL);
-      glade_project_verify_properties_internal (widget, path_name, string,
-                                                FALSE);
-      glade_project_verify_signals (widget, path_name, string, FALSE);
+          glade_project_verify_adaptor (project, glade_widget_get_adaptor (widget),
+                                        path_name, string, saving, FALSE, NULL);
+          glade_project_verify_properties_internal (widget, path_name, string,
+                                                    FALSE);
+          glade_project_verify_signals (widget, path_name, string, FALSE);
 
-      g_free (path_name);
+          g_free (path_name);
+        }
     }
 
   if (string->len > 0)
@@ -3439,7 +3566,16 @@ glade_project_required_libs (GladeProject *project)
   if (!required)
     required = g_list_prepend (required, g_strdup ("gtk+"));
 
-  return g_list_reverse (required);
+  required = g_list_reverse (required);
+    
+  for (l = project->priv->unknown_catalogs; l; l = g_list_next (l))
+    {
+      CatalogInfo *data = l->data;
+      /* Keep position to make sure we do not create a diff when saving */
+      required = g_list_insert (required, g_strdup (data->catalog), data->position);
+    }
+  
+  return required;
 }
 
 /**
@@ -4742,8 +4878,8 @@ gtk_tree_model_iface_init (GtkTreeModelIface *iface)
 void
 glade_project_copy_selection (GladeProject *project)
 {
-  GList       *widgets = NULL, *list;
-  GladeWidget *widget;
+  GList *widgets = NULL, *list;
+  gboolean has_unknown = FALSE;
 
   g_return_if_fail (GLADE_IS_PROJECT (project));
 
@@ -4759,12 +4895,19 @@ glade_project_copy_selection (GladeProject *project)
 
   for (list = project->priv->selection; list && list->data; list = list->next)
     {
-      widget = glade_widget_get_from_gobject (list->data);
-      widget = glade_widget_dup (widget, FALSE);
-
-      widgets = g_list_prepend (widgets, widget);
+      if (GLADE_IS_OBJECT_STUB (list->data))
+        has_unknown = TRUE;
+      else
+        {
+          GladeWidget *widget = glade_widget_get_from_gobject (list->data);
+          widgets = g_list_prepend (widgets, glade_widget_dup (widget, FALSE));
+        }
     }
 
+  if (has_unknown)
+    glade_util_ui_message (glade_app_get_window (),
+                           GLADE_UI_INFO, NULL, _("Unknown widgets ignored."));
+  
   glade_clipboard_add (glade_app_get_clipboard (), widgets);
   g_list_free (widgets);
 }
@@ -4773,7 +4916,7 @@ void
 glade_project_command_cut (GladeProject *project)
 {
   GList *widgets = NULL, *list;
-  GladeWidget *widget;
+  gboolean has_unknown = FALSE;
   gboolean failed = FALSE;
 
   g_return_if_fail (GLADE_IS_PROJECT (project));
@@ -4783,12 +4926,20 @@ glade_project_command_cut (GladeProject *project)
 
   for (list = project->priv->selection; list && list->data; list = list->next)
     {
-      widget  = glade_widget_get_from_gobject (list->data);
-      widgets = g_list_prepend (widgets, widget);
+      if (GLADE_IS_OBJECT_STUB (list->data))
+        has_unknown = TRUE;
+      else
+        {
+          GladeWidget *widget = glade_widget_get_from_gobject (list->data);
+          widgets = g_list_prepend (widgets, widget);
+        }
     }
-
+  
   if (failed == FALSE && widgets != NULL)
     glade_command_cut (widgets);
+  else if (has_unknown)
+    glade_util_ui_message (glade_app_get_window (),
+                           GLADE_UI_INFO, NULL, _("Unknown widgets ignored."));
   else if (widgets == NULL)
     glade_util_ui_message (glade_app_get_window (),
                            GLADE_UI_INFO, NULL, _("No widget selected."));
diff --git a/gladeui/glade-utils.c b/gladeui/glade-utils.c
index 358c5e6..140854d 100644
--- a/gladeui/glade-utils.c
+++ b/gladeui/glade-utils.c
@@ -249,15 +249,18 @@ glade_util_ui_message (GtkWidget * parent,
 
   dialog = gtk_message_dialog_new (GTK_WINDOW (parent),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
-                                   message_type, buttons_type, "%s", string);
-
-  gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+                                   message_type, buttons_type, NULL);
+  
+  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string);
 
   if (widget)
-    gtk_box_pack_end (GTK_BOX
-                      (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
-                      widget, TRUE, TRUE, 2);
-
+    {
+      gtk_box_pack_end (GTK_BOX
+                        (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+                        widget, TRUE, TRUE, 2);
+      gtk_widget_show (widget);
+    }
+  
   response = gtk_dialog_run (GTK_DIALOG (dialog));
 
   gtk_widget_destroy (dialog);
diff --git a/gladeui/glade-widget.c b/gladeui/glade-widget.c
index b28b94c..1130d3f 100644
--- a/gladeui/glade-widget.c
+++ b/gladeui/glade-widget.c
@@ -56,7 +56,7 @@
 #include "glade-design-view.h"
 #include "glade-widget-action.h"
 #include "glade-signal-model.h"
-
+#include "glade-object-stub.h"
 
 static void glade_widget_set_adaptor (GladeWidget * widget,
                                       GladeWidgetAdaptor * adaptor);
@@ -3779,7 +3779,22 @@ glade_widget_read (GladeProject * project,
                 }
 
               glade_widget_adaptor_read_widget (adaptor, widget, node);
-
+            }
+          else
+            {
+              GObject *stub = g_object_new (GLADE_TYPE_OBJECT_STUB,
+                                            "object-type", klass,
+                                            "xml-node", node,
+                                            NULL);
+              
+              widget = glade_widget_adaptor_create_widget (glade_widget_adaptor_get_by_type (GTK_TYPE_WIDGET),
+                                                           FALSE,
+                                                           "parent", parent,
+                                                           "project", project,
+                                                           "reason", GLADE_CREATE_LOAD,
+                                                           "object", stub,
+                                                           "name", id,
+                                                           NULL);
             }
           g_free (id);
         }
@@ -3903,9 +3918,18 @@ void
 glade_widget_write (GladeWidget * widget,
                     GladeXmlContext * context, GladeXmlNode * node)
 {
+  GObject *object = glade_widget_get_object (widget);
   GladeXmlNode *widget_node;
   GList *l, *list;
 
+  /* Check if its an unknown object, and use saved xml if so */
+  if (GLADE_IS_OBJECT_STUB (object))
+    {
+      g_object_get (object, "xml-node", &widget_node, NULL);
+      glade_xml_node_append_child (node, widget_node);
+      return;
+    }
+
   widget_node = glade_xml_node_new (context, GLADE_XML_TAG_WIDGET);
   glade_xml_node_append_child (node, widget_node);
 
diff --git a/gladeui/glade-xml-utils.c b/gladeui/glade-xml-utils.c
index 558adcd..29fb2ae 100644
--- a/gladeui/glade-xml-utils.c
+++ b/gladeui/glade-xml-utils.c
@@ -750,6 +750,17 @@ glade_xml_node_new_comment (GladeXmlContext * context, const gchar * comment)
                                             BAD_CAST (comment));
 }
 
+GladeXmlNode *
+glade_xml_node_copy (GladeXmlNode *node)
+{
+  if (node)
+    {
+      xmlNodePtr xnode = (xmlNodePtr) node;
+      return (GladeXmlNode *) xmlDocCopyNode (xnode, NULL, 1);
+    }
+  else
+    return NULL;
+}
 
 void
 glade_xml_node_delete (GladeXmlNode * node)
diff --git a/gladeui/glade-xml-utils.h b/gladeui/glade-xml-utils.h
index c07da74..c7b116f 100644
--- a/gladeui/glade-xml-utils.h
+++ b/gladeui/glade-xml-utils.h
@@ -219,6 +219,7 @@ void glade_xml_node_set_property_boolean (GladeXmlNode *node_in, const gchar *na
 /* Node operations */
 GladeXmlNode * glade_xml_node_new (GladeXmlContext *context, const gchar *name);
 GladeXmlNode * glade_xml_node_new_comment (GladeXmlContext *context, const gchar *comment);
+GladeXmlNode * glade_xml_node_copy (GladeXmlNode *node);
 void           glade_xml_node_delete (GladeXmlNode *node);
 GladeXmlNode * glade_xml_node_get_children (GladeXmlNode *node);
 GladeXmlNode * glade_xml_node_get_parent (GladeXmlNode *node_in);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]