[glade] GladeCatalog: Load templates from extra catalog paths.



commit 286555acc6754954dc1094ba7169f8432ed1c7e0
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Mon Aug 17 19:08:46 2020 -0300

    GladeCatalog: Load templates from extra catalog paths.
    
    Load template files as new GObject types and add them to
    "User templates" widget group.

 gladeui/glade-catalog.c        |  88 +++++++++++++++++++++++
 gladeui/glade-private.h        |  17 ++++-
 gladeui/glade-template.c       | 154 +++++++++++++++++++++++++++++------------
 gladeui/glade-utils.c          |  20 +++---
 gladeui/glade-widget-adaptor.c |  30 +++++++-
 5 files changed, 250 insertions(+), 59 deletions(-)
---
diff --git a/gladeui/glade-catalog.c b/gladeui/glade-catalog.c
index c629cc1c..6b34d2ed 100644
--- a/gladeui/glade-catalog.c
+++ b/gladeui/glade-catalog.c
@@ -607,6 +607,91 @@ glade_catalog_get_extra_paths (void)
   return catalog_paths;
 }
 
+static void
+load_templates_from_path (GladeCatalog *catalog,
+                          GladeWidgetGroup *group,
+                          const gchar *path)
+{
+  GError *error = NULL;
+  GDir *dir;
+
+  if (!g_file_test (path, G_FILE_TEST_IS_DIR))
+    return;
+
+  if ((dir = g_dir_open (path, 0, &error)) != NULL)
+    {
+      const gchar *filename;
+      GladeXmlNode *class_node = glade_xml_node_new (catalog->context,
+                                                     GLADE_TAG_GLADE_WIDGET_CLASS);
+
+      while ((filename = g_dir_read_name (dir)))
+        {
+          g_autofree gchar *tmpl_type = NULL, *tmpl_parent = NULL;
+          g_autofree gchar *abs_filename = NULL, *generic_name = NULL;
+          GladeWidgetAdaptor *adaptor;
+
+          if (!g_str_has_suffix (filename, ".ui") &&
+              !g_str_has_suffix (filename, ".glade"))
+            continue;
+
+          /* Allways use absolute paths */
+          if (g_path_is_absolute (filename))
+            abs_filename = g_strdup (filename);
+          else
+            abs_filename = g_build_filename (path, filename, NULL);
+
+          /* Load template and parse type and parent */
+          if (!_glade_template_load (abs_filename, &tmpl_type, &tmpl_parent))
+            continue;
+
+          /* Add data to class_node */
+          generic_name = g_ascii_strdown (tmpl_type, -1);
+          glade_xml_node_set_property_string (class_node, GLADE_TAG_NAME, tmpl_type);
+          glade_xml_node_set_property_string (class_node, GLADE_XML_TAG_TEMPLATE, abs_filename);
+          glade_xml_node_set_property_string (class_node, GLADE_TAG_TITLE, tmpl_type);
+          glade_xml_node_set_property_string (class_node, GLADE_TAG_GENERIC_NAME, generic_name);
+
+          /* Load from fake catalog */
+          adaptor = glade_widget_adaptor_from_catalog (catalog, class_node, NULL);
+
+          /* Append adaptor to catalog */
+          catalog->adaptors = g_list_prepend (catalog->adaptors, adaptor);
+
+          /* And group */
+          group->adaptors = g_list_prepend (group->adaptors, adaptor);
+        }
+
+      g_dir_close (dir);
+    }
+}
+
+static GList *
+load_user_templates_catalog (GList *catalogs)
+{
+  GladeWidgetGroup *group;
+  GladeCatalog *catalog;
+  GList *l;
+
+  /* Use just one group for all the adaptors */
+  group = g_slice_new0 (GladeWidgetGroup);
+  group->name = g_strdup ("user-templates");
+  group->title = g_strdup (_("User templates"));
+  group->expanded = FALSE;
+
+  /* Create a runtime catalog for user templates */
+  catalog = catalog_allocate ();
+  catalog->context = glade_xml_context_new (glade_xml_doc_new (), NULL);
+  catalog->name = g_strdup( "user-templates");
+  catalog->widget_groups = g_list_prepend (NULL, group);
+
+  /* Load templates from extra catalog paths */
+  for (l = catalog_paths; l; l = g_list_next (l))
+    load_templates_from_path (catalog, group, l->data);
+
+  /* Prepend instead of append to give priority over other catalogs */
+  return g_list_prepend (catalogs, catalog);
+}
+
 /**
  * glade_catalog_load_all:
  * 
@@ -694,6 +779,9 @@ glade_catalog_load_all (void)
 
   g_list_free (adaptors);
 
+  /* Load User defined templates */
+  catalogs = load_user_templates_catalog (catalogs);
+
   if (icon_warning)
     {
       g_message ("%s", icon_warning->str);
diff --git a/gladeui/glade-private.h b/gladeui/glade-private.h
index 016184c0..a5c744ef 100644
--- a/gladeui/glade-private.h
+++ b/gladeui/glade-private.h
@@ -87,6 +87,8 @@ _glade_property_def_reset_version (GladePropertyDef *property_def);
 
 /* glade-utils.c */
 
+gchar *_glade_util_compose_get_type_func (const gchar *name);
+
 void   _glade_util_dialog_set_hig (GtkDialog *dialog);
 
 gchar *_glade_util_strreplace (gchar *str,
@@ -104,9 +106,18 @@ void    _glade_xml_error_reset_last       (void);
 gchar  *_glade_xml_error_get_last_message (void);
 
 /* glade-template.c */
-GType _glade_template_generate_type_from_file (GladeCatalog *catalog,
-                                               const gchar  *parent,
-                                               const gchar  *filename);
+gchar   *_glade_template_load (const gchar *filename,
+                               gchar      **type,
+                               gchar      **parent);
+
+gboolean _glade_template_parse (const gchar *tmpl,
+                                gchar      **type,
+                                gchar      **parent);
+
+GType    _glade_template_generate_type (const gchar *type,
+                                        const gchar *parent);
+
+const gchar *_glade_template_lookup (const gchar *type);
 
 G_END_DECLS
 
diff --git a/gladeui/glade-template.c b/gladeui/glade-template.c
index 067eeedb..b5248ecb 100644
--- a/gladeui/glade-template.c
+++ b/gladeui/glade-template.c
@@ -1,7 +1,7 @@
 /*
  * glade-template.c:
  *
- * Copyright (C) 2017-2018 Juan Pablo Ugarte.
+ * Copyright (C) 2017-2020 Juan Pablo Ugarte.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as
@@ -72,74 +72,140 @@ start_element (GMarkupParseContext *context,
       data->parent = g_strdup (attribute_values[i]);
 }
 
-GType
-_glade_template_generate_type_from_file (GladeCatalog *catalog,
-                                         const gchar  *name,
-                                         const gchar  *filename)
+gboolean
+_glade_template_parse (const gchar *tmpl, gchar **type, gchar **parent)
 {
   GMarkupParser parser = { start_element, NULL, };
   GMarkupParseContext *context;
   ParserData data = { 0, };
-  GType parent_type;
-  GTypeQuery query;
-  GTypeInfo *info;
-  gchar *template = NULL;
-  gsize len = 0;
-  GError *error = NULL;
 
-  g_return_val_if_fail (name != NULL, 0);
-  g_return_val_if_fail (filename != NULL, 0);
+  context = g_markup_parse_context_new (&parser, 0, &data, NULL);
+
+  g_markup_parse_context_parse (context, tmpl, -1, NULL);
 
-  if (g_path_is_absolute (filename))
-    g_file_get_contents (filename, &template, &len, &error);
-  else
+  /*
+  while (g_markup_parse_context_parse (context, tmpl, 128, NULL) &&
+         (data.class == NULL || data.parent == NULL))
+    tmpl += 128;
+*/
+
+  g_markup_parse_context_end_parse (context, NULL);
+
+  if (data.class && data.parent)
     {
-      gchar *fullpath = g_build_filename (glade_catalog_get_prefix (catalog),
-                                          filename, NULL);
-      g_file_get_contents (fullpath, &template, &len, &error);
-      g_free (fullpath);
+      *type = data.class;
+      *parent = data.parent;
+
+      return TRUE;
     }
 
-  if (error)
+  g_free (data.class);
+  g_free (data.parent);
+
+  return FALSE;
+}
+
+static GType
+get_type_from_name (const gchar *name)
+{
+  g_autofree gchar *func_name = NULL;
+  static GModule *allsymbols = NULL;
+  GType (*get_type) (void);
+  GType type = 0;
+
+  if (g_once_init_enter (&allsymbols))
     {
-      g_warning ("Error loading template file %s for %s class - %s", filename, name, error->message);
-      return G_TYPE_INVALID;
+      GModule *symbols = g_module_open (NULL, 0);
+      g_once_init_leave (&allsymbols, symbols);
     }
 
-  context = g_markup_parse_context_new (&parser, 0, &data, NULL);
-  g_markup_parse_context_parse (context, template, -1, NULL);
-  g_markup_parse_context_end_parse (context, NULL);
-
-  if (!g_str_equal (name, data.class))
+  if ((type = g_type_from_name (name)) == 0 &&
+      ((func_name = _glade_util_compose_get_type_func (name)) != NULL))
     {
-      g_warning ("Template %s is for class %s, not %s", filename, data.class, name);
-      return G_TYPE_INVALID;
+      if (g_module_symbol (allsymbols, func_name, (gpointer) & get_type))
+        {
+          g_assert (get_type);
+          type = get_type ();
+        }
     }
 
-  parent_type = glade_util_get_type_from_name (data.parent, FALSE);
-  g_return_val_if_fail (parent_type != 0, 0);
+  return type;
+}
 
-  g_type_query (parent_type, &query);
-  g_return_val_if_fail (query.type != 0, 0);
+gchar *
+_glade_template_load (const gchar *filename, gchar **type, gchar **parent)
+{
+  g_autoptr(GError) error = NULL;
+  gchar *template = NULL;
+  gsize len = 0;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+
+  g_file_get_contents (filename, &template, &len, &error);
+
+  if (error)
+    g_warning ("Error loading template file %s - %s", filename, error->message);
 
-  if (g_once_init_enter (&templates))
+  if (!template || !_glade_template_parse (template, type, parent))
+    *type = *parent = NULL;
+  else
     {
-      GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                                 (GDestroyNotify) g_free,
-                                                 (GDestroyNotify) g_bytes_unref);
-      g_once_init_leave (&templates, table);
+      GType tmpl_type = get_type_from_name (*type);
+
+      /* Type is already registered, do not try to load it as a template */
+      if (tmpl_type != G_TYPE_INVALID)
+        {
+          g_clear_pointer (type, g_free);
+          g_clear_pointer (parent, g_free);
+          g_free (template);
+          return NULL;
+        }
+
+      if (g_once_init_enter (&templates))
+        {
+          GHashTable *table = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                     (GDestroyNotify) g_free,
+                                                     (GDestroyNotify) g_bytes_unref);
+          g_once_init_leave (&templates, table);
+        }
+
+      /* Add template to global hash table */
+      g_hash_table_insert (templates,
+                           g_strdup (*type),
+                           g_bytes_new_take (template, len));
     }
 
