]> git.pld-linux.org Git - packages/gnome-desktop.git/blame - gnome-desktop-recently-used-apps.patch
- updated to 2.18.0; merged changes from DEVEL
[packages/gnome-desktop.git] / gnome-desktop-recently-used-apps.patch
CommitLineData
8f66dda0
PZ
1diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c ./libgnome-desktop/egg-recent-item.c
2--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.c 1969-12-31 19:00:00.000000000 -0500
3+++ ./libgnome-desktop/egg-recent-item.c 2006-05-08 16:18:44.000000000 -0400
4@@ -0,0 +1,426 @@
5+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6+/*
7+ * This program is free software; you can redistribute it and/or modify
8+ * it under the terms of the GNU General Public License as
9+ * published by the Free Software Foundation; either version 2 of the
10+ * License, or (at your option) any later version.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License
18+ * along with this program; if not, write to the Free Software
19+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20+ *
21+ * Authors:
22+ * James Willcox <jwillcox@cs.indiana.edu>
23+ */
24+
25+
26+#include <stdio.h>
27+#include <string.h>
28+#include <glib.h>
29+#include <libgnomevfs/gnome-vfs.h>
30+#include <libgnomevfs/gnome-vfs-mime-utils.h>
31+#include "egg-recent-item.h"
32+
33+
34+
35+EggRecentItem *
36+egg_recent_item_new (void)
37+{
38+ EggRecentItem *item;
39+
40+ item = g_new (EggRecentItem, 1);
41+
42+ item->groups = NULL;
43+ item->private_data = FALSE;
44+ item->uri = NULL;
45+ item->mime_type = NULL;
46+ item->mime_type_is_explicit = FALSE;
47+
48+ item->refcount = 1;
49+
50+ return item;
51+}
52+
53+static void
54+egg_recent_item_free (EggRecentItem *item)
55+{
56+ if (item->uri)
57+ g_free (item->uri);
58+
59+ if (item->mime_type)
60+ g_free (item->mime_type);
61+
62+ if (item->groups) {
63+ g_list_foreach (item->groups, (GFunc)g_free, NULL);
64+ g_list_free (item->groups);
65+ item->groups = NULL;
66+ }
67+
68+ g_free (item);
69+}
70+
71+EggRecentItem *
72+egg_recent_item_ref (EggRecentItem *item)
73+{
74+ item->refcount++;
75+ return item;
76+}
77+
78+EggRecentItem *
79+egg_recent_item_unref (EggRecentItem *item)
80+{
81+ item->refcount--;
82+
83+ if (item->refcount == 0) {
84+ egg_recent_item_free (item);
85+ }
86+
87+ return item;
88+}
89+
90+
91+EggRecentItem *
92+egg_recent_item_new_from_uri (const gchar *uri)
93+{
94+ EggRecentItem *item;
95+
96+ g_return_val_if_fail (uri != NULL, NULL);
97+
98+ item = egg_recent_item_new ();
99+
100+ if (!egg_recent_item_set_uri (item ,uri)) {
101+ egg_recent_item_free (item);
102+ return NULL;
103+ }
104+
105+ return item;
106+}
107+
108+static void
109+egg_recent_item_update_mime_type (EggRecentItem *item)
110+{
111+ if (!item->mime_type_is_explicit) {
112+ g_free (item->mime_type);
113+ item->mime_type = NULL;
114+
115+ if (item->uri)
116+ item->mime_type = gnome_vfs_get_mime_type (item->uri);
117+
118+ if (!item->mime_type)
119+ item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN);
120+ }
121+}
122+
123+gboolean
124+egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri)
125+{
126+ gchar *utf8_uri;
127+
128+ /* if G_BROKEN_FILENAMES is not set, this should succede */
129+ if (g_utf8_validate (uri, -1, NULL)) {
130+ item->uri = gnome_vfs_make_uri_from_input (uri);
131+ } else {
132+ utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL);
133+
134+ if (utf8_uri == NULL) {
135+ g_warning ("Couldn't convert URI to UTF-8");
136+ return FALSE;
137+ }
138+
139+ if (g_utf8_validate (utf8_uri, -1, NULL)) {
140+ item->uri = gnome_vfs_make_uri_from_input (utf8_uri);
141+ } else {
142+ g_free (utf8_uri);
143+ return FALSE;
144+ }
145+
146+ g_free (utf8_uri);
147+ }
148+
149+ return TRUE;
150+}
151+
152+gchar *
153+egg_recent_item_get_uri (const EggRecentItem *item)
154+{
155+ return g_strdup (item->uri);
156+}
157+
158+G_CONST_RETURN gchar *
159+egg_recent_item_peek_uri (const EggRecentItem *item)
160+{
161+ return item->uri;
162+}
163+
164+gchar *
165+egg_recent_item_get_uri_utf8 (const EggRecentItem *item)
166+{
167+ /* this could fail, but it's not likely, since we've already done it
168+ * once in set_uri()
169+ */
170+ return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL);
171+}
172+
173+gchar *
174+egg_recent_item_get_uri_for_display (const EggRecentItem *item)
175+{
176+ return gnome_vfs_format_uri_for_display (item->uri);
177+}
178+
179+/* Stolen from gnome_vfs_make_valid_utf8() */
180+static char *
181+make_valid_utf8 (const char *name)
182+{
183+ GString *string;
184+ const char *remainder, *invalid;
185+ int remaining_bytes, valid_bytes;
186+
187+ string = NULL;
188+ remainder = name;
189+ remaining_bytes = name ? strlen (name) : 0;
190+
191+ while (remaining_bytes != 0) {
192+ if (g_utf8_validate (remainder, remaining_bytes, &invalid))
193+ break;
194+
195+ valid_bytes = invalid - remainder;
196+
197+ if (string == NULL)
198+ string = g_string_sized_new (remaining_bytes);
199+
200+ g_string_append_len (string, remainder, valid_bytes);
201+ g_string_append_c (string, '?');
202+
203+ remaining_bytes -= valid_bytes + 1;
204+ remainder = invalid + 1;
205+ }
206+
207+ if (string == NULL)
208+ return g_strdup (name);
209+
210+ g_string_append (string, remainder);
211+/* g_string_append (string, _(" (invalid file name)")); */
212+ g_assert (g_utf8_validate (string->str, -1, NULL));
213+
214+ return g_string_free (string, FALSE);
215+}
216+
217+static gchar *
218+get_uri_shortname_for_display (GnomeVFSURI *uri)
219+{
220+ gchar *name;
221+ gboolean validated;
222+
223+ validated = FALSE;
224+ name = gnome_vfs_uri_extract_short_name (uri);
225+
226+ if (name == NULL)
227+ {
228+ name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
229+ }
230+ else if (g_ascii_strcasecmp (uri->method_string, "file") == 0)
231+ {
232+ gchar *text_uri;
233+ gchar *local_file;
234+ text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD);
235+ local_file = gnome_vfs_get_local_path_from_uri (text_uri);
236+
237+ if (local_file != NULL)
238+ {
239+ g_free (name);
240+ name = g_filename_display_basename (local_file);
241+ validated = TRUE;
242+ }
243+
244+ g_free (local_file);
245+ g_free (text_uri);
246+ }
247+ else if (!gnome_vfs_uri_has_parent (uri))
248+ {
249+ const gchar *method;
250+
251+ method = uri->method_string;
252+
253+ if (name == NULL ||
254+ strcmp (name, GNOME_VFS_URI_PATH_STR) == 0)
255+ {
256+ g_free (name);
257+ name = g_strdup (method);
258+ }
259+ else
260+ {
261+ gchar *tmp;
262+
263+ tmp = name;
264+ name = g_strdup_printf ("%s: %s", method, name);
265+ g_free (tmp);
266+ }
267+ }
268+
269+ if (!validated && !g_utf8_validate (name, -1, NULL))
270+ {
271+ gchar *utf8_name;
272+
273+ utf8_name = make_valid_utf8 (name);
274+ g_free (name);
275+ name = utf8_name;
276+ }
277+
278+ return name;
279+}
280+
281+/**
282+ * egg_recent_item_get_short_name:
283+ * @item: an #EggRecentItem
284+ *
285+ * Computes a valid UTF-8 string that can be used as the name of the item in a
286+ * menu or list. For example, calling this function on an item that refers to
287+ * "file:///foo/bar.txt" will yield "bar.txt".
288+ *
289+ * Return value: A newly-allocated string in UTF-8 encoding; free it with
290+ * g_free().
291+ **/
292+gchar *
293+egg_recent_item_get_short_name (const EggRecentItem *item)
294+{
295+ GnomeVFSURI *uri;
296+ gchar *short_name;
297+
298+ g_return_val_if_fail (item != NULL, NULL);
299+
300+ if (item->uri == NULL)
301+ return NULL;
302+
303+ uri = gnome_vfs_uri_new (item->uri);
304+ if (uri == NULL)
305+ return NULL;
306+
307+ short_name = get_uri_shortname_for_display (uri);
308+
309+ gnome_vfs_uri_unref (uri);
310+
311+ return short_name;
312+}
313+
314+void
315+egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime)
316+{
317+ g_free (item->mime_type);
318+ item->mime_type = NULL;
319+
320+ if (mime && mime[0]) {
321+ item->mime_type_is_explicit = TRUE;
322+ item->mime_type = g_strdup (mime);
323+ } else {
324+ item->mime_type_is_explicit = FALSE;
325+ }
326+}
327+
328+gchar *
329+egg_recent_item_get_mime_type (EggRecentItem *item)
330+{
331+ egg_recent_item_update_mime_type (item);
332+
333+ return g_strdup (item->mime_type);
334+}
335+
336+void
337+egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp)
338+{
339+ if (timestamp == (time_t) -1)
340+ time (&timestamp);
341+
342+ item->timestamp = timestamp;
343+}
344+
345+time_t
346+egg_recent_item_get_timestamp (const EggRecentItem *item)
347+{
348+ return item->timestamp;
349+}
350+
351+G_CONST_RETURN GList *
352+egg_recent_item_get_groups (const EggRecentItem *item)
353+{
354+ return item->groups;
355+}
356+
357+gboolean
358+egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name)
359+{
360+ GList *tmp;
361+
362+ tmp = item->groups;
363+ while (tmp != NULL) {
364+ gchar *val = (gchar *)tmp->data;
365+
366+ if (strcmp (group_name, val) == 0)
367+ return TRUE;
368+
369+ tmp = tmp->next;
370+ }
371+
372+ return FALSE;
373+}
374+
375+void
376+egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name)
377+{
378+ g_return_if_fail (group_name != NULL);
379+
380+ if (!egg_recent_item_in_group (item, group_name))
381+ item->groups = g_list_append (item->groups, g_strdup (group_name));
382+}
383+
384+void
385+egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name)
386+{
387+ GList *tmp;
388+
389+ g_return_if_fail (group_name != NULL);
390+
391+ tmp = item->groups;
392+ while (tmp != NULL) {
393+ gchar *val = (gchar *)tmp->data;
394+
395+ if (strcmp (group_name, val) == 0) {
396+ item->groups = g_list_remove (item->groups,
397+ val);
398+ g_free (val);
399+ break;
400+ }
401+
402+ tmp = tmp->next;
403+ }
404+}
405+
406+void
407+egg_recent_item_set_private (EggRecentItem *item, gboolean priv)
408+{
409+ item->private_data = priv;
410+}
411+
412+gboolean
413+egg_recent_item_get_private (const EggRecentItem *item)
414+{
415+ return item->private_data;
416+}
417+
418+GType
419+egg_recent_item_get_type (void)
420+{
421+ static GType boxed_type = 0;
422+
423+ if (!boxed_type) {
424+ boxed_type = g_boxed_type_register_static ("EggRecentItem",
425+ (GBoxedCopyFunc)egg_recent_item_ref,
426+ (GBoxedFreeFunc)egg_recent_item_unref);
427+ }
428+
429+ return boxed_type;
430+}
431diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h ./libgnome-desktop/egg-recent-item.h
432--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-item.h 1969-12-31 19:00:00.000000000 -0500
433+++ ./libgnome-desktop/egg-recent-item.h 2006-05-08 16:18:44.000000000 -0400
434@@ -0,0 +1,80 @@
435+
436+#ifndef __EGG_RECENT_ITEM_H__
437+#define __EGG_RECENT_ITEM_H__
438+
439+#include <time.h>
440+#include <glib.h>
441+#include <glib-object.h>
442+
443+G_BEGIN_DECLS
444+
445+#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ())
446+
447+#define EGG_RECENT_ITEM_LIST_UNREF(list) \
448+ g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \
449+ g_list_free (list);
450+
451+typedef struct _EggRecentItem EggRecentItem;
452+
453+struct _EggRecentItem {
454+ /* do not access any of these directly */
455+ gchar *uri;
456+ gchar *mime_type;
457+ time_t timestamp;
458+
459+ gboolean private_data;
460+
461+ GList *groups;
462+
463+ int refcount;
464+
465+ guint mime_type_is_explicit : 1;
466+};
467+
468+GType egg_recent_item_get_type (void) G_GNUC_CONST;
469+
470+/* constructors */
471+EggRecentItem * egg_recent_item_new (void);
472+
473+EggRecentItem * egg_recent_item_ref (EggRecentItem *item);
474+EggRecentItem * egg_recent_item_unref (EggRecentItem *item);
475+
476+/* automatically fetches the mime type, etc */
477+EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri);
478+
479+gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri);
480+gchar * egg_recent_item_get_uri (const EggRecentItem *item);
481+gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item);
482+gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item);
483+gchar * egg_recent_item_get_short_name (const EggRecentItem *item);
484+
485+void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime);
486+gchar * egg_recent_item_get_mime_type (EggRecentItem *item);
487+
488+void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp);
489+time_t egg_recent_item_get_timestamp (const EggRecentItem *item);
490+
491+G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item);
492+
493+
494+/* groups */
495+G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item);
496+
497+gboolean egg_recent_item_in_group (const EggRecentItem *item,
498+ const gchar *group_name);
499+
500+void egg_recent_item_add_group (EggRecentItem *item,
501+ const gchar *group_name);
502+
503+void egg_recent_item_remove_group (EggRecentItem *item,
504+ const gchar *group_name);
505+
506+void egg_recent_item_set_private (EggRecentItem *item,
507+ gboolean priv);
508+
509+gboolean egg_recent_item_get_private (const EggRecentItem *item);
510+
511+
512+G_END_DECLS
513+
514+#endif /* __EGG_RECENT_ITEM_H__ */
515diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c ./libgnome-desktop/egg-recent-model-multi.c
516--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.c 1969-12-31 19:00:00.000000000 -0500
517+++ ./libgnome-desktop/egg-recent-model-multi.c 2006-05-08 17:03:13.000000000 -0400
518@@ -0,0 +1,1990 @@
519+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
520+/*
521+ * This program is free software; you can redistribute it and/or modify
522+ * it under the terms of the GNU General Public License as
523+ * published by the Free Software Foundation; either version 2 of the
524+ * License, or (at your option) any later version.
525+ *
526+ * This program is distributed in the hope that it will be useful,
527+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
528+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
529+ * GNU General Public License for more details.
530+ *
531+ * You should have received a copy of the GNU General Public License
532+ * along with this program; if not, write to the Free Software
533+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
534+ *
535+ * Authors:
536+ * James Willcox <jwillcox@cs.indiana.edu>
537+ */
538+
539+#ifdef HAVE_CONFIG_H
540+#include <config.h>
541+#endif
542+
543+#include <stdio.h>
544+#include <string.h>
545+#include <errno.h>
546+#include <stdlib.h>
547+#include <unistd.h>
548+#include <fcntl.h>
549+#include <sys/time.h>
550+#include <sys/stat.h>
551+#include <time.h>
552+#include <gtk/gtk.h>
553+#include <libgnomevfs/gnome-vfs.h>
554+#include <libgnomevfs/gnome-vfs-mime-utils.h>
555+#include <gconf/gconf-client.h>
556+#include "egg-recent-model-multi.h"
557+#include "egg-recent-item.h"
558+
559+#define EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH ".recently-used"
560+#define EGG_RECENT_MODEL_MULTI_BUFFER_SIZE 8192
561+
562+#define EGG_RECENT_MODEL_MULTI_MAX_ITEMS 500
563+#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT 10
564+#define EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH 200
565+#define EGG_RECENT_MODEL_MULTI_POLL_TIME 3
566+
567+/* needed for Darwin */
568+#if !HAVE_DECL_LOCKF
569+int lockf (int filedes, int function, off_t size);
570+#endif
571+
572+#define EGG_RECENT_MODEL_MULTI_KEY_DIR "/desktop/gnome/recent_files"
573+#define EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/default_limit"
574+#define EGG_RECENT_MODEL_MULTI_EXPIRE_KEY EGG_RECENT_MODEL_MULTI_KEY_DIR "/expire"
575+
576+struct _EggRecentModelMultiPrivate {
577+ GSList *mime_filter_values; /* list of mime types we allow */
578+ GSList *group_filter_values; /* list of groups we allow */
579+ GSList *scheme_filter_values; /* list of URI schemes we allow */
580+
581+ EggRecentModelMultiSort sort_type; /* type of sorting to be done */
582+
583+ int limit; /* soft limit for length of the list */
584+ int expire_days; /* number of days to hold an item */
585+
586+ char *path; /* path to the file we store stuff in */
587+
588+ GHashTable *monitors;
589+
590+ GnomeVFSMonitorHandle *monitor;
591+
592+ GConfClient *client;
593+ gboolean use_default_limit;
594+
595+ guint limit_change_notify_id;
596+ guint expiration_change_notify_id;
597+
598+ guint changed_timeout;
599+ guint poll_timeout;
600+ time_t last_mtime;
601+};
602+
603+/* signals */
604+enum {
605+ CHANGED,
606+ LAST_SIGNAL
607+};
608+
609+static GType model_signals[LAST_SIGNAL] = { 0 };
610+
611+/* properties */
612+enum {
613+ PROP_BOGUS,
614+ PROP_MIME_FILTERS,
615+ PROP_GROUP_FILTERS,
616+ PROP_SCHEME_FILTERS,
617+ PROP_SORT_TYPE,
618+ PROP_LIMIT,
619+ PROP_FILE_PATH
620+};
621+
622+typedef struct {
623+ GSList *states;
624+ GList *items;
625+ EggRecentItem *current_item;
626+} ParseInfo;
627+
628+typedef enum {
629+ STATE_START,
630+ STATE_RECENT_FILES,
631+ STATE_RECENT_ITEM,
632+ STATE_URI,
633+ STATE_MIME_TYPE,
634+ STATE_TIMESTAMP,
635+ STATE_PRIVATE,
636+ STATE_GROUPS,
637+ STATE_GROUP
638+} ParseState;
639+
640+typedef struct {
641+ EggRecentModelMulti *model;
642+ GList *list;
643+} ChangedData;
644+
645+#define TAG_RECENT_FILES "RecentFiles"
646+#define TAG_RECENT_ITEM "RecentItem"
647+#define TAG_URI "URI"
648+#define TAG_MIME_TYPE "Mime-Type"
649+#define TAG_TIMESTAMP "Timestamp"
650+#define TAG_PRIVATE "Private"
651+#define TAG_GROUPS "Groups"
652+#define TAG_GROUP "Group"
653+
654+static void start_element_handler (GMarkupParseContext *context,
655+ const gchar *element_name,
656+ const gchar **attribute_names,
657+ const gchar **attribute_values,
658+ gpointer user_data,
659+ GError **error);
660+
661+static void end_element_handler (GMarkupParseContext *context,
662+ const gchar *element_name,
663+ gpointer user_data,
664+ GError **error);
665+
666+static void text_handler (GMarkupParseContext *context,
667+ const gchar *text,
668+ gsize text_len,
669+ gpointer user_data,
670+ GError **error);
671+
672+static void error_handler (GMarkupParseContext *context,
673+ GError *error,
674+ gpointer user_data);
675+
676+static GMarkupParser parser = {start_element_handler, end_element_handler,
677+ text_handler,
678+ NULL,
679+ error_handler};
680+
681+static GObjectClass *parent_class;
682+
683+static void egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model);
684+static void egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model);
685+static void egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model);
686+
687+static GObjectClass *parent_class;
688+
689+static gboolean
690+egg_recent_model_multi_string_match (const GSList *list, const gchar *str)
691+{
692+ const GSList *tmp;
693+
694+ if (list == NULL || str == NULL)
695+ return TRUE;
696+
697+ tmp = list;
698+
699+ while (tmp) {
700+ if (g_pattern_match_string (tmp->data, str))
701+ return TRUE;
702+
703+ tmp = tmp->next;
704+ }
705+
706+ return FALSE;
707+}
708+
709+static gboolean
710+egg_recent_model_multi_write_raw (EggRecentModelMulti *model, FILE *file,
711+ const gchar *content)
712+{
713+ int len;
714+ int fd;
715+ struct stat sbuf;
716+
717+ rewind (file);
718+
719+ len = strlen (content);
720+ fd = fileno (file);
721+
722+ if (fstat (fd, &sbuf) < 0)
723+ g_warning ("Couldn't stat XML document.");
724+
725+ if ((off_t)len < sbuf.st_size) {
726+ ftruncate (fd, len);
727+ }
728+
729+ if (fputs (content, file) == EOF)
730+ return FALSE;
731+
732+#ifndef G_OS_WIN32
733+ fsync (fd);
734+#endif
735+ rewind (file);
736+
737+ return TRUE;
738+}
739+
740+static GList *
741+egg_recent_model_multi_delete_from_list (GList *list,
742+ const gchar *uri)
743+{
744+ GList *tmp;
745+
746+ if (!uri)
747+ return list;
748+
749+ tmp = list;
750+
751+ while (tmp) {
752+ EggRecentItem *item = tmp->data;
753+ GList *next;
754+
755+ next = tmp->next;
756+
757+ if (!strcmp (egg_recent_item_peek_uri (item), uri)) {
758+ egg_recent_item_unref (item);
759+
760+ list = g_list_remove_link (list, tmp);
761+ g_list_free_1 (tmp);
762+ }
763+
764+ tmp = next;
765+ }
766+
767+ return list;
768+}
769+
770+static void
771+egg_recent_model_multi_add_new_groups (EggRecentItem *item,
772+ EggRecentItem *upd_item)
773+{
774+ const GList *tmp;
775+
776+ tmp = egg_recent_item_get_groups (upd_item);
777+
778+ while (tmp) {
779+ char *group = tmp->data;
780+
781+ if (!egg_recent_item_in_group (item, group))
782+ egg_recent_item_add_group (item, group);
783+
784+ tmp = tmp->next;
785+ }
786+}
787+
788+static gboolean
789+egg_recent_model_multi_update_item (GList *items, EggRecentItem *upd_item)
790+{
791+ GList *tmp;
792+ const char *uri;
793+
794+ uri = egg_recent_item_peek_uri (upd_item);
795+
796+ tmp = items;
797+
798+ while (tmp) {
799+ EggRecentItem *item = tmp->data;
800+
801+ if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) {
802+ egg_recent_item_set_timestamp (item, (time_t) -1);
803+
804+ egg_recent_model_multi_add_new_groups (item, upd_item);
805+
806+ return TRUE;
807+ }
808+
809+ tmp = tmp->next;
810+ }
811+
812+ return FALSE;
813+}
814+
815+static gchar *
816+egg_recent_model_multi_read_raw (EggRecentModelMulti *model, FILE *file)
817+{
818+ GString *string;
819+ char buf[EGG_RECENT_MODEL_MULTI_BUFFER_SIZE];
820+
821+ rewind (file);
822+
823+ string = g_string_new (NULL);
824+ while (fgets (buf, EGG_RECENT_MODEL_MULTI_BUFFER_SIZE, file)) {
825+ string = g_string_append (string, buf);
826+ }
827+
828+ rewind (file);
829+
830+ return g_string_free (string, FALSE);
831+}
832+
833+
834+
835+static ParseInfo *
836+parse_info_init (void)
837+{
838+ ParseInfo *retval;
839+
840+ retval = g_new0 (ParseInfo, 1);
841+ retval->states = g_slist_prepend (NULL, STATE_START);
842+ retval->items = NULL;
843+
844+ return retval;
845+}
846+
847+static void
848+parse_info_free (ParseInfo *info)
849+{
850+ g_slist_free (info->states);
851+ g_free (info);
852+}
853+
854+static void
855+push_state (ParseInfo *info,
856+ ParseState state)
857+{
858+ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
859+}
860+
861+static void
862+pop_state (ParseInfo *info)
863+{
864+ g_return_if_fail (info->states != NULL);
865+
866+ info->states = g_slist_remove (info->states, info->states->data);
867+}
868+
869+static ParseState
870+peek_state (ParseInfo *info)
871+{
872+ g_return_val_if_fail (info->states != NULL, STATE_START);
873+
874+ return GPOINTER_TO_INT (info->states->data);
875+}
876+
877+#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
878+
879+static gboolean
880+valid_element (ParseInfo *info,
881+ int valid_parent_state,
882+ const gchar *element_name,
883+ const gchar *valid_element,
884+ GError **error)
885+{
886+ if (peek_state (info) != valid_parent_state) {
887+ g_set_error (error,
888+ G_MARKUP_ERROR,
889+ G_MARKUP_ERROR_INVALID_CONTENT,
890+ "Unexpected tag '%s', tag '%s' expected",
891+ element_name, valid_element);
892+ return FALSE;
893+ }
894+
895+ return TRUE;
896+}
897+
898+static void
899+start_element_handler (GMarkupParseContext *context,
900+ const gchar *element_name,
901+ const gchar **attribute_names,
902+ const gchar **attribute_values,
903+ gpointer user_data,
904+ GError **error)
905+{
906+ ParseInfo *info = (ParseInfo *)user_data;
907+
908+ if (ELEMENT_IS (TAG_RECENT_FILES))
909+ push_state (info, STATE_RECENT_FILES);
910+ else if (ELEMENT_IS (TAG_RECENT_ITEM)) {
911+ if (valid_element (info, STATE_RECENT_FILES,
912+ TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) {
913+ info->current_item = egg_recent_item_new ();
914+ push_state (info, STATE_RECENT_ITEM);
915+ }
916+ } else if (ELEMENT_IS (TAG_URI)) {
917+ if (valid_element (info, STATE_RECENT_ITEM,
918+ TAG_URI, TAG_RECENT_ITEM, error)) {
919+ push_state (info, STATE_URI);
920+ }
921+ } else if (ELEMENT_IS (TAG_MIME_TYPE)) {
922+ if (valid_element (info, STATE_RECENT_ITEM,
923+ TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) {
924+ push_state (info, STATE_MIME_TYPE);
925+ }
926+ } else if (ELEMENT_IS (TAG_TIMESTAMP)) {
927+ if (valid_element (info, STATE_RECENT_ITEM,
928+ TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) {
929+ push_state (info, STATE_TIMESTAMP);
930+ }
931+ } else if (ELEMENT_IS (TAG_PRIVATE)) {
932+ if (valid_element (info, STATE_RECENT_ITEM,
933+ TAG_PRIVATE, TAG_RECENT_ITEM, error)) {
934+ push_state (info, STATE_PRIVATE);
935+ egg_recent_item_set_private (info->current_item, TRUE);
936+ }
937+ } else if (ELEMENT_IS (TAG_GROUPS)) {
938+ if (valid_element (info, STATE_RECENT_ITEM,
939+ TAG_GROUPS, TAG_RECENT_ITEM, error)) {
940+ push_state (info, STATE_GROUPS);
941+ }
942+ } else if (ELEMENT_IS (TAG_GROUP)) {
943+ if (valid_element (info, STATE_GROUPS,
944+ TAG_GROUP, TAG_GROUPS, error)) {
945+ push_state (info, STATE_GROUP);
946+ }
947+ }
948+}
949+
950+static gint
951+list_compare_func_mru (gpointer a, gpointer b)
952+{
953+ EggRecentItem *item_a = (EggRecentItem *)a;
954+ EggRecentItem *item_b = (EggRecentItem *)b;
955+
956+ return item_a->timestamp < item_b->timestamp;
957+}
958+
959+static gint
960+list_compare_func_lru (gpointer a, gpointer b)
961+{
962+ EggRecentItem *item_a = (EggRecentItem *)a;
963+ EggRecentItem *item_b = (EggRecentItem *)b;
964+
965+ return item_a->timestamp > item_b->timestamp;
966+}
967+
968+
969+
970+static void
971+end_element_handler (GMarkupParseContext *context,
972+ const gchar *element_name,
973+ gpointer user_data,
974+ GError **error)
975+{
976+ ParseInfo *info = (ParseInfo *)user_data;
977+
978+ switch (peek_state (info)) {
979+ case STATE_RECENT_ITEM:
980+ if (!info->current_item) {
981+ g_warning ("No recent item found\n");
982+ break;
983+ }
984+
985+ if (!info->current_item->uri) {
986+ g_warning ("Invalid item found\n");
987+ break;
988+ }
989+
990+ info->items = g_list_prepend (info->items,
991+ info->current_item);
992+ info->current_item = NULL;
993+ break;
994+ default:
995+ break;
996+ }
997+
998+ pop_state (info);
999+}
1000+
1001+static void
1002+text_handler (GMarkupParseContext *context,
1003+ const gchar *text,
1004+ gsize text_len,
1005+ gpointer user_data,
1006+ GError **error)
1007+{
1008+ ParseInfo *info = (ParseInfo *)user_data;
1009+ gchar *value;
1010+
1011+ value = g_strndup (text, text_len);
1012+
1013+ switch (peek_state (info)) {
1014+ case STATE_START:
1015+ case STATE_RECENT_FILES:
1016+ case STATE_RECENT_ITEM:
1017+ case STATE_PRIVATE:
1018+ case STATE_GROUPS:
1019+ break;
1020+ case STATE_URI:
1021+ egg_recent_item_set_uri (info->current_item, value);
1022+ break;
1023+ case STATE_MIME_TYPE:
1024+ egg_recent_item_set_mime_type (info->current_item, value);
1025+ break;
1026+ case STATE_TIMESTAMP:
1027+ egg_recent_item_set_timestamp (info->current_item,
1028+ (time_t)atoi (value));
1029+ break;
1030+ case STATE_GROUP:
1031+ egg_recent_item_add_group (info->current_item,
1032+ text);
1033+ break;
1034+ }
1035+
1036+ g_free (value);
1037+}
1038+
1039+static void
1040+error_handler (GMarkupParseContext *context,
1041+ GError *error,
1042+ gpointer user_data)
1043+{
1044+ g_warning ("Error in parse: %s", error->message);
1045+}
1046+
1047+static void
1048+egg_recent_model_multi_enforce_limit (GList *list, int limit)
1049+{
1050+ int len;
1051+ GList *end;
1052+
1053+ /* limit < 0 means unlimited */
1054+ if (limit <= 0)
1055+ return;
1056+
1057+ len = g_list_length (list);
1058+
1059+ if (len > limit) {
1060+ GList *next;
1061+
1062+ end = g_list_nth (list, limit-1);
1063+ next = end->next;
1064+
1065+ end->next = NULL;
1066+
1067+ EGG_RECENT_ITEM_LIST_UNREF (next);
1068+ }
1069+}
1070+
1071+static GList *
1072+egg_recent_model_multi_sort (EggRecentModelMulti *model, GList *list)
1073+{
1074+ switch (model->priv->sort_type) {
1075+ case EGG_RECENT_MODEL_MULTI_SORT_MRU:
1076+ list = g_list_sort (list,
1077+ (GCompareFunc)list_compare_func_mru);
1078+ break;
1079+ case EGG_RECENT_MODEL_MULTI_SORT_LRU:
1080+ list = g_list_sort (list,
1081+ (GCompareFunc)list_compare_func_lru);
1082+ break;
1083+ case EGG_RECENT_MODEL_MULTI_SORT_NONE:
1084+ break;
1085+ }
1086+
1087+ return list;
1088+}
1089+
1090+static gboolean
1091+egg_recent_model_multi_group_match (EggRecentItem *item, GSList *groups)
1092+{
1093+ GSList *tmp;
1094+
1095+ tmp = groups;
1096+
1097+ while (tmp != NULL) {
1098+ const gchar * group = (const gchar *)tmp->data;
1099+
1100+ if (egg_recent_item_in_group (item, group))
1101+ return TRUE;
1102+
1103+ tmp = tmp->next;
1104+ }
1105+
1106+ return FALSE;
1107+}
1108+
1109+static GList *
1110+egg_recent_model_multi_filter (EggRecentModelMulti *model, GList *list)
1111+{
1112+ GList *newlist = NULL;
1113+ GList *l;
1114+ gchar *mime_type;
1115+ gchar *uri;
1116+
1117+ g_return_val_if_fail (list != NULL, NULL);
1118+
1119+ for (l = list; l != NULL ; l = l->next) {
1120+ EggRecentItem *item = (EggRecentItem *) l->data;
1121+ gboolean pass_mime_test = FALSE;
1122+ gboolean pass_group_test = FALSE;
1123+ gboolean pass_scheme_test = FALSE;
1124+
1125+ g_assert (item != NULL);
1126+
1127+ uri = egg_recent_item_get_uri (item);
1128+
1129+ /* filter by mime type */
1130+ if (model->priv->mime_filter_values != NULL) {
1131+ mime_type = egg_recent_item_get_mime_type (item);
1132+
1133+ if (egg_recent_model_multi_string_match
1134+ (model->priv->mime_filter_values,
1135+ mime_type))
1136+ pass_mime_test = TRUE;
1137+
1138+ g_free (mime_type);
1139+ } else
1140+ pass_mime_test = TRUE;
1141+
1142+ /* filter by group */
1143+ if (pass_mime_test && model->priv->group_filter_values != NULL) {
1144+ if (egg_recent_model_multi_group_match
1145+ (item, model->priv->group_filter_values))
1146+ pass_group_test = TRUE;
1147+ } else if (egg_recent_item_get_private (item)) {
1148+ pass_group_test = FALSE;
1149+ } else
1150+ pass_group_test = TRUE;
1151+
1152+ /* filter by URI scheme */
1153+ if (pass_mime_test && pass_group_test &&
1154+ model->priv->scheme_filter_values != NULL) {
1155+ gchar *scheme;
1156+
1157+ scheme = gnome_vfs_get_uri_scheme (uri);
1158+
1159+ if (egg_recent_model_multi_string_match
1160+ (model->priv->scheme_filter_values, scheme))
1161+ pass_scheme_test = TRUE;
1162+
1163+ g_free (scheme);
1164+ } else
1165+ pass_scheme_test = TRUE;
1166+
1167+ if (pass_mime_test && pass_group_test && pass_scheme_test)
1168+ newlist = g_list_prepend (newlist, item);
1169+ else
1170+ egg_recent_item_unref (item);
1171+
1172+ g_free (uri);
1173+ }
1174+
1175+ g_list_free (list);
1176+
1177+ return g_list_reverse (newlist);
1178+}
1179+
1180+
1181+
1182+#if 0
1183+static void
1184+egg_recent_model_multi_monitor_list_cb (GnomeVFSMonitorHandle *handle,
1185+ const gchar *monitor_uri,
1186+ const gchar *info_uri,
1187+ GnomeVFSMonitorEventType event_type,
1188+ gpointer user_data)
1189+{
1190+ EggRecentModelMulti *model;
1191+
1192+ model = EGG_RECENT_MODEL_MULTI (user_data);
1193+
1194+ if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
1195+ egg_recent_model_multi_delete (model, monitor_uri);
1196+ g_hash_table_remove (model->priv->monitors, monitor_uri);
1197+ }
1198+}
1199+
1200+
1201+
1202+static void
1203+egg_recent_model_multi_monitor_list (EggRecentModelMulti *model, GList *list)
1204+{
1205+ GList *tmp;
1206+
1207+ tmp = list;
1208+ while (tmp) {
1209+ EggRecentItem *item = (EggRecentItem *)tmp->data;
1210+ GnomeVFSMonitorHandle *handle;
1211+ GnomeVFSResult res;
1212+ gchar *uri;
1213+
1214+ tmp = tmp->next;
1215+
1216+ uri = egg_recent_item_get_uri (item);
1217+ if (g_hash_table_lookup (model->priv->monitors, uri)) {
1218+ /* already monitoring this one */
1219+ g_free (uri);
1220+ continue;
1221+ }
1222+
1223+ res = gnome_vfs_monitor_add (&handle, uri,
1224+ GNOME_VFS_MONITOR_FILE,
1225+ egg_recent_model_multi_monitor_list_cb,
1226+ model);
1227+
1228+ if (res == GNOME_VFS_OK)
1229+ g_hash_table_insert (model->priv->monitors, uri, handle);
1230+ else
1231+ g_free (uri);
1232+ }
1233+}
1234+#endif
1235+
1236+
1237+static gboolean
1238+egg_recent_model_multi_changed_timeout (EggRecentModelMulti *model)
1239+{
1240+ model->priv->changed_timeout = 0;
1241+
1242+ egg_recent_model_multi_changed (model);
1243+
1244+ return FALSE;
1245+}
1246+
1247+static void
1248+egg_recent_model_multi_monitor_cb (GnomeVFSMonitorHandle *handle,
1249+ const gchar *monitor_uri,
1250+ const gchar *info_uri,
1251+ GnomeVFSMonitorEventType event_type,
1252+ gpointer user_data)
1253+{
1254+ EggRecentModelMulti *model;
1255+
1256+ g_return_if_fail (user_data != NULL);
1257+ g_return_if_fail (EGG_IS_RECENT_MODEL_MULTI (user_data));
1258+ model = EGG_RECENT_MODEL_MULTI (user_data);
1259+
1260+ if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED ||
1261+ event_type == GNOME_VFS_MONITOR_EVENT_CREATED ||
1262+ event_type == GNOME_VFS_MONITOR_EVENT_DELETED) {
1263+ if (model->priv->changed_timeout > 0) {
1264+ g_source_remove (model->priv->changed_timeout);
1265+ }
1266+
1267+ model->priv->changed_timeout = g_timeout_add (
1268+ EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH,
1269+ (GSourceFunc)egg_recent_model_multi_changed_timeout,
1270+ model);
1271+ }
1272+}
1273+
1274+static gboolean
1275+egg_recent_model_multi_poll_timeout (gpointer user_data)
1276+{
1277+ EggRecentModelMulti *model;
1278+ struct stat stat_buf;
1279+ int stat_res;
1280+
1281+ model = EGG_RECENT_MODEL_MULTI (user_data);
1282+ stat_res = stat (model->priv->path, &stat_buf);
1283+
1284+ if (!stat_res && stat_buf.st_mtime &&
1285+ stat_buf.st_mtime != model->priv->last_mtime) {
1286+ model->priv->last_mtime = stat_buf.st_mtime;
1287+
1288+ if (model->priv->changed_timeout > 0)
1289+ g_source_remove (model->priv->changed_timeout);
1290+
1291+ model->priv->changed_timeout = g_timeout_add (
1292+ EGG_RECENT_MODEL_MULTI_TIMEOUT_LENGTH,
1293+ (GSourceFunc)egg_recent_model_multi_changed_timeout,
1294+ model);
1295+ }
1296+ return TRUE;
1297+}
1298+
1299+static void
1300+egg_recent_model_multi_monitor (EggRecentModelMulti *model, gboolean should_monitor)
1301+{
1302+ if (should_monitor && model->priv->monitor == NULL) {
1303+ char *uri;
1304+ GnomeVFSResult result;
1305+
1306+ uri = gnome_vfs_get_uri_from_local_path (model->priv->path);
1307+
1308+ result = gnome_vfs_monitor_add (&model->priv->monitor,
1309+ uri,
1310+ GNOME_VFS_MONITOR_FILE,
1311+ egg_recent_model_multi_monitor_cb,
1312+ model);
1313+
1314+ g_free (uri);
1315+
1316+ /* if the above fails, don't worry about it.
1317+ * local notifications will still happen
1318+ */
1319+ if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
1320+ if (model->priv->poll_timeout > 0)
1321+ g_source_remove (model->priv->poll_timeout);
1322+
1323+ model->priv->poll_timeout = g_timeout_add (
1324+ EGG_RECENT_MODEL_MULTI_POLL_TIME * 1000,
1325+ egg_recent_model_multi_poll_timeout,
1326+ model);
1327+ }
1328+
1329+ } else if (!should_monitor && model->priv->monitor != NULL) {
1330+ gnome_vfs_monitor_cancel (model->priv->monitor);
1331+ model->priv->monitor = NULL;
1332+ }
1333+}
1334+
1335+static void
1336+egg_recent_model_multi_set_limit_internal (EggRecentModelMulti *model, int limit)
1337+{
1338+ model->priv->limit = limit;
1339+
1340+ if (limit <= 0)
1341+ egg_recent_model_multi_monitor (model, FALSE);
1342+ else {
1343+ egg_recent_model_multi_monitor (model, TRUE);
1344+ egg_recent_model_multi_changed (model);
1345+ }
1346+}
1347+
1348+static GList *
1349+egg_recent_model_multi_read (EggRecentModelMulti *model, FILE *file)
1350+{
1351+ GList *list=NULL;
1352+ gchar *content;
1353+ GMarkupParseContext *ctx;
1354+ ParseInfo *info;
1355+ GError *error;
1356+
1357+ content = egg_recent_model_multi_read_raw (model, file);
1358+
1359+ if (strlen (content) <= 0) {
1360+ g_free (content);
1361+ return NULL;
1362+ }
1363+
1364+ info = parse_info_init ();
1365+
1366+ ctx = g_markup_parse_context_new (&parser, 0, info, NULL);
1367+
1368+ error = NULL;
1369+ if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) {
1370+ g_warning ("Error while parsing the .recently-used file: %s\n",
1371+ error->message);
1372+
1373+ g_error_free (error);
1374+ parse_info_free (info);
1375+
1376+ return NULL;
1377+ }
1378+
1379+ error = NULL;
1380+ if (!g_markup_parse_context_end_parse (ctx, &error)) {
1381+ g_warning ("Unable to complete parsing of the .recently-used file: %s\n",
1382+ error->message);
1383+
1384+ g_error_free (error);
1385+ g_markup_parse_context_free (ctx);
1386+ parse_info_free (info);
1387+
1388+ return NULL;
1389+ }
1390+
1391+ list = g_list_reverse (info->items);
1392+
1393+ g_markup_parse_context_free (ctx);
1394+ parse_info_free (info);
1395+ g_free (content);
1396+
1397+ return list;
1398+}
1399+
1400+
1401+static gboolean
1402+egg_recent_model_multi_write (EggRecentModelMulti *model, FILE *file, GList *list)
1403+{
1404+ GString *string;
1405+ gchar *data;
1406+ EggRecentItem *item;
1407+ const GList *groups;
1408+ int i;
1409+ int ret;
1410+
1411+ string = g_string_new ("<?xml version=\"1.0\"?>\n");
1412+ string = g_string_append (string, "<" TAG_RECENT_FILES ">\n");
1413+
1414+ i=0;
1415+ while (list) {
1416+ gchar *uri;
1417+ gchar *mime_type;
1418+ gchar *escaped_uri;
1419+ time_t timestamp;
1420+ item = (EggRecentItem *)list->data;
1421+
1422+
1423+ uri = egg_recent_item_get_uri_utf8 (item);
1424+ escaped_uri = g_markup_escape_text (uri,
1425+ strlen (uri));
1426+ g_free (uri);
1427+
1428+ mime_type = egg_recent_item_get_mime_type (item);
1429+ timestamp = egg_recent_item_get_timestamp (item);
1430+
1431+ string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n");
1432+
1433+ g_string_append_printf (string,
1434+ " <" TAG_URI ">%s</" TAG_URI ">\n", escaped_uri);
1435+
1436+ if (mime_type)
1437+ g_string_append_printf (string,
1438+ " <" TAG_MIME_TYPE ">%s</" TAG_MIME_TYPE ">\n", mime_type);
1439+ else
1440+ g_string_append_printf (string,
1441+ " <" TAG_MIME_TYPE "></" TAG_MIME_TYPE ">\n");
1442+
1443+
1444+ g_string_append_printf (string,
1445+ " <" TAG_TIMESTAMP ">%d</" TAG_TIMESTAMP ">\n", (int)timestamp);
1446+
1447+ if (egg_recent_item_get_private (item))
1448+ string = g_string_append (string,
1449+ " <" TAG_PRIVATE "/>\n");
1450+
1451+ /* write the groups */
1452+ string = g_string_append (string,
1453+ " <" TAG_GROUPS ">\n");
1454+ groups = egg_recent_item_get_groups (item);
1455+
1456+ if (groups == NULL && egg_recent_item_get_private (item))
1457+ g_warning ("Item with URI \"%s\" marked as private, but"
1458+ " does not belong to any groups.\n", uri);
1459+
1460+ while (groups) {
1461+ const gchar *group = (const gchar *)groups->data;
1462+ gchar *escaped_group;
1463+
1464+ escaped_group = g_markup_escape_text (group, strlen(group));
1465+
1466+ g_string_append_printf (string,
1467+ " <" TAG_GROUP ">%s</" TAG_GROUP ">\n",
1468+ escaped_group);
1469+
1470+ g_free (escaped_group);
1471+
1472+ groups = groups->next;
1473+ }
1474+
1475+ string = g_string_append (string, " </" TAG_GROUPS ">\n");
1476+
1477+ string = g_string_append (string,
1478+ " </" TAG_RECENT_ITEM ">\n");
1479+
1480+ g_free (mime_type);
1481+ g_free (escaped_uri);
1482+
1483+ list = list->next;
1484+ i++;
1485+ }
1486+
1487+ string = g_string_append (string, "</" TAG_RECENT_FILES ">");
1488+
1489+ data = g_string_free (string, FALSE);
1490+
1491+ ret = egg_recent_model_multi_write_raw (model, file, data);
1492+
1493+ g_free (data);
1494+
1495+ return ret;
1496+}
1497+
1498+static FILE *
1499+egg_recent_model_multi_open_file (EggRecentModelMulti *model,
1500+ gboolean for_writing)
1501+{
1502+ FILE *file;
1503+ mode_t prev_umask;
1504+
1505+ file = fopen (model->priv->path, "r+");
1506+ if (file == NULL && for_writing) {
1507+ /* be paranoid */
1508+ prev_umask = umask (077);
1509+
1510+ file = fopen (model->priv->path, "w+");
1511+
1512+ umask (prev_umask);
1513+
1514+ g_return_val_if_fail (file != NULL, NULL);
1515+ }
1516+
1517+ return file;
1518+}
1519+
1520+static gboolean
1521+egg_recent_model_multi_lock_file (FILE *file)
1522+{
1523+#ifdef HAVE_LOCKF
1524+ int fd;
1525+ gint try = 5;
1526+
1527+ rewind (file);
1528+ fd = fileno (file);
1529+
1530+ /* Attempt to lock the file 5 times,
1531+ * waiting a random interval (< 1 second)
1532+ * in between attempts.
1533+ * We should really be doing asynchronous
1534+ * locking, but requires substantially larger
1535+ * changes.
1536+ */
1537+
1538+ while (try > 0)
1539+ {
1540+ int rand_interval;
1541+
1542+ if (lockf (fd, F_TLOCK, 0) == 0)
1543+ return TRUE;
1544+
1545+ rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0));
1546+
1547+ g_usleep (100000 * rand_interval);
1548+
1549+ --try;
1550+ }
1551+
1552+ return FALSE;
1553+#else
1554+ return TRUE;
1555+#endif /* HAVE_LOCKF */
1556+}
1557+
1558+static gboolean
1559+egg_recent_model_multi_unlock_file (FILE *file)
1560+{
1561+#ifdef HAVE_LOCKF
1562+ int fd;
1563+
1564+ rewind (file);
1565+ fd = fileno (file);
1566+
1567+ return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE;
1568+#else
1569+ return TRUE;
1570+#endif /* HAVE_LOCKF */
1571+}
1572+
1573+static void
1574+egg_recent_model_multi_finalize (GObject *object)
1575+{
1576+ EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1577+
1578+ if (model->priv->changed_timeout > 0) {
1579+ g_source_remove (model->priv->changed_timeout);
1580+ }
1581+
1582+ egg_recent_model_multi_monitor (model, FALSE);
1583+
1584+
1585+ g_slist_foreach (model->priv->mime_filter_values,
1586+ (GFunc) g_pattern_spec_free, NULL);
1587+ g_slist_free (model->priv->mime_filter_values);
1588+ model->priv->mime_filter_values = NULL;
1589+
1590+ g_slist_foreach (model->priv->scheme_filter_values,
1591+ (GFunc) g_pattern_spec_free, NULL);
1592+ g_slist_free (model->priv->scheme_filter_values);
1593+ model->priv->scheme_filter_values = NULL;
1594+
1595+ g_slist_foreach (model->priv->group_filter_values,
1596+ (GFunc) g_free, NULL);
1597+ g_slist_free (model->priv->group_filter_values);
1598+ model->priv->group_filter_values = NULL;
1599+
1600+
1601+ if (model->priv->limit_change_notify_id)
1602+ gconf_client_notify_remove (model->priv->client,
1603+ model->priv->limit_change_notify_id);
1604+ model->priv->expiration_change_notify_id = 0;
1605+
1606+ if (model->priv->expiration_change_notify_id)
1607+ gconf_client_notify_remove (model->priv->client,
1608+ model->priv->expiration_change_notify_id);
1609+ model->priv->expiration_change_notify_id = 0;
1610+
1611+ g_object_unref (model->priv->client);
1612+ model->priv->client = NULL;
1613+
1614+
1615+ g_free (model->priv->path);
1616+ model->priv->path = NULL;
1617+
1618+ g_hash_table_destroy (model->priv->monitors);
1619+ model->priv->monitors = NULL;
1620+
1621+ if (model->priv->poll_timeout > 0)
1622+ g_source_remove (model->priv->poll_timeout);
1623+ model->priv->poll_timeout =0;
1624+
1625+ g_free (model->priv);
1626+
1627+ parent_class->finalize (object);
1628+}
1629+
1630+static void
1631+egg_recent_model_multi_set_property (GObject *object,
1632+ guint prop_id,
1633+ const GValue *value,
1634+ GParamSpec *pspec)
1635+{
1636+ EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1637+
1638+ switch (prop_id)
1639+ {
1640+ case PROP_MIME_FILTERS:
1641+ if (model->priv->mime_filter_values != NULL)
1642+ egg_recent_model_multi_clear_mime_filter (model);
1643+
1644+ model->priv->mime_filter_values =
1645+ (GSList *)g_value_get_pointer (value);
1646+ break;
1647+
1648+ case PROP_GROUP_FILTERS:
1649+ if (model->priv->group_filter_values != NULL)
1650+ egg_recent_model_multi_clear_group_filter (model);
1651+
1652+ model->priv->group_filter_values =
1653+ (GSList *)g_value_get_pointer (value);
1654+ break;
1655+
1656+ case PROP_SCHEME_FILTERS:
1657+ if (model->priv->scheme_filter_values != NULL)
1658+ egg_recent_model_multi_clear_scheme_filter (model);
1659+
1660+ model->priv->scheme_filter_values =
1661+ (GSList *)g_value_get_pointer (value);
1662+ break;
1663+
1664+ case PROP_SORT_TYPE:
1665+ model->priv->sort_type = g_value_get_int (value);
1666+ break;
1667+
1668+ case PROP_FILE_PATH:
1669+ model->priv->path = g_strdup_printf (
1670+ "%s/%s",
1671+ g_get_home_dir (),
1672+ g_value_get_string (value)
1673+ );
1674+ egg_recent_model_multi_monitor (model, TRUE);
1675+ break;
1676+
1677+ case PROP_LIMIT:
1678+ egg_recent_model_multi_set_limit (model,
1679+ g_value_get_int (value));
1680+ break;
1681+
1682+ default:
1683+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1684+ break;
1685+ }
1686+}
1687+
1688+static void
1689+egg_recent_model_multi_get_property (GObject *object,
1690+ guint prop_id,
1691+ GValue *value,
1692+ GParamSpec *pspec)
1693+{
1694+ EggRecentModelMulti *model = EGG_RECENT_MODEL_MULTI (object);
1695+
1696+ switch (prop_id)
1697+ {
1698+ case PROP_MIME_FILTERS:
1699+ g_value_set_pointer (value, model->priv->mime_filter_values);
1700+ break;
1701+
1702+ case PROP_GROUP_FILTERS:
1703+ g_value_set_pointer (value, model->priv->group_filter_values);
1704+ break;
1705+
1706+ case PROP_SCHEME_FILTERS:
1707+ g_value_set_pointer (value, model->priv->scheme_filter_values);
1708+ break;
1709+
1710+ case PROP_SORT_TYPE:
1711+ g_value_set_int (value, model->priv->sort_type);
1712+ break;
1713+
1714+ case PROP_FILE_PATH:
1715+ g_value_set_string (value, model->priv->path);
1716+ break;
1717+
1718+ case PROP_LIMIT:
1719+ g_value_set_int (value, model->priv->limit);
1720+ break;
1721+
1722+ default:
1723+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1724+ break;
1725+ }
1726+}
1727+
1728+static void
1729+egg_recent_model_multi_class_init (EggRecentModelMultiClass * klass)
1730+{
1731+ GObjectClass *object_class;
1732+
1733+ parent_class = g_type_class_peek_parent (klass);
1734+
1735+ parent_class = g_type_class_peek_parent (klass);
1736+
1737+ object_class = G_OBJECT_CLASS (klass);
1738+ object_class->set_property = egg_recent_model_multi_set_property;
1739+ object_class->get_property = egg_recent_model_multi_get_property;
1740+ object_class->finalize = egg_recent_model_multi_finalize;
1741+
1742+ model_signals[CHANGED] = g_signal_new ("changed",
1743+ G_OBJECT_CLASS_TYPE (object_class),
1744+ G_SIGNAL_RUN_LAST,
1745+ G_STRUCT_OFFSET (EggRecentModelMultiClass, changed),
1746+ NULL, NULL,
1747+ g_cclosure_marshal_VOID__POINTER,
1748+ G_TYPE_NONE, 1,
1749+ G_TYPE_POINTER);
1750+
1751+
1752+ g_object_class_install_property (object_class,
1753+ PROP_MIME_FILTERS,
1754+ g_param_spec_pointer ("mime-filters",
1755+ "Mime Filters",
1756+ "List of mime types to be allowed.",
1757+ G_PARAM_READWRITE));
1758+
1759+ g_object_class_install_property (object_class,
1760+ PROP_GROUP_FILTERS,
1761+ g_param_spec_pointer ("group-filters",
1762+ "Group Filters",
1763+ "List of groups to be allowed.",
1764+ G_PARAM_READWRITE));
1765+
1766+ g_object_class_install_property (object_class,
1767+ PROP_SCHEME_FILTERS,
1768+ g_param_spec_pointer ("scheme-filters",
1769+ "Scheme Filters",
1770+ "List of URI schemes to be allowed.",
1771+ G_PARAM_READWRITE));
1772+
1773+ g_object_class_install_property (object_class,
1774+ PROP_SORT_TYPE,
1775+ g_param_spec_int ("sort-type",
1776+ "Sort Type",
1777+ "Type of sorting to be done.",
1778+ 0, EGG_RECENT_MODEL_MULTI_SORT_NONE,
1779+ EGG_RECENT_MODEL_MULTI_SORT_MRU,
1780+ G_PARAM_READWRITE));
1781+
1782+ g_object_class_install_property (object_class,
1783+ PROP_FILE_PATH,
1784+ g_param_spec_string ("file-path",
1785+ "File Path",
1786+ "File path relative to $HOME.",
1787+ EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH,
1788+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1789+
1790+ g_object_class_install_property (object_class,
1791+ PROP_LIMIT,
1792+ g_param_spec_int ("limit",
1793+ "Limit",
1794+ "Max number of items allowed.",
1795+ -1, EGG_RECENT_MODEL_MULTI_MAX_ITEMS,
1796+ EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT,
1797+ G_PARAM_READWRITE));
1798+
1799+ klass->changed = NULL;
1800+}
1801+
1802+
1803+
1804+static void
1805+egg_recent_model_multi_limit_changed (GConfClient *client, guint cnxn_id,
1806+ GConfEntry *entry, gpointer user_data)
1807+{
1808+ EggRecentModelMulti *model;
1809+ GConfValue *value;
1810+
1811+ model = EGG_RECENT_MODEL_MULTI (user_data);
1812+
1813+ g_return_if_fail (model != NULL);
1814+
1815+ if (model->priv->use_default_limit == FALSE)
1816+ return; /* ignore this key */
1817+
1818+ /* the key was unset, and the schema has apparently failed */
1819+ if (entry == NULL)
1820+ return;
1821+
1822+ value = gconf_entry_get_value (entry);
1823+
1824+ if (value->type != GCONF_VALUE_INT) {
1825+ g_warning ("Expected GConfValue of type integer, "
1826+ "got something else");
1827+ }
1828+
1829+
1830+ egg_recent_model_multi_set_limit_internal (model, gconf_value_get_int (value));
1831+}
1832+
1833+static void
1834+egg_recent_model_multi_expiration_changed (GConfClient *client, guint cnxn_id,
1835+ GConfEntry *entry, gpointer user_data)
1836+{
1837+
1838+}
1839+
1840+static void
1841+egg_recent_model_multi_init (EggRecentModelMulti * model)
1842+{
1843+ if (!gnome_vfs_init ()) {
1844+ g_warning ("gnome-vfs initialization failed.");
1845+ return;
1846+ }
1847+
1848+
1849+ model->priv = g_new0 (EggRecentModelMultiPrivate, 1);
1850+
1851+ model->priv->mime_filter_values = NULL;
1852+ model->priv->group_filter_values = NULL;
1853+ model->priv->scheme_filter_values = NULL;
1854+
1855+ model->priv->client = gconf_client_get_default ();
1856+ gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_MULTI_KEY_DIR,
1857+ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
1858+
1859+ model->priv->limit_change_notify_id =
1860+ gconf_client_notify_add (model->priv->client,
1861+ EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY,
1862+ egg_recent_model_multi_limit_changed,
1863+ model, NULL, NULL);
1864+
1865+ model->priv->expiration_change_notify_id =
1866+ gconf_client_notify_add (model->priv->client,
1867+ EGG_RECENT_MODEL_MULTI_EXPIRE_KEY,
1868+ egg_recent_model_multi_expiration_changed,
1869+ model, NULL, NULL);
1870+
1871+ model->priv->expire_days = gconf_client_get_int (
1872+ model->priv->client,
1873+ EGG_RECENT_MODEL_MULTI_EXPIRE_KEY,
1874+ NULL);
1875+
1876+#if 0
1877+ /* keep this out, for now */
1878+ model->priv->limit = gconf_client_get_int (
1879+ model->priv->client,
1880+ EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT_KEY, NULL);
1881+ model->priv->use_default_limit = TRUE;
1882+#endif
1883+ model->priv->limit = EGG_RECENT_MODEL_MULTI_DEFAULT_LIMIT;
1884+ model->priv->use_default_limit = FALSE;
1885+
1886+ model->priv->monitors = g_hash_table_new_full (
1887+ g_str_hash, g_str_equal,
1888+ (GDestroyNotify) g_free,
1889+ (GDestroyNotify) gnome_vfs_monitor_cancel);
1890+
1891+ model->priv->monitor = NULL;
1892+ model->priv->poll_timeout = 0;
1893+ model->priv->last_mtime = 0;
1894+}
1895+
1896+
1897+/**
1898+ * egg_recent_model_multi_new:
1899+ * @sort: the type of sorting to use
1900+ * @limit: maximum number of items in the list
1901+ *
1902+ * This creates a new EggRecentModelMulti object.
1903+ *
1904+ * Returns: a EggRecentModelMulti object
1905+ */
1906+EggRecentModelMulti *
1907+egg_recent_model_multi_new (EggRecentModelMultiSort sort)
1908+{
1909+ return egg_recent_model_multi_new_with_file_path (sort, EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH);
1910+}
1911+
1912+EggRecentModelMulti *
1913+egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path)
1914+{
1915+ EggRecentModelMulti *model;
1916+
1917+ if (! file_path)
1918+ file_path = EGG_RECENT_MODEL_MULTI_DEFAULT_FILE_PATH;
1919+
1920+ model = EGG_RECENT_MODEL_MULTI (g_object_new (egg_recent_model_multi_get_type (),
1921+ "sort-type", sort, "file-path", file_path, NULL));
1922+
1923+ g_return_val_if_fail (model, NULL);
1924+
1925+ return model;
1926+}
1927+
1928+/**
1929+ * egg_recent_model_multi_add_full:
1930+ * @model: A EggRecentModelMulti object.
1931+ * @item: A EggRecentItem
1932+ *
1933+ * This function adds an item to the list of recently used URIs.
1934+ *
1935+ * Returns: gboolean
1936+ */
1937+gboolean
1938+egg_recent_model_multi_add_full (EggRecentModelMulti * model, EggRecentItem *item)
1939+{
1940+ FILE *file;
1941+ GList *list = NULL;
1942+ gboolean ret = FALSE;
1943+ gboolean updated = FALSE;
1944+ char *uri;
1945+ time_t t;
1946+
1947+ g_return_val_if_fail (model != NULL, FALSE);
1948+ g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE);
1949+
1950+ uri = egg_recent_item_get_uri (item);
1951+ if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) {
1952+ g_free (uri);
1953+ return FALSE;
1954+ } else {
1955+ g_free (uri);
1956+ }
1957+
1958+ file = egg_recent_model_multi_open_file (model, TRUE);
1959+ g_return_val_if_fail (file != NULL, FALSE);
1960+
1961+ time (&t);
1962+ egg_recent_item_set_timestamp (item, t);
1963+
1964+ if (egg_recent_model_multi_lock_file (file)) {
1965+
1966+ /* read existing stuff */
1967+ list = egg_recent_model_multi_read (model, file);
1968+
1969+ /* if it's already there, we just update it */
1970+ updated = egg_recent_model_multi_update_item (list, item);
1971+
1972+ if (!updated) {
1973+ list = g_list_prepend (list, item);
1974+
1975+ egg_recent_model_multi_enforce_limit (list,
1976+ EGG_RECENT_MODEL_MULTI_MAX_ITEMS);
1977+ }
1978+
1979+ /* write new stuff */
1980+ if (!egg_recent_model_multi_write (model, file, list))
1981+ g_warning ("Write failed: %s", strerror (errno));
1982+
1983+ if (!updated)
1984+ list = g_list_remove (list, item);
1985+
1986+ EGG_RECENT_ITEM_LIST_UNREF (list);
1987+ ret = TRUE;
1988+ } else {
1989+ g_warning ("Failed to lock: %s", strerror (errno));
1990+ fclose (file);
1991+ return FALSE;
1992+ }
1993+
1994+ if (!egg_recent_model_multi_unlock_file (file))
1995+ g_warning ("Failed to unlock: %s", strerror (errno));
1996+
1997+ fclose (file);
1998+
1999+ if (model->priv->monitor == NULL) {
2000+ /* since monitoring isn't working, at least give a
2001+ * local notification
2002+ */
2003+ egg_recent_model_multi_changed (model);
2004+ }
2005+
2006+ return ret;
2007+}
2008+
2009+/**
2010+ * egg_recent_model_multi_add:
2011+ * @model: A EggRecentModelMulti object.
2012+ * @uri: A string URI
2013+ *
2014+ * This function adds an item to the list of recently used URIs.
2015+ *
2016+ * Returns: gboolean
2017+ */
2018+gboolean
2019+egg_recent_model_multi_add (EggRecentModelMulti *model, const gchar *uri)
2020+{
2021+ EggRecentItem *item;
2022+ gboolean ret = FALSE;
2023+
2024+ g_return_val_if_fail (model != NULL, FALSE);
2025+ g_return_val_if_fail (uri != NULL, FALSE);
2026+
2027+ item = egg_recent_item_new_from_uri (uri);
2028+
2029+ g_return_val_if_fail (item != NULL, FALSE);
2030+
2031+ ret = egg_recent_model_multi_add_full (model, item);
2032+
2033+ egg_recent_item_unref (item);
2034+
2035+ return ret;
2036+}
2037+
2038+
2039+
2040+/**
2041+ * egg_recent_model_multi_delete:
2042+ * @model: A EggRecentModelMulti object.
2043+ * @uri: The URI you want to delete.
2044+ *
2045+ * This function deletes a URI from the file of recently used URIs.
2046+ *
2047+ * Returns: gboolean
2048+ */
2049+gboolean
2050+egg_recent_model_multi_delete (EggRecentModelMulti * model, const gchar * uri)
2051+{
2052+ FILE *file;
2053+ GList *list;
2054+ unsigned int length;
2055+ gboolean ret = FALSE;
2056+
2057+ g_return_val_if_fail (model != NULL, FALSE);
2058+ g_return_val_if_fail (EGG_IS_RECENT_MODEL_MULTI (model), FALSE);
2059+ g_return_val_if_fail (uri != NULL, FALSE);
2060+
2061+ file = egg_recent_model_multi_open_file (model, TRUE);
2062+ g_return_val_if_fail (file != NULL, FALSE);
2063+
2064+ if (egg_recent_model_multi_lock_file (file)) {
2065+ list = egg_recent_model_multi_read (model, file);
2066+
2067+ if (list == NULL)
2068+ goto out;
2069+
2070+ length = g_list_length (list);
2071+
2072+ list = egg_recent_model_multi_delete_from_list (list, uri);
2073+
2074+ if (length == g_list_length (list)) {
2075+ /* nothing was deleted */
2076+ EGG_RECENT_ITEM_LIST_UNREF (list);
2077+ } else {
2078+ egg_recent_model_multi_write (model, file, list);
2079+ EGG_RECENT_ITEM_LIST_UNREF (list);
2080+ ret = TRUE;
2081+
2082+ }
2083+ } else {
2084+ g_warning ("Failed to lock: %s", strerror (errno));
2085+ return FALSE;
2086+ }
2087+
2088+out:
2089+
2090+ if (!egg_recent_model_multi_unlock_file (file))
2091+ g_warning ("Failed to unlock: %s", strerror (errno));
2092+
2093+ fclose (file);
2094+
2095+ g_hash_table_remove (model->priv->monitors, uri);
2096+
2097+ if (model->priv->monitor == NULL && ret) {
2098+ /* since monitoring isn't working, at least give a
2099+ * local notification
2100+ */
2101+ egg_recent_model_multi_changed (model);
2102+ }
2103+
2104+ return ret;
2105+}
2106+
2107+
2108+/**
2109+ * egg_recent_model_multi_get_list:
2110+ * @model: A EggRecentModelMulti object.
2111+ *
2112+ * This function gets the current contents of the file
2113+ *
2114+ * Returns: a GList
2115+ */
2116+GList *
2117+egg_recent_model_multi_get_list (EggRecentModelMulti *model)
2118+{
2119+ FILE *file;
2120+ GList *list = NULL;
2121+
2122+ file = egg_recent_model_multi_open_file (model, FALSE);
2123+ if (file == NULL)
2124+ return NULL;
2125+
2126+ if (egg_recent_model_multi_lock_file (file))
2127+ list = egg_recent_model_multi_read (model, file);
2128+ else {
2129+ g_warning ("Failed to lock: %s", strerror (errno));
2130+ fclose (file);
2131+ return NULL;
2132+ }
2133+
2134+ if (!egg_recent_model_multi_unlock_file (file))
2135+ g_warning ("Failed to unlock: %s", strerror (errno));
2136+
2137+ if (list != NULL) {
2138+ list = egg_recent_model_multi_filter (model, list);
2139+ list = egg_recent_model_multi_sort (model, list);
2140+
2141+ egg_recent_model_multi_enforce_limit (list, model->priv->limit);
2142+ }
2143+
2144+ fclose (file);
2145+
2146+ return list;
2147+}
2148+
2149+
2150+
2151+/**
2152+ * egg_recent_model_multi_set_limit:
2153+ * @model: A EggRecentModelMulti object.
2154+ * @limit: The maximum length of the list
2155+ *
2156+ * This function sets the maximum length of the list. Note: This only affects
2157+ * the length of the list emitted in the "changed" signal, not the list stored
2158+ * on disk.
2159+ *
2160+ * Returns: void
2161+ */
2162+void
2163+egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit)
2164+{
2165+ model->priv->use_default_limit = FALSE;
2166+
2167+ egg_recent_model_multi_set_limit_internal (model, limit);
2168+}
2169+
2170+/**
2171+ * egg_recent_model_multi_get_limit:
2172+ * @model: A EggRecentModelMulti object.
2173+ *
2174+ * This function gets the maximum length of the list.
2175+ *
2176+ * Returns: int
2177+ */
2178+int
2179+egg_recent_model_multi_get_limit (EggRecentModelMulti *model)
2180+{
2181+ return model->priv->limit;
2182+}
2183+
2184+
2185+/**
2186+ * egg_recent_model_multi_clear:
2187+ * @model: A EggRecentModelMulti object.
2188+ *
2189+ * This function clears the contents of the file
2190+ *
2191+ * Returns: void
2192+ */
2193+void
2194+egg_recent_model_multi_clear (EggRecentModelMulti *model)
2195+{
2196+ FILE *file;
2197+ int fd;
2198+
2199+ file = egg_recent_model_multi_open_file (model, TRUE);
2200+ g_return_if_fail (file != NULL);
2201+
2202+ fd = fileno (file);
2203+
2204+ if (egg_recent_model_multi_lock_file (file)) {
2205+ ftruncate (fd, 0);
2206+ } else {
2207+ g_warning ("Failed to lock: %s", strerror (errno));
2208+ return;
2209+ }
2210+
2211+ if (!egg_recent_model_multi_unlock_file (file))
2212+ g_warning ("Failed to unlock: %s", strerror (errno));
2213+
2214+ fclose (file);
2215+
2216+ if (model->priv->monitor == NULL) {
2217+ /* since monitoring isn't working, at least give a
2218+ * local notification
2219+ */
2220+ egg_recent_model_multi_changed (model);
2221+ }
2222+}
2223+
2224+static void
2225+egg_recent_model_multi_clear_mime_filter (EggRecentModelMulti *model)
2226+{
2227+ g_return_if_fail (model != NULL);
2228+
2229+ if (model->priv->mime_filter_values != NULL) {
2230+ g_slist_foreach (model->priv->mime_filter_values,
2231+ (GFunc) g_pattern_spec_free, NULL);
2232+ g_slist_free (model->priv->mime_filter_values);
2233+ model->priv->mime_filter_values = NULL;
2234+ }
2235+}
2236+
2237+/**
2238+ * egg_recent_model_multi_set_filter_mime_types:
2239+ * @model: A EggRecentModelMulti object.
2240+ *
2241+ * Sets which mime types are allowed in the list.
2242+ *
2243+ * Returns: void
2244+ */
2245+void
2246+egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model,
2247+ ...)
2248+{
2249+ va_list valist;
2250+ GSList *list = NULL;
2251+ gchar *str;
2252+
2253+ g_return_if_fail (model != NULL);
2254+
2255+ egg_recent_model_multi_clear_mime_filter (model);
2256+
2257+ va_start (valist, model);
2258+
2259+ str = va_arg (valist, gchar*);
2260+
2261+ while (str != NULL) {
2262+ list = g_slist_prepend (list, g_pattern_spec_new (str));
2263+
2264+ str = va_arg (valist, gchar*);
2265+ }
2266+
2267+ va_end (valist);
2268+
2269+ model->priv->mime_filter_values = list;
2270+}
2271+
2272+static void
2273+egg_recent_model_multi_clear_group_filter (EggRecentModelMulti *model)
2274+{
2275+ g_return_if_fail (model != NULL);
2276+
2277+ if (model->priv->group_filter_values != NULL) {
2278+ g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL);
2279+ g_slist_free (model->priv->group_filter_values);
2280+ model->priv->group_filter_values = NULL;
2281+ }
2282+}
2283+
2284+/**
2285+ * egg_recent_model_multi_set_filter_groups:
2286+ * @model: A EggRecentModelMulti object.
2287+ *
2288+ * Sets which groups are allowed in the list.
2289+ *
2290+ * Returns: void
2291+ */
2292+void
2293+egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model,
2294+ ...)
2295+{
2296+ va_list valist;
2297+ GSList *list = NULL;
2298+ gchar *str;
2299+
2300+ g_return_if_fail (model != NULL);
2301+
2302+ egg_recent_model_multi_clear_group_filter (model);
2303+
2304+ va_start (valist, model);
2305+
2306+ str = va_arg (valist, gchar*);
2307+
2308+ while (str != NULL) {
2309+ list = g_slist_prepend (list, g_strdup (str));
2310+
2311+ str = va_arg (valist, gchar*);
2312+ }
2313+
2314+ va_end (valist);
2315+
2316+ model->priv->group_filter_values = list;
2317+}
2318+
2319+static void
2320+egg_recent_model_multi_clear_scheme_filter (EggRecentModelMulti *model)
2321+{
2322+ g_return_if_fail (model != NULL);
2323+
2324+ if (model->priv->scheme_filter_values != NULL) {
2325+ g_slist_foreach (model->priv->scheme_filter_values,
2326+ (GFunc) g_pattern_spec_free, NULL);
2327+ g_slist_free (model->priv->scheme_filter_values);
2328+ model->priv->scheme_filter_values = NULL;
2329+ }
2330+}
2331+
2332+/**
2333+ * egg_recent_model_multi_set_filter_uri_schemes:
2334+ * @model: A EggRecentModelMulti object.
2335+ *
2336+ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list.
2337+ *
2338+ * Returns: void
2339+ */
2340+void
2341+egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model, ...)
2342+{
2343+ va_list valist;
2344+ GSList *list = NULL;
2345+ gchar *str;
2346+
2347+ g_return_if_fail (model != NULL);
2348+
2349+ egg_recent_model_multi_clear_scheme_filter (model);
2350+
2351+ va_start (valist, model);
2352+
2353+ str = va_arg (valist, gchar*);
2354+
2355+ while (str != NULL) {
2356+ list = g_slist_prepend (list, g_pattern_spec_new (str));
2357+
2358+ str = va_arg (valist, gchar*);
2359+ }
2360+
2361+ va_end (valist);
2362+
2363+ model->priv->scheme_filter_values = list;
2364+}
2365+
2366+/**
2367+ * egg_recent_model_multi_set_sort:
2368+ * @model: A EggRecentModelMulti object.
2369+ * @sort: A EggRecentModelMultiSort type
2370+ *
2371+ * Sets the type of sorting to be used.
2372+ *
2373+ * Returns: void
2374+ */
2375+void
2376+egg_recent_model_multi_set_sort (EggRecentModelMulti *model,
2377+ EggRecentModelMultiSort sort)
2378+{
2379+ g_return_if_fail (model != NULL);
2380+
2381+ model->priv->sort_type = sort;
2382+}
2383+
2384+/**
2385+ * egg_recent_model_multi_changed:
2386+ * @model: A EggRecentModelMulti object.
2387+ *
2388+ * This function causes a "changed" signal to be emitted.
2389+ *
2390+ * Returns: void
2391+ */
2392+void
2393+egg_recent_model_multi_changed (EggRecentModelMulti *model)
2394+{
2395+ GList *list = NULL;
2396+
2397+ if (model->priv->limit > 0) {
2398+ list = egg_recent_model_multi_get_list (model);
2399+ /* egg_recent_model_multi_monitor_list (model, list); */
2400+
2401+ g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0,
2402+ list);
2403+ }
2404+
2405+ if (list)
2406+ EGG_RECENT_ITEM_LIST_UNREF (list);
2407+}
2408+
2409+static void
2410+egg_recent_model_multi_remove_expired_list (EggRecentModelMulti *model, GList *list)
2411+{
2412+ time_t current_time;
2413+ time_t day_seconds;
2414+
2415+ time (&current_time);
2416+ day_seconds = model->priv->expire_days*24*60*60;
2417+
2418+ while (list != NULL) {
2419+ EggRecentItem *item = list->data;
2420+ time_t timestamp;
2421+
2422+ timestamp = egg_recent_item_get_timestamp (item);
2423+
2424+ if ((timestamp+day_seconds) < current_time) {
2425+ gchar *uri = egg_recent_item_get_uri (item);
2426+ egg_recent_model_multi_delete (model, uri);
2427+
2428+ g_strdup (uri);
2429+ }
2430+
2431+ list = list->next;
2432+ }
2433+}
2434+
2435+
2436+/**
2437+ * egg_recent_model_multi_remove_expired:
2438+ * @model: A EggRecentModelMulti object.
2439+ *
2440+ * Goes through the entire list, and removes any items that are older than
2441+ * the user-specified expiration period.
2442+ *
2443+ * Returns: void
2444+ */
2445+void
2446+egg_recent_model_multi_remove_expired (EggRecentModelMulti *model)
2447+{
2448+ FILE *file;
2449+ GList *list=NULL;
2450+
2451+ g_return_if_fail (model != NULL);
2452+
2453+ file = egg_recent_model_multi_open_file (model, FALSE);
2454+ if (file == NULL)
2455+ return;
2456+
2457+ if (egg_recent_model_multi_lock_file (file)) {
2458+ list = egg_recent_model_multi_read (model, file);
2459+
2460+ } else {
2461+ g_warning ("Failed to lock: %s", strerror (errno));
2462+ return;
2463+ }
2464+
2465+ if (!egg_recent_model_multi_unlock_file (file))
2466+ g_warning ("Failed to unlock: %s", strerror (errno));
2467+
2468+ if (list != NULL) {
2469+ egg_recent_model_multi_remove_expired_list (model, list);
2470+ EGG_RECENT_ITEM_LIST_UNREF (list);
2471+ }
2472+
2473+ fclose (file);
2474+}
2475+
2476+/**
2477+ * egg_recent_model_multi_get_type:
2478+ *
2479+ * This returns a GType representing a EggRecentModelMulti object.
2480+ *
2481+ * Returns: a GType
2482+ */
2483+GType
2484+egg_recent_model_multi_get_type (void)
2485+{
2486+ static GType egg_recent_model_multi_type = 0;
2487+
2488+ if(!egg_recent_model_multi_type) {
2489+ static const GTypeInfo egg_recent_model_multi_info = {
2490+ sizeof (EggRecentModelMultiClass),
2491+ NULL, /* base init */
2492+ NULL, /* base finalize */
2493+ (GClassInitFunc)egg_recent_model_multi_class_init, /* class init */
2494+ NULL, /* class finalize */
2495+ NULL, /* class data */
2496+ sizeof (EggRecentModelMulti),
2497+ 0,
2498+ (GInstanceInitFunc) egg_recent_model_multi_init
2499+ };
2500+
2501+ egg_recent_model_multi_type = g_type_register_static (G_TYPE_OBJECT,
2502+ "EggRecentModelMulti",
2503+ &egg_recent_model_multi_info, 0);
2504+ }
2505+
2506+ return egg_recent_model_multi_type;
2507+}
2508+
2509diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h ./libgnome-desktop/egg-recent-model-multi.h
2510--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/egg-recent-model-multi.h 1969-12-31 19:00:00.000000000 -0500
2511+++ ./libgnome-desktop/egg-recent-model-multi.h 2006-05-08 16:48:27.000000000 -0400
2512@@ -0,0 +1,81 @@
2513+/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2514+#ifndef __EGG_RECENT_MODEL_MULTI_H__
2515+#define __EGG_RECENT_MODEL_MULTI_H__
2516+
2517+#include "egg-recent-item.h"
2518+
2519+G_BEGIN_DECLS
2520+
2521+#define EGG_TYPE_RECENT_MODEL_MULTI (egg_recent_model_multi_get_type ())
2522+#define EGG_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMulti)
2523+#define EGG_RECENT_MODEL_MULTI_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL_MULTI, EggRecentModelMultiClass)
2524+#define EGG_IS_RECENT_MODEL_MULTI(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_multi_get_type ())
2525+
2526+typedef struct _EggRecentModelMulti EggRecentModelMulti;
2527+typedef struct _EggRecentModelMultiPrivate EggRecentModelMultiPrivate;
2528+typedef struct _EggRecentModelMultiClass EggRecentModelMultiClass;
2529+
2530+struct _EggRecentModelMulti {
2531+ GObject parent_instance;
2532+
2533+ EggRecentModelMultiPrivate *priv;
2534+};
2535+
2536+struct _EggRecentModelMultiClass {
2537+ GObjectClass parent_class;
2538+
2539+ void (*changed) (EggRecentModelMulti *model, GList *list);
2540+};
2541+
2542+typedef enum {
2543+ EGG_RECENT_MODEL_MULTI_SORT_MRU,
2544+ EGG_RECENT_MODEL_MULTI_SORT_LRU,
2545+ EGG_RECENT_MODEL_MULTI_SORT_NONE
2546+} EggRecentModelMultiSort;
2547+
2548+
2549+/* Standard group names */
2550+#define EGG_RECENT_GROUP_LAUNCHERS "Launchers"
2551+
2552+
2553+GType egg_recent_model_multi_get_type (void);
2554+
2555+/* constructors */
2556+EggRecentModelMulti * egg_recent_model_multi_new (EggRecentModelMultiSort sort);
2557+EggRecentModelMulti * egg_recent_model_multi_new_with_file_path (EggRecentModelMultiSort sort, const gchar *file_path);
2558+
2559+/* public methods */
2560+void egg_recent_model_multi_set_filter_mime_types (EggRecentModelMulti *model,
2561+ ...);
2562+
2563+void egg_recent_model_multi_set_filter_groups (EggRecentModelMulti *model, ...);
2564+
2565+void egg_recent_model_multi_set_filter_uri_schemes (EggRecentModelMulti *model,
2566+ ...);
2567+
2568+void egg_recent_model_multi_set_sort (EggRecentModelMulti *model,
2569+ EggRecentModelMultiSort sort);
2570+
2571+gboolean egg_recent_model_multi_add_full (EggRecentModelMulti *model,
2572+ EggRecentItem *item);
2573+
2574+gboolean egg_recent_model_multi_add (EggRecentModelMulti *model,
2575+ const gchar *uri);
2576+
2577+gboolean egg_recent_model_multi_delete (EggRecentModelMulti *model,
2578+ const gchar *uri);
2579+
2580+void egg_recent_model_multi_clear (EggRecentModelMulti *model);
2581+
2582+GList * egg_recent_model_multi_get_list (EggRecentModelMulti *model);
2583+
2584+void egg_recent_model_multi_changed (EggRecentModelMulti *model);
2585+
2586+void egg_recent_model_multi_set_limit (EggRecentModelMulti *model, int limit);
2587+int egg_recent_model_multi_get_limit (EggRecentModelMulti *model);
2588+
2589+void egg_recent_model_multi_remove_expired (EggRecentModelMulti *model);
2590+
2591+G_END_DECLS
2592+
2593+#endif /* __EGG_RECENT_MODEL_MULTI_H__ */
2594diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c ./libgnome-desktop/gnome-desktop-item.c
2595--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/gnome-desktop-item.c 2005-11-04 03:43:42.000000000 -0500
2596+++ ./libgnome-desktop/gnome-desktop-item.c 2006-05-08 17:07:23.000000000 -0400
2597@@ -59,6 +59,9 @@
2598 #include <gtk/gtk.h>
2599 #endif
2600
2601+#include "egg-recent-item.h"
2602+#include "egg-recent-model-multi.h"
2603+
2604 #define sure_string(s) ((s)!=NULL?(s):"")
2605
2606 struct _GnomeDesktopItem {
2607@@ -128,6 +131,8 @@
2608 const char *uri,
2609 GError **error);
2610
2611+static void update_recently_used_apps (const GnomeDesktopItem *item);
2612+
2613 static int
2614 readbuf_getc (ReadBuf *rb)
2615 {
2616@@ -2091,6 +2096,8 @@
2617 (flags & GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS),
2618 error);
2619
2620+ update_recently_used_apps (item);
2621+
2622 return ret;
2623 }
2624
2625@@ -4095,3 +4102,22 @@
2626
2627 return q;
2628 }
2629+
2630+static void
2631+update_recently_used_apps (const GnomeDesktopItem *item)
2632+{
2633+ EggRecentModelMulti *model;
2634+
2635+
2636+ if (! item)
2637+ return;
2638+
2639+ model = egg_recent_model_multi_new_with_file_path (EGG_RECENT_MODEL_MULTI_SORT_MRU, ".recently-used-apps");
2640+
2641+ if (! model)
2642+ return;
2643+
2644+ egg_recent_model_multi_add (model, gnome_desktop_item_get_location (item));
2645+
2646+ g_object_unref (G_OBJECT (model));
2647+}
2648Files ../gnome-desktop-2.12.2-pristine/libgnome-desktop/.gnome-desktop-item.c.swp and ./libgnome-desktop/.gnome-desktop-item.c.swp differ
2649diff -urN ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am ./libgnome-desktop/Makefile.am
2650--- ../gnome-desktop-2.12.2-pristine/libgnome-desktop/Makefile.am 2004-11-18 12:42:53.000000000 -0500
2651+++ ./libgnome-desktop/Makefile.am 2006-05-08 16:55:25.000000000 -0400
2652@@ -16,9 +16,13 @@
2653
2654 noinst_PROGRAMS = test-ditem test-hint test-ditem-edit
2655
2656-libgnome_desktop_2_la_SOURCES = \
2657- gnome-desktop-item.c \
2658- gnome-ditem-edit.c \
2659+libgnome_desktop_2_la_SOURCES = \
2660+ gnome-desktop-item.c \
2661+ gnome-ditem-edit.c \
2662+ egg-recent-item.h \
2663+ egg-recent-item.c \
2664+ egg-recent-model-multi.h \
2665+ egg-recent-model-multi.c \
2666 gnome-hint.c
2667
2668 libgnome_desktop_2_la_LIBADD = \
This page took 0.450481 seconds and 4 git commands to generate.