]> git.sur5r.net Git - glabels/commitdiff
Replacing gnome-recent* with current egg recent files module.
authorJim Evins <evins@snaught.com>
Thu, 9 Oct 2003 01:48:17 +0000 (01:48 +0000)
committerJim Evins <evins@snaught.com>
Thu, 9 Oct 2003 01:48:17 +0000 (01:48 +0000)
git-svn-id: https://glabels.svn.sourceforge.net/svnroot/glabels/trunk@308 f5e0f49d-192f-0410-a22d-a8d8700d0965

16 files changed:
glabels2/src/recent-files/Makefile.am [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-item.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-item.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-model.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-model.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-util.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-util.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-vfs-utils.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-vfs-utils.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view-bonobo.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view-bonobo.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view-gtk.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view-gtk.h [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view.c [new file with mode: 0644]
glabels2/src/recent-files/egg-recent-view.h [new file with mode: 0644]
glabels2/src/recent-files/update-from-egg.sh [new file with mode: 0755]

diff --git a/glabels2/src/recent-files/Makefile.am b/glabels2/src/recent-files/Makefile.am
new file mode 100644 (file)
index 0000000..e331db2
--- /dev/null
@@ -0,0 +1,36 @@
+INCLUDES = \
+       $(GLABELS_CFLAGS)                                       \
+       -DG_DISABLE_DEPRECATED                                  \
+       -DGDK_DISABLE_DEPRECATED                                \
+       -DGTK_DISABLE_DEPRECATED                                \
+       -DGDK_PIXBUF_DISABLE_DEPRECATED                         \
+       -DGNOME_DISABLE_DEPRECATED                      
+
+headerdir = $(prefix)/include/gedit-@GEDIT_MAJOR@/gedit/recent-files
+header_DATA =                          \
+        egg-recent-model.h             \
+        egg-recent-item.h              \
+        egg-recent-view.h              \
+        egg-recent-view-bonobo.h       \
+        egg-recent-view-gtk.h          \
+       egg-recent-vfs-utils.h          \
+       egg-recent-util.h
+
+EGG_FILES =                            \
+        egg-recent-model.c             \
+        egg-recent-item.c              \
+        egg-recent-view.c              \
+        egg-recent-view-bonobo.c       \
+        egg-recent-view-gtk.c          \
+       egg-recent-vfs-utils.c          \
+        egg-recent-util.c              \
+       $(header_DATA)
+
+noinst_LTLIBRARIES = librecent.la
+
+librecent_la_SOURCES = $(EGG_FILES)
+
+EGGDIR=$(srcdir)/../../../libegg/libegg/recent-files
+regenerate-built-sources:
+       EGGFILES="$(EGG_FILES)" EGGDIR="$(EGGDIR)" $(srcdir)/update-from-egg.sh
+
diff --git a/glabels2/src/recent-files/egg-recent-item.c b/glabels2/src/recent-files/egg-recent-item.c
new file mode 100644 (file)
index 0000000..139b637
--- /dev/null
@@ -0,0 +1,347 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   James Willcox <jwillcox@cs.indiana.edu>
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include "egg-recent-item.h"
+
+
+
+EggRecentItem *
+egg_recent_item_new (void)
+{
+       EggRecentItem *item;
+
+       item = g_new (EggRecentItem, 1);
+
+       item->groups = NULL;
+       item->private = FALSE;
+       item->uri = NULL;
+       item->mime_type = NULL;
+
+       item->refcount = 1;
+
+       return item;
+}
+
+static void
+egg_recent_item_free (EggRecentItem *item)
+{
+       if (item->uri)
+               g_free (item->uri);
+
+       if (item->mime_type)
+               g_free (item->mime_type);
+
+       if (item->groups) {
+               g_list_foreach (item->groups, (GFunc)g_free, NULL);
+               g_list_free (item->groups);
+               item->groups = NULL;
+       }
+
+       g_free (item);
+}
+
+EggRecentItem *
+egg_recent_item_ref (EggRecentItem *item)
+{
+       item->refcount++;
+       return item;
+}
+
+EggRecentItem *
+egg_recent_item_unref (EggRecentItem *item)
+{
+       item->refcount--;
+
+       if (item->refcount == 0) {
+               egg_recent_item_free (item);
+       }
+
+       return item;
+}
+
+
+EggRecentItem * 
+egg_recent_item_new_from_uri (const gchar *uri)
+{
+       EggRecentItem *item;
+
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       item = egg_recent_item_new ();
+
+       if (!egg_recent_item_set_uri (item ,uri)) {
+               egg_recent_item_free (item);
+               return NULL;
+       }
+       
+       item->mime_type = gnome_vfs_get_mime_type (item->uri);
+
+       if (!item->mime_type)
+               item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN);
+
+       return item;
+}
+
+/*
+static GList *
+egg_recent_item_copy_groups (const GList *list)
+{
+       GList *newlist = NULL;
+
+       while (list) {
+               gchar *group = (gchar *)list->data;
+
+               newlist = g_list_prepend (newlist, g_strdup (group));
+
+               list = list->next;
+       }
+
+       return newlist;
+}
+
+
+EggRecentItem *
+egg_recent_item_copy (const EggRecentItem *item)
+{
+       EggRecentItem *newitem;
+
+       newitem = egg_recent_item_new ();
+       newitem->uri = g_strdup (item->uri);
+       if (item->mime_type)
+               newitem->mime_type = g_strdup (item->mime_type);
+       newitem->timestamp = item->timestamp;
+       newitem->private = item->private;
+       newitem->groups = egg_recent_item_copy_groups (item->groups);
+
+       return newitem;
+}
+*/
+
+/*
+EggRecentItem *
+egg_recent_item_new_valist (const gchar *uri, va_list args)
+{
+       EggRecentItem *item;
+       EggRecentArg arg;
+       gchar *str1;
+       gchar *str2;
+       gboolean priv;
+
+       item = egg_recent_item_new ();
+
+       arg = va_arg (args, EggRecentArg);
+
+       while (arg != EGG_RECENT_ARG_NONE) {
+               switch (arg) {
+                       case EGG_RECENT_ARG_MIME_TYPE:
+                               str1 = va_arg (args, gchar*);
+
+                               egg_recent_item_set_mime_type (item, str1);
+                       break;
+                       case EGG_RECENT_ARG_GROUP:
+                               str1 = va_arg (args, gchar*);
+
+                               egg_recent_item_add_group (item, str1);
+                       break;
+                       case EGG_RECENT_ARG_PRIVATE:
+                               priv = va_arg (args, gboolean);
+
+                               egg_recent_item_set_private (item, priv);
+                       break;
+                       default:
+                       break;
+               }
+
+               arg = va_arg (args, EggRecentArg);
+       }
+
+       return item;
+}
+*/
+
+gboolean
+egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri)
+{
+       gchar *utf8_uri;
+
+       /* if G_BROKEN_FILENAMES is not set, this should succede */
+       if (g_utf8_validate (uri, -1, NULL)) {
+               item->uri = gnome_vfs_make_uri_from_input (uri);
+       } else {
+               utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL);
+
+               if (utf8_uri == NULL) {
+                       g_warning ("Couldn't convert URI to UTF-8");
+                       return FALSE;
+               }
+
+               if (g_utf8_validate (utf8_uri, -1, NULL)) {
+                       item->uri = gnome_vfs_make_uri_from_input (utf8_uri);
+               } else {
+                       g_free (utf8_uri);
+                       return FALSE;
+               }
+
+               g_free (utf8_uri);
+       }
+
+       return TRUE;
+}
+
+gchar * 
+egg_recent_item_get_uri (const EggRecentItem *item)
+{
+       return g_strdup (item->uri);
+}
+
+G_CONST_RETURN gchar * 
+egg_recent_item_peek_uri (const EggRecentItem *item)
+{
+       return item->uri;
+}
+
+gchar * 
+egg_recent_item_get_uri_utf8 (const EggRecentItem *item)
+{
+       /* this could fail, but it's not likely, since we've already done it
+        * once in set_uri()
+        */
+       return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL);
+}
+
+gchar *
+egg_recent_item_get_uri_for_display (const EggRecentItem *item)
+{
+       return gnome_vfs_format_uri_for_display (item->uri);
+}
+
+void 
+egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime)
+{
+       item->mime_type = g_strdup (mime);
+}
+
+gchar * 
+egg_recent_item_get_mime_type (const EggRecentItem *item)
+{
+       return g_strdup (item->mime_type);
+}
+
+void 
+egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp)
+{
+       if (timestamp == (time_t) -1)
+               time (&timestamp);
+
+       item->timestamp = timestamp;
+}
+
+time_t 
+egg_recent_item_get_timestamp (const EggRecentItem *item)
+{
+       return item->timestamp;
+}
+
+G_CONST_RETURN GList *
+egg_recent_item_get_groups (const EggRecentItem *item)
+{
+       return item->groups;
+}
+
+gboolean
+egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
+{
+       GList *tmp;
+
+       tmp = item->groups;
+       while (tmp != NULL) {
+               gchar *val = (gchar *)tmp->data;
+               
+               if (strcmp (group_name, val) == 0)
+                       return TRUE;
+
+               tmp = tmp->next;
+       }
+       
+       return FALSE;
+}
+
+void
+egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
+{
+       g_return_if_fail (group_name != NULL);
+
+       if (!egg_recent_item_in_group (item, group_name))
+               item->groups = g_list_append (item->groups, g_strdup (group_name));
+}
+
+void
+egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
+{
+       GList *tmp;
+
+       g_return_if_fail (group_name != NULL);
+
+       tmp = item->groups;
+       while (tmp != NULL) {
+               gchar *val = (gchar *)tmp->data;
+               
+               if (strcmp (group_name, val) == 0) {
+                       item->groups = g_list_remove (item->groups,
+                                                     val);
+                       g_free (val);
+                       break;
+               }
+
+               tmp = tmp->next;
+       }
+}
+
+void
+egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
+{
+       item->private = priv;
+}
+
+gboolean
+egg_recent_item_get_private (const EggRecentItem *item)
+{
+       return item->private;
+}
+
+GType
+egg_recent_item_get_type (void)
+{
+       static GType boxed_type = 0;
+       
+       if (!boxed_type) {
+               boxed_type = g_boxed_type_register_static ("EggRecentItem",
+                                       (GBoxedCopyFunc)egg_recent_item_ref,
+                                       (GBoxedFreeFunc)egg_recent_item_unref);
+       }
+       
+       return boxed_type;
+}
diff --git a/glabels2/src/recent-files/egg-recent-item.h b/glabels2/src/recent-files/egg-recent-item.h
new file mode 100644 (file)
index 0000000..556c31e
--- /dev/null
@@ -0,0 +1,77 @@
+
+#ifndef __EGG_RECENT_ITEM_H__
+#define __EGG_RECENT_ITEM_H__
+
+#include <time.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_ITEM       (egg_recent_item_get_type ())
+
+#define EGG_RECENT_ITEM_LIST_UNREF(list) \
+       g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
+       g_list_free (list);
+
+typedef struct _EggRecentItem EggRecentItem;
+
+struct _EggRecentItem {
+       /* do not access any of these directly */
+       gchar *uri;
+       gchar *mime_type;
+       time_t timestamp;
+
+       gboolean private;
+
+       GList *groups;
+       
+       int refcount;
+};
+
+GType          egg_recent_item_get_type (void) G_GNUC_CONST;
+
+/* constructors */
+EggRecentItem * egg_recent_item_new (void);
+
+EggRecentItem *        egg_recent_item_ref (EggRecentItem *item);
+EggRecentItem *        egg_recent_item_unref (EggRecentItem *item);
+
+/* automatically fetches the mime type, etc */
+EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
+
+gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
+gchar * egg_recent_item_get_uri (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
+gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
+
+void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
+gchar * egg_recent_item_get_mime_type (const EggRecentItem *item);
+
+void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
+time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
+
+G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
+
+
+/* groups */
+G_CONST_RETURN GList *  egg_recent_item_get_groups (const EggRecentItem *item);
+
+gboolean       egg_recent_item_in_group (const EggRecentItem *item,
+                                          const gchar *group_name);
+
+void           egg_recent_item_add_group (EggRecentItem *item,
+                                           const gchar *group_name);
+
+void           egg_recent_item_remove_group (EggRecentItem *item,
+                                              const gchar *group_name);
+
+void           egg_recent_item_set_private (EggRecentItem *item,
+                                             gboolean priv);
+
+gboolean       egg_recent_item_get_private (const EggRecentItem *item);
+
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_ITEM_H__ */
diff --git a/glabels2/src/recent-files/egg-recent-model.c b/glabels2/src/recent-files/egg-recent-model.c
new file mode 100644 (file)
index 0000000..cc82e9b
--- /dev/null
@@ -0,0 +1,1749 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   James Willcox <jwillcox@cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <time.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime-utils.h>
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_MODEL_FILE_PATH "/.recently-used"
+#define EGG_RECENT_MODEL_BUFFER_SIZE 8192
+
+#define EGG_RECENT_MODEL_MAX_ITEMS 500
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT 10
+#define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200
+
+#define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files"
+#define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit"
+#define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire"
+
+struct _EggRecentModelPrivate {
+       GSList *mime_filter_values;     /* list of mime types we allow */
+       GSList *group_filter_values;    /* list of groups we allow */
+       GSList *scheme_filter_values;   /* list of URI schemes we allow */
+
+       EggRecentModelSort sort_type; /* type of sorting to be done */
+
+       int limit;                      /* soft limit for length of the list */
+       int expire_days;                /* number of days to hold an item */
+
+       char *path;                     /* path to the file we store stuff in */
+
+       GHashTable *monitors;
+
+       GnomeVFSMonitorHandle *monitor;
+
+       GConfClient *client;
+       gboolean use_default_limit;
+
+       guint limit_change_notify_id;
+       guint expiration_change_notify_id;
+
+       guint changed_timeout;
+};
+
+/* signals */
+enum {
+       CHANGED,
+       LAST_SIGNAL
+};
+
+static GType model_signals[LAST_SIGNAL] = { 0 };
+
+/* properties */
+enum {
+       PROP_BOGUS,
+       PROP_MIME_FILTERS,
+       PROP_GROUP_FILTERS,
+       PROP_SCHEME_FILTERS,
+       PROP_SORT_TYPE,
+       PROP_LIMIT
+};
+
+typedef struct {
+       GSList *states;
+       GList *items;
+       EggRecentItem *current_item;
+}ParseInfo;
+
+typedef enum {
+       STATE_START,
+       STATE_RECENT_FILES,
+       STATE_RECENT_ITEM,
+       STATE_URI,
+       STATE_MIME_TYPE,
+       STATE_TIMESTAMP,
+       STATE_PRIVATE,
+       STATE_GROUPS,
+       STATE_GROUP
+} ParseState;
+
+typedef struct _ChangedData {
+       EggRecentModel *model;
+       GList *list;
+}ChangedData;
+
+#define TAG_RECENT_FILES "RecentFiles"
+#define TAG_RECENT_ITEM "RecentItem"
+#define TAG_URI "URI"
+#define TAG_MIME_TYPE "Mime-Type"
+#define TAG_TIMESTAMP "Timestamp"
+#define TAG_PRIVATE "Private"
+#define TAG_GROUPS "Groups"
+#define TAG_GROUP "Group"
+
+static void start_element_handler (GMarkupParseContext *context,
+                             const gchar *element_name,
+                             const gchar **attribute_names,
+                             const gchar **attribute_values,
+                             gpointer user_data,
+                             GError **error);
+
+static void end_element_handler (GMarkupParseContext *context,
+                           const gchar *element_name,
+                           gpointer user_data,
+                           GError **error);
+
+static void text_handler (GMarkupParseContext *context,
+                    const gchar *text,
+                    gsize text_len,
+                    gpointer user_data,
+                    GError **error);
+
+static void error_handler (GMarkupParseContext *context,
+                     GError *error,
+                     gpointer user_data);
+
+static GMarkupParser parser = {start_element_handler, end_element_handler,
+                       text_handler,
+                       NULL,
+                       error_handler};
+
+static gboolean
+egg_recent_model_string_match (const GSList *list, const gchar *str)
+{
+       const GSList *tmp;
+
+       if (list == NULL || str == NULL)
+               return TRUE;
+
+       tmp = list;
+       
+       while (tmp) {
+               if (g_pattern_match_string (tmp->data, str))
+                       return TRUE;
+               
+               tmp = tmp->next;
+       }
+
+       return FALSE;
+}
+
+static gboolean
+egg_recent_model_write_raw (EggRecentModel *model, FILE *file,
+                             const gchar *content)
+{
+       int len;
+       int fd;
+       struct stat sbuf;
+
+       rewind (file);
+
+       len = strlen (content);
+       fd = fileno (file);
+
+       if (fstat (fd, &sbuf) < 0)
+               g_warning ("Couldn't stat XML document.");
+
+       if ((off_t)len < sbuf.st_size) {
+               ftruncate (fd, len);
+       }
+
+       if (fputs (content, file) == EOF)
+               return FALSE;
+
+       fsync (fd);
+       rewind (file);
+
+       return TRUE;
+}
+
+static GList *
+egg_recent_model_delete_from_list (GList *list,
+                                      const gchar *uri)
+{
+       GList *tmp;
+
+       if (!uri)
+               return list;
+
+       tmp = list;
+
+       while (tmp) {
+               EggRecentItem *item = tmp->data;
+               GList         *next;
+
+               next = tmp->next;
+
+               if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
+                       egg_recent_item_unref (item);
+
+                       list = g_list_remove_link (list, tmp);
+                       g_list_free_1 (tmp);
+               }
+
+               tmp = next;
+       }
+
+       return list;
+}
+
+static void
+egg_recent_model_add_new_groups (EggRecentItem *item,
+                                EggRecentItem *upd_item)
+{
+       const GList *tmp;
+
+       tmp = egg_recent_item_get_groups (upd_item);
+
+       while (tmp) {
+               char *group = tmp->data;
+
+               if (!egg_recent_item_in_group (item, group))
+                       egg_recent_item_add_group (item, group);
+
+               tmp = tmp->next;
+       }
+}
+
+static gboolean
+egg_recent_model_update_item (GList *items, EggRecentItem *upd_item)
+{
+       GList      *tmp;
+       const char *uri;
+
+       uri = egg_recent_item_peek_uri (upd_item);
+
+       tmp = items;
+
+       while (tmp) {
+               EggRecentItem *item = tmp->data;
+
+               if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
+                       egg_recent_item_set_timestamp (item, (time_t) -1);
+
+                       egg_recent_model_add_new_groups (item, upd_item);
+
+                       return TRUE;
+               }
+
+               tmp = tmp->next;
+       }
+
+       return FALSE;
+}
+
+static gchar *
+egg_recent_model_read_raw (EggRecentModel *model, FILE *file)
+{
+       GString *string;
+       char buf[EGG_RECENT_MODEL_BUFFER_SIZE];
+
+       rewind (file);
+
+       string = g_string_new (NULL);
+       while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) {
+               string = g_string_append (string, buf);
+       }
+
+       rewind (file);
+
+       return g_string_free (string, FALSE);
+}
+
+
+
+static void
+parse_info_init (ParseInfo *info)
+{
+       info->states = g_slist_prepend (NULL, STATE_START);
+       info->items = NULL;
+}
+
+static void
+parse_info_free (ParseInfo *info)
+{
+       g_slist_free (info->states);
+}
+
+static void
+push_state (ParseInfo  *info,
+            ParseState  state)
+{
+  info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
+}
+
+static void
+pop_state (ParseInfo *info)
+{
+  g_return_if_fail (info->states != NULL);
+
+  info->states = g_slist_remove (info->states, info->states->data);
+}
+
+static ParseState
+peek_state (ParseInfo *info)
+{
+  g_return_val_if_fail (info->states != NULL, STATE_START);
+
+  return GPOINTER_TO_INT (info->states->data);
+}
+
+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
+
+static void
+start_element_handler (GMarkupParseContext *context,
+                             const gchar *element_name,
+                             const gchar **attribute_names,
+                             const gchar **attribute_values,
+                             gpointer user_data,
+                             GError **error)
+{
+       ParseInfo *info = (ParseInfo *)user_data;
+
+       if (ELEMENT_IS (TAG_RECENT_FILES))
+               push_state (info, STATE_RECENT_FILES);
+       else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
+               info->current_item = egg_recent_item_new ();
+               push_state (info, STATE_RECENT_ITEM);
+       } else if (ELEMENT_IS (TAG_URI))
+               push_state (info, STATE_URI);
+       else if (ELEMENT_IS (TAG_MIME_TYPE))
+               push_state (info, STATE_MIME_TYPE);
+       else if (ELEMENT_IS (TAG_TIMESTAMP))
+               push_state (info, STATE_TIMESTAMP);
+       else if (ELEMENT_IS (TAG_PRIVATE)) {
+               push_state (info, STATE_PRIVATE);
+               egg_recent_item_set_private (info->current_item, TRUE);
+       } else if (ELEMENT_IS (TAG_GROUPS))
+               push_state (info, STATE_GROUPS);
+       else if (ELEMENT_IS (TAG_GROUP)) 
+               push_state (info, STATE_GROUP);
+}
+
+static gint
+list_compare_func_mru (gpointer a, gpointer b)
+{
+       EggRecentItem *item_a = (EggRecentItem *)a;
+       EggRecentItem *item_b = (EggRecentItem *)b;
+
+       return item_a->timestamp < item_b->timestamp;
+}
+
+static gint
+list_compare_func_lru (gpointer a, gpointer b)
+{
+       EggRecentItem *item_a = (EggRecentItem *)a;
+       EggRecentItem *item_b = (EggRecentItem *)b;
+
+       return item_a->timestamp > item_b->timestamp;
+}
+
+
+
+static void
+end_element_handler (GMarkupParseContext *context,
+                           const gchar *element_name,
+                           gpointer user_data,
+                           GError **error)
+{
+       ParseInfo *info = (ParseInfo *)user_data;
+
+       switch (peek_state (info)) {
+               case STATE_RECENT_ITEM:
+                       info->items = g_list_append (info->items,
+                                                   info->current_item);
+                       if (info->current_item->uri == NULL ||
+                           strlen (info->current_item->uri) == 0)
+                               g_warning ("URI NOT LOADED");
+               break;
+               default:
+               break;
+       }
+
+       pop_state (info);
+}
+
+static void
+text_handler (GMarkupParseContext *context,
+                    const gchar *text,
+                    gsize text_len,
+                    gpointer user_data,
+                    GError **error)
+{
+       ParseInfo *info = (ParseInfo *)user_data;
+
+       switch (peek_state (info)) {
+               case STATE_START:
+               case STATE_RECENT_FILES:
+               case STATE_RECENT_ITEM:
+               case STATE_PRIVATE:
+               case STATE_GROUPS:
+               break;
+               case STATE_URI:
+                       egg_recent_item_set_uri (info->current_item, text);
+               break;
+               case STATE_MIME_TYPE:
+                       egg_recent_item_set_mime_type (info->current_item,
+                                                        text);
+               break;
+               case STATE_TIMESTAMP:
+                       egg_recent_item_set_timestamp (info->current_item,
+                                                        (time_t)atoi (text));
+               break;
+               case STATE_GROUP:
+                       egg_recent_item_add_group (info->current_item,
+                                                    text);
+               break;
+       }
+                       
+}
+
+static void
+error_handler (GMarkupParseContext *context,
+                     GError *error,
+                     gpointer user_data)
+{
+       g_warning ("Error in parse: %s", error->message);
+}
+
+static void
+egg_recent_model_enforce_limit (GList *list, int limit)
+{
+       int len;
+       GList *end;
+
+       /* limit < 0 means unlimited */
+       if (limit <= 0)
+               return;
+
+       len = g_list_length (list);
+
+       if (len > limit) {
+               GList *next;
+
+               end = g_list_nth (list, limit-1);
+               next = end->next;
+
+               end->next = NULL;
+
+               EGG_RECENT_ITEM_LIST_UNREF (next);
+       }
+}
+
+static GList *
+egg_recent_model_sort (EggRecentModel *model, GList *list)
+{
+       switch (model->priv->sort_type) {
+               case EGG_RECENT_MODEL_SORT_MRU:
+                       list = g_list_sort (list,
+                                       (GCompareFunc)list_compare_func_mru);   
+               break;
+               case EGG_RECENT_MODEL_SORT_LRU:
+                       list = g_list_sort (list,
+                                       (GCompareFunc)list_compare_func_lru);
+               break;
+               case EGG_RECENT_MODEL_SORT_NONE:
+               break;
+       }
+
+       return list;
+}
+
+static gboolean
+egg_recent_model_group_match (EggRecentItem *item, GSList *groups)
+{
+       GSList *tmp;
+
+       tmp = groups;
+
+       while (tmp != NULL) {
+               const gchar * group = (const gchar *)tmp->data;
+
+               if (egg_recent_item_in_group (item, group))
+                       return TRUE;
+
+               tmp = tmp->next;
+       }
+
+       return FALSE;
+}
+
+static GList *
+egg_recent_model_filter (EggRecentModel *model,
+                               GList *list)
+{
+       EggRecentItem *item;
+       GList *newlist = NULL;
+       gchar *mime_type;
+       gchar *uri;
+
+       g_return_val_if_fail (list != NULL, NULL);
+
+       while (list) {
+               gboolean pass_mime_test = FALSE;
+               gboolean pass_group_test = FALSE;
+               gboolean pass_scheme_test = FALSE;
+               item = (EggRecentItem *)list->data;
+               list = list->next;
+
+               uri = egg_recent_item_get_uri (item);
+
+               /* filter by mime type */
+               if (model->priv->mime_filter_values != NULL) {
+                       mime_type = egg_recent_item_get_mime_type (item);
+
+                       if (egg_recent_model_string_match
+                                       (model->priv->mime_filter_values,
+                                        mime_type))
+                               pass_mime_test = TRUE;
+
+                       g_free (mime_type);
+               } else
+                       pass_mime_test = TRUE;
+
+               /* filter by group */
+               if (pass_mime_test && model->priv->group_filter_values != NULL) {
+                       if (egg_recent_model_group_match
+                                       (item, model->priv->group_filter_values))
+                               pass_group_test = TRUE;
+               } else if (egg_recent_item_get_private (item)) {
+                       pass_group_test = FALSE;
+               } else
+                       pass_group_test = TRUE;
+
+               /* filter by URI scheme */
+               if (pass_mime_test && pass_group_test &&
+                   model->priv->scheme_filter_values != NULL) {
+                       gchar *scheme;
+                       
+                       scheme = gnome_vfs_get_uri_scheme (uri);
+
+                       if (egg_recent_model_string_match
+                               (model->priv->scheme_filter_values, scheme))
+                               pass_scheme_test = TRUE;
+
+                       g_free (scheme);
+               } else
+                       pass_scheme_test = TRUE;
+
+               if (pass_mime_test && pass_group_test && pass_scheme_test)
+                       newlist = g_list_prepend (newlist, item);
+
+               g_free (uri);
+       }
+
+       if (newlist) {
+               newlist = g_list_reverse (newlist);
+               g_list_free (list);
+       }
+
+       
+       return newlist;
+}
+
+
+
+#if 0
+static void
+egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle,
+                              const gchar *monitor_uri,
+                              const gchar *info_uri,
+                              GnomeVFSMonitorEventType event_type,
+                              gpointer user_data)
+{
+       EggRecentModel *model;
+
+       model = EGG_RECENT_MODEL (user_data);
+
+       if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
+               egg_recent_model_delete (model, monitor_uri);
+               g_hash_table_remove (model->priv->monitors, monitor_uri);
+       }
+}
+
+
+
+static void
+egg_recent_model_monitor_list (EggRecentModel *model, GList *list)
+{
+       GList *tmp;
+
+       tmp = list;
+       while (tmp) {
+               EggRecentItem *item = (EggRecentItem *)tmp->data;
+               GnomeVFSMonitorHandle *handle;
+               GnomeVFSResult res;
+               gchar *uri;
+
+               tmp = tmp->next;
+               
+               uri = egg_recent_item_get_uri (item);
+               if (g_hash_table_lookup (model->priv->monitors, uri)) {
+                       /* already monitoring this one */
+                       g_free (uri);
+                       continue;
+               }
+
+               res = gnome_vfs_monitor_add (&handle, uri,
+                                            GNOME_VFS_MONITOR_FILE,
+                                            egg_recent_model_monitor_list_cb,
+                                            model);
+               
+               if (res == GNOME_VFS_OK)
+                       g_hash_table_insert (model->priv->monitors, uri, handle);
+               else
+                       g_free (uri);
+       }
+}
+#endif
+
+
+static gboolean
+egg_recent_model_changed_timeout (EggRecentModel *model)
+{
+       egg_recent_model_changed (model);
+
+       return FALSE;
+}
+
+static void
+egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle,
+                              const gchar *monitor_uri,
+                              const gchar *info_uri,
+                              GnomeVFSMonitorEventType event_type,
+                              gpointer user_data)
+{
+       EggRecentModel *model;
+
+       g_return_if_fail (user_data != NULL);
+       g_return_if_fail (EGG_IS_RECENT_MODEL (user_data));
+       model = EGG_RECENT_MODEL (user_data);
+
+       if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED) {
+               if (model->priv->changed_timeout > 0) {
+                       g_source_remove (model->priv->changed_timeout);
+               }
+
+               model->priv->changed_timeout = g_timeout_add (
+                       EGG_RECENT_MODEL_TIMEOUT_LENGTH,
+                       (GSourceFunc)egg_recent_model_changed_timeout,
+                       model);
+       }
+}
+
+static void
+egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor)
+{
+       if (should_monitor && model->priv->monitor == NULL) {
+
+               gnome_vfs_monitor_add (&model->priv->monitor,
+                                            model->priv->path,
+                                            GNOME_VFS_MONITOR_FILE,
+                                            egg_recent_model_monitor_cb,
+                                            model);
+
+               /* if the above fails, don't worry about it.
+                * local notifications will still happen
+                */
+
+       } else if (!should_monitor && model->priv->monitor != NULL) {
+               gnome_vfs_monitor_cancel (model->priv->monitor);
+               model->priv->monitor = NULL;
+       }
+}
+
+static void
+egg_recent_model_set_limit_internal (EggRecentModel *model, int limit)
+{
+       model->priv->limit = limit;
+
+       if (limit <= 0)
+               egg_recent_model_monitor (model, FALSE);
+       else {
+               egg_recent_model_monitor (model, TRUE);
+               egg_recent_model_changed (model);
+       }
+}
+
+static GList *
+egg_recent_model_read (EggRecentModel *model, FILE *file)
+{
+       GList *list=NULL;
+       gchar *content;
+       GMarkupParseContext *ctx;
+       ParseInfo info;
+       GError *error;
+
+       content = egg_recent_model_read_raw (model, file);
+
+       if (strlen (content) <= 0) {
+               g_free (content);
+               return NULL;
+       }
+
+       parse_info_init (&info);
+       
+       ctx = g_markup_parse_context_new (&parser, 0, &info, NULL);
+       
+       error = NULL;
+       if (!g_markup_parse_context_parse (ctx, content, strlen (content),
+                                          &error)) {
+               g_warning (error->message);
+               g_error_free (error);
+               error = NULL;
+               goto out;
+       }
+
+       error = NULL;
+       if (!g_markup_parse_context_end_parse (ctx, &error))
+               goto out;
+       
+       g_markup_parse_context_free (ctx);
+out:
+       list = info.items;
+
+       parse_info_free (&info);
+
+       g_free (content);
+
+       /*
+       g_print ("Total items: %d\n", g_list_length (list));
+       */
+
+       return list;
+}
+
+
+static gboolean
+egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list)
+{
+       GString *string;
+       gchar *data;
+       EggRecentItem *item;
+       const GList *groups;
+       int i;
+       int ret;
+       
+       string = g_string_new ("<?xml version=\"1.0\"?>\n");
+       string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
+
+       i=0;
+       while (list) {
+               gchar *uri;
+               gchar *mime_type;
+               gchar *escaped_uri;
+               time_t timestamp;
+               item = (EggRecentItem *)list->data;
+
+
+               uri = egg_recent_item_get_uri_utf8 (item);
+               escaped_uri = g_markup_escape_text (uri,
+                                                   strlen (uri));
+               g_free (uri);
+
+               mime_type = egg_recent_item_get_mime_type (item);
+               timestamp = egg_recent_item_get_timestamp (item);
+               
+               string = g_string_append (string, "  <" TAG_RECENT_ITEM ">\n");
+
+               g_string_append_printf (string,
+                               "    <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
+
+               if (mime_type)
+                       g_string_append_printf (string,
+                               "    <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
+               else
+                       g_string_append_printf (string,
+                               "    <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
+
+               
+               g_string_append_printf (string,
+                               "    <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
+
+               if (egg_recent_item_get_private (item))
+                       string = g_string_append (string,
+                                       "    <" TAG_PRIVATE "/>\n");
+
+               /* write the groups */
+               string = g_string_append (string,
+                               "    <" TAG_GROUPS ">\n");
+               groups = egg_recent_item_get_groups (item);
+
+               if (groups == NULL && egg_recent_item_get_private (item))
+                       g_warning ("Item with URI \"%s\" marked as private, but"
+                                  " does not belong to any groups.\n", uri);
+               
+               while (groups) {
+                       const gchar *group = (const gchar *)groups->data;
+                       gchar *escaped_group;
+
+                       escaped_group = g_markup_escape_text (group, strlen(group));
+
+                       g_string_append_printf (string,
+                                       "      <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
+                                       escaped_group);
+
+                       g_free (escaped_group);
+
+                       groups = groups->next;
+               }
+               
+               string = g_string_append (string, "    </" TAG_GROUPS ">\n");
+
+               string = g_string_append (string,
+                               "  </" TAG_RECENT_ITEM ">\n");
+
+               g_free (mime_type);
+               g_free (escaped_uri);
+
+               list = list->next;
+               i++;
+       }
+
+       string = g_string_append (string, "</" TAG_RECENT_FILES ">");
+
+       data = g_string_free (string, FALSE);
+
+       ret = egg_recent_model_write_raw (model, file, data);
+
+       g_free (data);
+
+       return ret;
+}
+
+static FILE *
+egg_recent_model_open_file (EggRecentModel *model)
+{
+       FILE *file;
+       
+       file = fopen (model->priv->path, "r+");
+       if (file == NULL) {
+               /* be paranoid */
+               umask (077);
+
+               file = fopen (model->priv->path, "w+");
+
+               g_return_val_if_fail (file != NULL, NULL);
+       }
+
+       return file;
+}
+
+static gboolean
+egg_recent_model_lock_file (FILE *file)
+{
+       int fd;
+
+       rewind (file);
+       fd = fileno (file);
+
+       return lockf (fd, F_LOCK, 0) == 0 ? TRUE : FALSE;
+}
+
+static gboolean
+egg_recent_model_unlock_file (FILE *file)
+{
+       int fd;
+
+       rewind (file);
+       fd = fileno (file);
+
+       return lockf (fd, F_ULOCK, 0) < 0 ? FALSE : TRUE;
+}
+
+static void
+egg_recent_model_finalize (GObject *object)
+{
+       EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+       egg_recent_model_monitor (model, FALSE);
+
+
+       g_slist_foreach (model->priv->mime_filter_values,
+                        (GFunc) g_pattern_spec_free, NULL);
+       g_slist_free (model->priv->mime_filter_values);
+       model->priv->mime_filter_values = NULL;
+
+       g_slist_foreach (model->priv->scheme_filter_values,
+                        (GFunc) g_pattern_spec_free, NULL);
+       g_slist_free (model->priv->scheme_filter_values);
+       model->priv->scheme_filter_values = NULL;
+
+       g_slist_foreach (model->priv->group_filter_values,
+                        (GFunc) g_free, NULL);
+       g_slist_free (model->priv->group_filter_values);
+       model->priv->group_filter_values = NULL;
+
+
+       if (model->priv->limit_change_notify_id)
+               gconf_client_notify_remove (model->priv->client,
+                                           model->priv->limit_change_notify_id);
+       model->priv->expiration_change_notify_id = 0;
+
+       if (model->priv->expiration_change_notify_id)
+               gconf_client_notify_remove (model->priv->client,
+                                           model->priv->expiration_change_notify_id);
+       model->priv->expiration_change_notify_id = 0;
+
+       g_object_unref (model->priv->client);
+       model->priv->client = NULL;
+
+
+       g_free (model->priv->path);
+       model->priv->path = NULL;
+       
+       g_hash_table_destroy (model->priv->monitors);
+       model->priv->monitors = NULL;
+
+
+       g_free (model->priv);
+}
+
+static void
+egg_recent_model_set_property (GObject *object,
+                              guint prop_id,
+                              const GValue *value,
+                              GParamSpec *pspec)
+{
+       EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+       switch (prop_id)
+       {
+               case PROP_MIME_FILTERS:
+                       model->priv->mime_filter_values =
+                               (GSList *)g_value_get_pointer (value);
+               break;
+
+               case PROP_GROUP_FILTERS:
+                       model->priv->group_filter_values =
+                               (GSList *)g_value_get_pointer (value);
+               break;
+
+               case PROP_SCHEME_FILTERS:
+                       model->priv->scheme_filter_values =
+                               (GSList *)g_value_get_pointer (value);
+               break;
+
+               case PROP_SORT_TYPE:
+                       model->priv->sort_type = g_value_get_int (value);
+               break;
+
+               case PROP_LIMIT:
+                       egg_recent_model_set_limit (model,
+                                               g_value_get_int (value));
+               break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+egg_recent_model_get_property (GObject *object,
+                              guint prop_id,
+                              GValue *value,
+                              GParamSpec *pspec)
+{
+       EggRecentModel *model = EGG_RECENT_MODEL (object);
+
+       switch (prop_id)
+       {
+               case PROP_MIME_FILTERS:
+                       g_value_set_pointer (value, model->priv->mime_filter_values);
+               break;
+
+               case PROP_GROUP_FILTERS:
+                       g_value_set_pointer (value, model->priv->group_filter_values);
+               break;
+
+               case PROP_SCHEME_FILTERS:
+                       g_value_set_pointer (value, model->priv->scheme_filter_values);
+               break;
+
+               case PROP_SORT_TYPE:
+                       g_value_set_int (value, model->priv->sort_type);
+               break;
+
+               case PROP_LIMIT:
+                       g_value_set_int (value, model->priv->limit);
+               break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+egg_recent_model_class_init (EggRecentModelClass * klass)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = egg_recent_model_set_property;
+       object_class->get_property = egg_recent_model_get_property;
+       object_class->finalize     = egg_recent_model_finalize;
+
+       model_signals[CHANGED] = g_signal_new ("changed",
+                       G_OBJECT_CLASS_TYPE (object_class),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (EggRecentModelClass, changed),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__POINTER,
+                       G_TYPE_NONE, 1,
+                       G_TYPE_POINTER);
+
+       
+       g_object_class_install_property (object_class,
+                                        PROP_MIME_FILTERS,
+                                        g_param_spec_pointer ("mime-filters",
+                                        "Mime Filters",
+                                        "List of mime types to be allowed.",
+                                        G_PARAM_READWRITE));
+       
+       g_object_class_install_property (object_class,
+                                        PROP_GROUP_FILTERS,
+                                        g_param_spec_pointer ("group-filters",
+                                        "Group Filters",
+                                        "List of groups to be allowed.",
+                                        G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SCHEME_FILTERS,
+                                        g_param_spec_pointer ("scheme-filters",
+                                        "Scheme Filters",
+                                        "List of URI schemes to be allowed.",
+                                        G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SORT_TYPE,
+                                        g_param_spec_int ("sort-type",
+                                        "Sort Type",
+                                        "Type of sorting to be done.",
+                                        0, EGG_RECENT_MODEL_SORT_NONE,
+                                        EGG_RECENT_MODEL_SORT_MRU,
+                                        G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_LIMIT,
+                                        g_param_spec_int ("limit",
+                                        "Limit",
+                                        "Max number of items allowed.",
+                                        -1, EGG_RECENT_MODEL_MAX_ITEMS,
+                                        EGG_RECENT_MODEL_DEFAULT_LIMIT,
+                                        G_PARAM_READWRITE));
+
+       klass->changed = NULL;
+}
+
+
+
+static void
+egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id,
+                               GConfEntry *entry, gpointer user_data)
+{
+       EggRecentModel *model;
+       GConfValue *value;
+
+       model = EGG_RECENT_MODEL (user_data);
+
+       g_return_if_fail (model != NULL);
+
+       if (model->priv->use_default_limit == FALSE)
+               return; /* ignore this key */
+
+       /* the key was unset, and the schema has apparently failed */
+       if (entry == NULL)
+               return;
+
+       value = gconf_entry_get_value (entry);
+
+       if (value->type != GCONF_VALUE_INT) {
+               g_warning ("Expected GConfValue of type integer, "
+                          "got something else");
+       }
+
+
+       egg_recent_model_set_limit_internal (model, gconf_value_get_int (value));
+}
+
+static void
+egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id,
+                                    GConfEntry *entry, gpointer user_data)
+{
+
+}
+
+static void
+egg_recent_model_init (EggRecentModel * model)
+{
+       if (!gnome_vfs_init ()) {
+               g_warning ("gnome-vfs initialization failed.");
+               return;
+       }
+       
+
+       model->priv = g_new0 (EggRecentModelPrivate, 1);
+
+       model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH,
+                                            g_get_home_dir ());
+
+       model->priv->mime_filter_values   = NULL;
+       model->priv->group_filter_values  = NULL;
+       model->priv->scheme_filter_values = NULL;
+       
+       model->priv->client = gconf_client_get_default ();
+       gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR,
+                             GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+
+       model->priv->limit_change_notify_id =
+                       gconf_client_notify_add (model->priv->client,
+                                                EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY,
+                                                egg_recent_model_limit_changed,
+                                                model, NULL, NULL);
+
+       model->priv->expiration_change_notify_id =
+                       gconf_client_notify_add (model->priv->client,
+                                                EGG_RECENT_MODEL_EXPIRE_KEY,
+                                                egg_recent_model_expiration_changed,
+                                                model, NULL, NULL);
+
+       model->priv->expire_days = gconf_client_get_int (
+                                       model->priv->client,
+                                       EGG_RECENT_MODEL_EXPIRE_KEY,
+                                       NULL);
+                                       
+#if 0
+       /* keep this out, for now */
+       model->priv->limit = gconf_client_get_int (
+                                       model->priv->client,
+                                       EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL);
+       model->priv->use_default_limit = TRUE;
+#endif
+       model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT;
+       model->priv->use_default_limit = FALSE;
+
+       model->priv->monitors = g_hash_table_new_full (
+                                       g_str_hash, g_str_equal,
+                                       (GDestroyNotify) g_free,
+                                       (GDestroyNotify) gnome_vfs_monitor_cancel);
+
+       model->priv->monitor = NULL;
+       egg_recent_model_monitor (model, TRUE);
+}
+
+
+/**
+ * egg_recent_model_new:
+ * @sort:  the type of sorting to use
+ * @limit:  maximum number of items in the list
+ *
+ * This creates a new EggRecentModel object.
+ *
+ * Returns: a EggRecentModel object
+ */
+EggRecentModel *
+egg_recent_model_new (EggRecentModelSort sort)
+{
+       EggRecentModel *model;
+
+       model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (),
+                                 "sort-type", sort, NULL));
+
+       g_return_val_if_fail (model, NULL);
+
+       return model;
+}
+
+/**
+ * egg_recent_model_add_full:
+ * @model:  A EggRecentModel object.
+ * @item:  A EggRecentItem
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item)
+{
+       FILE *file;
+       GList *list = NULL;
+       gboolean ret = FALSE;
+       gboolean updated = FALSE;
+       char *uri;
+       time_t t;
+       
+       g_return_val_if_fail (model != NULL, FALSE);
+       g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+
+       uri = egg_recent_item_get_uri (item);
+       if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
+               g_free (uri);
+               return FALSE;
+       } else {
+               g_free (uri);
+       }
+
+       file = egg_recent_model_open_file (model);
+       g_return_val_if_fail (file != NULL, FALSE);
+
+       time (&t);
+       egg_recent_item_set_timestamp (item, t);
+
+       if (egg_recent_model_lock_file (file)) {
+
+               /* read existing stuff */
+               list = egg_recent_model_read (model, file);
+
+               /* if it's already there, we just update it */
+               updated = egg_recent_model_update_item (list, item);
+
+               if (!updated) {
+                       list = g_list_prepend (list, item);
+
+                       egg_recent_model_enforce_limit (list,
+                                               EGG_RECENT_MODEL_MAX_ITEMS);
+               }
+
+               /* write new stuff */
+               if (!egg_recent_model_write (model, file, list))
+                       g_warning ("Write failed: %s", strerror (errno));
+
+               if (!updated)
+                       list = g_list_remove (list, item);
+
+               EGG_RECENT_ITEM_LIST_UNREF (list);
+               ret = TRUE;
+       } else {
+               g_warning ("Failed to lock:  %s", strerror (errno));
+               return FALSE;
+       }
+
+       if (!egg_recent_model_unlock_file (file))
+               g_warning ("Failed to unlock: %s", strerror (errno));
+
+       fclose (file);
+
+       if (model->priv->monitor == NULL) {
+               /* since monitoring isn't working, at least give a
+                * local notification
+                */
+               egg_recent_model_changed (model);
+       }
+
+       return ret;
+}
+
+/**
+ * egg_recent_model_add:
+ * @model:  A EggRecentModel object.
+ * @uri:  A string URI
+ *
+ * This function adds an item to the list of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_add (EggRecentModel *model, const gchar *uri)
+{
+       EggRecentItem *item;
+       gboolean ret = FALSE;
+
+       g_return_val_if_fail (model != NULL, FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       item = egg_recent_item_new_from_uri (uri);
+
+       g_return_val_if_fail (item != NULL, FALSE);
+
+       ret = egg_recent_model_add_full (model, item);
+
+       egg_recent_item_unref (item);
+
+       return ret;
+}
+
+
+
+/**
+ * egg_recent_model_delete:
+ * @model:  A EggRecentModel object.
+ * @uri: The URI you want to delete.
+ *
+ * This function deletes a URI from the file of recently used URIs.
+ *
+ * Returns: gboolean
+ */
+gboolean
+egg_recent_model_delete (EggRecentModel * model, const gchar * uri)
+{
+       FILE *file;
+       GList *list;
+       unsigned int length;
+       gboolean ret = FALSE;
+
+       g_return_val_if_fail (model != NULL, FALSE);
+       g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE);
+       g_return_val_if_fail (uri != NULL, FALSE);
+
+       file = egg_recent_model_open_file (model);
+       g_return_val_if_fail (file != NULL, FALSE);
+
+       if (egg_recent_model_lock_file (file)) {
+               list = egg_recent_model_read (model, file);
+
+               if (list == NULL)
+                       goto out;
+
+               length = g_list_length (list);
+
+               list = egg_recent_model_delete_from_list (list, uri);
+               
+               if (length == g_list_length (list)) {
+                       /* nothing was deleted */
+                       EGG_RECENT_ITEM_LIST_UNREF (list);
+               } else {
+                       egg_recent_model_write (model, file, list);
+                       EGG_RECENT_ITEM_LIST_UNREF (list);
+                       ret = TRUE;
+
+               }
+       } else {
+               g_warning ("Failed to lock:  %s", strerror (errno));
+               return FALSE;
+       }
+
+out:
+               
+       if (!egg_recent_model_unlock_file (file))
+               g_warning ("Failed to unlock: %s", strerror (errno));
+
+       fclose (file);
+
+       g_hash_table_remove (model->priv->monitors, uri);
+
+       if (model->priv->monitor == NULL && ret) {
+               /* since monitoring isn't working, at least give a
+                * local notification
+                */
+               egg_recent_model_changed (model);
+       }
+
+       return ret;
+}
+
+
+/**
+ * egg_recent_model_get_list:
+ * @model:  A EggRecentModel object.
+ *
+ * This function gets the current contents of the file
+ *
+ * Returns: a GList
+ */
+GList *
+egg_recent_model_get_list (EggRecentModel *model)
+{
+       FILE *file;
+       GList *list=NULL;
+
+       file = egg_recent_model_open_file (model);
+       g_return_val_if_fail (file != NULL, NULL);
+       
+       if (egg_recent_model_lock_file (file)) {
+               list = egg_recent_model_read (model, file);
+               
+       } else {
+               g_warning ("Failed to lock:  %s", strerror (errno));
+               fclose (file);
+               return NULL;
+       }
+
+       if (!egg_recent_model_unlock_file (file))
+               g_warning ("Failed to unlock: %s", strerror (errno));
+
+       if (list != NULL) {
+               list = egg_recent_model_filter (model, list);
+               list = egg_recent_model_sort (model, list);
+
+               egg_recent_model_enforce_limit (list, model->priv->limit);
+       }
+
+       fclose (file);
+
+       return list;
+}
+
+
+
+/**
+ * egg_recent_model_set_limit:
+ * @model:  A EggRecentModel object.
+ * @limit:  The maximum length of the list
+ *
+ * This function sets the maximum length of the list.  Note:  This only affects
+ * the length of the list emitted in the "changed" signal, not the list stored
+ * on disk.
+ *
+ * Returns:  void
+ */
+void
+egg_recent_model_set_limit (EggRecentModel *model, int limit)
+{
+       model->priv->use_default_limit = FALSE;
+
+       egg_recent_model_set_limit_internal (model, limit);
+}
+
+/**
+ * egg_recent_model_get_limit:
+ * @model:  A EggRecentModel object.
+ *
+ * This function gets the maximum length of the list. 
+ *
+ * Returns:  int
+ */
+int
+egg_recent_model_get_limit (EggRecentModel *model)
+{
+       return model->priv->limit;
+}
+
+
+/**
+ * egg_recent_model_clear:
+ * @model:  A EggRecentModel object.
+ *
+ * This function clears the contents of the file
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_clear (EggRecentModel *model)
+{
+       FILE *file;
+       int fd;
+
+       file = egg_recent_model_open_file (model);
+       g_return_if_fail (file != NULL);
+
+       fd = fileno (file);
+
+       if (egg_recent_model_lock_file (file)) {
+               ftruncate (fd, 0);
+       } else {
+               g_warning ("Failed to lock:  %s", strerror (errno));
+               return;
+       }
+
+       if (!egg_recent_model_unlock_file (file))
+               g_warning ("Failed to unlock: %s", strerror (errno));
+
+       fclose (file);
+}
+
+
+/**
+ * egg_recent_model_set_filter_mime_types:
+ * @model:  A EggRecentModel object.
+ *
+ * Sets which mime types are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_mime_types (EggRecentModel *model,
+                              ...)
+{
+       va_list valist;
+       GSList *list = NULL;
+       gchar *str;
+
+       g_return_if_fail (model != NULL);
+
+       if (model->priv->mime_filter_values != NULL) {
+               g_slist_foreach (model->priv->mime_filter_values,
+                                (GFunc) g_pattern_spec_free, NULL);
+               g_slist_free (model->priv->mime_filter_values);
+               model->priv->mime_filter_values = NULL;
+       }
+
+       va_start (valist, model);
+
+       str = va_arg (valist, gchar*);
+
+       while (str != NULL) {
+               list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+               str = va_arg (valist, gchar*);
+       }
+
+       va_end (valist);
+
+       model->priv->mime_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_groups:
+ * @model:  A EggRecentModel object.
+ *
+ * Sets which groups are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_groups (EggRecentModel *model,
+                              ...)
+{
+       va_list valist;
+       GSList *list = NULL;
+       gchar *str;
+
+       g_return_if_fail (model != NULL);
+
+       if (model->priv->group_filter_values != NULL) {
+               g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
+               g_slist_free (model->priv->group_filter_values);
+               model->priv->group_filter_values = NULL;
+       }
+
+       va_start (valist, model);
+
+       str = va_arg (valist, gchar*);
+
+       while (str != NULL) {
+               list = g_slist_prepend (list, g_strdup (str));
+
+               str = va_arg (valist, gchar*);
+       }
+
+       va_end (valist);
+
+       model->priv->group_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_filter_uri_schemes:
+ * @model:  A EggRecentModel object.
+ *
+ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...)
+{
+       va_list valist;
+       GSList *list = NULL;
+       gchar *str;
+
+       g_return_if_fail (model != NULL);
+
+       if (model->priv->scheme_filter_values != NULL) {
+               g_slist_foreach (model->priv->scheme_filter_values,
+                               (GFunc) g_pattern_spec_free, NULL);
+               g_slist_free (model->priv->scheme_filter_values);
+               model->priv->scheme_filter_values = NULL;
+       }
+
+       va_start (valist, model);
+
+       str = va_arg (valist, gchar*);
+
+       while (str != NULL) {
+               list = g_slist_prepend (list, g_pattern_spec_new (str));
+
+               str = va_arg (valist, gchar*);
+       }
+
+       va_end (valist);
+
+       model->priv->scheme_filter_values = list;
+}
+
+/**
+ * egg_recent_model_set_sort:
+ * @model:  A EggRecentModel object.
+ * @sort:  A EggRecentModelSort type
+ *
+ * Sets the type of sorting to be used.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_set_sort (EggRecentModel *model,
+                            EggRecentModelSort sort)
+{
+       g_return_if_fail (model != NULL);
+       
+       model->priv->sort_type = sort;
+}
+
+/**
+ * egg_recent_model_changed:
+ * @model:  A EggRecentModel object.
+ *
+ * This function causes a "changed" signal to be emitted.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_changed (EggRecentModel *model)
+{
+       GList *list = NULL;
+
+       if (model->priv->limit > 0) {
+               list = egg_recent_model_get_list (model);
+               /* egg_recent_model_monitor_list (model, list); */
+       
+               g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
+                              list);
+       }
+
+       if (list)
+               EGG_RECENT_ITEM_LIST_UNREF (list);
+}
+
+static void
+egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list)
+{
+       time_t current_time;
+       time_t day_seconds;
+
+       time (&current_time);
+       day_seconds = model->priv->expire_days*24*60*60;
+
+       while (list != NULL) {
+               EggRecentItem *item = list->data;
+               time_t timestamp;
+
+               timestamp = egg_recent_item_get_timestamp (item);
+
+               if ((timestamp+day_seconds) < current_time) {
+                       gchar *uri = egg_recent_item_get_uri (item);
+                       egg_recent_model_delete (model, uri);
+
+                       g_strdup (uri);
+               }
+
+               list = list->next;
+       }
+}
+
+
+/**
+ * egg_recent_model_remove_expired:
+ * @model:  A EggRecentModel object.
+ *
+ * Goes through the entire list, and removes any items that are older than
+ * the user-specified expiration period.
+ *
+ * Returns: void
+ */
+void
+egg_recent_model_remove_expired (EggRecentModel *model)
+{
+       FILE *file;
+       GList *list=NULL;
+
+       g_return_if_fail (model != NULL);
+
+       file = egg_recent_model_open_file (model);
+       g_return_if_fail (file != NULL);
+       
+       if (egg_recent_model_lock_file (file)) {
+               list = egg_recent_model_read (model, file);
+               
+       } else {
+               g_warning ("Failed to lock:  %s", strerror (errno));
+               return;
+       }
+
+       if (!egg_recent_model_unlock_file (file))
+               g_warning ("Failed to unlock: %s", strerror (errno));
+
+       if (list != NULL) {
+               egg_recent_model_remove_expired_list (model, list);
+               EGG_RECENT_ITEM_LIST_UNREF (list);
+       }
+
+       fclose (file);
+}
+
+/**
+ * egg_recent_model_get_type:
+ *
+ * This returns a GType representing a EggRecentModel object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_model_get_type (void)
+{
+       static GType egg_recent_model_type = 0;
+
+       if(!egg_recent_model_type) {
+               static const GTypeInfo egg_recent_model_info = {
+                       sizeof (EggRecentModelClass),
+                       NULL, /* base init */
+                       NULL, /* base finalize */
+                       (GClassInitFunc)egg_recent_model_class_init, /* class init */
+                       NULL, /* class finalize */
+                       NULL, /* class data */
+                       sizeof (EggRecentModel),
+                       0,
+                       (GInstanceInitFunc) egg_recent_model_init
+               };
+
+               egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT,
+                                                       "EggRecentModel",
+                                                       &egg_recent_model_info, 0);
+       }
+
+       return egg_recent_model_type;
+}
+
diff --git a/glabels2/src/recent-files/egg-recent-model.h b/glabels2/src/recent-files/egg-recent-model.h
new file mode 100644 (file)
index 0000000..8c2eb8d
--- /dev/null
@@ -0,0 +1,80 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_MODEL_H__
+#define __EGG_RECENT_MODEL_H__
+
+#include "egg-recent-item.h"
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_RECENT_MODEL          (egg_recent_model_get_type ())
+#define EGG_RECENT_MODEL(obj)          G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL, EggRecentModel)
+#define EGG_RECENT_MODEL_CLASS(klass)  G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL, EggRecentModelClass)
+#define EGG_IS_RECENT_MODEL(obj)       G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_get_type ())
+
+typedef struct _EggRecentModel        EggRecentModel;
+typedef struct _EggRecentModelPrivate EggRecentModelPrivate;
+typedef struct _EggRecentModelClass   EggRecentModelClass;
+
+struct _EggRecentModel {
+       GObject                parent_instance;
+
+       EggRecentModelPrivate *priv;
+};
+
+struct _EggRecentModelClass {
+       GObjectClass parent_class;
+                       
+       void (*changed) (EggRecentModel *model, GList *list);
+};
+
+typedef enum {
+       EGG_RECENT_MODEL_SORT_MRU,
+       EGG_RECENT_MODEL_SORT_LRU,
+       EGG_RECENT_MODEL_SORT_NONE
+} EggRecentModelSort;
+
+
+/* Standard group names */
+#define EGG_RECENT_GROUP_LAUNCHERS "Launchers"
+
+
+GType    egg_recent_model_get_type     (void);
+
+/* constructors */
+EggRecentModel * egg_recent_model_new (EggRecentModelSort sort);
+
+/* public methods */
+void     egg_recent_model_set_filter_mime_types (EggRecentModel *model,
+                                                  ...);
+
+void     egg_recent_model_set_filter_groups (EggRecentModel *model, ...);
+
+void     egg_recent_model_set_filter_uri_schemes (EggRecentModel *model,
+                                                  ...);
+
+void     egg_recent_model_set_sort (EggRecentModel *model,
+                                     EggRecentModelSort sort);
+
+gboolean egg_recent_model_add_full (EggRecentModel *model,
+                                     EggRecentItem *item);
+
+gboolean egg_recent_model_add       (EggRecentModel *model,
+                                     const gchar *uri);
+
+gboolean egg_recent_model_delete   (EggRecentModel *model,
+                                     const gchar *uri);
+
+void egg_recent_model_clear        (EggRecentModel *model);
+
+GList * egg_recent_model_get_list  (EggRecentModel *model);
+
+void egg_recent_model_changed      (EggRecentModel *model);
+
+void egg_recent_model_set_limit    (EggRecentModel *model, int limit);
+int  egg_recent_model_get_limit    (EggRecentModel *model);
+
+void egg_recent_model_remove_expired (EggRecentModel *model);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_MODEL_H__ */
diff --git a/glabels2/src/recent-files/egg-recent-util.c b/glabels2/src/recent-files/egg-recent-util.c
new file mode 100644 (file)
index 0000000..91c0c68
--- /dev/null
@@ -0,0 +1,154 @@
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#include <libgnomeui/gnome-icon-lookup.h>
+#endif
+#include <math.h>
+#include "egg-recent-util.h"
+
+#define EGG_RECENT_UTIL_HOSTNAME_SIZE 512
+
+/* ripped out of gedit2 */
+gchar* 
+egg_recent_util_escape_underlines (const gchar* text)
+{
+       GString *str;
+       gint length;
+       const gchar *p;
+       const gchar *end;
+
+       g_return_val_if_fail (text != NULL, NULL);
+
+       length = strlen (text);
+
+       str = g_string_new ("");
+
+       p = text;
+       end = text + length;
+
+       while (p != end)
+       {
+               const gchar *next;
+               next = g_utf8_next_char (p);
+
+               switch (*p)
+               {
+                               case '_':
+                               g_string_append (str, "__");
+                               break;
+                       default:
+                               g_string_append_len (str, p, next - p);
+                               break;
+               }
+
+               p = next;
+       }
+
+       return g_string_free (str, FALSE);
+}
+
+static GdkPixbuf *
+scale_icon (GdkPixbuf *pixbuf,
+           double *scale)
+{
+       guint width, height;
+
+       width = gdk_pixbuf_get_width (pixbuf);
+       height = gdk_pixbuf_get_height (pixbuf);
+
+       width = floor (width * *scale + 0.5);
+       height = floor (height * *scale + 0.5);
+       
+       return gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
+}
+
+static GdkPixbuf *
+load_icon_file (char          *filename,
+               guint          base_size,
+               guint          nominal_size)
+{
+       GdkPixbuf *pixbuf, *scaled_pixbuf;
+       guint width, height, size;
+       double scale;
+
+       pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+
+       if (pixbuf == NULL) {
+               return NULL;
+       }
+       
+       if (base_size == 0) {
+               width = gdk_pixbuf_get_width (pixbuf); 
+               height = gdk_pixbuf_get_height (pixbuf);
+               size = MAX (width, height);
+               if (size > nominal_size) {
+                       base_size = size;
+               } else {
+                       /* Don't scale up small icons */
+                       base_size = nominal_size;
+               }
+       }
+       
+       if (base_size != nominal_size) {
+               scale = (double)nominal_size/base_size;
+               scaled_pixbuf = scale_icon (pixbuf, &scale);
+               g_object_unref (pixbuf);
+               pixbuf = scaled_pixbuf;
+       }
+
+       return pixbuf;
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+GdkPixbuf *
+egg_recent_util_get_icon (GnomeIconTheme *theme, const gchar *uri,
+                         const gchar *mime_type, int size)
+{
+       gchar *icon;
+       gchar *filename;
+       const GnomeIconData *icon_data;
+       int base_size;
+       GdkPixbuf *pixbuf;
+       
+       icon = gnome_icon_lookup (theme, NULL, uri, NULL, NULL,
+                                 mime_type, 0, NULL);
+       
+
+       g_return_val_if_fail (icon != NULL, NULL);
+
+       filename = gnome_icon_theme_lookup_icon (theme, icon,
+                                                size,
+                                                &icon_data,
+                                                &base_size);
+       g_free (icon);
+
+       pixbuf = load_icon_file (filename, base_size, size);
+       g_free (filename);
+       
+       
+       return pixbuf;
+}
+#endif /* !USE_STABLE_LIBGNOMEUI */
+
+gchar *
+egg_recent_util_get_unique_id (void)
+{
+       char hostname[EGG_RECENT_UTIL_HOSTNAME_SIZE];
+       time_t the_time;
+       guint32 rand;
+       int pid;
+       
+       gethostname (hostname, EGG_RECENT_UTIL_HOSTNAME_SIZE);
+       
+       time (&the_time);
+       rand = g_random_int ();
+       pid = getpid ();
+
+       return g_strdup_printf ("%s-%d-%d-%d", hostname, (int)time, (int)rand, (int)pid);
+}
diff --git a/glabels2/src/recent-files/egg-recent-util.h b/glabels2/src/recent-files/egg-recent-util.h
new file mode 100644 (file)
index 0000000..ae8a641
--- /dev/null
@@ -0,0 +1,23 @@
+
+#ifndef __EGG_RECENT_UTIL__
+#define __EGG_RECENT_UTIL__
+
+#include <gtk/gtk.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+
+G_BEGIN_DECLS
+
+gchar * egg_recent_util_escape_underlines (const gchar *uri);
+gchar * egg_recent_util_get_unique_id (void);
+#ifndef USE_STABLE_LIBGNOMEUI
+GdkPixbuf * egg_recent_util_get_icon (GnomeIconTheme *theme,
+                                     const gchar *uri,
+                                     const gchar *mime_type,
+                                     int size);
+#endif
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_UTIL__ */
diff --git a/glabels2/src/recent-files/egg-recent-vfs-utils.c b/glabels2/src/recent-files/egg-recent-vfs-utils.c
new file mode 100644 (file)
index 0000000..51083dd
--- /dev/null
@@ -0,0 +1,570 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* gnome-vfs-utils.c - Utility gnome-vfs methods. Will use gnome-vfs
+                       HEAD in time.
+
+   Copyright (C) 1999 Free Software Foundation
+   Copyright (C) 2000, 2001 Eazel, Inc.
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: Ettore Perazzoli <ettore@comm2000.it>
+           John Sullivan <sullivan@eazel.com> 
+            Darin Adler <darin@eazel.com>
+*/
+
+#include <config.h>
+
+#include "egg-recent-vfs-utils.h"
+
+#include <libgnomevfs/gnome-vfs-utils.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef ENABLE_NLS
+#include <glib.h>
+
+#include <libintl.h>
+#define _(String) gettext(String)
+
+#ifdef gettext_noop
+#define N_(String) gettext_noop(String)
+#else
+#define N_(String) (String)
+#endif
+#else /* NLS is disabled */
+#define _(String) (String)
+#define N_(String) (String)
+#define textdomain(String) (String)
+#define gettext(String) (String)
+#define dgettext(Domain,String) (String)
+#define dcgettext(Domain,String,Type) (String)
+#define bindtextdomain(Domain,Directory) (Domain)
+#endif
+
+static char *
+make_valid_utf8 (const char *name)
+{
+       GString *string;
+       const char *remainder, *invalid;
+       int remaining_bytes, valid_bytes;
+
+       string = NULL;
+       remainder = name;
+       remaining_bytes = strlen (name);
+
+       while (remaining_bytes != 0) {
+               if (g_utf8_validate (remainder, remaining_bytes, &invalid)) {
+                       break;
+               }
+               valid_bytes = invalid - remainder;
+
+               if (string == NULL) {
+                       string = g_string_sized_new (remaining_bytes);
+               }
+               g_string_append_len (string, remainder, valid_bytes);
+               g_string_append_c (string, '?');
+
+               remaining_bytes -= valid_bytes + 1;
+               remainder = invalid + 1;
+       }
+
+       if (string == NULL) {
+               return g_strdup (name);
+       }
+
+       g_string_append (string, remainder);
+       g_string_append (string, _(" (invalid Unicode)"));
+       g_assert (g_utf8_validate (string->str, -1, NULL));
+
+       return g_string_free (string, FALSE);
+}
+
+static gboolean
+istr_has_prefix (const char *haystack, const char *needle)
+{
+       const char *h, *n;
+       char hc, nc;
+
+       /* Eat one character at a time. */
+       h = haystack == NULL ? "" : haystack;
+       n = needle == NULL ? "" : needle;
+       do {
+               if (*n == '\0') {
+                       return TRUE;
+               }
+               if (*h == '\0') {
+                       return FALSE;
+               }
+               hc = *h++;
+               nc = *n++;
+               hc = g_ascii_tolower (hc);
+               nc = g_ascii_tolower (nc);
+       } while (hc == nc);
+       return FALSE;
+}
+
+static gboolean
+str_has_prefix (const char *haystack, const char *needle)
+{
+       const char *h, *n;
+
+       /* Eat one character at a time. */
+       h = haystack == NULL ? "" : haystack;
+       n = needle == NULL ? "" : needle;
+       do {
+               if (*n == '\0') {
+                       return TRUE;
+               }
+               if (*h == '\0') {
+                       return FALSE;
+               }
+       } while (*h++ == *n++);
+       return FALSE;
+}
+
+static gboolean
+uri_is_local_scheme (const char *uri)
+{
+       gboolean is_local_scheme;
+       char *temp_scheme;
+       int i;
+       char *local_schemes[] = {"file:", "help:", "ghelp:", "gnome-help:",
+                                "trash:", "man:", "info:", 
+                                "hardware:", "search:", "pipe:",
+                                "gnome-trash:", NULL};
+
+       is_local_scheme = FALSE;
+       for (temp_scheme = *local_schemes, i = 0; temp_scheme != NULL; i++, temp_scheme = local_schemes[i]) {
+               is_local_scheme = istr_has_prefix (uri, temp_scheme);
+               if (is_local_scheme) {
+                       break;
+               }
+       }
+
+       return is_local_scheme;
+}
+
+static char *
+handle_trailing_slashes (const char *uri)
+{
+       char *temp, *uri_copy;
+       gboolean previous_char_is_column, previous_chars_are_slashes_without_column;
+       gboolean previous_chars_are_slashes_with_column;
+       gboolean is_local_scheme;
+
+       g_assert (uri != NULL);
+
+       uri_copy = g_strdup (uri);
+       if (strlen (uri_copy) <= 2) {
+               return uri_copy;
+       }
+
+       is_local_scheme = uri_is_local_scheme (uri);
+
+       previous_char_is_column = FALSE;
+       previous_chars_are_slashes_without_column = FALSE;
+       previous_chars_are_slashes_with_column = FALSE;
+
+       /* remove multiple trailing slashes */
+       for (temp = uri_copy; *temp != '\0'; temp++) {
+               if (*temp == '/' && !previous_char_is_column) {
+                       previous_chars_are_slashes_without_column = TRUE;
+               } else if (*temp == '/' && previous_char_is_column) {
+                       previous_chars_are_slashes_without_column = FALSE;
+                       previous_char_is_column = TRUE;
+                       previous_chars_are_slashes_with_column = TRUE;
+               } else {
+                       previous_chars_are_slashes_without_column = FALSE;
+                       previous_char_is_column = FALSE;
+                       previous_chars_are_slashes_with_column = FALSE;
+               }
+
+               if (*temp == ':') {
+                       previous_char_is_column = TRUE;
+               }
+       }
+
+       if (*temp == '\0' && previous_chars_are_slashes_without_column) {
+               if (is_local_scheme) {
+                       /* go back till you remove them all. */
+                       for (temp--; *(temp) == '/'; temp--) {
+                               *temp = '\0';
+                       }
+               } else {
+                       /* go back till you remove them all but one. */
+                       for (temp--; *(temp - 1) == '/'; temp--) {
+                               *temp = '\0';
+                       }                       
+               }
+       }
+
+       if (*temp == '\0' && previous_chars_are_slashes_with_column) {
+               /* go back till you remove them all but three. */
+               for (temp--; *(temp - 3) != ':' && *(temp - 2) != ':' && *(temp - 1) != ':'; temp--) {
+                       *temp = '\0';
+               }
+       }
+
+
+       return uri_copy;
+}
+
+static char *
+make_uri_canonical (const char *uri)
+{
+       char *canonical_uri, *old_uri, *p;
+       gboolean relative_uri;
+
+       relative_uri = FALSE;
+
+       if (uri == NULL) {
+               return NULL;
+       }
+
+       /* FIXME bugzilla.eazel.com 648: 
+        * This currently ignores the issue of two uris that are not identical but point
+        * to the same data except for the specific cases of trailing '/' characters,
+        * file:/ and file:///, and "lack of file:".
+        */
+
+       canonical_uri = handle_trailing_slashes (uri);
+
+       /* Note: In some cases, a trailing slash means nothing, and can
+        * be considered equivalent to no trailing slash. But this is
+        * not true in every case; specifically not for web addresses passed
+        * to a web-browser. So we don't have the trailing-slash-equivalence
+        * logic here, but we do use that logic in EelDirectory where
+        * the rules are more strict.
+        */
+
+       /* Add file: if there is no scheme. */
+       if (strchr (canonical_uri, ':') == NULL) {
+               old_uri = canonical_uri;
+
+               if (old_uri[0] != '/') {
+                       /* FIXME bugzilla.eazel.com 5069: 
+                        *  bandaid alert. Is this really the right thing to do?
+                        * 
+                        * We got what really is a relative path. We do a little bit of
+                        * a stretch here and assume it was meant to be a cryptic absolute path,
+                        * and convert it to one. Since we can't call gnome_vfs_uri_new and
+                        * gnome_vfs_uri_to_string to do the right make-canonical conversion,
+                        * we have to do it ourselves.
+                        */
+                       relative_uri = TRUE;
+                       canonical_uri = gnome_vfs_make_path_name_canonical (old_uri);
+                       g_free (old_uri);
+                       old_uri = canonical_uri;
+                       canonical_uri = g_strconcat ("file:///", old_uri, NULL);
+               } else {
+                       canonical_uri = g_strconcat ("file:", old_uri, NULL);
+               }
+               g_free (old_uri);
+       }
+
+       /* Lower-case the scheme. */
+       for (p = canonical_uri; *p != ':'; p++) {
+               g_assert (*p != '\0');
+               *p = g_ascii_tolower (*p);
+       }
+
+       if (!relative_uri) {
+               old_uri = canonical_uri;
+               canonical_uri = gnome_vfs_make_uri_canonical (canonical_uri);
+               if (canonical_uri != NULL) {
+                       g_free (old_uri);
+               } else {
+                       canonical_uri = old_uri;
+               }
+       }
+       
+       /* FIXME bugzilla.eazel.com 2802:
+        * Work around gnome-vfs's desire to convert file:foo into file://foo
+        * by converting to file:///foo here. When you remove this, check that
+        * typing "foo" into location bar does not crash and returns an error
+        * rather than displaying the contents of /
+        */
+       if (str_has_prefix (canonical_uri, "file://")
+           && !str_has_prefix (canonical_uri, "file:///")) {
+               old_uri = canonical_uri;
+               canonical_uri = g_strconcat ("file:/", old_uri + 5, NULL);
+               g_free (old_uri);
+       }
+
+       return canonical_uri;
+}
+
+static char *
+format_uri_for_display (const char *uri, gboolean filenames_are_locale_encoded)
+{
+       char *canonical_uri, *path, *utf8_path;
+
+       g_return_val_if_fail (uri != NULL, g_strdup (""));
+
+       canonical_uri = make_uri_canonical (uri);
+
+       /* If there's no fragment and it's a local path. */
+       path = gnome_vfs_get_local_path_from_uri (canonical_uri);
+       
+       if (path != NULL) {
+               if (filenames_are_locale_encoded) {
+                       utf8_path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
+                       if (utf8_path) {
+                               g_free (canonical_uri);
+                               g_free (path);
+                               return utf8_path;
+                       } 
+               } else if (g_utf8_validate (path, -1, NULL)) {
+                       g_free (canonical_uri);
+                       return path;
+               }
+       }
+
+       if (canonical_uri && !g_utf8_validate (canonical_uri, -1, NULL)) {
+               utf8_path = make_valid_utf8 (canonical_uri);
+               g_free (canonical_uri);
+               canonical_uri = utf8_path;
+       }
+
+       g_free (path);
+       return canonical_uri;
+}
+
+char *
+egg_recent_vfs_format_uri_for_display (const char *uri)
+{
+       static gboolean broken_filenames;
+       
+       broken_filenames = g_getenv ("G_BROKEN_FILENAMES") != NULL;
+
+       return format_uri_for_display (uri, broken_filenames);
+}
+
+static gboolean
+is_valid_scheme_character (char c)
+{
+       return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
+}
+
+static gboolean
+has_valid_scheme (const char *uri)
+{
+       const char *p;
+
+       p = uri;
+
+       if (!is_valid_scheme_character (*p)) {
+               return FALSE;
+       }
+
+       do {
+               p++;
+       } while (is_valid_scheme_character (*p));
+
+       return *p == ':';
+}
+
+static char *
+escape_high_chars (const guchar *string)
+{
+       char *result;
+       const guchar *scanner;
+       guchar *result_scanner;
+       int escape_count;
+       static const gchar hex[16] = "0123456789ABCDEF";
+
+#define ACCEPTABLE(a) ((a)>=32 && (a)<128)
+       
+       escape_count = 0;
+
+       if (string == NULL) {
+               return NULL;
+       }
+
+       for (scanner = string; *scanner != '\0'; scanner++) {
+               if (!ACCEPTABLE(*scanner)) {
+                       escape_count++;
+               }
+       }
+       
+       if (escape_count == 0) {
+               return g_strdup (string);
+       }
+
+       /* allocate two extra characters for every character that
+        * needs escaping and space for a trailing zero
+        */
+       result = g_malloc (scanner - string + escape_count * 2 + 1);
+       for (scanner = string, result_scanner = result; *scanner != '\0'; scanner++) {
+               if (!ACCEPTABLE(*scanner)) {
+                       *result_scanner++ = '%';
+                       *result_scanner++ = hex[*scanner >> 4];
+                       *result_scanner++ = hex[*scanner & 15];
+                       
+               } else {
+                       *result_scanner++ = *scanner;
+               }
+       }
+
+       *result_scanner = '\0';
+
+       return result;
+}
+
+static char *
+make_uri_from_input_internal (const char *text,
+                                 gboolean filenames_are_locale_encoded,
+                                 gboolean strip_trailing_whitespace)
+{
+       char *stripped, *path, *uri, *locale_path, *filesystem_path, *escaped;
+
+       g_return_val_if_fail (text != NULL, g_strdup (""));
+
+       /* Strip off leading whitespaces (since they can't be part of a valid
+          uri).   Only strip off trailing whitespaces when requested since
+          they might be part of a valid uri.
+        */
+       if (strip_trailing_whitespace) {
+               stripped = g_strstrip (g_strdup (text));
+       } else {
+               stripped = g_strchug (g_strdup (text));
+       }
+
+       switch (stripped[0]) {
+       case '\0':
+               uri = g_strdup ("");
+               break;
+       case '/':
+               if (filenames_are_locale_encoded) {
+                       GError *error = NULL;
+                       locale_path = g_locale_from_utf8 (stripped, -1, NULL, NULL, &error);
+                       if (locale_path != NULL) {
+                               uri = gnome_vfs_get_uri_from_local_path (locale_path);
+                               g_free (locale_path);
+                       } else {
+                               /* We couldn't convert to the locale. */
+                               /* FIXME: We should probably give a user-visible error here. */
+                               uri = g_strdup("");
+                       }
+               } else {
+                       uri = gnome_vfs_get_uri_from_local_path (stripped);
+               }
+               break;
+       case '~':
+               if (filenames_are_locale_encoded) {
+                       filesystem_path = g_locale_from_utf8 (stripped, -1, NULL, NULL, NULL);
+               } else {
+                       filesystem_path = g_strdup (stripped);
+               }
+                /* deliberately falling into default case on fail */
+               if (filesystem_path != NULL) {
+                       path = gnome_vfs_expand_initial_tilde (filesystem_path);
+                       g_free (filesystem_path);
+                       if (*path == '/') {
+                               uri = gnome_vfs_get_uri_from_local_path (path);
+                               g_free (path);
+                               break;
+                       }
+                       g_free (path);
+               }
+                /* don't insert break here, read above comment */
+       default:
+               if (has_valid_scheme (stripped)) {
+                       uri = escape_high_chars (stripped);
+               } else {
+                       escaped = escape_high_chars (stripped);
+                       uri = g_strconcat ("http://", escaped, NULL);
+                       g_free (escaped);
+               }
+       }
+
+       g_free (stripped);
+
+       return uri;
+       
+}
+
+char *
+egg_recent_vfs_make_uri_from_input (const char *uri)
+{
+       static gboolean broken_filenames;
+
+       broken_filenames = g_getenv ("G_BROKEN_FILENAMES") != NULL;
+
+       return make_uri_from_input_internal (uri, broken_filenames, TRUE);
+}
+
+static char *
+make_uri_canonical_strip_fragment (const char *uri)
+{
+       const char *fragment;
+       char *without_fragment, *canonical;
+
+       fragment = strchr (uri, '#');
+       if (fragment == NULL) {
+               return make_uri_canonical (uri);
+       }
+
+       without_fragment = g_strndup (uri, fragment - uri);
+       canonical = make_uri_canonical (without_fragment);
+       g_free (without_fragment);
+       return canonical;
+}
+
+static gboolean
+uris_match (const char *uri_1, const char *uri_2, gboolean ignore_fragments)
+{
+       char *canonical_1, *canonical_2;
+       gboolean result;
+
+       if (ignore_fragments) {
+               canonical_1 = make_uri_canonical_strip_fragment (uri_1);
+               canonical_2 = make_uri_canonical_strip_fragment (uri_2);
+       } else {
+               canonical_1 = make_uri_canonical (uri_1);
+               canonical_2 = make_uri_canonical (uri_2);
+       }
+
+       result = strcmp (canonical_1, canonical_2) == 0;
+
+       g_free (canonical_1);
+       g_free (canonical_2);
+       
+       return result;
+}
+
+gboolean
+egg_recent_vfs_uris_match (const char *uri_1, const char *uri_2)
+{
+       return uris_match (uri_1, uri_2, FALSE);
+}
+
+char *
+egg_recent_vfs_get_uri_scheme (const char *uri)
+{
+       char *colon;
+
+       g_return_val_if_fail (uri != NULL, NULL);
+
+       colon = strchr (uri, ':');
+       
+       if (colon == NULL) {
+               return NULL;
+       }
+       
+       return g_strndup (uri, colon - uri);
+}
diff --git a/glabels2/src/recent-files/egg-recent-vfs-utils.h b/glabels2/src/recent-files/egg-recent-vfs-utils.h
new file mode 100644 (file)
index 0000000..619b25b
--- /dev/null
@@ -0,0 +1,42 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* gnome-vfs-utils.h - Utility gnome-vfs methods. Will use gnome-vfs
+                       HEAD in time.
+
+   Copyright (C) 1999 Free Software Foundation
+   Copyright (C) 2000 Eazel, Inc.
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: Ettore Perazzoli <ettore@comm2000.it>
+           John Sullivan <sullivan@eazel.com> 
+*/
+
+#ifndef EGG_RECENT_VFS_UTILS_H
+#define EGG_RECENT_VFS_UTILS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+char     *egg_recent_vfs_format_uri_for_display (const char *uri);
+char     *egg_recent_vfs_make_uri_from_input    (const char *uri);
+gboolean  egg_recent_vfs_uris_match             (const char *uri_1,
+                                                const char *uri_2);
+char     *egg_recent_vfs_get_uri_scheme         (const char *uri);
+
+G_END_DECLS
+
+#endif /* GNOME_VFS_UTILS_H */
diff --git a/glabels2/src/recent-files/egg-recent-view-bonobo.c b/glabels2/src/recent-files/egg-recent-view-bonobo.c
new file mode 100644 (file)
index 0000000..e728e70
--- /dev/null
@@ -0,0 +1,714 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   James Willcox <jwillcox@cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <libbonoboui.h>
+#include <libgnomevfs/gnome-vfs.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-bonobo.h"
+#include "egg-recent-util.h"
+#include "egg-recent-item.h"
+
+struct _EggRecentViewBonobo {
+       GObject parent_instance;        /* We emit signals */
+
+       BonoboUIComponent *uic;
+       gchar *path;                    /* The menu path where our stuff
+                                        *  will go
+                                        */
+
+       gulong changed_cb_id;
+
+       gchar *uid;                     /* unique id used for the verb name */
+
+       gboolean show_icons;
+       gboolean show_numbers;
+#ifndef USE_STABLE_LIBGNOMEUI
+       GnomeIconTheme *theme;
+#endif
+       EggRecentViewBonoboTooltipFunc tooltip_func;
+       gpointer tooltip_func_data;
+
+       EggRecentModel *model;
+       GConfClient *client;
+       GtkIconSize icon_size;
+};
+
+
+struct _EggRecentViewBonoboMenuData {
+       EggRecentViewBonobo *view;
+       EggRecentItem *item;
+};
+
+typedef struct _EggRecentViewBonoboMenuData EggRecentViewBonoboMenuData;
+
+enum {
+       ACTIVATE,
+       LAST_SIGNAL
+};
+
+/* GObject properties */
+enum {
+       PROP_BOGUS,
+       PROP_UI_COMPONENT,
+       PROP_MENU_PATH,
+       PROP_SHOW_ICONS,
+       PROP_SHOW_NUMBERS
+};
+
+static guint egg_recent_view_bonobo_signals[LAST_SIGNAL] = { 0 };
+
+static void
+egg_recent_view_bonobo_clear (EggRecentViewBonobo *view)
+{
+       gint i=1;
+       gboolean done=FALSE;
+       EggRecentModel *model;
+
+       g_return_if_fail (view->uic);
+
+       model = egg_recent_view_get_model (EGG_RECENT_VIEW (view));
+       
+       while (!done)
+       {
+               gchar *verb_name = g_strdup_printf ("%s-%d", view->uid, i);
+               gchar *item_path = g_strconcat (view->path, "/", verb_name, NULL);
+               if (bonobo_ui_component_path_exists (view->uic, item_path, NULL))
+                       bonobo_ui_component_rm (view->uic, item_path, NULL);
+               else
+                       done=TRUE;
+
+               g_free (item_path);
+               g_free (verb_name);
+
+               i++;
+       }
+}
+
+static void
+egg_recent_view_bonobo_menu_cb (BonoboUIComponent *uic, gpointer data, const char *cname)
+{
+       EggRecentViewBonoboMenuData *md = (EggRecentViewBonoboMenuData *) data;
+       EggRecentItem *item;
+
+       g_return_if_fail (md);
+       g_return_if_fail (md->item);
+       g_return_if_fail (md->view);
+       g_return_if_fail (EGG_IS_RECENT_VIEW_BONOBO (md->view));
+
+       item = md->item;
+       egg_recent_item_ref (item);
+
+       g_signal_emit (G_OBJECT(md->view),
+                      egg_recent_view_bonobo_signals[ACTIVATE], 0,
+                      item);
+
+       egg_recent_item_unref (item);
+}
+
+static void
+egg_recent_view_bonobo_menu_data_destroy_cb (gpointer data, GClosure *closure)
+{
+       EggRecentViewBonoboMenuData *md = data;
+
+       egg_recent_item_unref (md->item);
+       g_free (md);
+}
+
+
+static void
+egg_recent_view_bonobo_set_list (EggRecentViewBonobo *view, GList *list)
+{
+       BonoboUIComponent* ui_component;
+       unsigned int i;
+       gchar *label = NULL;
+       gchar *verb_name = NULL;
+       gchar *tip = NULL;
+       gchar *escaped_name = NULL;
+       gchar *item_path = NULL;
+       gchar *base_uri;
+       gchar *utf8_uri;
+       gchar *cmd;
+       gchar *xml_escaped_name;
+       EggRecentViewBonoboMenuData *md;
+       EggRecentModel *model;
+       GClosure *closure;
+
+       g_return_if_fail (view);
+
+       ui_component = view->uic;
+       g_return_if_fail (BONOBO_IS_UI_COMPONENT (ui_component));
+
+
+       model = egg_recent_view_get_model (EGG_RECENT_VIEW (view));
+
+       egg_recent_view_bonobo_clear (view);
+
+       
+       bonobo_ui_component_freeze (ui_component, NULL);
+
+       for (i = 1; i <= g_list_length (list); ++i)
+       {
+               EggRecentItem *item = (EggRecentItem *)g_list_nth_data (list, i-1);     
+
+               utf8_uri = egg_recent_item_get_uri_for_display (item);
+               if (utf8_uri == NULL)
+                       continue;
+               
+               /* this is what gets passed to our private "activate" callback */
+               md = (EggRecentViewBonoboMenuData *)g_malloc (sizeof (EggRecentViewBonoboMenuData));
+               md->view = view;
+               md->item = item;
+
+               egg_recent_item_ref (md->item);
+
+               base_uri = g_path_get_basename (utf8_uri);
+               xml_escaped_name = g_markup_escape_text (base_uri,
+                                                        strlen (base_uri));
+       
+               escaped_name = egg_recent_util_escape_underlines (xml_escaped_name);
+               g_free (xml_escaped_name);
+
+               tip = NULL;
+               if (view->tooltip_func != NULL) {
+                       gchar *tmp_tip;
+                       tip = view->tooltip_func (item,
+                                                 view->tooltip_func_data);
+                       tmp_tip = g_markup_escape_text (tip, strlen (tip));
+                       g_free (tip);
+                       tip = tmp_tip;
+               }
+
+               if (tip == NULL)
+                       tip = g_strdup ("");
+
+               verb_name = g_strdup_printf ("%s-%d", view->uid, i);
+
+               if (view->show_icons) {
+                       GdkPixbuf *pixbuf;
+                       gchar *mime_type;
+                       gchar *uri;
+
+                       mime_type = egg_recent_item_get_mime_type (item);
+                       uri = egg_recent_item_get_uri (item);
+#ifndef USE_STABLE_LIBGNOMEUI
+                       {
+                               int width, height;
+
+                               gtk_icon_size_lookup_for_settings
+                                       (gtk_settings_get_default (),
+                                        view->icon_size,
+                                        &width, &height);
+                               pixbuf = egg_recent_util_get_icon
+                                                       (view->theme,
+                                                        uri, mime_type,
+                                                        height);
+                       }
+#else
+                       pixbuf = NULL;
+#endif
+
+
+                       if (pixbuf != NULL) {
+                               gchar *pixbuf_xml;
+
+                               /* Riiiiight.... */
+                               pixbuf_xml = bonobo_ui_util_pixbuf_to_xml (pixbuf);
+                               
+                               cmd = g_strdup_printf ("<cmd name=\"%s\" pixtype=\"pixbuf\" pixname=\"%s\"/>", verb_name, pixbuf_xml);
+
+                               g_free (pixbuf_xml);
+                               g_object_unref (pixbuf);
+                       } else {
+                               cmd = g_strdup_printf ("<cmd name=\"%s\"/> ",
+                                                      verb_name);
+                       }
+
+                       g_free (mime_type);
+                       g_free (uri);
+               } else
+                       cmd = g_strdup_printf ("<cmd name=\"%s\"/> ",
+                                              verb_name);
+               bonobo_ui_component_set_translate (ui_component, "/commands/", cmd, NULL);
+
+               closure = g_cclosure_new (G_CALLBACK (egg_recent_view_bonobo_menu_cb),
+                                         md, egg_recent_view_bonobo_menu_data_destroy_cb);
+                                         
+               bonobo_ui_component_add_verb_full (ui_component, verb_name,
+                                                  closure); 
+               
+               if (view->show_numbers) {
+                       if (i < 10)
+                               label = g_strdup_printf ("_%d. %s", i,
+                                                        escaped_name);
+                       else
+                               label = g_strdup_printf ("%d. %s", i, escaped_name);
+               } else {
+                       label = g_strdup (escaped_name);
+               }
+                       
+               
+               
+               item_path = g_strconcat (view->path, "/", verb_name, NULL);
+
+               if (bonobo_ui_component_path_exists (ui_component, item_path, NULL))
+               {
+                       bonobo_ui_component_set_prop (ui_component, item_path, 
+                                                     "label", label, NULL);
+
+                       bonobo_ui_component_set_prop (ui_component, item_path, 
+                                                     "tip", tip, NULL);
+               }
+               else
+               {
+                       gchar *xml;
+                       
+                       xml = g_strdup_printf ("<menuitem name=\"%s\" "
+                                               "verb=\"%s\""
+                                               " _label=\"%s\"  _tip=\"%s\" "
+                                               "hidden=\"0\" />", 
+                                               verb_name, verb_name, label,
+                                               tip);
+
+                       bonobo_ui_component_set_translate (ui_component, view->path, xml, NULL);
+
+                       g_free (xml); 
+               }
+               
+               g_free (label);
+               g_free (verb_name);
+               g_free (tip);
+               g_free (escaped_name);
+               g_free (item_path);
+               g_free (utf8_uri);
+               g_free (base_uri);
+               g_free (cmd);
+
+       }
+
+
+       bonobo_ui_component_thaw (ui_component, NULL);
+}
+
+static void
+model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewBonobo *view)
+{
+       if (list != NULL)
+               egg_recent_view_bonobo_set_list (view, list);
+       else
+               egg_recent_view_bonobo_clear (view);
+}
+
+
+static EggRecentModel *
+egg_recent_view_bonobo_get_model (EggRecentView *view_parent)
+{
+       EggRecentViewBonobo *view;
+       
+       g_return_val_if_fail (view_parent, NULL);
+       view = EGG_RECENT_VIEW_BONOBO (view_parent);
+       
+       return view->model;
+}
+
+static void
+egg_recent_view_bonobo_set_model (EggRecentView *view_parent, EggRecentModel *model)
+{
+       EggRecentViewBonobo *view;
+       
+       g_return_if_fail (view_parent);
+       view = EGG_RECENT_VIEW_BONOBO (view_parent);
+       
+       if (view->model)
+               g_signal_handler_disconnect (G_OBJECT (view->model),
+                                            view->changed_cb_id);
+       
+       view->model = model;
+       g_object_ref (view->model);
+       view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
+                                               "changed",
+                                               G_CALLBACK (model_changed_cb),
+                                               view, 0);
+
+       egg_recent_model_changed (view->model);
+}
+
+static void
+egg_recent_view_bonobo_set_property (GObject *object,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+       EggRecentViewBonobo *view = EGG_RECENT_VIEW_BONOBO (object);
+
+       switch (prop_id)
+       {
+               case PROP_UI_COMPONENT:
+                       egg_recent_view_bonobo_set_ui_component (EGG_RECENT_VIEW_BONOBO (view),
+                                                      BONOBO_UI_COMPONENT (g_value_get_object (value)));
+               break;
+               case PROP_MENU_PATH:
+                       view->path = g_strdup (g_value_get_string (value));
+               break;
+               case PROP_SHOW_ICONS:
+                       egg_recent_view_bonobo_show_icons (view,
+                                               g_value_get_boolean (value));
+               default:
+               case PROP_SHOW_NUMBERS:
+                       egg_recent_view_bonobo_show_numbers (view,
+                                               g_value_get_boolean (value));
+               break;
+               break;
+       }
+}
+
+static void
+egg_recent_view_bonobo_get_property (GObject *object,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+       EggRecentViewBonobo *view = EGG_RECENT_VIEW_BONOBO (object);
+
+       switch (prop_id)
+       {
+               case PROP_UI_COMPONENT:
+                       g_value_set_pointer (value, view->uic);
+               break;
+               case PROP_MENU_PATH:
+                       g_value_set_string (value, g_strdup (view->path));
+               break;
+               case PROP_SHOW_ICONS:
+                       g_value_set_boolean (value, view->show_icons);
+               break;
+               case PROP_SHOW_NUMBERS:
+                       g_value_set_boolean (value, view->show_numbers);
+               break;
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+egg_recent_view_bonobo_finalize (GObject *object)
+{
+       EggRecentViewBonobo *view = EGG_RECENT_VIEW_BONOBO (object);
+
+       g_free (view->path);
+       g_free (view->uid);
+
+       g_object_unref (view->model);
+       g_object_unref (view->uic);
+#ifndef USE_STABLE_LIBGNOMEUI
+       g_object_unref (view->theme);
+#endif
+       g_object_unref (view->client);
+}
+
+static void
+egg_recent_view_bonobo_class_init (EggRecentViewBonoboClass * klass)
+{
+       GObjectClass *object_class;
+
+       
+       object_class = G_OBJECT_CLASS (klass);
+
+       object_class->set_property = egg_recent_view_bonobo_set_property;
+       object_class->get_property = egg_recent_view_bonobo_get_property;
+       object_class->finalize     = egg_recent_view_bonobo_finalize;
+
+       egg_recent_view_bonobo_signals[ACTIVATE] = g_signal_new ("activate",
+                       G_OBJECT_CLASS_TYPE (object_class),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (EggRecentViewBonoboClass, activate),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__BOXED,
+                       G_TYPE_NONE, 1,
+                       EGG_TYPE_RECENT_ITEM);
+
+       g_object_class_install_property (object_class,
+                                        PROP_UI_COMPONENT,
+                                        g_param_spec_object ("ui-component",
+                                          "UI Component",
+                                          "BonoboUIComponent for menus.",
+                                          bonobo_ui_component_get_type(),
+                                          G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_MENU_PATH,
+                                        g_param_spec_string ("ui-path",
+                                          "Path",
+                                          "The path to put the menu items.",
+                                          "/menus/File/EggRecentDocuments",
+                                          G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_ICONS,
+                                        g_param_spec_boolean ("show-icons",
+                                          "Show Icons",
+                                          "Whether or not to show icons",
+                                          FALSE,
+                                          G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_NUMBERS,
+                                        g_param_spec_boolean ("show-numbers",
+                                          "Show Numbers",
+                                          "Whether or not to show numbers",
+                                          TRUE,
+                                          G_PARAM_READWRITE));
+
+
+
+       klass->activate = NULL;
+}
+
+static void
+egg_recent_view_init (EggRecentViewClass *iface)
+{
+       iface->do_get_model = egg_recent_view_bonobo_get_model;
+       iface->do_set_model = egg_recent_view_bonobo_set_model;
+}
+
+static void
+show_menus_changed_cb (GConfClient *client,
+                       guint cnxn_id,
+                       GConfEntry *entry,
+                       EggRecentViewBonobo *view)
+{
+        GConfValue *value;
+
+        value = gconf_entry_get_value (entry);
+
+        g_return_if_fail (value->type == GCONF_VALUE_BOOL);
+
+        egg_recent_view_bonobo_show_icons (view,
+                                gconf_value_get_bool (value));
+
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static void
+theme_changed_cb (GnomeIconTheme *theme, EggRecentViewBonobo *view)
+{
+       if (view->model != NULL)
+               egg_recent_model_changed (view->model);
+}
+#endif
+
+static void
+egg_recent_view_bonobo_init (EggRecentViewBonobo *view)
+{
+       view->uid = egg_recent_util_get_unique_id ();
+#ifndef USE_STABLE_LIBGNOMEUI
+       view->theme = gnome_icon_theme_new ();
+       gnome_icon_theme_set_allow_svg (view->theme, TRUE);
+       g_signal_connect_object (view->theme, "changed",
+                                G_CALLBACK (theme_changed_cb), view, 0);
+#endif
+
+       view->client = gconf_client_get_default ();
+       view->show_icons =
+               gconf_client_get_bool (view->client,
+                       "/desktop/gnome/interface/menus_have_icons",
+                       NULL);
+
+       gconf_client_add_dir (view->client, "/desktop/gnome/interface",
+                             GCONF_CLIENT_PRELOAD_NONE,
+                             NULL);
+       gconf_client_notify_add (view->client,
+                       "/desktop/gnome/interface/menus_have_icons",
+                       (GConfClientNotifyFunc)show_menus_changed_cb,
+                       view, NULL, NULL);
+
+       view->tooltip_func = NULL;
+       view->tooltip_func_data = NULL;
+
+       view->icon_size = GTK_ICON_SIZE_MENU;
+}
+
+void
+egg_recent_view_bonobo_set_icon_size (EggRecentViewBonobo *view,
+                                     GtkIconSize icon_size)
+{
+       if (view->icon_size != icon_size) {
+               view->icon_size = icon_size;
+               egg_recent_model_changed (view->model);
+       } else {
+               view->icon_size = icon_size;
+       }
+}
+                                                                              
+GtkIconSize
+egg_recent_view_bonobo_get_icon_size (EggRecentViewBonobo *view)
+{
+       return view->icon_size;
+}
+
+void
+egg_recent_view_bonobo_show_icons (EggRecentViewBonobo *view, gboolean show)
+{
+       view->show_icons = show;
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_bonobo_show_numbers (EggRecentViewBonobo *view, gboolean show)
+{
+       view->show_numbers = show;
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_bonobo_set_ui_component (EggRecentViewBonobo *view, BonoboUIComponent *uic)
+{
+       g_return_if_fail (view);
+       g_return_if_fail (uic);
+
+       view->uic = uic;
+
+       g_object_ref (view->uic);
+}
+
+void
+egg_recent_view_bonobo_set_ui_path (EggRecentViewBonobo *view, const gchar *path)
+{
+       g_return_if_fail (view);
+       g_return_if_fail (path);
+
+       view->path = g_strdup (path);
+}
+
+const BonoboUIComponent *
+egg_recent_view_bonobo_get_ui_component (EggRecentViewBonobo *view)
+{
+       g_return_val_if_fail (view, NULL);
+
+       return view->uic;
+}
+
+gchar *
+egg_recent_view_bonobo_get_ui_path (EggRecentViewBonobo *view)
+{
+       g_return_val_if_fail (view, NULL);
+
+       return g_strdup (view->path);
+}
+
+void
+egg_recent_view_bonobo_set_tooltip_func (EggRecentViewBonobo *view,
+                                        EggRecentViewBonoboTooltipFunc func,
+                                        gpointer user_data)
+{
+       view->tooltip_func = func;
+       view->tooltip_func_data = user_data;
+       
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+/**
+ * egg_recent_view_bonobo_new:
+ * @appname: The name of your application.
+ * @limit:  The maximum number of items allowed.
+ *
+ * This creates a new EggRecentViewBonobo object.
+ *
+ * Returns: a EggRecentViewBonobo object
+ */
+EggRecentViewBonobo *
+egg_recent_view_bonobo_new (BonoboUIComponent *uic, const gchar *path)
+{
+       EggRecentViewBonobo *view;
+
+       g_return_val_if_fail (uic, NULL);
+       g_return_val_if_fail (path, NULL);
+
+       view = EGG_RECENT_VIEW_BONOBO (g_object_new (egg_recent_view_bonobo_get_type (),
+                                          "ui-path", path,
+                                          "ui-component", uic,
+                                          "show-icons", FALSE,
+                                          "show-numbers", TRUE, NULL));
+
+       g_return_val_if_fail (view, NULL);
+       
+       return view;
+}
+
+/**
+ * egg_recent_view_bonobo_get_type:
+ * @:
+ *
+ * This returns a GType representing a EggRecentViewBonobo object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_view_bonobo_get_type (void)
+{
+       static GType egg_recent_view_bonobo_type = 0;
+
+       if(!egg_recent_view_bonobo_type) {
+               static const GTypeInfo egg_recent_view_bonobo_info = {
+                       sizeof (EggRecentViewBonoboClass),
+                       NULL, /* base init */
+                       NULL, /* base finalize */
+                       (GClassInitFunc)egg_recent_view_bonobo_class_init, /* class init */
+                       NULL, /* class finalize */
+                       NULL, /* class data */
+                       sizeof (EggRecentViewBonobo),
+                       0,
+                       (GInstanceInitFunc) egg_recent_view_bonobo_init
+               };
+
+               static const GInterfaceInfo view_info =
+               {
+                       (GInterfaceInitFunc) egg_recent_view_init,
+                       NULL,
+                       NULL
+               };
+
+               egg_recent_view_bonobo_type = g_type_register_static (G_TYPE_OBJECT,
+                                                       "EggRecentViewBonobo",
+                                                       &egg_recent_view_bonobo_info, 0);
+               g_type_add_interface_static (egg_recent_view_bonobo_type,
+                                            EGG_TYPE_RECENT_VIEW,
+                                            &view_info);
+       }
+
+       return egg_recent_view_bonobo_type;
+}
+
diff --git a/glabels2/src/recent-files/egg-recent-view-bonobo.h b/glabels2/src/recent-files/egg-recent-view-bonobo.h
new file mode 100644 (file)
index 0000000..0b283ef
--- /dev/null
@@ -0,0 +1,58 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_VIEW_BONOBO_H__
+#define __EGG_RECENT_VIEW_BONOBO_H__
+
+#include <libbonoboui.h>
+
+G_BEGIN_DECLS
+
+#define EGG_RECENT_VIEW_BONOBO(obj)            G_TYPE_CHECK_INSTANCE_CAST (obj, egg_recent_view_bonobo_get_type (), EggRecentViewBonobo)
+#define EGG_RECENT_VIEW_BONOBO_CLASS(klass)    G_TYPE_CHECK_CLASS_CAST (klass, egg_recent_view_bonobo_get_type (), EggRecentViewBonoboClass)
+#define EGG_IS_RECENT_VIEW_BONOBO(obj)         G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_view_bonobo_get_type ())
+
+typedef char *(*EggRecentViewBonoboTooltipFunc) (EggRecentItem *item,
+                                                gpointer user_data);
+
+typedef struct _EggRecentViewBonobo EggRecentViewBonobo;
+
+typedef struct _EggRecentViewBonoboClass EggRecentViewBonoboClass;
+
+struct _EggRecentViewBonoboClass {
+       GObjectClass parent_class;
+       
+       void (*activate) (EggRecentViewBonobo *view, EggRecentItem *item);
+};
+
+GType        egg_recent_view_bonobo_get_type (void);
+
+EggRecentViewBonobo * egg_recent_view_bonobo_new (BonoboUIComponent *uic,
+                                                     const gchar *path);
+
+
+void egg_recent_view_bonobo_set_ui_component (EggRecentViewBonobo *view,
+                                               BonoboUIComponent *uic);
+
+void egg_recent_view_bonobo_set_ui_path      (EggRecentViewBonobo *view,
+                                               const gchar *path);
+
+gchar * egg_recent_view_bonobo_get_ui_path   (EggRecentViewBonobo *view);
+const BonoboUIComponent *egg_recent_view_bonobo_get_ui_component (EggRecentViewBonobo *view);
+
+void egg_recent_view_bonobo_show_icons (EggRecentViewBonobo *view,
+                                       gboolean show);
+
+void egg_recent_view_bonobo_show_numbers (EggRecentViewBonobo *view,
+                                         gboolean show);
+
+void egg_recent_view_bonobo_set_tooltip_func (EggRecentViewBonobo *view,
+                                       EggRecentViewBonoboTooltipFunc func,
+                                       gpointer user_data);
+
+void egg_recent_view_bonobo_set_icon_size (EggRecentViewBonobo *view,
+                                          GtkIconSize icon_size);
+
+GtkIconSize egg_recent_view_bonobo_get_icon_size (EggRecentViewBonobo *view);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_VIEW_BONOBO_H__ */
diff --git a/glabels2/src/recent-files/egg-recent-view-gtk.c b/glabels2/src/recent-files/egg-recent-view-gtk.c
new file mode 100644 (file)
index 0000000..8fefd32
--- /dev/null
@@ -0,0 +1,803 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   James Willcox <jwillcox@cs.indiana.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs.h>
+#ifndef USE_STABLE_LIBGNOMEUI
+#include <libgnomeui/gnome-icon-theme.h>
+#endif
+#include <gconf/gconf-client.h>
+#include "egg-recent-model.h"
+#include "egg-recent-view.h"
+#include "egg-recent-view-gtk.h"
+#include "egg-recent-util.h"
+#include "egg-recent-item.h"
+
+struct _EggRecentViewGtk {
+       GObject parent_instance;        /* We emit signals */
+
+       GtkWidget *menu;
+       GtkWidget *start_menu_item;
+
+       gboolean leading_sep;
+       gboolean trailing_sep;
+
+       gulong changed_cb_id;
+
+       gchar *uid;
+
+       gboolean show_icons;
+       gboolean show_numbers;
+#ifndef USE_STABLE_LIBGNOMEUI
+       GnomeIconTheme *theme;
+#endif
+
+       GtkTooltips *tooltips;
+       EggRecentViewGtkTooltipFunc tooltip_func;
+       gpointer tooltip_func_data;
+
+       EggRecentModel *model;
+       GConfClient *client;
+       GtkIconSize icon_size;
+};
+
+
+
+struct _EggRecentViewGtkMenuData {
+       EggRecentViewGtk *view;
+       EggRecentItem *item;
+};
+
+typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;
+
+enum {
+       ACTIVATE,
+       LAST_SIGNAL
+};
+
+/* GObject properties */
+enum {
+       PROP_BOGUS,
+       PROP_MENU,
+       PROP_START_MENU_ITEM,
+       PROP_SHOW_ICONS,
+       PROP_SHOW_NUMBERS
+};
+
+static guint view_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+egg_recent_view_gtk_clear (EggRecentViewGtk *view)
+{
+       GList *menu_children;
+       GList *p;
+       GObject *menu_item;
+       gint *menu_data=NULL;
+
+       g_return_if_fail (view->menu != NULL);
+
+       menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));
+
+       p = menu_children;
+       while (p != NULL) {
+               menu_item = (GObject *)p->data;
+
+               menu_data = (gint *)g_object_get_data (menu_item,
+                                                      view->uid);
+       
+               if (menu_data) {
+                       gtk_container_remove (GTK_CONTAINER (view->menu),
+                                            GTK_WIDGET (menu_item));
+
+               }
+               
+               p = p->next;
+       }
+}
+
+
+static gint
+egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
+{
+       gint i;
+       GList *menu_children;
+       GList *p;
+       GtkWidget *menu_item;
+       gint menu_loc=-1;
+
+       g_return_val_if_fail (view, 0);
+
+       menu_children = GTK_MENU_SHELL (view->menu)->children;
+
+       i = 0;
+       p = menu_children;
+       while (p != NULL) {
+               menu_item = (GtkWidget *)p->data;
+
+               if (menu_item == view->start_menu_item) {
+                       menu_loc = i;
+                       break;
+               }
+
+               p = p->next;
+               i++;
+       }
+
+       return menu_loc;
+}
+
+static void
+egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
+{
+       EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
+       EggRecentItem *item;
+
+       g_return_if_fail (md);
+       g_return_if_fail (md->item);
+       g_return_if_fail (md->view);
+       g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));
+
+       item = md->item;
+       
+       egg_recent_item_ref (item);
+       
+       g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
+                      item);
+
+       egg_recent_item_unref (item);
+}
+
+static void
+egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
+{
+       EggRecentViewGtkMenuData *md = data;
+
+       egg_recent_item_unref (md->item);
+       g_free (md);
+}
+
+static GtkWidget *
+egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
+                                  EggRecentItem *item,
+                                  gint index)
+{
+       GtkWidget *menu_item;
+       EggRecentViewGtkMenuData *md=(EggRecentViewGtkMenuData *)g_malloc (sizeof (EggRecentViewGtkMenuData));
+       gchar *text;
+       gchar *basename;
+       gchar *escaped;
+       gchar *uri;
+
+       g_return_val_if_fail (view, NULL);
+
+       if (item != NULL) {
+               gchar *mime_type;
+               GtkWidget *image;
+               GdkPixbuf *pixbuf;
+
+               uri = egg_recent_item_get_uri_for_display (item);
+               if (uri == NULL)
+                       return NULL;
+               
+               basename = g_path_get_basename (uri);
+               escaped = egg_recent_util_escape_underlines (basename);
+               g_free (basename);
+               g_free (uri);
+
+               if (view->show_numbers) {
+                       /* avoid having conflicting mnemonics */
+                       if (index >= 10)
+                               text = g_strdup_printf ("%d.  %s", index,
+                                                       escaped);
+                       else
+                               text = g_strdup_printf ("_%d.  %s", index,
+                                                       escaped);
+                       g_free (escaped);
+               } else {
+                       text = escaped;
+               }
+
+
+               mime_type = egg_recent_item_get_mime_type (item);
+#ifndef USE_STABLE_LIBGNOMEUI
+               {
+                       int width, height;
+
+                       gtk_icon_size_lookup_for_settings
+                                       (gtk_widget_get_settings (view->menu),
+                                        view->icon_size,
+                                        &width, &height);
+
+                       pixbuf = egg_recent_util_get_icon (view->theme, uri,
+                                                          mime_type,
+                                                          height);
+               }
+#else
+               pixbuf = NULL;
+#endif
+               image = gtk_image_new_from_pixbuf (pixbuf);
+               if (pixbuf)
+                       g_object_unref (pixbuf);
+
+               if (view->show_icons)
+                       gtk_widget_show (image);
+
+               menu_item = gtk_image_menu_item_new_with_mnemonic (text);
+               gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
+                                              image);
+
+               md->view = view;
+               md->item = item;
+
+               egg_recent_item_ref (md->item);
+
+               g_signal_connect_data (G_OBJECT (menu_item), "activate",
+                                G_CALLBACK (egg_recent_view_gtk_menu_cb),
+                                md,
+                                (GClosureNotify)egg_recent_view_gtk_destroy_cb,
+                                0);
+
+               g_free (mime_type);
+               g_free (text);
+       }
+       else {
+               menu_item = gtk_separator_menu_item_new ();
+       }
+       
+       g_return_val_if_fail (menu_item, NULL);
+
+       /**
+        * this is a tag so we can distinguish our menu items
+        * from others that may be in the menu.
+        */
+       g_object_set_data (G_OBJECT (menu_item),
+                          view->uid,
+                          GINT_TO_POINTER (1));
+
+
+       gtk_widget_show (menu_item);
+
+       return menu_item;
+}
+
+static void
+egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
+                                EggRecentItem *item,
+                                gint display,
+                                gint index)
+{
+       GtkWidget *menu_item;
+       gint menu_offset;
+       
+       g_return_if_fail (view);
+       g_return_if_fail (view->menu);
+
+       menu_offset = egg_recent_view_gtk_find_menu_offset (view);
+
+       menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);
+
+       if (view->tooltip_func != NULL && menu_item != NULL) {
+               view->tooltip_func (view->tooltips, menu_item,
+                                   item, view->tooltip_func_data);
+       }
+       
+       if (menu_item)
+               gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
+                              menu_offset+index);
+}
+
+
+
+
+static void
+egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
+{
+       EggRecentItem *item;
+       GList *p;
+       gint display=1;
+       gint index=1;
+
+       g_return_if_fail (view);
+
+       egg_recent_view_gtk_clear (view);
+
+       if (view->leading_sep) {
+               egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+               index++;
+       }
+
+       p = list;
+       while (p != NULL) {
+               item = (EggRecentItem *)p->data;
+
+               egg_recent_view_gtk_add_to_menu (view, item, display, index);
+
+               p = p->next;
+               display++;
+               index++;
+       }
+
+       if (view->trailing_sep)
+               egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
+}
+
+static void
+model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
+{
+       if (list != NULL)
+               egg_recent_view_gtk_set_list (view, list);
+       else
+               egg_recent_view_gtk_clear (view);
+}
+
+static EggRecentModel *
+egg_recent_view_gtk_get_model (EggRecentView *view_parent)
+{
+       EggRecentViewGtk *view;
+       
+       g_return_val_if_fail (view_parent != NULL, NULL);
+       view = EGG_RECENT_VIEW_GTK (view_parent);
+       return view->model;
+}
+
+static void
+egg_recent_view_gtk_set_model (EggRecentView *view_parent,
+                                EggRecentModel *model)
+{
+       EggRecentViewGtk *view;
+       
+       g_return_if_fail (view_parent != NULL);
+       view = EGG_RECENT_VIEW_GTK (view_parent);
+
+       if (view->model != NULL) {
+               g_object_unref (view->model);
+               g_signal_handler_disconnect (G_OBJECT (model),
+                                            view->changed_cb_id);
+       }
+       
+       view->model = model;
+       g_object_ref (view->model);
+
+       view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
+                                               "changed",
+                                               G_CALLBACK (model_changed_cb),
+                                               view, 0);
+
+       egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
+{
+       view->leading_sep = val;
+
+       egg_recent_view_gtk_clear (view);
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
+{
+       view->trailing_sep = val;
+
+       egg_recent_view_gtk_clear (view);
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+static void
+egg_recent_view_gtk_set_property (GObject *object,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+       EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+       switch (prop_id)
+       {
+               case PROP_MENU:
+                       egg_recent_view_gtk_set_menu (view,
+                                              GTK_WIDGET (g_value_get_object (value)));
+               break;
+               case PROP_START_MENU_ITEM:
+                       egg_recent_view_gtk_set_start_menu_item (view,
+                                       g_value_get_object (value));
+               break;
+               case PROP_SHOW_ICONS:
+                       egg_recent_view_gtk_show_icons (view,
+                                       g_value_get_boolean (value));
+               break;
+               case PROP_SHOW_NUMBERS:
+                       egg_recent_view_gtk_show_numbers (view,
+                                       g_value_get_boolean (value));
+               break;
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+egg_recent_view_gtk_get_property (GObject *object,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+       EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+       switch (prop_id)
+       {
+               case PROP_MENU:
+                       g_value_set_object (value, view->menu);
+               break;
+               case PROP_START_MENU_ITEM:
+                       g_value_set_object (value, view->start_menu_item);
+               break;
+               case PROP_SHOW_ICONS:
+                       g_value_set_boolean (value, view->show_icons);
+               break;
+               case PROP_SHOW_NUMBERS:
+                       g_value_set_boolean (value, view->show_numbers);
+               break;
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+               break;
+       }
+}
+
+static void
+egg_recent_view_gtk_finalize (GObject *object)
+{
+       EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
+
+       g_signal_handler_disconnect (G_OBJECT (view->model),
+                                    view->changed_cb_id);
+
+       g_free (view->uid);
+
+       g_object_unref (view->menu);
+       g_object_unref (view->model);
+#ifndef USE_STABLE_LIBGNOMEUI
+       g_object_unref (view->theme);
+#endif
+       g_object_unref (view->client);
+
+       g_object_unref (view->tooltips);
+}
+
+static void
+egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
+{
+       GObjectClass *object_class;
+
+       object_class = G_OBJECT_CLASS (klass);
+
+       object_class->set_property = egg_recent_view_gtk_set_property;
+       object_class->get_property = egg_recent_view_gtk_get_property;
+       object_class->finalize     = egg_recent_view_gtk_finalize;
+
+       view_signals[ACTIVATE] = g_signal_new ("activate",
+                       G_OBJECT_CLASS_TYPE (object_class),
+                       G_SIGNAL_RUN_LAST,
+                       G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
+                       NULL, NULL,
+                       g_cclosure_marshal_VOID__BOXED,
+                       G_TYPE_NONE, 1,
+                       EGG_TYPE_RECENT_ITEM);
+
+       g_object_class_install_property (object_class,
+                                        PROP_MENU,
+                                        g_param_spec_object ("menu",
+                                                             "Menu",
+                                                             "The GtkMenuShell this object will update.",
+                                                             gtk_menu_get_type(),
+                                                             G_PARAM_READWRITE));
+       g_object_class_install_property (object_class,
+                                        PROP_START_MENU_ITEM,
+                                        g_param_spec_object ("start-menu-item",
+                                                             "Start Menu Item",
+                                                             "The menu item that precedes where are menu items will go",
+                                                             gtk_menu_item_get_type (),
+                                                             G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_ICONS,
+                                        g_param_spec_boolean ("show-icons",
+                                          "Show Icons",
+                                          "Whether or not to show icons",
+                                          FALSE,
+                                          G_PARAM_READWRITE));
+
+       g_object_class_install_property (object_class,
+                                        PROP_SHOW_NUMBERS,
+                                        g_param_spec_boolean ("show-numbers",
+                                          "Show Numbers",
+                                          "Whether or not to show numbers",
+                                          TRUE,
+                                          G_PARAM_READWRITE));
+
+       klass->activate = NULL;
+}
+
+static void
+egg_recent_view_init (EggRecentViewClass *iface)
+{
+       iface->do_get_model = egg_recent_view_gtk_get_model;
+       iface->do_set_model = egg_recent_view_gtk_set_model;
+}
+
+static void
+show_menus_changed_cb (GConfClient *client,
+                      guint cnxn_id,
+                      GConfEntry *entry,
+                      EggRecentViewGtk *view)
+{
+       GConfValue *value;
+
+       value = gconf_entry_get_value (entry);
+
+       g_return_if_fail (value->type == GCONF_VALUE_BOOL);
+
+       egg_recent_view_gtk_show_icons (view,
+                               gconf_value_get_bool (value));
+
+}
+
+#ifndef USE_STABLE_LIBGNOMEUI
+static void
+theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
+{
+       if (view->model != NULL)
+               egg_recent_model_changed (view->model);
+}
+#endif
+
+static void
+egg_recent_view_gtk_init (EggRecentViewGtk * view)
+{
+       view->client = gconf_client_get_default ();
+
+       view->show_icons =
+               gconf_client_get_bool (view->client,
+                       "/desktop/gnome/interface/menus_have_icons",
+                       NULL);
+
+       gconf_client_add_dir (view->client, "/desktop/gnome/interface",
+                             GCONF_CLIENT_PRELOAD_NONE,
+                             NULL);
+       gconf_client_notify_add (view->client,
+                       "/desktop/gnome/interface/menus_have_icons",
+                       (GConfClientNotifyFunc)show_menus_changed_cb,
+                       view, NULL, NULL);
+
+
+       view->leading_sep = FALSE;
+       view->trailing_sep = FALSE;
+
+       view->uid = egg_recent_util_get_unique_id ();
+#ifndef USE_STABLE_LIBGNOMEUI
+       view->theme = gnome_icon_theme_new ();
+       gnome_icon_theme_set_allow_svg (view->theme, TRUE);
+       g_signal_connect_object (view->theme, "changed",
+                                G_CALLBACK (theme_changed_cb), view, 0);
+#endif
+       view->tooltips = gtk_tooltips_new ();
+       g_object_ref (view->tooltips);
+       gtk_object_sink (GTK_OBJECT (view->tooltips));
+       view->tooltip_func = NULL;
+       view->tooltip_func_data = NULL;
+
+       view->icon_size = GTK_ICON_SIZE_MENU;
+}
+
+void
+egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
+                                  GtkIconSize icon_size)
+{
+       if (view->icon_size != icon_size) {
+               view->icon_size = icon_size;
+               egg_recent_model_changed (view->model);
+       } else {
+               view->icon_size = icon_size;
+       }
+}
+
+GtkIconSize
+egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
+{
+       return view->icon_size;
+}
+
+void
+egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
+{
+       view->show_icons = show;
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
+{
+       view->show_numbers = show;
+
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+void
+egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
+                                     EggRecentViewGtkTooltipFunc func,
+                                     gpointer user_data)
+{
+       view->tooltip_func = func;
+       view->tooltip_func_data = user_data;
+       
+       if (view->model)
+               egg_recent_model_changed (view->model);
+}
+
+/**
+ * egg_recent_view_gtk_set_menu:
+ * @view: A EggRecentViewGtk object.
+ * @menu: The GtkMenuShell to put the menu items in.
+ *
+ * Use this function to change the GtkMenuShell that the recent
+ * documents appear in.
+ *
+ */
+void
+egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
+                               GtkWidget *menu)
+{
+       g_return_if_fail (view);
+       g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+       g_return_if_fail (menu);
+
+       if (view->menu != NULL)
+               g_object_unref (view->menu);
+       
+       view->menu = menu;
+       g_object_ref (view->menu);
+}
+
+/**
+ * egg_recent_view_gtk_set_start_menu_item:
+ * @view: A EggRecentViewGtk object.
+ * @start_menu_item: The menu item that appears just before where our menu
+ * items should appear
+ *
+ */
+void
+egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+                                        GtkWidget *menu_item)
+{
+       g_return_if_fail (view);
+       g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
+       
+       view->start_menu_item = menu_item;
+}
+
+/**
+ * egg_recent_view_gtk_get_menu:
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
+{
+       return view->menu;
+}
+
+/**
+ * egg_recent_view_gtk_get_start_menu_item
+ * @view: A EggRecentViewGtk object.
+ *
+ */
+GtkWidget *
+egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
+{
+       return view->start_menu_item;
+}
+
+
+/**
+ * egg_recent_view_gtk_new:
+ * @appname: The name of your application.
+ * @limit:  The maximum number of items allowed.
+ *
+ * This creates a new EggRecentViewGtk object.
+ *
+ * Returns: a EggRecentViewGtk object
+ */
+EggRecentViewGtk *
+egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
+{
+       EggRecentViewGtk *view;
+
+       g_return_val_if_fail (menu, NULL);
+
+       view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
+                                          "start-menu-item",
+                                          start_menu_item,
+                                          "menu", menu,
+                                          "show-numbers", TRUE, NULL));
+
+       g_return_val_if_fail (view, NULL);
+       
+       return view;
+}
+
+/**
+ * egg_recent_view_gtk_get_type:
+ * @:
+ *
+ * This returns a GType representing a EggRecentViewGtk object.
+ *
+ * Returns: a GType
+ */
+GType
+egg_recent_view_gtk_get_type (void)
+{
+       static GType egg_recent_view_gtk_type = 0;
+
+       if(!egg_recent_view_gtk_type) {
+               static const GTypeInfo egg_recent_view_gtk_info = {
+                       sizeof (EggRecentViewGtkClass),
+                       NULL, /* base init */
+                       NULL, /* base finalize */
+                       (GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
+                       NULL, /* class finalize */
+                       NULL, /* class data */
+                       sizeof (EggRecentViewGtk),
+                       0,
+                       (GInstanceInitFunc) egg_recent_view_gtk_init
+               };
+
+               static const GInterfaceInfo view_info =
+               {
+                       (GInterfaceInitFunc) egg_recent_view_init,
+                       NULL,
+                       NULL
+               };
+
+               egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
+                                                       "EggRecentViewGtk",
+                                                       &egg_recent_view_gtk_info, 0);
+               g_type_add_interface_static (egg_recent_view_gtk_type,
+                                            EGG_TYPE_RECENT_VIEW,
+                                            &view_info);
+       }
+
+       return egg_recent_view_gtk_type;
+}
+
diff --git a/glabels2/src/recent-files/egg-recent-view-gtk.h b/glabels2/src/recent-files/egg-recent-view-gtk.h
new file mode 100644 (file)
index 0000000..7e56baf
--- /dev/null
@@ -0,0 +1,64 @@
+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+#ifndef __EGG_RECENT_VIEW_GTK_H__
+#define __EGG_RECENT_VIEW_GTK_H__
+
+G_BEGIN_DECLS
+
+#include <gtk/gtk.h>
+#include "egg-recent-item.h"
+
+#define EGG_RECENT_VIEW_GTK(obj)               G_TYPE_CHECK_INSTANCE_CAST (obj, egg_recent_view_gtk_get_type (), EggRecentViewGtk)
+#define EGG_RECENT_VIEW_GTK_CLASS(klass)       G_TYPE_CHECK_CLASS_CAST (klass, egg_recent_view_gtk_get_type (), EggRecentViewGtkClass)
+#define EGG_IS_RECENT_VIEW_GTK(obj)            G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_view_gtk_get_type ())
+
+typedef void (*EggRecentViewGtkTooltipFunc) (GtkTooltips *tooltips,
+                                            GtkWidget *menu,
+                                            EggRecentItem *item,
+                                            gpointer user_data);
+
+typedef struct _EggRecentViewGtk EggRecentViewGtk;
+
+typedef struct _EggRecentViewGtkClass EggRecentViewGtkClass;
+
+struct _EggRecentViewGtkClass {
+       GObjectClass parent_class;
+       
+       void (*activate) (EggRecentViewGtk *view, EggRecentItem *item);
+};
+
+GType        egg_recent_view_gtk_get_type (void);
+
+EggRecentViewGtk * egg_recent_view_gtk_new (GtkWidget *menu,
+                                           GtkWidget *start_menu_item);
+
+void egg_recent_view_gtk_set_menu            (EggRecentViewGtk *view,
+                                               GtkWidget *menu);
+GtkWidget * egg_recent_view_gtk_get_menu     (EggRecentViewGtk *view);
+
+
+void egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
+                                             GtkWidget *menu_item);
+GtkWidget *egg_recent_view_gtk_get_start_menu_item     (EggRecentViewGtk *view);
+
+void egg_recent_view_gtk_set_leading_sep     (EggRecentViewGtk *view,
+                                               gboolean val);
+
+void egg_recent_view_gtk_set_trailing_sep    (EggRecentViewGtk *view,
+                                               gboolean val);
+
+void egg_recent_view_gtk_show_icons          (EggRecentViewGtk *view,
+                                             gboolean show);
+void egg_recent_view_gtk_show_numbers        (EggRecentViewGtk *view,
+                                             gboolean show);
+
+void egg_recent_view_gtk_set_tooltip_func    (EggRecentViewGtk *view,
+                                             EggRecentViewGtkTooltipFunc func,
+                                             gpointer user_data);
+
+void egg_recent_view_gtk_set_icon_size       (EggRecentViewGtk *view,
+                                             GtkIconSize icon_size);
+GtkIconSize egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view);
+
+G_END_DECLS
+
+#endif /* __EGG_RECENT_VIEW_GTK_H__ */
diff --git a/glabels2/src/recent-files/egg-recent-view.c b/glabels2/src/recent-files/egg-recent-view.c
new file mode 100644 (file)
index 0000000..0aa38b9
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *   James Willcox <jwillcox@cs.indiana.edu>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "egg-recent-view.h"
+
+
+GtkType
+egg_recent_view_get_type (void)
+{
+       static GtkType view_type = 0;
+
+       if (!view_type)
+       {
+               static const GTypeInfo view_info =
+               {
+                       sizeof (EggRecentViewClass),  /* class_size */
+                       NULL,                       /* base_init */
+                       NULL,                       /* base_finalize */
+               };
+
+               view_type = g_type_register_static (G_TYPE_INTERFACE,
+                                                   "EggRecentView",
+                                                   &view_info, 0);
+       }
+
+       return view_type;
+}
+
+EggRecentModel *
+egg_recent_view_get_model (EggRecentView *view)
+{
+       g_return_val_if_fail (view, NULL);
+
+       return EGG_RECENT_VIEW_GET_CLASS (view)->do_get_model (view);
+}
+
+void
+egg_recent_view_set_model (EggRecentView *view, EggRecentModel *model)
+{
+       g_return_if_fail (view);
+       g_return_if_fail (model);
+
+       EGG_RECENT_VIEW_GET_CLASS (view)->do_set_model (view, model);
+}
diff --git a/glabels2/src/recent-files/egg-recent-view.h b/glabels2/src/recent-files/egg-recent-view.h
new file mode 100644 (file)
index 0000000..48f2a19
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef __EGG_RECENT_VIEW_H__
+#define __EGG_RECENT_VIEW_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkwidget.h>
+#include "egg-recent-model.h"
+#include "egg-recent-item.h"
+
+#define EGG_TYPE_RECENT_VIEW             (egg_recent_view_get_type ())
+#define EGG_RECENT_VIEW(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_RECENT_VIEW, EggRecentView))
+#define EGG_RECENT_VIEW_CLASS(vtable)    (G_TYPE_CHECK_CLASS_CAST ((vtable), EGG_TYPE_RECENT_VIEW, EggRecentViewClass))
+#define EGG_IS_RECENT_VIEW(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_RECENT_VIEW))
+#define EGG_IS_RECENT_VIEW_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), EGG_TYPE_RECENT_VIEW))
+#define EGG_RECENT_VIEW_GET_CLASS(inst)  (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EGG_TYPE_RECENT_VIEW, EggRecentViewClass))
+
+
+typedef struct _EggRecentView       EggRecentView;
+typedef struct _EggRecentViewClass  EggRecentViewClass;
+
+struct _EggRecentViewClass
+{
+       GTypeInterface             base_iface;
+  
+       /* vtable, not signals */
+       void (* do_set_model)                   (EggRecentView *view,
+                                                EggRecentModel *model);
+       EggRecentModel * (* do_get_model)       (EggRecentView *view);
+};
+
+GtkType        egg_recent_view_get_type                (void) G_GNUC_CONST;
+void   egg_recent_view_set_list                (EggRecentView *view,
+                                                GSList *list);
+void   egg_recent_view_clear                   (EggRecentView *view);
+EggRecentModel *egg_recent_view_get_model      (EggRecentView *view);
+void   egg_recent_view_set_model               (EggRecentView *view,
+                                                EggRecentModel *model);
+
+
+#endif /* __EGG_RECENT_VIEW_H__ */
diff --git a/glabels2/src/recent-files/update-from-egg.sh b/glabels2/src/recent-files/update-from-egg.sh
new file mode 100755 (executable)
index 0000000..9be68a9
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+function die() {
+  echo $*
+  exit 1
+}
+
+if test -z "$EGGDIR"; then
+   echo "Must set EGGDIR"
+   exit 1
+fi
+
+if test -z "$EGGFILES"; then
+   echo "Must set EGGFILES"
+   exit 1
+fi
+
+for FILE in $EGGFILES; do
+  if cmp -s $EGGDIR/$FILE $FILE; then
+     echo "File $FILE is unchanged"
+  else
+     cp $EGGDIR/$FILE $FILE || die "Could not move $EGGDIR/$FILE to $FILE"
+     echo "Updated $FILE"
+  fi
+done