-  /* Add template to global hash table */
-  g_hash_table_insert (templates, g_strdup (name), g_bytes_new_take (template, len));
+  return template;
+}
+
+GType
+_glade_template_generate_type (const gchar *type, const gchar *parent)
+{
+  GType parent_type;
+  GTypeQuery query;
+  GTypeInfo *info;
+
+  /* make sure the template is loaded */
+  g_return_val_if_fail (g_hash_table_lookup (templates, type) != NULL, G_TYPE_INVALID);
+
+  parent_type = glade_util_get_type_from_name (parent, FALSE);
+  g_return_val_if_fail (parent_type != 0, 0);
+
+  g_type_query (parent_type, &query);
+  g_return_val_if_fail (query.type != 0, 0);
 
   info = g_new0 (GTypeInfo, 1);
   info->class_size = query.class_size;
   info->instance_size = query.instance_size;
   info->instance_init = glade_template_instance_init;
 
-  g_free (data.class);
-  g_free (data.parent);
+  return g_type_register_static (parent_type, type, info, 0);
+}
 
-  return g_type_register_static (parent_type, name, info, 0);
+const gchar *
+_glade_template_lookup (const gchar *type)
+{
+  GBytes *tmpl = g_hash_table_lookup (templates, type);
+  return (tmpl) ? g_bytes_get_data (tmpl, NULL) : "";
 }
