[glabels] Added "custom" tab to media select widget



commit 07cc165a7d57312cffee8b56a318fe4a597cdb83
Author: Jim Evins <evins snaught com>
Date:   Sun Mar 28 23:03:39 2010 -0400

    Added "custom" tab to media select widget
    
    Added "custom" tab to media select widget.  In this tab, the user can
    launch the template designer or delete an existing user-defined template.
    Added notification callbacks to libglabels db subsystem so that the
    media select widget will automatically update when user-defined templates
    are added or deleted.  The "edit" button does not do anything yet.
    
    Also did some visual cleanup to the new label dialog.
    
    Also include ../libglabels in SUBDIRS in src/ so that libglabels can
    be rebuilt from src/ directory.

 data/builder/media-select.builder     |  124 ++++++++++-
 data/builder/new-label-dialog.builder |    3 +-
 libglabels/db.c                       |  429 ++++++++++++++++++++++++---------
 libglabels/db.h                       |   27 ++
 src/Makefile.am                       |    2 +-
 src/media-select.c                    |  358 +++++++++++++++++++++++++--
 6 files changed, 801 insertions(+), 142 deletions(-)
---
diff --git a/data/builder/media-select.builder b/data/builder/media-select.builder
index 9cb814f..567648b 100644
--- a/data/builder/media-select.builder
+++ b/data/builder/media-select.builder
@@ -55,7 +55,8 @@
             <child type="tab">
               <object class="GtkLabel" id="recent_tab_label">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">Recent templates</property>
+                <property name="xpad">12</property>
+                <property name="label" translatable="yes">Recent</property>
               </object>
               <packing>
                 <property name="tab_fill">False</property>
@@ -211,19 +212,138 @@
             <child type="tab">
               <object class="GtkLabel" id="search_all_tabLlabel">
                 <property name="visible">True</property>
-                <property name="label" translatable="yes">Search all templates</property>
+                <property name="xpad">12</property>
+                <property name="label" translatable="yes">Search all</property>
               </object>
               <packing>
                 <property name="position">1</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkVBox" id="custom_tab_vbox">
+                <property name="height_request">320</property>
+                <property name="visible">True</property>
+                <property name="border_width">6</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkVBox" id="custom_info_vbox">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolledwindow3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hscrollbar_policy">automatic</property>
+                    <property name="vscrollbar_policy">automatic</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTreeView" id="custom_treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="headers_visible">False</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="spacing">9</property>
+                    <child>
+                      <object class="GtkButton" id="custom_add_button">
+                        <property name="label">gtk-add</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="custom_edit_button">
+                        <property name="label">gtk-edit</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkButton" id="custom_delete_button">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">True</property>
+                        <property name="use_stock">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="padding">6</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="xpad">12</property>
+                <property name="label" translatable="yes">Custom</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+                <property name="tab_expand">True</property>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
           </object>
           <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
             <property name="position">0</property>
           </packing>
         </child>
       </object>
     </child>
   </object>
+  <object class="GtkSizeGroup" id="custom_buttons_sizegroup">
+    <widgets>
+      <widget name="custom_add_button"/>
+      <widget name="custom_edit_button"/>
+      <widget name="custom_delete_button"/>
+    </widgets>
+  </object>
 </interface>
diff --git a/data/builder/new-label-dialog.builder b/data/builder/new-label-dialog.builder
index 9f3f5d0..2f4d70b 100644
--- a/data/builder/new-label-dialog.builder
+++ b/data/builder/new-label-dialog.builder
@@ -22,8 +22,7 @@
                   <object class="GtkLabel" id="label19">
                     <property name="visible">True</property>
                     <property name="xalign">0</property>
-                    <property name="label" translatable="yes">Choose label or card product from hundreds of predefined templates.  You may also
-define your own templates.</property>
+                    <property name="label" translatable="yes">Choose label or card product from hundreds of predefined templates or define your own.</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
diff --git a/libglabels/db.c b/libglabels/db.c
index af2adca..37f9247 100644
--- a/libglabels/db.c
+++ b/libglabels/db.c
@@ -24,6 +24,7 @@
 
 #include <glib/gi18n.h>
 #include <glib.h>
+#include <glib-object.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -40,24 +41,63 @@
 /* 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 GList *papers     = NULL;
-static GList *categories = NULL;
-static GList *vendors    = NULL;
-static GList *templates  = NULL;
+static guint signals[LAST_SIGNAL] = {0};
 
-static GHashTable *template_cache = NULL;
+static lglDbModel *model = NULL;
 
 
 /*===========================================*/
 /* Local function prototypes                 */
 /*===========================================*/
 
-static void   init_template_cache          (void);
+static void   lgl_db_model_finalize        (GObject     *object);
+
 static void   add_to_template_cache        (lglTemplate *template);
 
 static GList *read_papers                  (void);
@@ -78,6 +118,106 @@ 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                     */
 /*===========================================*/
@@ -103,63 +243,69 @@ lgl_db_init (void)
         GList       *page_sizes;
         GList       *p;
 
+        model = lgl_db_model_new ();
+
         /*
          * Paper definitions
          */
-	if (!papers)
-        {
-
-                papers = read_papers ();
+        model->papers = read_papers ();
 
-                /* Create and append an "Other" entry. */
-                paper_other = lgl_paper_new ("Other", _("Other"), 0.0, 0.0, NULL);
-                papers = g_list_append (papers, paper_other);
-
-	}
+        /* Create and append an "Other" entry. */
+        paper_other = lgl_paper_new ("Other", _("Other"), 0.0, 0.0, NULL);
+        model->papers = g_list_append (model->papers, paper_other);
 
         /*
          * Categories
          */
