]> git.sur5r.net Git - glabels/blobdiff - libglabels/lgl-db.c
Imported Upstream version 3.0.0
[glabels] / libglabels / lgl-db.c
diff --git a/libglabels/lgl-db.c b/libglabels/lgl-db.c
new file mode 100644 (file)
index 0000000..0225a98
--- /dev/null
@@ -0,0 +1,2123 @@
+/*
+ *  lgl-db.c
+ *  Copyright (C) 2003-2010  Jim Evins <evins@snaught.com>.
+ *
+ *  This file is part of libglabels.
+ *
+ *  libglabels 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  libglabels 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 libglabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "lgl-db.h"
+
+#include <glib/gi18n.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "libglabels-private.h"
+
+#include "lgl-xml-paper.h"
+#include "lgl-xml-category.h"
+#include "lgl-xml-vendor.h"
+#include "lgl-xml-template.h"
+
+/*===========================================*/
+/* Private macros and constants.             */
+/*===========================================*/
+
+/* Data system and user data directories.  (must free w/ g_free()) */
+#define SYSTEM_CONFIG_DIR     g_build_filename (LIBGLABELS_CONFIG_DIR, "templates", NULL)
+#define USER_CONFIG_DIR       g_build_filename (g_get_user_config_dir (), "libglabels", "templates" , NULL)
+#define ALT_USER_CONFIG_DIR   g_build_filename (g_get_home_dir (), ".glabels", NULL)
+
+
+/*===========================================*/
+/* Private types                             */
+/*===========================================*/
+
+#define TYPE_LGL_DB_MODEL              (lgl_db_model_get_type ())
+#define LGL_DB_MODEL(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_LGL_DB_MODEL, lglDbModel))
+#define LGL_DB_MODEL_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_LGL_DB_MODEL, lglDbModelClass))
+#define IS_LGL_DB_MODEL(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_LGL_DB_MODEL))
+#define IS_LGL_DB_MODEL_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_LGL_DB_MODEL))
+#define LGL_DB_MODEL_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), TYPE_LGL_DB_MODEL, lglDbModelClass))
+
+
+typedef struct _lglDbModel          lglDbModel;
+typedef struct _lglDbModelClass     lglDbModelClass;
+
+
+struct _lglDbModel {
+        GObject     parent;
+
+        GList      *papers;
+        GList      *categories;
+        GList      *vendors;
+        GList      *templates;
+
+        GHashTable *template_cache;
+};
+
+
+struct _lglDbModelClass {
+        GObjectClass  parent_class;
+
+        /*
+         * Signals
+         */
+        void (*changed)     (lglDbModel *this,
+                             gpointer    user_data);
+
+};
+
+
+enum {
+        CHANGED,
+        LAST_SIGNAL
+};
+
+
+/*===========================================*/
+/* Private globals                           */
+/*===========================================*/
+
+static guint signals[LAST_SIGNAL] = {0};
+
+static lglDbModel *model = NULL;
+
+
+/*===========================================*/
+/* Local function prototypes                 */
+/*===========================================*/
+
+static void   lgl_db_model_finalize        (GObject     *object);
+
+static void   add_to_template_cache        (lglTemplate *template);
+
+static GList *read_papers                  (void);
+static GList *read_paper_files_from_dir    (GList       *papers,
+                                            const gchar *dirname);
+
+static GList *read_categories              (void);
+static GList *read_category_files_from_dir (GList       *categories,
+                                            const gchar *dirname);
+
+static GList *read_vendors                 (void);
+static GList *read_vendor_files_from_dir   (GList       *vendors,
+                                            const gchar *dirname);
+
+static void   read_templates               (void);
+static void   read_template_files_from_dir (const gchar *dirname);
+
+static lglTemplate *template_full_page     (const gchar *page_size);
+
+
+/*****************************************************************************/
+/* Object infrastructure.                                                    */
+/*****************************************************************************/
+G_DEFINE_TYPE (lglDbModel, lgl_db_model, G_TYPE_OBJECT);
+
+
+/*****************************************************************************/
+/* Class Init Function.                                                      */
+/*****************************************************************************/
+static void
+lgl_db_model_class_init (lglDbModelClass *class)
+{
+        GObjectClass  *gobject_class = (GObjectClass *) class;
+
+        lgl_db_model_parent_class = g_type_class_peek_parent (class);
+
+        gobject_class->finalize = lgl_db_model_finalize;
+
+        signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_OBJECT_CLASS_TYPE (gobject_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (lglDbModelClass, changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+}
+
+
+/*****************************************************************************/
+/* Object Instance Init Function.                                            */
+/*****************************************************************************/
+static void
+lgl_db_model_init (lglDbModel *this)
+{
+        this->template_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)lgl_template_free);
+}
+
+
+/*****************************************************************************/
+/* Finalize Method.                                                          */
+/*****************************************************************************/
+static void
+lgl_db_model_finalize (GObject *object)
+{
+        lglDbModel    *this;
+        GList         *p;
+
+        g_return_if_fail (object && IS_LGL_DB_MODEL (object));
+        this = LGL_DB_MODEL (object);
+
+        g_hash_table_unref (this->template_cache);
+
+        for (p = this->papers; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+        g_list_free (this->papers);
+
+        for (p = this->categories; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+        g_list_free (this->categories);
+
+        for (p = this->vendors; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+        g_list_free (this->vendors);
+
+        for (p = this->templates; p != NULL; p = p->next)
+        {
+                lgl_template_free ((lglTemplate *)p->data);
+                p->data = NULL;
+        }
+        g_list_free (this->templates);
+
+        G_OBJECT_CLASS (lgl_db_model_parent_class)->finalize (object);
+}
+
+
+/*****************************************************************************/
+/** New Object Generator.                                                    */
+/*****************************************************************************/
+lglDbModel *
+lgl_db_model_new (void)
+{
+        lglDbModel *this;
+
+        this = g_object_new (TYPE_LGL_DB_MODEL, NULL);
+
+        return this;
+}
+
+
+/*===========================================*/
+/* Module initialization                     */
+/*===========================================*/
+
+/**
+ * lgl_db_init:
+ *
+ * Initialize all libglabels subsystems.  It is not necessary for an application to call
+ * lgl_db_init(), because libglabels will initialize on demand.  An application programmer may
+ * choose to call lgl_db_init() at startup to minimize the impact of the first libglabels call
+ * on GUI response time.
+ *
+ * This function initializes its paper definitions, category definitions, vendor definitions,
+ * and its template database. It will search both system and user template directories to locate
+ * this data.
+ */
+void
+lgl_db_init (void)
+{
+        lglPaper    *paper_other;
+        lglCategory *category_user_defined;
+        lglTemplate *template;
+        GList       *page_sizes;
+        GList       *p;
+
+        model = lgl_db_model_new ();
+
+        /*
+         * Paper definitions
+         */
+        model->papers = read_papers ();
+
+        /* Create and append an "Other" entry. */
+        /* Translators: "Other" here means other page size.  Meaning a page size
+         * other than the standard ones that libglabels knows about such as
+         * "letter", "A4", etc. */
+        paper_other = lgl_paper_new ("Other", _("Other"), 0.0, 0.0, NULL);
+        model->papers = g_list_append (model->papers, paper_other);
+
+        /*
+         * Categories
+         */
+        model->categories = read_categories ();
+
+        /* Create and append a "User defined" entry. */
+        category_user_defined = lgl_category_new ("user-defined", _("User defined"));
+        model->categories = g_list_append (model->categories, category_user_defined);
+
+        /*
+         * Vendors
+         */
+        model->vendors = read_vendors ();
+
+        /*
+         * Templates
+         */
+        read_templates ();
+
+        /* Create and append generic full page templates. */
+        page_sizes = lgl_db_get_paper_id_list ();
+        for ( p=page_sizes; p != NULL; p=p->next )
+        {
+                if ( !lgl_db_is_paper_id_other (p->data) )
+                {
+                        template = template_full_page (p->data);
+                        _lgl_db_register_template_internal (template);
+                        lgl_template_free (template);
+                }
+        }
+        lgl_db_free_paper_id_list (page_sizes);
+
+}
+
+
+/**
+ * lgl_db_notify_add:
+ * @func: Callback function to be called when database changes.
+ * @user_data: Passback user data to supply to callback function.
+ *
+ * Register a notification callback function to be called when the database changes.
+ *
+ * Returns: an ID for this notification registration.
+ */
+gulong
+lgl_db_notify_add (lglDbNotifyFunc func,
+                   gpointer        user_data)
+{
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        return g_signal_connect_swapped (G_OBJECT (model), "changed", G_CALLBACK (func), user_data);
+}
+
+
+/**
+ * lgl_db_notify_remove:
+ * @id: ID of notification registration to cancel (see lgl_db_notify_add()).
+ *
+ * Cancel a previous registration a notification callback function.
+ */
+void
+lgl_db_notify_remove  (gulong id)
+{
+        g_signal_handler_disconnect (G_OBJECT (model), id);
+}
+
+
+/*===========================================*/
+/* Paper db functions.                       */
+/*===========================================*/
+
+/**
+ * lgl_db_get_paper_id_list:
+ *
+ * Get a list of all paper ids known to libglabels.
+ *
+ * Returns: a list of paper ids.
+ *
+ */
+GList *
+lgl_db_get_paper_id_list (void)
+{
+        GList           *ids = NULL;
+        GList           *p;
+        lglPaper        *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for ( p=model->papers; p != NULL; p=p->next )
+        {
+                paper = (lglPaper *)p->data;
+                ids = g_list_append (ids, g_strdup (paper->id));
+        }
+
+        return ids;
+}
+
+
+/**
+ * lgl_db_free_paper_id_list:
+ * @ids: List of id strings to be freed.
+ *
+ * Free up all storage associated with an id list obtained with
+ * lgl_db_get_paper_id_list().
+ *
+ */
+void
+lgl_db_free_paper_id_list (GList *ids)
+{
+        GList *p;
+
+        for (p = ids; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+
+        g_list_free (ids);
+}
+
+
+/**
+ * lgl_db_get_paper_name_list:
+ *
+ * Get a list of all localized paper names known to libglabels.
+ *
+ * Returns: a list of localized paper names.
+ *
+ */
+GList *
+lgl_db_get_paper_name_list (void)
+{
+        GList           *names = NULL;
+        GList           *p;
+        lglPaper        *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for ( p=model->papers; p != NULL; p=p->next )
+        {
+                paper = (lglPaper *)p->data;
+                names = g_list_append (names, g_strdup (paper->name));
+        }
+
+        return names;
+}
+
+
+/**
+ * lgl_db_free_paper_name_list:
+ * @names: List of localized paper name strings to be freed.
+ *
+ * Free up all storage associated with a name list obtained with
+ * lgl_db_get_paper_name_list().
+ *
+ */
+void
+lgl_db_free_paper_name_list (GList *names)
+{
+        GList *p;
+
+        for (p = names; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+
+        g_list_free (names);
+}
+
+
+/**
+ * lgl_db_lookup_paper_from_name:
+ * @name: localized paper name string
+ *
+ * Lookup paper definition from localized paper name string.
+ *
+ * Returns: pointer to a newly allocated #lglPaper structure.
+ *
+ */
+lglPaper *
+lgl_db_lookup_paper_from_name (const gchar *name)
+{
+        GList       *p;
+        lglPaper    *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                /* If no name, return first paper as a default */
+                return lgl_paper_dup ((lglPaper *) model->papers->data);
+        }
+
+        for (p = model->papers; p != NULL; p = p->next)
+        {
+                paper = (lglPaper *) p->data;
+                if (UTF8_EQUAL (paper->name, name))
+                {
+                        return lgl_paper_dup (paper);
+                }
+        }
+
+        return NULL;
+}
+
+
+/**
+ * lgl_db_lookup_paper_from_id:
+ * @id: paper id string
+ *
+ * Lookup paper definition from id string.
+ *
+ * Returns: pointer to a newly allocated #lglPaper structure.
+ *
+ */
+lglPaper *
+lgl_db_lookup_paper_from_id (const gchar *id)
+{
+        GList       *p;
+        lglPaper    *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (id == NULL)
+        {
+                /* If no id, return first paper as a default */
+                return lgl_paper_dup ((lglPaper *) model->papers->data);
+        }
+
+        for (p = model->papers; p != NULL; p = p->next)
+        {
+                paper = (lglPaper *) p->data;
+                if (ASCII_EQUAL (paper->id, id))
+                {
+                        return lgl_paper_dup (paper);
+                }
+        }
+
+        return NULL;
+}
+
+
+/**
+ * lgl_db_lookup_paper_id_from_name:
+ * @name: localized paper name stringp
+ *
+ * Lookup paper name string from localized paper name string.
+ *
+ * Returns: pointer to a newly allocated id string.
+ *
+ */
+gchar *
+lgl_db_lookup_paper_id_from_name (const gchar *name)
+{
+        lglPaper *paper = NULL;
+        gchar    *id = NULL;
+
+        if (name != NULL)
+        {
+                paper = lgl_db_lookup_paper_from_name (name);
+                if ( paper != NULL )
+                {
+                        id = g_strdup (paper->id);
+                        lgl_paper_free (paper);
+                        paper = NULL;
+                }
+        }
+
+        return id;
+}
+
+
+/**
+ * lgl_db_lookup_paper_name_from_id:
+ * @id: paper id string
+ *
+ * Lookup localized paper name string from paper id string.
+ *
+ * Returns: pointer to a newly allocated localized paper name string.
+ *
+ */
+gchar *
+lgl_db_lookup_paper_name_from_id (const gchar         *id)
+{
+        lglPaper *paper = NULL;
+        gchar    *name = NULL;
+
+        if (id != NULL)
+        {
+                paper = lgl_db_lookup_paper_from_id (id);
+                if ( paper != NULL )
+                {
+                        name = g_strdup (paper->name);
+                        lgl_paper_free (paper);
+                        paper = NULL;
+                }
+        }
+
+        return name;
+}
+
+
+/**
+ * lgl_db_is_paper_id_known:
+ * @id: paper id to test
+ *
+ * Determine if given paper id is known to libglabels.
+ *
+ * Returns: TRUE if id is known, otherwise FALSE.
+ *
+ */
+gboolean
+lgl_db_is_paper_id_known (const gchar *id)
+{
+        GList       *p;
+        lglPaper    *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (id == NULL)
+        {
+                return FALSE;
+        }
+
+        for (p = model->papers; p != NULL; p = p->next)
+        {
+                paper = (lglPaper *) p->data;
+                if (ASCII_EQUAL (paper->id, id))
+                {
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+
+/**
+ * lgl_db_is_paper_id_other:
+ * @id: paper id to test
+ *
+ * Determine if given paper id is the special id "Other."
+ *
+ * Returns: TRUE if id is "Other", otherwise FALSE.
+ *
+ */
+gboolean
+lgl_db_is_paper_id_other (const gchar *id)
+{
+        if (id == NULL)
+        {
+                return FALSE;
+        }
+
+        return (ASCII_EQUAL (id, "Other"));
+}
+
+
+static GList *
+read_papers (void)
+{
+        gchar *data_dir;
+        GList *papers = NULL;
+
+        data_dir = SYSTEM_CONFIG_DIR;
+        papers = read_paper_files_from_dir (papers, data_dir);
+        g_free (data_dir);
+
+        data_dir = USER_CONFIG_DIR;
+        papers = read_paper_files_from_dir (papers, data_dir);
+        g_free (data_dir);
+
+        if (papers == NULL)
+        {
+                g_critical (_("Unable to locate paper size definitions.  Libglabels may not be installed correctly!"));
+        }
+
+        return papers;
+}
+
+
+static GList *
+read_paper_files_from_dir (GList       *papers,
+                           const gchar *dirname)
+{
+        GDir        *dp;
+        const gchar *filename, *extension;
+        gchar       *full_filename = NULL;
+        GError      *gerror = NULL;
+        GList       *new_papers = NULL;
+
+        if (dirname == NULL)
+        {
+                return papers;
+        }
+
+        if (!g_file_test (dirname, G_FILE_TEST_EXISTS))
+        {
+                return papers;
+        }
+
+        dp = g_dir_open (dirname, 0, &gerror);
+        if (gerror != NULL)
+        {
+                g_message ("cannot open data directory: %s", gerror->message );
+                return papers;
+        }
+
+        while ((filename = g_dir_read_name (dp)) != NULL)
+        {
+
+                extension = strrchr (filename, '.');
+
+                if (extension != NULL)
+                {
+
+                        if ( ASCII_EQUAL (filename, "paper-sizes.xml") )
+                        {
+
+                                full_filename =
+                                    g_build_filename (dirname, filename, NULL);
+                                new_papers =
+                                    lgl_xml_paper_read_papers_from_file (full_filename);
+                                g_free (full_filename);
+
+                                papers = g_list_concat (papers, new_papers);
+                                new_papers = NULL;
+
+                        }
+
+                }
+
+        }
+
+        g_dir_close (dp);
+
+        return papers;
+}
+
+
+/**
+ * lgl_db_print_known_papers:
+ *
+ * For debugging purposes: print a list of all paper definitions known to
+ * libglabels.
+ *
+ */
+void
+lgl_db_print_known_papers (void)
+{
+        GList       *p;
+        lglPaper    *paper;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        g_print ("%s():\n", __FUNCTION__);
+        for (p = model->papers; p != NULL; p = p->next)
+        {
+                paper = (lglPaper *) p->data;
+
+                g_print ("PAPER id=\"%s\", name=\"%s\", width=%gpts, height=%gpts\n",
+                         paper->id, paper->name, paper->width, paper->height);
+        }
+        g_print ("\n");
+
+}
+
+
+/*===========================================*/
+/* Category db functions.                    */
+/*===========================================*/
+
+/**
+ * lgl_db_get_category_id_list:
+ *
+ * Get a list of all category ids known to libglabels.
+ *
+ * Returns: a list of category ids.
+ *
+ */
+GList *
+lgl_db_get_category_id_list (void)
+{
+        GList           *ids = NULL;
+        GList           *p;
+        lglCategory     *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for ( p=model->categories; p != NULL; p=p->next )
+        {
+                category = (lglCategory *)p->data;
+                ids = g_list_append (ids, g_strdup (category->id));
+        }
+
+        return ids;
+}
+
+
+/**
+ * lgl_db_free_category_id_list:
+ * @ids: List of id strings to be freed.
+ *
+ * Free up all storage associated with an id list obtained with
+ * lgl_db_get_category_id_list().
+ *
+ */
+void
+lgl_db_free_category_id_list (GList *ids)
+{
+        GList *p;
+
+        for (p = ids; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+
+        g_list_free (ids);
+}
+
+
+/**
+ * lgl_db_get_category_name_list:
+ *
+ * Get a list of all localized category names known to libglabels.
+ *
+ * Returns: a list of localized category names.
+ *
+ */
+GList *
+lgl_db_get_category_name_list (void)
+{
+        GList           *names = NULL;
+        GList           *p;
+        lglCategory     *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for ( p=model->categories; p != NULL; p=p->next )
+        {
+                category = (lglCategory *)p->data;
+                names = g_list_append (names, g_strdup (category->name));
+        }
+
+        return names;
+}
+
+
+/**
+ * lgl_db_free_category_name_list:
+ * @names: List of localized category name strings to be freed.
+ *
+ * Free up all storage associated with a name list obtained with
+ * lgl_db_get_category_name_list().
+ *
+ */
+void
+lgl_db_free_category_name_list (GList *names)
+{
+        GList *p;
+
+        for (p = names; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+
+        g_list_free (names);
+}
+
+
+/**
+ * lgl_db_lookup_category_from_name:
+ * @name: localized category name string
+ *
+ * Lookup category definition from localized category name string.
+ *
+ * Returns: pointer to a newly allocated #lglCategory structure.
+ *
+ */
+lglCategory *
+lgl_db_lookup_category_from_name (const gchar *name)
+{
+        GList       *p;
+        lglCategory *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                /* If no name, return first category as a default */
+                return lgl_category_dup ((lglCategory *) model->categories->data);
+        }
+
+        for (p = model->categories; p != NULL; p = p->next)
+        {
+                category = (lglCategory *) p->data;
+                if (UTF8_EQUAL (category->name, name))
+                {
+                        return lgl_category_dup (category);
+                }
+        }
+
+        return NULL;
+}
+
+
+/**
+ * lgl_db_lookup_category_from_id:
+ * @id: category id string
+ *
+ * Lookup category definition from id string.
+ *
+ * Returns: pointer to a newly allocated #lglCategory structure.
+ *
+ */
+lglCategory *
+lgl_db_lookup_category_from_id (const gchar *id)
+{
+        GList       *p;
+        lglCategory *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (id == NULL)
+        {
+                /* If no id, return first category as a default */
+                return lgl_category_dup ((lglCategory *) model->categories->data);
+        }
+
+        for (p = model->categories; p != NULL; p = p->next)
+        {
+                category = (lglCategory *) p->data;
+                if (ASCII_EQUAL (category->id, id))
+                {
+                        return lgl_category_dup (category);
+                }
+        }
+
+        return NULL;
+}
+
+
+/**
+ * lgl_db_lookup_category_id_from_name:
+ * @name: localized category name stringp
+ *
+ * Lookup category name string from localized category name string.
+ *
+ * Returns: pointer to a newly allocated id string.
+ *
+ */
+gchar *
+lgl_db_lookup_category_id_from_name (const gchar *name)
+{
+        lglCategory *category = NULL;
+        gchar       *id = NULL;
+
+        if (name != NULL)
+        {
+                category = lgl_db_lookup_category_from_name (name);
+                if ( category != NULL )
+                {
+                        id = g_strdup (category->id);
+                        lgl_category_free (category);
+                        category = NULL;
+                }
+        }
+
+        return id;
+}
+
+
+/**
+ * lgl_db_lookup_category_name_from_id:
+ * @id: category id string
+ *
+ * Lookup localized category name string from category id string.
+ *
+ * Returns: pointer to a newly allocated localized category name string.
+ *
+ */
+gchar *
+lgl_db_lookup_category_name_from_id (const gchar         *id)
+{
+        lglCategory *category = NULL;
+        gchar       *name = NULL;
+
+        if (id != NULL)
+        {
+                category = lgl_db_lookup_category_from_id (id);
+                if ( category != NULL )
+                {
+                        name = g_strdup (category->name);
+                        lgl_category_free (category);
+                        category = NULL;
+                }
+        }
+
+        return name;
+}
+
+
+/**
+ * lgl_db_is_category_id_known:
+ * @id: category id to test
+ *
+ * Determine if given category id is known to libglabels.
+ *
+ * Returns: TRUE if id is known, otherwise FALSE.
+ *
+ */
+gboolean
+lgl_db_is_category_id_known (const gchar *id)
+{
+        GList       *p;
+        lglCategory *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (id == NULL)
+        {
+                return FALSE;
+        }
+
+        for (p = model->categories; p != NULL; p = p->next)
+        {
+                category = (lglCategory *) p->data;
+                if (ASCII_EQUAL (category->id, id))
+                {
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+
+static GList *
+read_categories (void)
+{
+        gchar *data_dir;
+        GList *categories = NULL;
+
+        data_dir = SYSTEM_CONFIG_DIR;
+        categories = read_category_files_from_dir (categories, data_dir);
+        g_free (data_dir);
+
+        data_dir = USER_CONFIG_DIR;
+        categories = read_category_files_from_dir (categories, data_dir);
+        g_free (data_dir);
+
+        if (categories == NULL)
+        {
+                g_critical (_("Unable to locate category definitions.  Libglabels may not be installed correctly!"));
+        }
+
+        return categories;
+}
+
+
+static GList *
+read_category_files_from_dir (GList       *categories,
+                              const gchar *dirname)
+{
+        GDir        *dp;
+        const gchar *filename, *extension;
+        gchar       *full_filename = NULL;
+        GError      *gerror = NULL;
+        GList       *new_categories = NULL;
+
+        if (dirname == NULL)
+        {
+                return categories;
+        }
+
+        if (!g_file_test (dirname, G_FILE_TEST_EXISTS))
+        {
+                return categories;
+        }
+
+        dp = g_dir_open (dirname, 0, &gerror);
+        if (gerror != NULL)
+        {
+                g_message ("cannot open data directory: %s", gerror->message );
+                return categories;
+        }
+
+        while ((filename = g_dir_read_name (dp)) != NULL)
+        {
+
+                extension = strrchr (filename, '.');
+
+                if (extension != NULL)
+                {
+
+                        if ( ASCII_EQUAL (filename, "categories.xml") )
+                        {
+
+                                full_filename =
+                                    g_build_filename (dirname, filename, NULL);
+                                new_categories =
+                                    lgl_xml_category_read_categories_from_file (full_filename);
+                                g_free (full_filename);
+
+                                categories = g_list_concat (categories, new_categories);
+                                new_categories = NULL;
+
+                        }
+
+                }
+
+        }
+
+        g_dir_close (dp);
+
+        return categories;
+}
+
+
+/**
+ * lgl_db_print_known_categories:
+ *
+ * For debugging purposes: print a list of all category definitions known to
+ * libglabels.
+ *
+ */
+void
+lgl_db_print_known_categories (void)
+{
+        GList       *p;
+        lglCategory *category;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        g_print ("%s():\n", __FUNCTION__);
+        for (p = model->categories; p != NULL; p = p->next)
+        {
+                category = (lglCategory *) p->data;
+
+                g_print ("CATEGORY id=\"%s\", name=\"%s\"\n", category->id, category->name);
+        }
+        g_print ("\n");
+
+}
+
+
+/*===========================================*/
+/* Vendor db functions.                       */
+/*===========================================*/
+
+/**
+ * lgl_db_get_vendor_name_list:
+ *
+ * Get a list of all localized vendor names known to libglabels.
+ *
+ * Returns: a list of localized vendor names.
+ *
+ */
+GList *
+lgl_db_get_vendor_name_list (void)
+{
+        GList           *names = NULL;
+        GList           *p;
+        lglVendor       *vendor;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for ( p=model->vendors; p != NULL; p=p->next )
+        {
+                vendor = (lglVendor *)p->data;
+                names = g_list_append (names, g_strdup (vendor->name));
+        }
+
+        return names;
+}
+
+
+/**
+ * lgl_db_free_vendor_name_list:
+ * @names: List of localized vendor name strings to be freed.
+ *
+ * Free up all storage associated with a name list obtained with
+ * lgl_db_get_vendor_name_list().
+ *
+ */
+void
+lgl_db_free_vendor_name_list (GList *names)
+{
+        GList *p;
+
+        for (p = names; p != NULL; p = p->next)
+        {
+                g_free (p->data);
+                p->data = NULL;
+        }
+
+        g_list_free (names);
+}
+
+
+/**
+ * lgl_db_lookup_vendor_from_name:
+ * @name: localized vendor name string
+ *
+ * Lookup vendor definition from localized vendor name string.
+ *
+ * Returns: pointer to a newly allocated #lglVendor structure.
+ *
+ */
+lglVendor *
+lgl_db_lookup_vendor_from_name (const gchar *name)
+{
+        GList       *p;
+        lglVendor   *vendor;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                /* If no name, return first vendor as a default */
+                return lgl_vendor_dup ((lglVendor *) model->vendors->data);
+        }
+
+        for (p = model->vendors; p != NULL; p = p->next)
+        {
+                vendor = (lglVendor *) p->data;
+                if (UTF8_EQUAL (vendor->name, name))
+                {
+                        return lgl_vendor_dup (vendor);
+                }
+        }
+
+        return NULL;
+}
+
+
+/**
+ * lgl_db_is_vendor_name_known:
+ * @name: vendor name to test
+ *
+ * Determine if given vendor id is known to libglabels.
+ *
+ * Returns: TRUE if id is known, otherwise FALSE.
+ *
+ */
+gboolean
+lgl_db_is_vendor_name_known (const gchar *name)
+{
+        GList       *p;
+        lglVendor   *vendor;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                return FALSE;
+        }
+
+        for (p = model->vendors; p != NULL; p = p->next)
+        {
+                vendor = (lglVendor *) p->data;
+                if (UTF8_EQUAL (vendor->name, name))
+                {
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+
+static GList *
+read_vendors (void)
+{
+        gchar *data_dir;
+        GList *vendors = NULL;
+
+        data_dir = SYSTEM_CONFIG_DIR;
+        vendors = read_vendor_files_from_dir (vendors, data_dir);
+        g_free (data_dir);
+
+        data_dir = USER_CONFIG_DIR;
+        vendors = read_vendor_files_from_dir (vendors, data_dir);
+        g_free (data_dir);
+
+        return vendors;
+}
+
+
+static GList *
+read_vendor_files_from_dir (GList      *vendors,
+                           const gchar *dirname)
+{
+        GDir        *dp;
+        const gchar *filename, *extension;
+        gchar       *full_filename = NULL;
+        GError      *gerror = NULL;
+        GList       *new_vendors = NULL;
+
+        if (dirname == NULL)
+        {
+                return vendors;
+        }
+
+        if (!g_file_test (dirname, G_FILE_TEST_EXISTS))
+        {
+                return vendors;
+        }
+
+        dp = g_dir_open (dirname, 0, &gerror);
+        if (gerror != NULL)
+        {
+                g_message ("cannot open data directory: %s", gerror->message );
+                return vendors;
+        }
+
+        while ((filename = g_dir_read_name (dp)) != NULL)
+        {
+
+                extension = strrchr (filename, '.');
+
+                if (extension != NULL)
+                {
+
+                        if ( ASCII_EQUAL (filename, "vendors.xml") )
+                        {
+
+                                full_filename =
+                                    g_build_filename (dirname, filename, NULL);
+                                new_vendors =
+                                    lgl_xml_vendor_read_vendors_from_file (full_filename);
+                                g_free (full_filename);
+
+                                vendors = g_list_concat (vendors, new_vendors);
+                                new_vendors = NULL;
+
+                        }
+
+                }
+
+        }
+
+        g_dir_close (dp);
+
+        return vendors;
+}
+
+
+/**
+ * lgl_db_print_known_vendors:
+ *
+ * For debugging purposes: print a list of all vendor definitions known to
+ * libglabels.
+ *
+ */
+void
+lgl_db_print_known_vendors (void)
+{
+        GList       *p;
+        lglVendor   *vendor;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        g_print ("%s():\n", __FUNCTION__);
+        for (p = model->vendors; p != NULL; p = p->next)
+        {
+                vendor = (lglVendor *) p->data;
+
+                g_print ("VENDOR name=\"%s\", url=\"%s\"\n",
+                         vendor->name, vendor->url);
+        }
+        g_print ("\n");
+
+}
+
+
+/*===========================================*/
+/* Brand db functions.                       */
+/*===========================================*/
+
+/**
+ * lgl_db_get_brand_list:
+ * @paper_id: If non NULL, limit results to given page size.
+ * @category_id: If non NULL, limit results to given template category.
+ *
+ * Get a list of all valid brands of templates in the template database.
+ * Results can be filtered by page size and/or template category.  A list of valid page
+ * sizes can be obtained using lgl_db_get_paper_id_list().  A list of valid template
+ * categories can be obtained using lgl_db_get_category_id_list().
+ *
+ * Returns: a list of brands
+ */
+GList *
+lgl_db_get_brand_list (const gchar *paper_id,
+                       const gchar *category_id)
+{
+        GList            *p_tmplt;
+        lglTemplate      *template;
+        GList            *brands = NULL;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+        {
+                template = (lglTemplate *) p_tmplt->data;
+                if (lgl_template_does_page_size_match (template, paper_id) &&
+                    lgl_template_does_category_match (template, category_id))
+                {
+
+                        if ( !g_list_find_custom (brands, template->brand,
+                                                  (GCompareFunc)lgl_str_utf8_casecmp) )
+                        {
+                                brands = g_list_insert_sorted (brands,
+                                                               g_strdup (template->brand),
+                                                               (GCompareFunc)lgl_str_utf8_casecmp);
+                        }
+                }
+        }
+
+        return brands;
+}
+
+
+/**
+ * lgl_db_free_brand_list:
+ * @brands: List of template brand strings to be freed.
+ *
+ * Free up all storage associated with a list of template names obtained with
+ * lgl_db_get_brand_list().
+ *
+ */
+void
+lgl_db_free_brand_list (GList *brands)
+{
+        GList *p_brand;
+
+        for (p_brand = brands; p_brand != NULL; p_brand = p_brand->next)
+        {
+                g_free (p_brand->data);
+                p_brand->data = NULL;
+        }
+
+        g_list_free (brands);
+}
+
+
+/*===========================================*/
+/* Template db functions.                    */
+/*===========================================*/
+
+void
+_lgl_db_register_template_internal (const lglTemplate   *template)
+{
+        lglTemplate *template_copy;
+
+        if (!lgl_db_does_template_exist (template->brand, template->part))
+        {
+                template_copy = lgl_template_dup (template);
+                model->templates = g_list_append (model->templates, template_copy);
+                add_to_template_cache (template_copy);
+        }
+        else
+        {
+                g_message ("Duplicate template: %s %s.", template->brand, template->part);
+        }
+}
+
+
+/**
+ * lgl_db_register_template:
+ * @template:  Pointer to a template structure to add to database.
+ *
+ * Register a template.  This function adds a template to the template database.
+ * The template will be stored in an individual XML file in the user template directory.
+ *
+ * Returns: Status of registration attempt (#lglDbRegStatus)
+ */
+lglDbRegStatus
+lgl_db_register_template (const lglTemplate *template)
+{
+        lglTemplate *template_copy;
+        gchar       *dir, *filename, *abs_filename;
+        gint         bytes_written;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (lgl_db_does_template_exist (template->brand, template->part))
+        {
+                return LGL_DB_REG_BRAND_PART_EXISTS;
+        }
+
+        if (lgl_db_is_paper_id_known (template->paper_id))
+        {
+                dir = USER_CONFIG_DIR;
+                g_mkdir_with_parents (dir, 0775); /* Try to make sure directory exists. */
+                filename = g_strdup_printf ("%s_%s.template", template->brand, template->part);
+                abs_filename = g_build_filename (dir, filename, NULL);
+                bytes_written = lgl_xml_template_write_template_to_file (template, abs_filename);
+                g_free (dir);
+                g_free (filename);
+                g_free (abs_filename);
+
+                if (bytes_written > 0)
+                {
+                        template_copy = lgl_template_dup (template);
+                        lgl_template_add_category (template_copy, "user-defined");
+                        model->templates = g_list_append (model->templates, template_copy);
+                        add_to_template_cache (template_copy);
+                        g_signal_emit (G_OBJECT (model), signals[CHANGED], 0);
+                        return LGL_DB_REG_OK;
+                }
+                else
+                {
+                        return LGL_DB_REG_FILE_WRITE_ERROR;
+                }
+        }
+        else
+        {
+                g_message ("Cannot register new template with unknown page size.");
+                return LGL_DB_REG_BAD_PAPER_ID;
+        }
+
+}
+
+
+/**
+ * lgl_db_delete_template_by_name:
+ * @name:  Name of template to be deleted.
+ *
+ * Delete a user defined template.  This function deletes a template from
+ * the template database. The individual XML file in the user template
+ * directory will also be removed.
+ *
+ * Returns: Status of registration attempt (#lglDbDeleteStatus)
+ */
+lglDbDeleteStatus
+lgl_db_delete_template_by_name (const gchar *name)
+{
+        lglTemplate *template, *template1;
+        gchar       *dir, *filename, *abs_filename;
+        GList       *p;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (!lgl_db_does_template_name_exist (name))
+        {
+                return LGL_DB_DELETE_DOES_NOT_EXIST;
+        }
+
+        template = lgl_db_lookup_template_from_name (name);
+        if ( lgl_template_does_category_match (template, "user-defined") )
+        {
+                dir = USER_CONFIG_DIR;
+                filename = g_strdup_printf ("%s_%s.template", template->brand, template->part);
+                abs_filename = g_build_filename (dir, filename, NULL);
+
+                if (!g_file_test (abs_filename, G_FILE_TEST_EXISTS))
+                {
+                        g_message ("File \"%s\" does not exist.  Cannot delete template.", abs_filename);
+                        return LGL_DB_DELETE_DOES_NOT_EXIST;
+                }
+
+                g_unlink (abs_filename);
+
+                g_free (dir);
+                g_free (filename);
+                g_free (abs_filename);
+
+                for ( p=model->templates; p != NULL; p=p->next )
+                {
+                        template1 = (lglTemplate *)p->data;
+
+                        if ( lgl_template_do_templates_match (template, template1) )
+                        {
+                                model->templates = g_list_delete_link (model->templates, p);
+                                g_hash_table_remove (model->template_cache, name);
+                                break;
+                        }
+                }
+
+                lgl_template_free (template);
+
+                g_signal_emit (G_OBJECT (model), signals[CHANGED], 0);
+                return LGL_DB_DELETE_OK;
+        }
+        else
+        {
+                return LGL_DB_DELETE_NOT_USER_DEFINED;
+        }
+
+}
+
+
+/**
+ * lgl_db_delete_template_by_brand_part:
+ * @brand:  Brand name or vendor of template to be deleted.
+ * @part:   Part name or number of template to be deleted.
+ *
+ * Delete a user defined template.  This function deletes a template from
+ * the template database. The individual XML file in the user template
+ * directory will also be removed.
+ *
+ * Returns: Status of registration attempt (#lglDbDeleteStatus)
+ */
+lglDbDeleteStatus
+lgl_db_delete_template_by_brand_part (const gchar  *brand,
+                                      const gchar  *part)
+{
+        gchar             *name;
+        lglDbDeleteStatus  status;
+
+        name = g_strdup_printf ("%s %s", brand, part);
+
+        status = lgl_db_delete_template_by_name (name);
+
+        g_free (name);
+
+        return status;
+}
+
+
+/**
+ * lgl_db_does_template_exist:
+ * @brand: Brand name.
+ * @part:  Part name/number.
+ *
+ * This function tests whether a template with the given brand and part name/number exists.
+ *
+ * Returns:  TRUE if such a template exists in the database.
+ */
+gboolean
+lgl_db_does_template_exist (const gchar *brand,
+                            const gchar *part)
+{
+        GList            *p_tmplt;
+        lglTemplate      *template;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if ((brand == NULL) || (part == NULL))
+        {
+                return FALSE;
+        }
+
+        for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+        {
+                template = (lglTemplate *) p_tmplt->data;
+
+                if ( UTF8_EQUAL (brand, template->brand) &&
+                     UTF8_EQUAL (part, template->part) )
+                {
+                        return TRUE;
+                }
+        }
+
+        return FALSE;
+}
+
+
+/**
+ * lgl_db_does_template_name_exist:
+ * @name: name string
+ *
+ * This function test whether a template with the given name exists.
+ *
+ * Returns: TRUE if such a template exists in the database.
+ *
+ */
+gboolean
+lgl_db_does_template_name_exist (const gchar *name)
+{
+        GList            *p_tmplt;
+        lglTemplate      *template;
+        gchar            *candidate_name;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                return FALSE;
+        }
+
+        for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+        {
+                template = (lglTemplate *) p_tmplt->data;
+                candidate_name = g_strdup_printf ("%s %s", template->brand, template->part);
+
+                if ( UTF8_EQUAL (candidate_name, name) )
+                {
+                        g_free (candidate_name);
+                        return TRUE;
+                }
+                g_free (candidate_name);
+        }
+
+        return FALSE;
+}
+
+
+/**
+ * lgl_db_get_template_name_list_all:
+ * @brand:     If non NULL, limit results to given brand
+ * @paper_id: If non NULL, limit results to given page size.
+ * @category_id: If non NULL, limit results to given template category.
+ *
+ * Get a list of all valid names of templates in the template database.
+ * Results can be filtered by page size and/or template category.  A list of valid page
+ * sizes can be obtained using lgl_db_get_paper_id_list().  A list of valid template
+ * categories can be obtained using lgl_db_get_category_id_list().
+ *
+ * Returns: a list of template names.
+ */
+GList *
+lgl_db_get_template_name_list_all (const gchar *brand,
+                                   const gchar *paper_id,
+                                   const gchar *category_id)
+{
+        GList            *p_tmplt;
+        lglTemplate      *template;
+        gchar            *name;
+        GList            *names = NULL;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+        {
+                template = (lglTemplate *) p_tmplt->data;
+                if (lgl_template_does_brand_match (template, brand) &&
+                    lgl_template_does_page_size_match (template, paper_id) &&
+                    lgl_template_does_category_match (template, category_id))
+                {
+                        name = g_strdup_printf ("%s %s", template->brand, template->part);
+                        names = g_list_insert_sorted (names, name, (GCompareFunc)lgl_str_part_name_cmp);
+                }
+        }
+
+        return names;
+}
+
+
+/**
+ * lgl_db_get_similar_template_name_list:
+ * @name:     Name of template under test.
+ *
+ * Get a list of all valid names of templates in the template database that
+ * have the same size and layout characteristics as the given template.
+ *
+ * Returns: a list of template names.
+ */
+GList *
+lgl_db_get_similar_template_name_list (const gchar  *name)
+{
+        GList            *p_tmplt;
+        lglTemplate      *template1;
+        lglTemplate      *template2;
+        gchar            *name2;
+        GList            *names = NULL;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if ( !name )
+        {
+                return NULL;
+        }
+
+        template1 = lgl_db_lookup_template_from_name (name);
+        if ( !template1 )
+        {
+                return NULL;
+        }
+
+        for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+        {
+                template2 = (lglTemplate *) p_tmplt->data;
+
+                if ( lgl_template_are_templates_identical (template1, template2) )
+                {
+
+                        name2 = g_strdup_printf ("%s %s", template2->brand, template2->part);
+                        if ( !UTF8_EQUAL (name2, name) )
+                        {
+                                names = g_list_insert_sorted (names, name2,
+                                                              (GCompareFunc)lgl_str_part_name_cmp);
+                        }
+
+                }
+        }
+
+        return names;
+}
+
+
+/**
+ * lgl_db_free_template_name_list:
+ * @names: List of template name strings to be freed.
+ *
+ * Free up all storage associated with a list of template names obtained with
+ * lgl_db_get_template_name_list_all().
+ *
+ */
+void
+lgl_db_free_template_name_list (GList *names)
+{
+        GList *p_name;
+
+        for (p_name = names; p_name != NULL; p_name = p_name->next)
+        {
+                g_free (p_name->data);
+                p_name->data = NULL;
+        }
+
+        g_list_free (names);
+}
+
+
+/**
+ * lgl_db_lookup_template_from_name:
+ * @name: name string
+ *
+ * Lookup template in template database from name string.
+ *
+ * Returns: pointer to a newly allocated #lglTemplate structure.
+ *
+ */
+lglTemplate *
+lgl_db_lookup_template_from_name (const gchar *name)
+{
+        lglTemplate      *template;
+        lglTemplate      *new_template;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if (name == NULL)
+        {
+                /* If no name, return first template as a default */
+                return lgl_template_dup ((lglTemplate *) model->templates->data);
+        }
+
+        template = g_hash_table_lookup (model->template_cache, name);
+
+        if (template)
+        {
+                new_template = lgl_template_dup (template);
+                return new_template;
+        }
+
+        /* No matching template has been found so return the first template */
+        return lgl_template_dup ((lglTemplate *) model->templates->data);
+}
+
+
+/**
+ * lgl_db_lookup_template_from_brand_part:
+ * @brand: brand name string
+ * @part:  part name string
+ *
+ * Lookup template in template database from brand and part strings.
+ *
+ * Returns: pointer to a newly allocated #lglTemplate structure.
+ *
+ */
+lglTemplate *
+lgl_db_lookup_template_from_brand_part(const gchar *brand,
+                                       const gchar *part)
+{
+        gchar            *name;
+        lglTemplate      *template;
+        lglTemplate      *new_template;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        if ((brand == NULL) || (part == NULL))
+        {
+                /* If no name, return first template as a default */
+                return lgl_template_dup ((lglTemplate *) model->templates->data);
+        }
+
+        name = g_strdup_printf ("%s %s", brand, part);
+        template = g_hash_table_lookup (model->template_cache, name);
+
+        if (template)
+        {
+                new_template = lgl_template_dup (template);
+                return new_template;
+        }
+
+        /* No matching template has been found so return the first template */
+        g_free (name);
+        return lgl_template_dup ((lglTemplate *) model->templates->data);
+}
+
+
+static void
+add_to_template_cache (lglTemplate *template)
+{
+        gchar            *name;
+
+        name = g_strdup_printf ("%s %s", template->brand, template->part);
+
+        g_hash_table_insert (model->template_cache, name, template);
+}
+
+
+void
+read_templates (void)
+{
+        gchar       *data_dir;
+        GList       *p;
+        lglTemplate *template;
+
+        /*
+         * User defined templates.  Add to user-defined category.
+         */
+        data_dir = USER_CONFIG_DIR;
+        read_template_files_from_dir (data_dir);
+        g_free (data_dir);
+        for ( p=model->templates; p != NULL; p=p->next )
+        {
+                template = (lglTemplate *)p->data;
+                lgl_template_add_category (template, "user-defined");
+        }
+
+        /*
+         * Alternate user defined templates.  (Used for manually created templates).
+         */
+        data_dir = ALT_USER_CONFIG_DIR;
+        read_template_files_from_dir (data_dir);
+        g_free (data_dir);
+
+        /*
+         * System templates.
+         */
+        data_dir = SYSTEM_CONFIG_DIR;
+        read_template_files_from_dir (data_dir);
+        g_free (data_dir);
+
+        if (model->templates == NULL)
+        {
+                g_critical (_("Unable to locate any template files.  Libglabels may not be installed correctly!"));
+        }
+}
+
+
+void
+read_template_files_from_dir (const gchar *dirname)
+{
+        GDir        *dp;
+        const gchar *filename, *extension, *extension2;
+        gchar       *full_filename = NULL;
+        GError      *gerror = NULL;
+
+        if (dirname == NULL)
+                return;
+
+        if (!g_file_test (dirname, G_FILE_TEST_EXISTS))
+        {
+                return;
+        }
+
+        dp = g_dir_open (dirname, 0, &gerror);
+        if (gerror != NULL)
+        {
+                g_message ("cannot open data directory: %s", gerror->message );
+                return;
+        }
+
+        while ((filename = g_dir_read_name (dp)) != NULL)
+        {
+
+                extension = strrchr (filename, '.');
+                extension2 = strrchr (filename, '-');
+
+                if ( (extension && ASCII_EQUAL (extension, ".template")) ||
+                     (extension2 && ASCII_EQUAL (extension2, "-templates.xml")) )
+                {
+
+                        full_filename = g_build_filename (dirname, filename, NULL);
+                        lgl_xml_template_read_templates_from_file (full_filename);
+                        g_free (full_filename);
+                }
+
+        }
+
+        g_dir_close (dp);
+}
+
+
+static lglTemplate *
+template_full_page (const gchar *paper_id)
+{
+        lglPaper              *paper = NULL;
+        lglTemplate           *template = NULL;
+        lglTemplateFrame      *frame = NULL;
+        gchar                 *part;
+        gchar                 *desc;
+
+        g_return_val_if_fail (paper_id, NULL);
+
+        paper = lgl_db_lookup_paper_from_id (paper_id);
+        if ( paper == NULL )
+        {
+                return NULL;
+        }
+
+        part = g_strdup_printf ("%s-Full-Page", paper->id);
+        desc = g_strdup_printf (_("%s full page label"), paper->name);
+
+        template = lgl_template_new ("Generic", part, desc,
+                                     paper_id, paper->width, paper->height);
+
+
+        frame = lgl_template_frame_rect_new ("0",
+                                             paper->width,
+                                             paper->height,
+                                             0.0,
+                                             0.0,
+                                             0.0);
+        lgl_template_add_frame (template, frame);
+
+        lgl_template_frame_add_layout (frame, lgl_template_layout_new (1, 1, 0., 0., 0., 0.));
+
+        lgl_template_frame_add_markup (frame, lgl_template_markup_margin_new (9.0));
+
+        g_free (desc);
+        desc = NULL;
+        lgl_paper_free (paper);
+        paper = NULL;
+
+        return template;
+}
+
+
+/**
+ * lgl_db_print_known_templates:
+ *
+ * Print all known templates (for debugging purposes).
+ *
+ */
+void
+lgl_db_print_known_templates (void)
+{
+        GList       *p;
+        lglTemplate *template;
+
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
+        g_print ("%s():\n", __FUNCTION__);
+        for (p=model->templates; p!=NULL; p=p->next)
+        {
+                template = (lglTemplate *)p->data;
+
+                g_print("TEMPLATE brand=\"%s\", part=\"%s\", description=\"%s\"\n",
+                        template->brand, template->part, template->description);
+
+        }
+        g_print ("\n");
+
+}
+
+
+
+
+/*
+ * Local Variables:       -- emacs
+ * mode: C                -- emacs
+ * c-basic-offset: 8      -- emacs
+ * tab-width: 8           -- emacs
+ * indent-tabs-mode: nil  -- emacs
+ * End:                   -- emacs
+ */