+
diff --git a/gladeui/glade-utils.c b/gladeui/glade-utils.c
index e7d91636..904e3ebb 100644
--- a/gladeui/glade-utils.c
+++ b/gladeui/glade-utils.c
@@ -65,8 +65,8 @@
  *
  * Returns: the type function getter
  */
-static gchar *
-glade_util_compose_get_type_func (const gchar *name)
+gchar *
+_glade_util_compose_get_type_func (const gchar *name)
 {
   gchar *retval;
   GString *tmp;
@@ -118,23 +118,23 @@ glade_util_get_type_from_name (const gchar *name, gboolean have_func)
   GType type = 0;
   gchar *func_name = (gchar *) name;
 
+  if (g_once_init_enter (&allsymbols))
+    {
+      GModule *symbols = g_module_open (NULL, 0);
+      g_once_init_leave (&allsymbols, symbols);
+    }
+
   if ((type = g_type_from_name (name)) == 0 &&
       (have_func ||
-       (func_name = glade_util_compose_get_type_func (name)) != NULL))
+       (func_name = _glade_util_compose_get_type_func (name)) != NULL))
     {
-
-      if (!allsymbols)
-        allsymbols = g_module_open (NULL, 0);
-
       if (g_module_symbol (allsymbols, func_name, (gpointer) & get_type))
         {
           g_assert (get_type);
           type = get_type ();
         }
       else
-        {
-          g_warning (_("We could not find the symbol \"%s\""), func_name);
-        }
+        g_warning (_("We could not find the symbol \"%s\""), func_name);
 
       if (!have_func)
         g_free (func_name);
diff --git a/gladeui/glade-widget-adaptor.c b/gladeui/glade-widget-adaptor.c
index f26f3914..7cbe3b41 100644
--- a/gladeui/glade-widget-adaptor.c
+++ b/gladeui/glade-widget-adaptor.c
@@ -2761,7 +2761,9 @@ glade_widget_adaptor_from_catalog (GladeCatalog *catalog,
   gchar *name, *generic_name, *icon_name, *adaptor_icon_name, *adaptor_name,
       *func_name = NULL, *template;
   gchar *title, *translated_title, *parent_name;
-  GType object_type, adaptor_type, parent_type;
+  GType object_type = G_TYPE_INVALID;
+  GType adaptor_type = G_TYPE_INVALID;
+  GType parent_type = G_TYPE_INVALID;
   gchar *missing_icon = NULL;
   GWADerivedClassData data;
 
@@ -2808,7 +2810,31 @@ glade_widget_adaptor_from_catalog (GladeCatalog *catalog,
             glade_xml_get_property_string (class_node,
                                            GLADE_XML_TAG_TEMPLATE)) != NULL)
     {
-      object_type = _glade_template_generate_type_from_file (catalog, name, template);
+      g_autofree gchar *tmpl_type = NULL, *tmpl_parent = NULL;
+      const gchar *tmpl = NULL;
+
+      if ((tmpl = _glade_template_lookup (name)))
+        _glade_template_parse (tmpl, &tmpl_type, &tmpl_parent);
+      else
+        {
+          if (g_path_is_absolute (template))
+            tmpl = _glade_template_load (template, &tmpl_type, &tmpl_parent);
+          else
+            {
+              const gchar *prefix = glade_catalog_get_prefix (catalog);
+              g_autofree gchar *fullpath = g_build_filename (prefix, template, NULL);
+              tmpl = _glade_template_load (fullpath, &tmpl_type, &tmpl_parent);
+            }
+        }
+
+      if (tmpl && tmpl_type && tmpl_parent)
+        {
+          if (g_str_equal (name, tmpl_type))
+            object_type = _glade_template_generate_type (tmpl_type, tmpl_parent);
+          else
+            g_warning ("Template %s is for class %s, not %s", template, tmpl_type, name);
+        }
+
       g_free (template);
     }
   else


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