-	if (!categories)
-        {
-                categories = read_categories ();
+        model->categories = read_categories ();
 
-                /* Create and append a "User defined" entry. */
-                category_user_defined = lgl_category_new ("user-defined", _("User defined"));
-                categories = g_list_append (categories, category_user_defined);
-	}
+        /* 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
          */
-	if (!vendors)
-        {
-                vendors = read_vendors ();
-	}
+        model->vendors = read_vendors ();
 
         /*
          * Templates
          */
-	if (!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);
 
-                init_template_cache ();
+}
 
-                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);
+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);
+}
+
+
+void
+lgl_db_notify_remove  (gulong id)
+{
+        g_signal_handler_disconnect (G_OBJECT (model), id);
 }
 
 
@@ -182,12 +328,12 @@ lgl_db_get_paper_id_list (void)
 	GList           *p;
 	lglPaper        *paper;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for ( p=papers; p != NULL; p=p->next )
+	for ( p=model->papers; p != NULL; p=p->next )
         {
 		paper = (lglPaper *)p->data;
 		ids = g_list_append (ids, g_strdup (paper->id));
@@ -235,12 +381,12 @@ lgl_db_get_paper_name_list (void)
 	GList           *p;
 	lglPaper        *paper;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for ( p=papers; p != NULL; p=p->next )
+	for ( p=model->papers; p != NULL; p=p->next )
         {
 		paper = (lglPaper *)p->data;
 		names = g_list_append (names, g_strdup (paper->name));
@@ -288,7 +434,7 @@ lgl_db_lookup_paper_from_name (const gchar *name)
 	GList       *p;
 	lglPaper    *paper;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -296,10 +442,10 @@ lgl_db_lookup_paper_from_name (const gchar *name)
 	if (name == NULL)
         {
 		/* If no name, return first paper as a default */
-		return lgl_paper_dup ((lglPaper *) papers->data);
+		return lgl_paper_dup ((lglPaper *) model->papers->data);
 	}
 
-	for (p = papers; p != NULL; p = p->next)
+	for (p = model->papers; p != NULL; p = p->next)
         {
 		paper = (lglPaper *) p->data;
 		if (UTF8_EQUAL (paper->name, name))
@@ -327,7 +473,7 @@ lgl_db_lookup_paper_from_id (const gchar *id)
 	GList       *p;
 	lglPaper    *paper;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -335,10 +481,10 @@ lgl_db_lookup_paper_from_id (const gchar *id)
 	if (id == NULL)
         {
 		/* If no id, return first paper as a default */
-		return lgl_paper_dup ((lglPaper *) papers->data);
+		return lgl_paper_dup ((lglPaper *) model->papers->data);
 	}
 
-	for (p = papers; p != NULL; p = p->next)
+	for (p = model->papers; p != NULL; p = p->next)
         {
 		paper = (lglPaper *) p->data;
 		if (ASCII_EQUAL (paper->id, id))
@@ -426,7 +572,7 @@ lgl_db_is_paper_id_known (const gchar *id)
 	GList       *p;
 	lglPaper    *paper;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -436,7 +582,7 @@ lgl_db_is_paper_id_known (const gchar *id)
 		return FALSE;
 	}
 
-	for (p = papers; p != NULL; p = p->next)
+	for (p = model->papers; p != NULL; p = p->next)
         {
 		paper = (lglPaper *) p->data;
 		if (ASCII_EQUAL (paper->id, id))
@@ -559,12 +705,12 @@ lgl_db_print_known_papers (void)
 	GList       *p;
 	lglPaper    *paper;
 
-	if (!papers) {
+	if (!model) {
 		lgl_db_init ();
 	}
 
 	g_print ("%s():\n", __FUNCTION__);
-	for (p = papers; p != NULL; p = p->next) {
+	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",
@@ -595,12 +741,12 @@ lgl_db_get_category_id_list (void)
 	GList           *p;
 	lglCategory     *category;
 
-	if (!categories)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for ( p=categories; p != NULL; p=p->next )
+	for ( p=model->categories; p != NULL; p=p->next )
         {
 		category = (lglCategory *)p->data;
 		ids = g_list_append (ids, g_strdup (category->id));
@@ -648,12 +794,12 @@ lgl_db_get_category_name_list (void)
 	GList           *p;
 	lglCategory     *category;
 
-	if (!categories)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for ( p=categories; p != NULL; p=p->next )
+	for ( p=model->categories; p != NULL; p=p->next )
         {
 		category = (lglCategory *)p->data;
 		names = g_list_append (names, g_strdup (category->name));
@@ -701,7 +847,7 @@ lgl_db_lookup_category_from_name (const gchar *name)
 	GList       *p;
 	lglCategory *category;
 
-	if (!categories)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -709,10 +855,10 @@ lgl_db_lookup_category_from_name (const gchar *name)
 	if (name == NULL)
         {
 		/* If no name, return first category as a default */
-		return lgl_category_dup ((lglCategory *) categories->data);
+		return lgl_category_dup ((lglCategory *) model->categories->data);
 	}
 
-	for (p = categories; p != NULL; p = p->next)
+	for (p = model->categories; p != NULL; p = p->next)
         {
 		category = (lglCategory *) p->data;
 		if (UTF8_EQUAL (category->name, name))
@@ -740,7 +886,7 @@ lgl_db_lookup_category_from_id (const gchar *id)
 	GList       *p;
 	lglCategory *category;
 
-	if (!categories)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -748,10 +894,10 @@ lgl_db_lookup_category_from_id (const gchar *id)
 	if (id == NULL)
         {
 		/* If no id, return first category as a default */
-		return lgl_category_dup ((lglCategory *) categories->data);
+		return lgl_category_dup ((lglCategory *) model->categories->data);
 	}
 
-	for (p = categories; p != NULL; p = p->next)
+	for (p = model->categories; p != NULL; p = p->next)
         {
 		category = (lglCategory *) p->data;
 		if (ASCII_EQUAL (category->id, id))
@@ -839,7 +985,7 @@ lgl_db_is_category_id_known (const gchar *id)
 	GList       *p;
 	lglCategory *category;
 
-	if (!categories)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -849,7 +995,7 @@ lgl_db_is_category_id_known (const gchar *id)
 		return FALSE;
 	}
 
-	for (p = categories; p != NULL; p = p->next)
+	for (p = model->categories; p != NULL; p = p->next)
         {
 		category = (lglCategory *) p->data;
 		if (ASCII_EQUAL (category->id, id))
@@ -951,12 +1097,12 @@ lgl_db_print_known_categories (void)
 	GList       *p;
 	lglCategory *category;
 
-	if (!categories) {
+	if (!model) {
 		lgl_db_init ();
 	}
 
 	g_print ("%s():\n", __FUNCTION__);
-	for (p = categories; p != NULL; p = p->next) {
+	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);
@@ -986,12 +1132,12 @@ lgl_db_get_vendor_name_list (void)
 	GList           *p;
 	lglVendor       *vendor;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for ( p=vendors; p != NULL; p=p->next )
+	for ( p=model->vendors; p != NULL; p=p->next )
         {
 		vendor = (lglVendor *)p->data;
 		names = g_list_append (names, g_strdup (vendor->name));
@@ -1039,7 +1185,7 @@ lgl_db_lookup_vendor_from_name (const gchar *name)
 	GList       *p;
 	lglVendor   *vendor;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1047,10 +1193,10 @@ lgl_db_lookup_vendor_from_name (const gchar *name)
 	if (name == NULL)
         {
 		/* If no name, return first vendor as a default */
-		return lgl_vendor_dup ((lglVendor *) vendors->data);
+		return lgl_vendor_dup ((lglVendor *) model->vendors->data);
 	}
 
-	for (p = vendors; p != NULL; p = p->next)
+	for (p = model->vendors; p != NULL; p = p->next)
         {
 		vendor = (lglVendor *) p->data;
 		if (UTF8_EQUAL (vendor->name, name))
@@ -1078,7 +1224,7 @@ lgl_db_is_vendor_name_known (const gchar *name)
 	GList       *p;
 	lglVendor   *vendor;
 
-	if (!papers)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1088,7 +1234,7 @@ lgl_db_is_vendor_name_known (const gchar *name)
 		return FALSE;
 	}
 
-	for (p = vendors; p != NULL; p = p->next)
+	for (p = model->vendors; p != NULL; p = p->next)
         {
 		vendor = (lglVendor *) p->data;
 		if (UTF8_EQUAL (vendor->name, name))
@@ -1186,12 +1332,12 @@ lgl_db_print_known_vendors (void)
 	GList       *p;
 	lglVendor   *vendor;
 
-	if (!papers) {
+	if (!model) {
 		lgl_db_init ();
 	}
 
 	g_print ("%s():\n", __FUNCTION__);
-	for (p = vendors; p != NULL; p = p->next) {
+	for (p = model->vendors; p != NULL; p = p->next) {
 		vendor = (lglVendor *) p->data;
 
 		g_print ("VENDOR name=\"%s\", url=\"%s\"\n",
@@ -1228,12 +1374,12 @@ lgl_db_get_brand_list (const gchar *paper_id,
 	lglTemplateAlias *alias;
 	GList            *brands = NULL;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	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) &&
@@ -1292,7 +1438,7 @@ _lgl_db_register_template_internal (const lglTemplate   *template)
         lglTemplate *template_copy;
 
         template_copy = lgl_template_dup (template);
-        templates = g_list_append (templates, template_copy);
+        model->templates = g_list_append (model->templates, template_copy);
         add_to_template_cache (template_copy);
 }
 
@@ -1313,7 +1459,7 @@ lgl_db_register_template (const lglTemplate *template)
         gchar       *dir, *filename, *abs_filename;
         gint         bytes_written;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1338,8 +1484,9 @@ lgl_db_register_template (const lglTemplate *template)
                 {
                         template_copy = lgl_template_dup (template);
                         lgl_template_add_category (template_copy, "user-defined");
-                        templates = g_list_append (templates, template_copy);
+                        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
@@ -1356,6 +1503,61 @@ lgl_db_register_template (const lglTemplate *template)
 }
 
 
+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 = LGL_USER_DATA_DIR;
+		filename = g_strdup_printf ("%s_%s.template", template->brand, template->part);
+		abs_filename = g_build_filename (dir, filename, NULL);
+
+                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_does_template_exist:
  * @brand: Brand name.
@@ -1373,7 +1575,7 @@ lgl_db_does_template_exist (const gchar *brand,
 	lglTemplate      *template;
         lglTemplateAlias *alias;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1383,7 +1585,7 @@ lgl_db_does_template_exist (const gchar *brand,
 		return FALSE;
 	}
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
         {
 		template = (lglTemplate *) p_tmplt->data;
 		for (p_alias = template->aliases; p_alias != NULL; p_alias = p_alias->next)
@@ -1419,7 +1621,7 @@ lgl_db_does_template_name_exist (const gchar *name)
         lglTemplateAlias *alias;
         gchar            *candidate_name;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1429,7 +1631,7 @@ lgl_db_does_template_name_exist (const gchar *name)
 		return FALSE;
 	}
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
         {
 		template = (lglTemplate *) p_tmplt->data;
 		for (p_alias = template->aliases; p_alias != NULL; p_alias = p_alias->next)
@@ -1475,12 +1677,12 @@ lgl_db_get_template_name_list_unique (const gchar *brand,
         gchar       *name;
 	GList       *names = NULL;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
         {
 		template = (lglTemplate *) p_tmplt->data;
 
@@ -1525,12 +1727,12 @@ lgl_db_get_template_name_list_all (const gchar *brand,
         gchar            *name;
 	GList            *names = NULL;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	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) &&
@@ -1574,7 +1776,7 @@ lgl_db_get_similar_template_name_list (const gchar  *name)
         gchar            *name2;
 	GList            *names = NULL;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1602,7 +1804,7 @@ lgl_db_get_similar_template_name_list (const gchar  *name)
                 }
         }
 
-	for (p_tmplt = templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
+	for (p_tmplt = model->templates; p_tmplt != NULL; p_tmplt = p_tmplt->next)
         {
 		template2 = (lglTemplate *) p_tmplt->data;
 
@@ -1667,7 +1869,7 @@ lgl_db_lookup_template_from_name (const gchar *name)
         gchar            *candidate_name;
 	lglTemplate      *new_template;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1675,10 +1877,10 @@ lgl_db_lookup_template_from_name (const gchar *name)
 	if (name == NULL)
         {
 		/* If no name, return first template as a default */
-		return lgl_template_dup ((lglTemplate *) templates->data);
+		return lgl_template_dup ((lglTemplate *) model->templates->data);
 	}
 
-        template = g_hash_table_lookup (template_cache, name);
+        template = g_hash_table_lookup (model->template_cache, name);
 
         if (template)
         {
@@ -1703,7 +1905,7 @@ lgl_db_lookup_template_from_name (const gchar *name)
         }
 
 	/* No matching template has been found so return the first template */
-	return lgl_template_dup ((lglTemplate *) templates->data);
+	return lgl_template_dup ((lglTemplate *) model->templates->data);
 }
 
 
@@ -1728,7 +1930,7 @@ lgl_db_lookup_template_from_brand_part(const gchar *brand,
         gchar            *candidate_name;
 	lglTemplate      *new_template;
 
-	if (!templates)
+	if (!model)
         {
 		lgl_db_init ();
 	}
@@ -1736,11 +1938,11 @@ lgl_db_lookup_template_from_brand_part(const gchar *brand,
 	if ((brand == NULL) || (part == NULL))
         {
 		/* If no name, return first template as a default */
-		return lgl_template_dup ((lglTemplate *) templates->data);
+		return lgl_template_dup ((lglTemplate *) model->templates->data);
 	}
 
         name = g_strdup_printf ("%s %s", brand, part);
-        template = g_hash_table_lookup (template_cache, name);
+        template = g_hash_table_lookup (model->template_cache, name);
 
         if (template)
         {
@@ -1767,14 +1969,7 @@ lgl_db_lookup_template_from_brand_part(const gchar *brand,
 
 	/* No matching template has been found so return the first template */
         g_free (name);
-	return lgl_template_dup ((lglTemplate *) templates->data);
-}
-
-
-static void
-init_template_cache (void)
-{
-        template_cache = g_hash_table_new (g_str_hash, g_str_equal);
+	return lgl_template_dup ((lglTemplate *) model->templates->data);
 }
 
 
@@ -1790,7 +1985,7 @@ add_to_template_cache (lglTemplate *template)
                 alias = (lglTemplateAlias *)p_alias->data;
                 name = g_strdup_printf ("%s %s", alias->brand, alias->part);
 
-                g_hash_table_insert (template_cache, name, template);
+                g_hash_table_insert (model->template_cache, name, template);
         }
 }
 
@@ -1808,7 +2003,7 @@ read_templates (void)
 	data_dir = LGL_USER_DATA_DIR;
 	read_template_files_from_dir (data_dir);
 	g_free (data_dir);
-        for ( p=templates; p != NULL; p=p->next )
+        for ( p=model->templates; p != NULL; p=p->next )
         {
                 template = (lglTemplate *)p->data;
                 lgl_template_add_category (template, "user-defined");
@@ -1821,7 +2016,7 @@ read_templates (void)
 	read_template_files_from_dir (data_dir);
 	g_free (data_dir);
 
-	if (templates == NULL)
+	if (model->templates == NULL)
         {
 		g_critical (_("Unable to locate any template files.  Libglabels may not be installed correctly!"));
 	}
@@ -1929,8 +2124,13 @@ lgl_db_print_known_templates (void)
 	GList       *p;
 	lglTemplate *template;
 
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
 	g_print ("%s():\n", __FUNCTION__);
-	for (p=templates; p!=NULL; p=p->next)
+	for (p=model->templates; p!=NULL; p=p->next)
         {
 		template = (lglTemplate *)p->data;
 
@@ -1956,6 +2156,11 @@ lgl_db_print_aliases (const lglTemplate *template)
 	GList            *p;
         lglTemplateAlias *alias;
 
+        if (!model)
+        {
+                lgl_db_init ();
+        }
+
 	g_print ("%s():\n", __FUNCTION__);
 	for (p=template->aliases; p!=NULL; p=p->next)
         {
diff --git a/libglabels/db.h b/libglabels/db.h
index 343573e..ddd162a 100644
--- a/libglabels/db.h
+++ b/libglabels/db.h
@@ -38,12 +38,37 @@ typedef enum
         LGL_DB_REG_FILE_WRITE_ERROR  = -3
 } lglDbRegStatus;
 
+typedef enum
+{
+        LGL_DB_DELETE_OK                =  0,
+        LGL_DB_DELETE_DOES_NOT_EXIST    = -1,
+        LGL_DB_DELETE_NOT_USER_DEFINED  = -2,
+        LGL_DB_DELETE_FILE_ERROR        = -3
+} lglDbDeleteStatus;
+
+
+typedef void  (*lglDbNotifyFunc) (gpointer user_data);
+
+
+
+
 /*
  * Module initialization
  */
 void           lgl_db_init                           (void);
 
 
+
+/*
+ * Notification
+ */
+gulong         lgl_db_notify_add                     (lglDbNotifyFunc      func,
+                                                      gpointer             user_data);
+
+void           lgl_db_notify_remove                  (gulong               id);
+
+
+
 /*
  * Paper
  */
@@ -117,6 +142,8 @@ void           lgl_db_free_brand_list                (GList               *brand
  */
 lglDbRegStatus lgl_db_register_template              (const lglTemplate   *template);
 
+lglDbDeleteStatus lgl_db_delete_template_by_name        (const gchar         *name);
+
 gboolean       lgl_db_does_template_exist            (const gchar         *brand,
                                                       const gchar         *part);
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 9c9f5f1..d18b29e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
 
-SUBDIRS= pixmaps stock-pixmaps
+SUBDIRS= ../libglabels pixmaps stock-pixmaps
 
 bin_PROGRAMS = glabels-3 glabels-3-batch
 
diff --git a/src/media-select.c b/src/media-select.c
index ae7860a..ea56e03 100644
--- a/src/media-select.c
+++ b/src/media-select.c
@@ -31,6 +31,7 @@
 #include "prefs.h"
 #include "message-bar.h"
 #include "template-history.h"
+#include "template-designer.h"
 #include "str-util.h"
 #include "combo-util.h"
 #include "builder-util.h"
@@ -57,6 +58,8 @@ enum {
 
 struct _glMediaSelectPrivate {
 
+        gulong        db_notify_id;
+
         GtkBuilder   *builder;
 
         GtkWidget    *notebook;
@@ -79,6 +82,16 @@ struct _glMediaSelectPrivate {
         GtkWidget    *search_all_treeview;
         GtkListStore *search_all_store;
 
+        gint          custom_page_num;
+        GtkWidget    *custom_tab_vbox;
+        GtkWidget    *custom_info_vbox;
+        GtkWidget    *custom_info_bar;
+        GtkWidget    *custom_treeview;
+        GtkListStore *custom_store;
+        GtkWidget    *custom_add_button;
+        GtkWidget    *custom_edit_button;
+        GtkWidget    *custom_delete_button;
+
         /* Prevent recursion */
 	gboolean    stop_signals;
 };
@@ -93,7 +106,7 @@ enum {
 /* Private globals                           */
 /*===========================================*/
 
-static gint media_select_signals[LAST_SIGNAL] = { 0 };
+static gint signals[LAST_SIGNAL] = { 0 };
 
 
 /*===========================================*/
@@ -104,22 +117,37 @@ static void   gl_media_select_finalize   (GObject                *object);
 
 static void   gl_media_select_construct  (glMediaSelect          *this);
 
-static void   filter_changed_cb          (GtkComboBox            *combo,
-                                          gpointer                user_data);
+static void   filter_changed_cb          (glMediaSelect          *this);
 
 static void   selection_changed_cb       (GtkTreeSelection       *selection,
                                           gpointer                user_data);
 
+static void   custom_add_clicked_cb      (GtkButton              *button,
+                                          gpointer                user_data);
+
+static void   custom_edit_clicked_cb     (GtkButton              *button,
+                                          gpointer                user_data);
+
+static void   custom_delete_clicked_cb   (GtkButton              *button,
+                                          gpointer                user_data);
+
 static void   page_changed_cb            (GtkNotebook            *notebook,
                                           GtkNotebookPage        *page,
                                           guint                   page_num,
                                           gpointer                user_data);
 
+static void   db_changed_cb              (glMediaSelect          *this);
+
 static void   load_recent_list           (glMediaSelect          *this,
                                           GtkListStore           *store,
                                           GtkTreeSelection       *selection,
                                           GList                  *list);
 
+static void   load_custom_list          (glMediaSelect          *this,
+                                          GtkListStore           *store,
+                                          GtkTreeSelection       *selection,
+                                          GList                  *list);
+
 static void   load_search_all_list       (glMediaSelect          *this,
                                           GtkListStore           *store,
                                           GtkTreeSelection       *selection,
@@ -143,7 +171,7 @@ gl_media_select_class_init (glMediaSelectClass *class)
 
         object_class->finalize = gl_media_select_finalize;
 
-        media_select_signals[CHANGED] =
+        signals[CHANGED] =
             g_signal_new ("changed",
                           G_OBJECT_CLASS_TYPE(object_class),
                           G_SIGNAL_RUN_LAST,
@@ -177,6 +205,11 @@ gl_media_select_finalize (GObject *object)
         g_return_if_fail (object != NULL);
         g_return_if_fail (GL_IS_MEDIA_SELECT (object));
 
+        if (this->priv->db_notify_id)
+        {
+                lgl_db_notify_remove (this->priv->db_notify_id);
+        }
+
         if (this->priv->builder)
         {
                 g_object_unref (this->priv->builder);
@@ -216,7 +249,9 @@ gl_media_select_construct (glMediaSelect *this)
 {
         gchar             *builder_filename;
         GtkBuilder        *builder;
-        static gchar      *object_ids[] = { "media_select_hbox", NULL };
+        static gchar      *object_ids[] = { "media_select_hbox",
+                                            "custom_buttons_sizegroup",
+                                            NULL };
         GError            *error = NULL;
         GtkWidget         *hbox;
         GList             *recent_list = NULL;
@@ -226,10 +261,12 @@ gl_media_select_construct (glMediaSelect *this)
         GList             *search_all_names = NULL;
         const gchar       *page_size_id;
         gchar             *page_size_name;
+        GList             *custom_list = NULL;
         GtkCellRenderer   *renderer;
         GtkTreeViewColumn *column;
         GtkTreeSelection  *recent_selection;
         GtkTreeSelection  *search_all_selection;
+        GtkTreeSelection  *custom_selection;
 
         gl_debug (DEBUG_MEDIA_SELECT, "START");
 
@@ -258,6 +295,12 @@ gl_media_select_construct (glMediaSelect *this)
                                      "category_combo",         &this->priv->category_combo,
                                      "search_all_info_vbox",   &this->priv->search_all_info_vbox,
                                      "search_all_treeview",    &this->priv->search_all_treeview,
+                                     "custom_tab_vbox",        &this->priv->custom_tab_vbox,
+                                     "custom_info_vbox",       &this->priv->custom_info_vbox,
+                                     "custom_treeview",        &this->priv->custom_treeview,
+                                     "custom_add_button",      &this->priv->custom_add_button,
+                                     "custom_edit_button",     &this->priv->custom_edit_button,
+                                     "custom_delete_button",   &this->priv->custom_delete_button,
                                      NULL);
 
         gtk_container_add (GTK_CONTAINER (this), hbox);
@@ -269,6 +312,9 @@ gl_media_select_construct (glMediaSelect *this)
         this->priv->search_all_page_num =
                 gtk_notebook_page_num (GTK_NOTEBOOK (this->priv->notebook),
                                        this->priv->search_all_tab_vbox);
+        this->priv->custom_page_num =
+                gtk_notebook_page_num (GTK_NOTEBOOK (this->priv->notebook),
+                                       this->priv->custom_tab_vbox);
 
         gtk_widget_show_all (GTK_WIDGET (this));
 
@@ -347,22 +393,60 @@ gl_media_select_construct (glMediaSelect *this)
         load_search_all_list (this, this->priv->search_all_store, search_all_selection, search_all_names);
         lgl_db_free_template_name_list (search_all_names);
 
+        /* Custom templates treeview */
+        this->priv->custom_store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
+        gtk_tree_view_set_model (GTK_TREE_VIEW (this->priv->custom_treeview),
+                                 GTK_TREE_MODEL (this->priv->custom_store));
+        renderer = gtk_cell_renderer_pixbuf_new ();
+        column = gtk_tree_view_column_new_with_attributes ("", renderer,
+                                                           "pixbuf", PREVIEW_COLUMN,
+                                                           "stock-id", PREVIEW_COLUMN_STOCK,
+                                                           "stock-size", PREVIEW_COLUMN_STOCK_SIZE,
+                                                           NULL);
+        gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (this->priv->custom_treeview), column);
+        renderer = gtk_cell_renderer_text_new ();
+        column = gtk_tree_view_column_new_with_attributes ("", renderer,
+                                                           "markup", DESCRIPTION_COLUMN,
+                                                           NULL);
+        gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+        gtk_tree_view_append_column (GTK_TREE_VIEW (this->priv->custom_treeview), column);
+        custom_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->custom_treeview));
+        custom_list = lgl_db_get_template_name_list_all (NULL, NULL, "user-defined");
+        load_custom_list (this, this->priv->custom_store, custom_selection, custom_list);
+        lgl_db_free_template_name_list (custom_list);
+
+        page_size_id = gl_prefs_model_get_default_page_size (gl_prefs);
+        page_size_name = lgl_db_lookup_paper_name_from_id (page_size_id);
+
         /* Connect signals to controls */
-        g_signal_connect (G_OBJECT (this->priv->brand_combo), "changed",
-                          G_CALLBACK (filter_changed_cb),
-                          this);
-        g_signal_connect (G_OBJECT (this->priv->page_size_combo), "changed",
-                          G_CALLBACK (filter_changed_cb),
-                          this);
-        g_signal_connect (G_OBJECT (this->priv->category_combo), "changed",
-                          G_CALLBACK (filter_changed_cb),
-                          this);
+        g_signal_connect_swapped (G_OBJECT (this->priv->brand_combo), "changed",
+                                  G_CALLBACK (filter_changed_cb),
+                                  this);
+        g_signal_connect_swapped (G_OBJECT (this->priv->page_size_combo), "changed",
+                                  G_CALLBACK (filter_changed_cb),
+                                  this);
+        g_signal_connect_swapped (G_OBJECT (this->priv->category_combo), "changed",
+                                  G_CALLBACK (filter_changed_cb),
+                                  this);
         g_signal_connect (G_OBJECT (recent_selection), "changed",
                           G_CALLBACK (selection_changed_cb),
                           this);
         g_signal_connect (G_OBJECT (search_all_selection), "changed",
                           G_CALLBACK (selection_changed_cb),
                           this);
+        g_signal_connect (G_OBJECT (custom_selection), "changed",
+                          G_CALLBACK (selection_changed_cb),
+                          this);
+        g_signal_connect (G_OBJECT (this->priv->custom_add_button), "clicked",
+                          G_CALLBACK (custom_add_clicked_cb),
+                          this);
+        g_signal_connect (G_OBJECT (this->priv->custom_edit_button), "clicked",
+                          G_CALLBACK (custom_edit_clicked_cb),
+                          this);
+        g_signal_connect (G_OBJECT (this->priv->custom_delete_button), "clicked",
+                          G_CALLBACK (custom_delete_clicked_cb),
+                          this);
         g_signal_connect (G_OBJECT (this->priv->notebook), "switch-page",
                           G_CALLBACK (page_changed_cb),
                           this);
@@ -381,6 +465,8 @@ gl_media_select_construct (glMediaSelect *this)
         }
         gl_template_history_model_free_name_list (recent_list);
 
+        this->priv->db_notify_id = lgl_db_notify_add ((lglDbNotifyFunc)db_changed_cb, this);
+
         gl_debug (DEBUG_MEDIA_SELECT, "END");
 }
 
@@ -389,10 +475,8 @@ gl_media_select_construct (glMediaSelect *this)
 /* PRIVATE.  modify widget due to change in selection                       */
 /*--------------------------------------------------------------------------*/
 static void
-filter_changed_cb (GtkComboBox *combo,
-                   gpointer     user_data)
+filter_changed_cb (glMediaSelect *this)
 {
-        glMediaSelect     *this = GL_MEDIA_SELECT (user_data);
         gchar             *brand;
         gchar             *page_size_name, *page_size_id;
         gchar             *category_name, *category_id;
@@ -432,8 +516,7 @@ filter_changed_cb (GtkComboBox *combo,
                 g_free (category_id);
 
                 /* Emit our "changed" signal */
-                g_signal_emit (G_OBJECT (user_data),
-                               media_select_signals[CHANGED], 0);
+                g_signal_emit (G_OBJECT (this), signals[CHANGED], 0);
         }
         g_free (brand);
         g_free (page_size_name);
@@ -453,21 +536,90 @@ static void
 selection_changed_cb (GtkTreeSelection       *selection,
                       gpointer                user_data)
 {
-        glMediaSelect *this = GL_MEDIA_SELECT (user_data);
+        glMediaSelect     *this = GL_MEDIA_SELECT (user_data);
+        GtkTreeSelection  *custom_selection;
 
 	if (this->priv->stop_signals) return;
 
         gl_debug (DEBUG_MEDIA_SELECT, "START");
 
+        custom_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->custom_treeview));
+        gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_edit_button),
+                                  gtk_tree_selection_get_mode (custom_selection) != GTK_SELECTION_NONE );
+        gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_delete_button),
+                                  gtk_tree_selection_get_mode (custom_selection) != GTK_SELECTION_NONE );
+
+        if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_NONE)
+
         /* Emit our "changed" signal */
-        g_signal_emit (G_OBJECT (user_data),
-                       media_select_signals[CHANGED], 0);
+        g_signal_emit (G_OBJECT (user_data), signals[CHANGED], 0);
 
         gl_debug (DEBUG_MEDIA_SELECT, "END");
 }
 
 
 /*--------------------------------------------------------------------------*/
+/* PRIVATE.  Custom add button clicked callback.                            */
+/*--------------------------------------------------------------------------*/
+static void
+custom_add_clicked_cb (GtkButton  *button,
+                       gpointer    user_data)
+{
+        glMediaSelect *this = GL_MEDIA_SELECT (user_data);
+        GtkWidget     *window;
+        GtkWidget     *dialog;
+
+        window = gtk_widget_get_toplevel (GTK_WIDGET (this));
+        
+        dialog = gl_template_designer_new (GTK_WINDOW (window));
+
+        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+        gtk_widget_show (dialog);
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* PRIVATE.  Custom edit button clicked callback.                           */
+/*--------------------------------------------------------------------------*/
+static void
+custom_edit_clicked_cb (GtkButton  *button,
+                        gpointer    user_data)
+{
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* PRIVATE.  Custom delete button clicked callback.                         */
+/*--------------------------------------------------------------------------*/
+static void
+custom_delete_clicked_cb (GtkButton  *button,
+                          gpointer    user_data)
+{
+        glMediaSelect     *this = GL_MEDIA_SELECT (user_data);
+        GtkTreeSelection  *selection;
+        GtkTreeIter        iter;
+        GtkTreeModel      *model;        
+        gchar             *name;
+
+	this->priv->stop_signals = TRUE;
+
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->custom_treeview));
+
+        if (!gtk_tree_selection_get_mode (selection) == GTK_SELECTION_NONE)
+        {
+                gtk_tree_selection_get_selected (selection, &model, &iter);
+                gtk_tree_model_get (model, &iter, NAME_COLUMN, &name, -1);
+
+                lgl_db_delete_template_by_name (name);
+
+                g_free (name);
+        }
+
+	this->priv->stop_signals = FALSE;
+}
+
+
+/*--------------------------------------------------------------------------*/
 /* PRIVATE.  modify widget due to change in selection                       */
 /*--------------------------------------------------------------------------*/
 static void
@@ -488,13 +640,67 @@ page_changed_cb (GtkNotebook            *notebook,
 	this->priv->current_page_num = page_num;
 
         /* Emit our "changed" signal */
-        g_signal_emit (G_OBJECT (user_data),
-                       media_select_signals[CHANGED], 0);
+        g_signal_emit (G_OBJECT (user_data), signals[CHANGED], 0);
 
         gl_debug (DEBUG_MEDIA_SELECT, "END");
 }
 
 
+/*--------------------------------------------------------------------------*/
+/* PRIVATE.  DB changed notification callback.                              */
+/*--------------------------------------------------------------------------*/
+static void
+db_changed_cb (glMediaSelect *this)
+{
+        gchar             *brand;
+        gchar             *page_size_name, *page_size_id;
+        gchar             *category_name, *category_id;
+        GtkTreeSelection  *selection;
+        GList             *list;
+
+	this->priv->stop_signals = TRUE;
+
+        /* Update search all page. */
+        brand = gtk_combo_box_get_active_text (GTK_COMBO_BOX (this->priv->brand_combo));
+        page_size_name = gtk_combo_box_get_active_text (GTK_COMBO_BOX (this->priv->page_size_combo));
+        category_name = gtk_combo_box_get_active_text (GTK_COMBO_BOX (this->priv->category_combo));
+        if ( brand && strlen(brand) &&
+             page_size_name && strlen(page_size_name) &&
+             category_name && strlen(category_name) )
+        {
+                if (!g_utf8_collate (brand, _("Any")))
+                {
+                        g_free (brand);
+                        brand = NULL;
+                }
+                page_size_id = lgl_db_lookup_paper_id_from_name (page_size_name);
+                category_id = lgl_db_lookup_category_id_from_name (category_name);
+                list = lgl_db_get_template_name_list_all (brand, page_size_id, category_id);
+                selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->search_all_treeview));
+                load_search_all_list (this, this->priv->search_all_store, selection, list);
+                lgl_db_free_template_name_list (list);
+                g_free (page_size_id);
+                g_free (category_id);
+        }
+        g_free (brand);
+        g_free (page_size_name);
+        g_free (category_name);
+
+        /* Update custom page. */
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->custom_treeview));
+        list = lgl_db_get_template_name_list_all (NULL, NULL, "user-defined");
+        load_custom_list (this, this->priv->custom_store, selection, list);
+        lgl_db_free_template_name_list (list);
+
+	this->priv->stop_signals = FALSE;
+
+        /* Emit our "changed" signal */
+        g_signal_emit (G_OBJECT (this), signals[CHANGED], 0);
+
+        filter_changed_cb (this);
+}
+
+
 /****************************************************************************/
 /* query selected label template name.                                      */
 /****************************************************************************/
@@ -518,6 +724,10 @@ gl_media_select_get_name (glMediaSelect *this)
         {
                 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->search_all_treeview));
         }
+        else if (page_num == this->priv->custom_page_num)
+        {
+                selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (this->priv->custom_treeview));
+        }
         else
         {
                 g_print ("notebook page = %d\n", page_num);
@@ -728,7 +938,7 @@ load_recent_list (glMediaSelect      *this,
                                                                           GTK_BUTTONS_NONE,
                                                                           "%s", _("No recent templates found."));
                 gl_message_bar_format_secondary_text (GL_MESSAGE_BAR (this->priv->recent_info_bar),
-                                                      "%s", _("Try selecting a template from the \"Search all templates\" page."));
+                                                      "%s", _("Try selecting a template in the \"Search all\" tab."));
 
                 gtk_box_pack_start (GTK_BOX (this->priv->recent_info_vbox),
                                     this->priv->recent_info_bar,
@@ -836,6 +1046,104 @@ load_search_all_list (glMediaSelect      *this,
 }
 
 
+/*--------------------------------------------------------------------------*/
+/* PRIVATE.  Load list store from template name list.                       */
+/*--------------------------------------------------------------------------*/
+static void
+load_custom_list (glMediaSelect      *this,
+                  GtkListStore       *store,
+                  GtkTreeSelection   *selection,
+                  GList              *list)
+{
+        GList            *p;
+        GtkTreeIter       iter;
+        lglUnits          units;
+        lglTemplate      *template;
+        lglTemplateFrame *frame;
+        GdkPixbuf        *pixbuf;
+        gchar            *size;
+        gchar            *layout;
+        gchar            *description;
+
+        gl_debug (DEBUG_MEDIA_SELECT, "START");
+
+        gtk_list_store_clear (store);
+
+
+        if ( this->priv->custom_info_bar )
+        {
+                gtk_container_remove (GTK_CONTAINER (this->priv->custom_info_vbox),
+                                      this->priv->custom_info_bar);
+                this->priv->custom_info_bar = NULL;
+        }
+
+        if (list)
+        {
+
+                units = gl_prefs_model_get_units (gl_prefs);
+
+                for ( p=list; p!=NULL; p=p->next )
+                {
+
+                        gl_debug (DEBUG_MEDIA_SELECT, "p->data = \"%s\"", p->data);
+
+                        template = lgl_db_lookup_template_from_name (p->data);
+                        frame    = (lglTemplateFrame *)template->frames->data;
+                        pixbuf   = gl_mini_preview_pixbuf_cache_get_pixbuf (p->data);
+
+                        size     = lgl_template_frame_get_size_description (frame, units);
+                        layout   = lgl_template_frame_get_layout_description (frame);
+                        description = g_strdup_printf ("<b>%s: %s</b>\n%s\n%s",
+                                                       (gchar *)p->data,
+                                                       template->description,
+                                                       size,
+                                                       layout);
+                        g_free (size);
+                        g_free (layout);
+
+                        lgl_template_free (template);
+
+                        gtk_list_store_append (store, &iter);
+                        gtk_list_store_set (store, &iter,
+                                            NAME_COLUMN, p->data,
+                                            PREVIEW_COLUMN, pixbuf,
+                                            DESCRIPTION_COLUMN, description,
+                                            -1);
+
+                        g_object_unref (G_OBJECT (pixbuf));
+                        g_free (description);
+                }
+
+                gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
+                gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
+                gtk_tree_selection_select_iter (selection, &iter);
+
+                gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_edit_button), TRUE);
+                gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_delete_button), TRUE);
+        }
+        else
+        {
+                this->priv->custom_info_bar = gl_message_bar_new (GTK_MESSAGE_INFO,
+                                                                          GTK_BUTTONS_NONE,
+                                                                          "%s", _("No custom templates found."));
+                gl_message_bar_format_secondary_text (GL_MESSAGE_BAR (this->priv->custom_info_bar),
+                                                      "%s", _("You may create new templates or try searching for pre-defined templates in the \"Search all\" tab."));
+
+                gtk_box_pack_start (GTK_BOX (this->priv->custom_info_vbox),
+                                    this->priv->custom_info_bar,
+                                    FALSE, FALSE, 0);
+                gtk_widget_show_all (this->priv->custom_info_bar);
+
+                gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+
+                gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_edit_button), FALSE);
+                gtk_widget_set_sensitive (GTK_WIDGET (this->priv->custom_delete_button), FALSE);
+        }
+
+        gl_debug (DEBUG_MEDIA_SELECT, "END");
+}
+
+
 
 /*
  * Local Variables:       -- emacs



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