]> Pileus Git - ~andy/gtk/blob - gtk/gtkrecentmanager.c
Merge branch 'master' into client-side-windows
[~andy/gtk] / gtk / gtkrecentmanager.c
1 /* GTK - The GIMP Toolkit
2  * gtkrecentmanager.c: a manager for the recently used resources
3  *
4  * Copyright (C) 2006 Emmanuele Bassi
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  */
20
21 #include "config.h"
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #ifdef HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #include <errno.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <glib.h>
32 #include <glib/gstdio.h>
33 #include <gio/gio.h>
34
35 #include "gtkrecentmanager.h"
36 #include "gtkintl.h"
37 #include "gtkstock.h"
38 #include "gtkicontheme.h"
39 #include "gtktypebuiltins.h"
40 #include "gtkprivate.h"
41 #include "gtkmarshalers.h"
42 #include "gtkalias.h"
43
44 /* the file where we store the recently used items */
45 #define GTK_RECENTLY_USED_FILE  ".recently-used.xbel"
46
47 /* return all items by default */
48 #define DEFAULT_LIMIT   -1
49
50 /* keep in sync with xdgmime */
51 #define GTK_RECENT_DEFAULT_MIME "application/octet-stream"
52
53 typedef struct
54 {
55   gchar *name;
56   gchar *exec;
57   
58   guint count;
59   
60   time_t stamp;
61 } RecentAppInfo;
62
63 struct _GtkRecentInfo
64 {
65   gchar *uri;
66   
67   gchar *display_name;
68   gchar *description;
69   
70   time_t added;
71   time_t modified;
72   time_t visited;
73   
74   gchar *mime_type;
75   
76   GSList *applications;
77   GHashTable *apps_lookup;
78   
79   GSList *groups;
80   
81   gboolean is_private;
82   
83   GdkPixbuf *icon;
84   
85   gint ref_count;
86 };
87
88 #define GTK_RECENT_MANAGER_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerPrivate))
89
90 struct _GtkRecentManagerPrivate
91 {
92   gchar *filename;
93
94   guint is_dirty : 1;
95   
96   gint limit;
97   gint size;
98
99   GBookmarkFile *recent_items;
100
101   GFileMonitor *monitor;
102 };
103
104 enum
105 {
106   PROP_0,
107
108   PROP_FILENAME,  
109   PROP_LIMIT,
110   PROP_SIZE
111 };
112
113 static void     gtk_recent_manager_dispose             (GObject           *object);
114 static void     gtk_recent_manager_finalize            (GObject           *object);
115 static void     gtk_recent_manager_set_property        (GObject           *object,
116                                                         guint              prop_id,
117                                                         const GValue      *value,
118                                                         GParamSpec        *pspec);
119 static void     gtk_recent_manager_get_property        (GObject           *object,
120                                                         guint              prop_id,
121                                                         GValue            *value,
122                                                         GParamSpec        *pspec);
123 static void     gtk_recent_manager_add_item_query_info (GObject           *source_object,
124                                                         GAsyncResult      *res,
125                                                         gpointer           user_data);
126 static void     gtk_recent_manager_monitor_changed     (GFileMonitor      *monitor,
127                                                         GFile             *file,
128                                                         GFile             *other_file,
129                                                         GFileMonitorEvent  event_type,
130                                                         gpointer           user_data);
131 static void     gtk_recent_manager_changed             (GtkRecentManager  *manager);
132 static void     gtk_recent_manager_real_changed        (GtkRecentManager  *manager);
133 static void     gtk_recent_manager_set_filename        (GtkRecentManager  *manager,
134                                                         const gchar       *filename);
135 static void     gtk_recent_manager_clamp_to_age        (GtkRecentManager  *manager,
136                                                         gint               age);
137
138
139 static void build_recent_items_list (GtkRecentManager  *manager);
140 static void purge_recent_items_list (GtkRecentManager  *manager,
141                                      GError           **error);
142
143 static RecentAppInfo *recent_app_info_new  (const gchar   *app_name);
144 static void           recent_app_info_free (RecentAppInfo *app_info);
145
146 static GtkRecentInfo *gtk_recent_info_new  (const gchar   *uri);
147 static void           gtk_recent_info_free (GtkRecentInfo *recent_info);
148
149 static guint signal_changed = 0;
150
151 static GtkRecentManager *recent_manager_singleton = NULL;
152
153 G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT)
154
155 static void
156 filename_warning (const gchar *format, 
157                   const gchar *filename, 
158                   const gchar *message)
159 {
160   gchar *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
161   g_warning (format, utf8 ? utf8 : "(invalid filename)", message);
162   g_free (utf8);
163 }
164
165 /* Test of haystack has the needle prefix, comparing case
166  * insensitive. haystack may be UTF-8, but needle must
167  * contain only lowercase ascii. */
168 static gboolean
169 has_case_prefix (const gchar *haystack, 
170                  const gchar *needle)
171 {
172   const gchar *h, *n;
173
174   /* Eat one character at a time. */
175   h = haystack;
176   n = needle;
177
178   while (*n && *h && *n == g_ascii_tolower (*h))
179     {
180       n++;
181       h++;
182     }
183
184   return *n == '\0';
185 }
186
187 GQuark
188 gtk_recent_manager_error_quark (void)
189 {
190   return g_quark_from_static_string ("gtk-recent-manager-error-quark");
191 }
192
193 static void
194 gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
195 {
196   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
197   
198   gobject_class->set_property = gtk_recent_manager_set_property;
199   gobject_class->get_property = gtk_recent_manager_get_property;
200   gobject_class->dispose = gtk_recent_manager_dispose;
201   gobject_class->finalize = gtk_recent_manager_finalize;
202   
203   /**
204    * GtkRecentManager:filename
205    *
206    * The full path to the file to be used to store and read the recently
207    * used resources list
208    *
209    * Since: 2.10
210    */
211   g_object_class_install_property (gobject_class,
212                                    PROP_FILENAME,
213                                    g_param_spec_string ("filename",
214                                                         P_("Filename"),
215                                                         P_("The full path to the file to be used to store and read the list"),
216                                                         NULL,
217                                                         (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)));
218   /**
219    * GtkRecentManager:limit
220    *
221    * The maximum number of items to be returned by the
222    * gtk_recent_manager_get_items() function.
223    *
224    * Since: 2.10
225    */
226   g_object_class_install_property (gobject_class,
227                                    PROP_LIMIT,
228                                    g_param_spec_int ("limit",
229                                                      P_("Limit"),
230                                                      P_("The maximum number of items to be returned by gtk_recent_manager_get_items()"),
231                                                      -1,
232                                                      G_MAXINT,
233                                                      DEFAULT_LIMIT,
234                                                      G_PARAM_READWRITE));
235   /**
236    * GtkRecentManager:size
237    * 
238    * The size of the recently used resources list.
239    *
240    * Since: 2.10
241    */
242   g_object_class_install_property (gobject_class,
243                                    PROP_SIZE,
244                                    g_param_spec_int ("size",
245                                                      P_("Size"),
246                                                      P_("The size of the recently used resources list"),
247                                                      -1,
248                                                      G_MAXINT,
249                                                      0,
250                                                      G_PARAM_READABLE));
251   
252   /**
253    * GtkRecentManager::changed
254    * @recent_manager: the recent manager
255    *
256    * Emitted when the current recently used resources manager changes its
257    * contents.
258    *
259    * Since: 2.10
260    */
261   signal_changed =
262     g_signal_new (I_("changed"),
263                   G_TYPE_FROM_CLASS (klass),
264                   G_SIGNAL_RUN_FIRST,
265                   G_STRUCT_OFFSET (GtkRecentManagerClass, changed),
266                   NULL, NULL,
267                   g_cclosure_marshal_VOID__VOID,
268                   G_TYPE_NONE, 0);
269   
270   klass->changed = gtk_recent_manager_real_changed;
271   
272   g_type_class_add_private (klass, sizeof (GtkRecentManagerPrivate));
273 }
274
275 static void
276 gtk_recent_manager_init (GtkRecentManager *manager)
277 {
278   GtkRecentManagerPrivate *priv;
279
280   manager->priv = priv = GTK_RECENT_MANAGER_GET_PRIVATE (manager);
281   
282   priv->limit = DEFAULT_LIMIT;
283   priv->size = 0;
284
285   priv->filename = NULL;
286 }
287
288 static void
289 gtk_recent_manager_set_property (GObject               *object,
290                                  guint                  prop_id,
291                                  const GValue          *value,
292                                  GParamSpec            *pspec)
293 {
294   GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
295  
296   switch (prop_id)
297     {
298     case PROP_FILENAME:
299       gtk_recent_manager_set_filename (recent_manager, g_value_get_string (value));
300       break;      
301     case PROP_LIMIT:
302       gtk_recent_manager_set_limit (recent_manager, g_value_get_int (value));
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306       break;
307     }
308 }
309
310 static void
311 gtk_recent_manager_get_property (GObject               *object,
312                                  guint                  prop_id,
313                                  GValue                *value,
314                                  GParamSpec            *pspec)
315 {
316   GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
317   
318   switch (prop_id)
319     {
320     case PROP_FILENAME:
321       g_value_set_string (value, recent_manager->priv->filename);
322       break;
323     case PROP_LIMIT:
324       g_value_set_int (value, recent_manager->priv->limit);
325       break;
326     case PROP_SIZE:
327       g_value_set_int (value, recent_manager->priv->size);
328       break;
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331       break;
332     }
333
334
335 static void
336 gtk_recent_manager_dispose (GObject *object)
337 {
338   GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
339   GtkRecentManagerPrivate *priv = manager->priv;
340
341   if (priv->monitor)
342     {
343       g_signal_handlers_disconnect_by_func (priv->monitor,
344                                             G_CALLBACK (gtk_recent_manager_monitor_changed),
345                                             manager);
346       g_object_unref (priv->monitor);
347       priv->monitor = NULL;
348     }
349
350   G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (object);
351 }
352
353 static void
354 gtk_recent_manager_finalize (GObject *object)
355 {
356   GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
357   GtkRecentManagerPrivate *priv = manager->priv;
358
359   g_free (priv->filename);
360   
361   if (priv->recent_items)
362     g_bookmark_file_free (priv->recent_items);
363
364   G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object);
365 }
366
367 static void
368 gtk_recent_manager_real_changed (GtkRecentManager *manager)
369 {
370   GtkRecentManagerPrivate *priv = manager->priv;
371
372   g_object_freeze_notify (G_OBJECT (manager));
373
374   if (priv->is_dirty)
375     {
376       GError *write_error;
377       
378       /* we are marked as dirty, so we dump the content of our
379        * recently used items list
380        */
381       g_assert (priv->filename != NULL);
382
383       if (!priv->recent_items)
384         {
385           /* if no container object has been defined, we create a new
386            * empty container, and dump it
387            */
388           priv->recent_items = g_bookmark_file_new ();
389           priv->size = 0;
390         }
391       else
392         {
393           GtkSettings *settings = gtk_settings_get_default ();
394           gint age = 30;
395
396           g_object_get (G_OBJECT (settings), "gtk-recent-files-max-age", &age, NULL);
397           if (age > 0)
398             gtk_recent_manager_clamp_to_age (manager, age);
399           else if (age == 0)
400             {
401               g_bookmark_file_free (priv->recent_items);
402               priv->recent_items = NULL;
403
404               priv->recent_items = g_bookmark_file_new ();
405             }
406         }
407
408       write_error = NULL;
409       g_bookmark_file_to_file (priv->recent_items, priv->filename, &write_error);
410       if (write_error)
411         {
412           filename_warning ("Attempting to store changes into `%s', "
413                             "but failed: %s",
414                             priv->filename,
415                             write_error->message);
416           g_error_free (write_error);
417         }
418
419       if (g_chmod (priv->filename, 0600) < 0)
420         {
421           filename_warning ("Attempting to set the permissions of `%s', "
422                             "but failed: %s",
423                             priv->filename,
424                             g_strerror (errno));
425         }
426
427       /* mark us as clean */
428       priv->is_dirty = FALSE;
429     }
430   else
431     {
432       /* we are not marked as dirty, so we have been called
433        * because the recently used resources file has been
434        * changed (and not from us).
435        */
436       build_recent_items_list (manager);
437     }
438
439   g_object_thaw_notify (G_OBJECT (manager));
440 }
441
442 static void
443 gtk_recent_manager_monitor_changed (GFileMonitor      *monitor,
444                                     GFile             *file,
445                                     GFile             *other_file,
446                                     GFileMonitorEvent  event_type,
447                                     gpointer           user_data)
448 {
449   GtkRecentManager *manager = user_data;
450
451   switch (event_type)
452     {
453     case G_FILE_MONITOR_EVENT_CHANGED:
454     case G_FILE_MONITOR_EVENT_CREATED:
455       gtk_recent_manager_changed (manager);
456       break;
457
458     case G_FILE_MONITOR_EVENT_DELETED:
459       break;
460
461     default:
462       break;
463     }
464 }
465
466 static void
467 gtk_recent_manager_set_filename (GtkRecentManager *manager,
468                                  const gchar      *filename)
469 {
470   GtkRecentManagerPrivate *priv;
471   GFile *file;
472   GError *error;
473   
474   g_assert (GTK_IS_RECENT_MANAGER (manager));
475
476   priv = manager->priv;
477
478   /* if a filename is already set and filename is not NULL, then copy
479    * it and reset the monitor; otherwise, if it's NULL we're being
480    * called from the finalization sequence, so we simply disconnect the
481    * monitoring and return.
482    *
483    * if no filename is set and filename is NULL, use the default.
484    */
485   if (priv->filename)
486     {
487       g_free (priv->filename);
488
489       if (priv->monitor)
490         {
491           g_signal_handlers_disconnect_by_func (priv->monitor,
492                                                 G_CALLBACK (gtk_recent_manager_monitor_changed),
493                                                 manager);
494           g_object_unref (priv->monitor);
495           priv->monitor = NULL;
496         }
497
498       if (!filename || *filename == '\0')
499         return;
500       else
501         priv->filename = g_strdup (filename);
502     }
503   else
504     {
505       if (!filename || *filename == '\0')
506         priv->filename = g_build_filename (g_get_home_dir (),
507                                            GTK_RECENTLY_USED_FILE,
508                                            NULL);
509       else
510         priv->filename = g_strdup (filename);
511     }
512
513   g_assert (priv->filename != NULL);
514   file = g_file_new_for_path (priv->filename);
515
516   error = NULL;
517   priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
518   if (error)
519     {
520       filename_warning ("Unable to monitor `%s': %s\n"
521                         "The GtkRecentManager will not update its contents "
522                         "if the file is changed from other instances",
523                         priv->filename,
524                         error->message);
525       g_error_free (error);
526     }
527   else
528     g_signal_connect (priv->monitor, "changed",
529                       G_CALLBACK (gtk_recent_manager_monitor_changed),
530                       manager);
531
532   g_object_unref (file);
533
534   priv->is_dirty = FALSE;
535   build_recent_items_list (manager);
536 }
537
538 /* reads the recently used resources file and builds the items list.
539  * we keep the items list inside the parser object, and build the
540  * RecentInfo object only on user's demand to avoid useless replication.
541  * this function resets the dirty bit of the manager.
542  */
543 static void
544 build_recent_items_list (GtkRecentManager *manager)
545 {
546   GtkRecentManagerPrivate *priv = manager->priv;
547   GError *read_error;
548   gint size;
549
550   g_assert (priv->filename != NULL);
551   
552   if (!priv->recent_items)
553     {
554       priv->recent_items = g_bookmark_file_new ();
555       priv->size = 0;
556     }
557
558   /* the file exists, and it's valid (we hope); if not, destroy the container
559    * object and hope for a better result when the next "changed" signal is
560    * fired. */
561   read_error = NULL;
562   g_bookmark_file_load_from_file (priv->recent_items, priv->filename, &read_error);
563   if (read_error)
564     {
565       /* if the file does not exist we just wait for the first write
566        * operation on this recent manager instance, to avoid creating
567        * empty files and leading to spurious file system events (Sabayon
568        * will not be happy about those)
569        */
570       if (read_error->domain == G_FILE_ERROR &&
571           read_error->code != G_FILE_ERROR_NOENT)
572         filename_warning ("Attempting to read the recently used resources "
573                           "file at `%s', but the parser failed: %s.",
574                           priv->filename,
575                           read_error->message);
576
577       g_bookmark_file_free (priv->recent_items);
578       priv->recent_items = NULL;
579
580       g_error_free (read_error);
581     }
582   else
583     {
584       size = g_bookmark_file_get_size (priv->recent_items);
585       if (priv->size != size)
586         {
587           priv->size = size;
588
589           g_object_notify (G_OBJECT (manager), "size");
590         }
591     }
592
593   priv->is_dirty = FALSE;
594 }
595
596
597 /********************
598  * GtkRecentManager *
599  ********************/
600
601
602 /**
603  * gtk_recent_manager_new:
604  * 
605  * Creates a new recent manager object.  Recent manager objects are used to
606  * handle the list of recently used resources.  A #GtkRecentManager object
607  * monitors the recently used resources list, and emits the "changed" signal
608  * each time something inside the list changes.
609  *
610  * #GtkRecentManager objects are expensive: be sure to create them only when
611  * needed. You should use gtk_recent_manager_get_default() instead.
612  *
613  * Return value: A newly created #GtkRecentManager object.
614  *
615  * Since: 2.10
616  */
617 GtkRecentManager *
618 gtk_recent_manager_new (void)
619 {
620   return g_object_new (GTK_TYPE_RECENT_MANAGER, NULL);
621 }
622
623 /**
624  * gtk_recent_manager_get_default:
625  *
626  * Gets a unique instance of #GtkRecentManager, that you can share
627  * in your application without caring about memory management. The
628  * returned instance will be freed when you application terminates.
629  *
630  * Return value: A unique #GtkRecentManager. Do not ref or unref it.
631  *
632  * Since: 2.10
633  */
634 GtkRecentManager *
635 gtk_recent_manager_get_default (void)
636 {
637   if (G_UNLIKELY (!recent_manager_singleton))
638     recent_manager_singleton = gtk_recent_manager_new ();
639
640   return recent_manager_singleton;
641 }
642
643 /**
644  * gtk_recent_manager_get_for_screen:
645  * @screen: a #GdkScreen
646  *
647  * Gets the recent manager object associated with @screen; if this
648  * function has not previously been called for the given screen,
649  * a new recent manager object will be created and associated with
650  * the screen. Recent manager objects are fairly expensive to create,
651  * so using this function is usually a better choice than calling 
652  * gtk_recent_manager_new() and setting the screen yourself; by using
653  * this function a single recent manager object will be shared between
654  * users.
655  *
656  * Return value: A unique #GtkRecentManager associated with the given
657  *   screen. This recent manager is associated to the with the screen
658  *   and can be used as long as the screen is open. Do not ref or
659  *   unref it.
660  *
661  * Deprecated: 2.12: This function has been deprecated and should
662  *   not be used in newly written code. Calling this function is
663  *   equivalent to calling gtk_recent_manager_get_default().
664  *
665  * Since: 2.10
666  */
667 GtkRecentManager *
668 gtk_recent_manager_get_for_screen (GdkScreen *screen)
669 {
670   return gtk_recent_manager_get_default ();
671 }
672
673 /**
674  * gtk_recent_manager_set_screen:
675  * @manager: a #GtkRecentManager
676  * @screen: a #GdkScreen
677  *
678  * Sets the screen for a recent manager; the screen is used to
679  * track the user's currently configured recently used documents
680  * storage.
681  * 
682  * Since: 2.10
683  *
684  * Deprecated: 2.12: This function has been deprecated and should
685  *   not be used in newly written code. Calling this function has
686  *   no effect.
687  */
688 void
689 gtk_recent_manager_set_screen (GtkRecentManager *manager,
690                                GdkScreen        *screen)
691 {
692
693 }
694
695 /**
696  * gtk_recent_manager_set_limit:
697  * @manager: a #GtkRecentManager
698  * @limit: the maximum number of items to return, or -1.
699  *
700  * Sets the maximum number of item that the gtk_recent_manager_get_items()
701  * function should return.  If @limit is set to -1, then return all the
702  * items.
703  *
704  * Since: 2.10
705  */
706 void
707 gtk_recent_manager_set_limit (GtkRecentManager *manager,
708                               gint              limit)
709 {
710   GtkRecentManagerPrivate *priv;
711   
712   g_return_if_fail (GTK_IS_RECENT_MANAGER (manager));
713   
714   priv = manager->priv;
715   priv->limit = limit;
716 }
717
718 /**
719  * gtk_recent_manager_get_limit:
720  * @manager: a #GtkRecentManager
721  *
722  * Gets the maximum number of items that the gtk_recent_manager_get_items()
723  * function should return.
724  *
725  * Return value: the number of items to return, or -1 for every item.
726  *
727  * Since: 2.10
728  */
729 gint
730 gtk_recent_manager_get_limit (GtkRecentManager *manager)
731 {
732   GtkRecentManagerPrivate *priv;
733   
734   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), DEFAULT_LIMIT);
735   
736   priv = manager->priv;
737   return priv->limit;
738 }
739
740 static void
741 gtk_recent_manager_add_item_query_info (GObject      *source_object,
742                                         GAsyncResult *res,
743                                         gpointer      user_data)
744 {
745   GFile *file = G_FILE (source_object);
746   GtkRecentManager *manager = user_data;
747   GtkRecentData recent_data;
748   GFileInfo *file_info;
749   gchar *uri;
750   GError *error;
751
752   uri = g_file_get_uri (file);
753
754   error = NULL;
755   file_info = g_file_query_info_finish (file, res, &error);
756   if (error)
757     {
758       g_warning ("Unable to retrieve the file info for `%s': %s",
759                  uri,
760                  error->message);
761       g_error_free (error);
762       goto out;
763     }
764
765   recent_data.display_name = NULL;
766   recent_data.description = NULL;
767
768   if (file_info)
769     {
770       gchar *content_type;
771
772       content_type = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
773
774       if (G_LIKELY (content_type))
775         recent_data.mime_type = g_content_type_get_mime_type (content_type);
776       else
777         recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
778
779       g_free (content_type);
780       g_object_unref (file_info);
781     }
782   else
783     recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
784
785   recent_data.app_name = g_strdup (g_get_application_name ());
786   recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
787   recent_data.groups = NULL;
788   recent_data.is_private = FALSE;
789
790   /* Ignore return value, this can't fail anyway since all required
791    * fields are set */
792   gtk_recent_manager_add_full (manager, uri, &recent_data);
793
794   manager->priv->is_dirty = TRUE;
795   gtk_recent_manager_changed (manager);
796
797   g_free (recent_data.mime_type);
798   g_free (recent_data.app_name);
799   g_free (recent_data.app_exec);
800
801 out:
802   g_object_unref (manager);
803   g_free (uri);
804 }
805
806 /**
807  * gtk_recent_manager_add_item:
808  * @manager: a #GtkRecentManager
809  * @uri: a valid URI
810  *
811  * Adds a new resource, pointed by @uri, into the recently used
812  * resources list.
813  *
814  * This function automatically retrieves some of the needed
815  * metadata and setting other metadata to common default values; it
816  * then feeds the data to gtk_recent_manager_add_full().
817  * 
818  * See gtk_recent_manager_add_full() if you want to explicitly
819  * define the metadata for the resource pointed by @uri.
820  *
821  * Return value: %TRUE if the new item was successfully added
822  *   to the recently used resources list
823  *
824  * Since: 2.10
825  */
826 gboolean
827 gtk_recent_manager_add_item (GtkRecentManager  *manager,
828                              const gchar       *uri)
829 {
830   GFile* file;
831   
832   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
833   g_return_val_if_fail (uri != NULL, FALSE);
834
835   file = g_file_new_for_uri (uri);
836
837   g_file_query_info_async (file,
838                            G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
839                            G_PRIORITY_DEFAULT,
840                            G_FILE_QUERY_INFO_NONE,
841                            NULL,
842                            gtk_recent_manager_add_item_query_info,
843                            g_object_ref (manager));
844
845   g_object_unref (file);
846
847   return TRUE;
848 }
849
850 /**
851  * gtk_recent_manager_add_full:
852  * @manager: a #GtkRecentManager
853  * @uri: a valid URI
854  * @recent_data: metadata of the resource
855  *
856  * Adds a new resource, pointed by @uri, into the recently used
857  * resources list, using the metadata specified inside the #GtkRecentData
858  * structure passed in @recent_data.
859  *
860  * The passed URI will be used to identify this resource inside the
861  * list.
862  *
863  * In order to register the new recently used resource, metadata about
864  * the resource must be passed as well as the URI; the metadata is
865  * stored in a #GtkRecentData structure, which must contain the MIME
866  * type of the resource pointed by the URI; the name of the application
867  * that is registering the item, and a command line to be used when
868  * launching the item.
869  *
870  * Optionally, a #GtkRecentData structure might contain a UTF-8 string
871  * to be used when viewing the item instead of the last component of the
872  * URI; a short description of the item; whether the item should be
873  * considered private - that is, should be displayed only by the
874  * applications that have registered it.
875  *
876  * Return value: %TRUE if the new item was successfully added to the
877  * recently used resources list, %FALSE otherwise.
878  *
879  * Since: 2.10
880  */
881 gboolean
882 gtk_recent_manager_add_full (GtkRecentManager     *manager,
883                              const gchar          *uri,
884                              const GtkRecentData  *data)
885 {
886   GtkRecentManagerPrivate *priv;
887   
888   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
889   g_return_val_if_fail (uri != NULL, FALSE);
890   g_return_val_if_fail (data != NULL, FALSE);
891
892   /* sanity checks */
893   if ((data->display_name) &&
894       (!g_utf8_validate (data->display_name, -1, NULL)))
895     {
896       g_warning ("Attempting to add `%s' to the list of recently used "
897                  "resources, but the display name is not a valid UTF-8 "
898                  "encoded string",
899                  uri);
900       return FALSE;
901     }
902   
903   if ((data->description) &&
904       (!g_utf8_validate (data->description, -1, NULL)))
905     {
906       g_warning ("Attempting to add `%s' to the list of recently used "
907                  "resources, but the description is not a valid UTF-8 "
908                  "encoded string",
909                  uri);
910       return FALSE;
911     }
912
913  
914   if (!data->mime_type)
915     {
916       g_warning ("Attempting to add `%s' to the list of recently used "
917                  "resources, but not MIME type was defined",
918                  uri);
919       return FALSE;
920     }
921   
922   if (!data->app_name)
923     {
924       g_warning ("Attempting to add `%s' to the list of recently used "
925                  "resources, but no name of the application that is "
926                  "registering it was defined",
927                  uri);
928       return FALSE;
929     }
930   
931   if (!data->app_exec)
932     {
933       g_warning ("Attempting to add `%s' to the list of recently used "
934                  "resources, but no command line for the application "
935                  "that is registering it was defined",
936                  uri);
937       return FALSE;
938     }
939   
940   priv = manager->priv;
941
942   if (!priv->recent_items)
943     {
944       priv->recent_items = g_bookmark_file_new ();
945       priv->size = 0;
946     }
947
948   if (data->display_name)  
949     g_bookmark_file_set_title (priv->recent_items, uri, data->display_name);
950   
951   if (data->description)
952     g_bookmark_file_set_description (priv->recent_items, uri, data->description);
953
954   g_bookmark_file_set_mime_type (priv->recent_items, uri, data->mime_type);
955   
956   if (data->groups && data->groups[0] != '\0')
957     {
958       gint j;
959       
960       for (j = 0; (data->groups)[j] != NULL; j++)
961         g_bookmark_file_add_group (priv->recent_items, uri, (data->groups)[j]);
962     }
963   
964   /* register the application; this will take care of updating the
965    * registration count and time in case the application has
966    * already registered the same document inside the list
967    */
968   g_bookmark_file_add_application (priv->recent_items, uri,
969                                    data->app_name,
970                                    data->app_exec);
971   
972   g_bookmark_file_set_is_private (priv->recent_items, uri,
973                                   data->is_private);
974   
975   /* mark us as dirty, so that when emitting the "changed" signal we
976    * will dump our changes
977    */
978   priv->is_dirty = TRUE;
979   
980   gtk_recent_manager_changed (manager);
981   
982   return TRUE;
983 }
984
985 /**
986  * gtk_recent_manager_remove_item:
987  * @manager: a #GtkRecentManager
988  * @uri: the URI of the item you wish to remove
989  * @error: return location for a #GError, or %NULL
990  *
991  * Removes a resource pointed by @uri from the recently used resources
992  * list handled by a recent manager.
993  *
994  * Return value: %TRUE if the item pointed by @uri has been successfully
995  *   removed by the recently used resources list, and %FALSE otherwise.
996  *
997  * Since: 2.10
998  */
999 gboolean
1000 gtk_recent_manager_remove_item (GtkRecentManager  *manager,
1001                                 const gchar       *uri,
1002                                 GError           **error)
1003 {
1004   GtkRecentManagerPrivate *priv;
1005   GError *remove_error = NULL;
1006
1007   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
1008   g_return_val_if_fail (uri != NULL, FALSE);
1009   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1010   
1011   priv = manager->priv;
1012   
1013   if (!priv->recent_items)
1014     {
1015       priv->recent_items = g_bookmark_file_new ();
1016       priv->size = 0;
1017
1018       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1019                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1020                    _("Unable to find an item with URI '%s'"),
1021                    uri);
1022
1023       return FALSE;
1024     }
1025
1026   g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
1027   if (remove_error)
1028     {
1029       g_error_free (remove_error);
1030
1031       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1032                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1033                    _("Unable to find an item with URI '%s'"),
1034                    uri);
1035       
1036       return FALSE;
1037     }
1038
1039   priv->is_dirty = TRUE;
1040
1041   gtk_recent_manager_changed (manager);
1042   
1043   return TRUE;
1044 }
1045
1046 /**
1047  * gtk_recent_manager_has_item:
1048  * @manager: a #GtkRecentManager
1049  * @uri: a URI
1050  *
1051  * Checks whether there is a recently used resource registered
1052  * with @uri inside the recent manager.
1053  *
1054  * Return value: %TRUE if the resource was found, %FALSE otherwise.
1055  *
1056  * Since: 2.10
1057  */
1058 gboolean
1059 gtk_recent_manager_has_item (GtkRecentManager *manager,
1060                              const gchar      *uri)
1061 {
1062   GtkRecentManagerPrivate *priv;
1063
1064   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
1065   g_return_val_if_fail (uri != NULL, FALSE);
1066
1067   priv = manager->priv;
1068   g_return_val_if_fail (priv->recent_items != NULL, FALSE);
1069
1070   return g_bookmark_file_has_item (priv->recent_items, uri);
1071 }
1072
1073 static void
1074 build_recent_info (GBookmarkFile  *bookmarks,
1075                    GtkRecentInfo  *info)
1076 {
1077   gchar **apps, **groups;
1078   gsize apps_len, groups_len, i;
1079
1080   g_assert (bookmarks != NULL);
1081   g_assert (info != NULL);
1082   
1083   info->display_name = g_bookmark_file_get_title (bookmarks, info->uri, NULL);
1084   info->description = g_bookmark_file_get_description (bookmarks, info->uri, NULL);
1085   info->mime_type = g_bookmark_file_get_mime_type (bookmarks, info->uri, NULL);
1086     
1087   info->is_private = g_bookmark_file_get_is_private (bookmarks, info->uri, NULL);
1088   
1089   info->added = g_bookmark_file_get_added (bookmarks, info->uri, NULL);
1090   info->modified = g_bookmark_file_get_modified (bookmarks, info->uri, NULL);
1091   info->visited = g_bookmark_file_get_visited (bookmarks, info->uri, NULL);
1092   
1093   groups = g_bookmark_file_get_groups (bookmarks, info->uri, &groups_len, NULL);
1094   for (i = 0; i < groups_len; i++)
1095     {
1096       gchar *group_name = g_strdup (groups[i]);
1097       
1098       info->groups = g_slist_append (info->groups, group_name);
1099     }
1100
1101   g_strfreev (groups);
1102   
1103   apps = g_bookmark_file_get_applications (bookmarks, info->uri, &apps_len, NULL);
1104   for (i = 0; i < apps_len; i++)
1105     {
1106       gchar *app_name, *app_exec;
1107       guint count;
1108       time_t stamp;
1109       RecentAppInfo *app_info;
1110       gboolean res;
1111       
1112       app_name = apps[i];
1113       
1114       res = g_bookmark_file_get_app_info (bookmarks, info->uri, app_name,
1115                                           &app_exec,
1116                                           &count,
1117                                           &stamp,
1118                                           NULL);
1119       if (!res)
1120         continue;
1121       
1122       app_info = recent_app_info_new (app_name);
1123       app_info->exec = app_exec;
1124       app_info->count = count;
1125       app_info->stamp = stamp;
1126       
1127       info->applications = g_slist_prepend (info->applications, app_info);
1128       g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
1129     }
1130   
1131   g_strfreev (apps);
1132 }
1133
1134 /**
1135  * gtk_recent_manager_lookup_item:
1136  * @manager: a #GtkRecentManager
1137  * @uri: a URI
1138  * @error: a return location for a #GError, or %NULL
1139  *
1140  * Searches for a URI inside the recently used resources list, and
1141  * returns a structure containing informations about the resource
1142  * like its MIME type, or its display name.
1143  *
1144  * Return value: a #GtkRecentInfo structure containing information
1145  *   about the resource pointed by @uri, or %NULL if the URI was
1146  *   not registered in the recently used resources list.  Free with
1147  *   gtk_recent_info_unref().
1148  *
1149  * Since: 2.10
1150  */
1151 GtkRecentInfo *
1152 gtk_recent_manager_lookup_item (GtkRecentManager  *manager,
1153                                 const gchar       *uri,
1154                                 GError           **error)
1155 {
1156   GtkRecentManagerPrivate *priv;
1157   GtkRecentInfo *info = NULL;
1158   
1159   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
1160   g_return_val_if_fail (uri != NULL, NULL);
1161   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1162   
1163   priv = manager->priv;
1164   if (!priv->recent_items)
1165     {
1166       priv->recent_items = g_bookmark_file_new ();
1167       priv->size = 0;
1168
1169       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1170                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1171                    _("Unable to find an item with URI '%s'"),
1172                    uri);
1173
1174       return NULL;
1175     }
1176   
1177   if (!g_bookmark_file_has_item (priv->recent_items, uri))
1178     {
1179       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1180                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1181                    _("Unable to find an item with URI '%s'"),
1182                    uri);
1183       return NULL;
1184     }
1185   
1186   info = gtk_recent_info_new (uri);
1187   g_return_val_if_fail (info != NULL, NULL);
1188   
1189   /* fill the RecentInfo structure with the data retrieved by our
1190    * parser object from the storage file 
1191    */
1192   build_recent_info (priv->recent_items, info);
1193
1194   return info;
1195 }
1196
1197 /**
1198  * gtk_recent_manager_move_item:
1199  * @manager: a #GtkRecentManager
1200  * @uri: the URI of a recently used resource
1201  * @new_uri: the new URI of the recently used resource, or %NULL to
1202  *    remove the item pointed by @uri in the list
1203  * @error: a return location for a #GError, or %NULL
1204  *
1205  * Changes the location of a recently used resource from @uri to @new_uri.
1206  * 
1207  * Please note that this function will not affect the resource pointed
1208  * by the URIs, but only the URI used in the recently used resources list.
1209  *
1210  * Return value: %TRUE on success.
1211  *
1212  * Since: 2.10
1213  */ 
1214 gboolean
1215 gtk_recent_manager_move_item (GtkRecentManager  *recent_manager,
1216                               const gchar       *uri,
1217                               const gchar       *new_uri,
1218                               GError           **error)
1219 {
1220   GtkRecentManagerPrivate *priv;
1221   GError *move_error;
1222   gboolean res;
1223   
1224   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
1225   g_return_val_if_fail (uri != NULL, FALSE);
1226   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1227   
1228   priv = recent_manager->priv;
1229
1230   if (!priv->recent_items)
1231     {
1232       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1233                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1234                    _("Unable to find an item with URI '%s'"),
1235                    uri);
1236       return FALSE;
1237     }
1238
1239   if (!g_bookmark_file_has_item (priv->recent_items, uri))
1240     {
1241       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1242                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1243                    _("Unable to find an item with URI '%s'"),
1244                    uri);
1245       return FALSE;
1246     }
1247   
1248   move_error = NULL;
1249   res = g_bookmark_file_move_item (priv->recent_items,
1250                                    uri, new_uri,
1251                                    &move_error);
1252   if (move_error)
1253     {
1254       g_error_free (move_error);
1255
1256       g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1257                    GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1258                    _("Unable to find an item with URI '%s'"),
1259                    uri);
1260       return FALSE;
1261     }
1262   
1263   priv->is_dirty = TRUE;
1264
1265   gtk_recent_manager_changed (recent_manager);
1266   
1267   return TRUE;
1268 }
1269
1270 /**
1271  * gtk_recent_manager_get_items:
1272  * @manager: a #GtkRecentManager
1273  *
1274  * Gets the list of recently used resources.
1275  *
1276  * Return value: a list of newly allocated #GtkRecentInfo objects. Use
1277  *   gtk_recent_info_unref() on each item inside the list, and then
1278  *   free the list itself using g_list_free().
1279  *
1280  * Since: 2.10
1281  */
1282 GList *
1283 gtk_recent_manager_get_items (GtkRecentManager *manager)
1284 {
1285   GtkRecentManagerPrivate *priv;
1286   GList *retval = NULL;
1287   gchar **uris;
1288   gsize uris_len, i;
1289   
1290   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
1291   
1292   priv = manager->priv;
1293   if (!priv->recent_items)
1294     return NULL;
1295
1296   if (priv->limit == 0)
1297     return NULL;
1298   
1299   uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
1300   for (i = 0; i < uris_len; i++)
1301     {
1302       GtkRecentInfo *info;
1303       
1304       if (priv->limit != -1 && i == priv->limit)
1305         break;
1306       
1307       info = gtk_recent_info_new (uris[i]);
1308       build_recent_info (priv->recent_items, info);
1309       
1310       retval = g_list_prepend (retval, info);
1311     }
1312   
1313   g_strfreev (uris);
1314   
1315   return retval;
1316 }
1317
1318 static void
1319 purge_recent_items_list (GtkRecentManager  *manager,
1320                          GError           **error)
1321 {
1322   GtkRecentManagerPrivate *priv = manager->priv;
1323
1324   if (!priv->recent_items)
1325     return;
1326   
1327   g_bookmark_file_free (priv->recent_items);
1328   priv->recent_items = NULL;
1329       
1330   priv->recent_items = g_bookmark_file_new ();
1331   priv->size = 0;
1332   priv->is_dirty = TRUE;
1333       
1334   /* emit the changed signal, to ensure that the purge is written */
1335   gtk_recent_manager_changed (manager);
1336 }
1337
1338 /**
1339  * gtk_recent_manager_purge_items:
1340  * @manager: a #GtkRecentManager
1341  * @error: a return location for a #GError, or %NULL
1342  *
1343  * Purges every item from the recently used resources list.
1344  *
1345  * Return value: the number of items that have been removed from the
1346  *   recently used resources list.
1347  *
1348  * Since: 2.10
1349  */
1350 gint
1351 gtk_recent_manager_purge_items (GtkRecentManager  *manager,
1352                                 GError           **error)
1353 {
1354   GtkRecentManagerPrivate *priv;
1355   gint count, purged;
1356   
1357   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), -1);
1358
1359   priv = manager->priv;
1360   if (!priv->recent_items)
1361     return 0;
1362   
1363   count = g_bookmark_file_get_size (priv->recent_items);
1364   if (!count)
1365     return 0;
1366   
1367   purge_recent_items_list (manager, error);
1368   
1369   purged = count - g_bookmark_file_get_size (priv->recent_items);
1370
1371   return purged;
1372 }
1373
1374 static void
1375 gtk_recent_manager_changed (GtkRecentManager *recent_manager)
1376 {
1377   g_signal_emit (recent_manager, signal_changed, 0);
1378 }
1379
1380 static void
1381 gtk_recent_manager_clamp_to_age (GtkRecentManager *manager,
1382                                  gint              age)
1383 {
1384   GtkRecentManagerPrivate *priv = manager->priv;
1385   gchar **uris;
1386   gsize n_uris, i;
1387   time_t now;
1388
1389   if (G_UNLIKELY (!priv->recent_items))
1390     return;
1391
1392   now = time (NULL);
1393
1394   uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
1395
1396   for (i = 0; i < n_uris; i++)
1397     {
1398       const gchar *uri = uris[i];
1399       time_t modified;
1400       gint item_age;
1401
1402       modified = g_bookmark_file_get_modified (priv->recent_items, uri, NULL);
1403       item_age = (gint) ((now - modified) / (60 * 60 * 24));
1404       if (item_age > age)
1405         g_bookmark_file_remove_item (priv->recent_items, uri, NULL);
1406     }
1407
1408   g_strfreev (uris);
1409 }
1410
1411 /*****************
1412  * GtkRecentInfo *
1413  *****************/
1414  
1415 GType
1416 gtk_recent_info_get_type (void)
1417 {
1418   static GType info_type = 0;
1419   
1420   if (!info_type)
1421     info_type = g_boxed_type_register_static (I_("GtkRecentInfo"),
1422                                               (GBoxedCopyFunc) gtk_recent_info_ref,
1423                                               (GBoxedFreeFunc) gtk_recent_info_unref);
1424   return info_type;
1425 }
1426
1427 static GtkRecentInfo *
1428 gtk_recent_info_new (const gchar *uri)
1429 {
1430   GtkRecentInfo *info;
1431
1432   g_assert (uri != NULL);
1433
1434   info = g_new0 (GtkRecentInfo, 1);
1435   info->uri = g_strdup (uri);
1436   
1437   info->applications = NULL;
1438   info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
1439   
1440   info->groups = NULL;
1441   
1442   info->ref_count = 1;
1443
1444   return info;
1445 }
1446
1447 static void
1448 gtk_recent_info_free (GtkRecentInfo *recent_info)
1449 {
1450   if (!recent_info)
1451     return;
1452
1453   g_free (recent_info->uri);
1454   g_free (recent_info->display_name);
1455   g_free (recent_info->description);
1456   g_free (recent_info->mime_type);
1457   
1458   if (recent_info->applications)
1459     {
1460       g_slist_foreach (recent_info->applications,
1461                        (GFunc) recent_app_info_free,
1462                        NULL);
1463       g_slist_free (recent_info->applications);
1464       
1465       recent_info->applications = NULL;
1466     }
1467   
1468   if (recent_info->apps_lookup)
1469     g_hash_table_destroy (recent_info->apps_lookup);
1470
1471   if (recent_info->groups)
1472     {
1473       g_slist_foreach (recent_info->groups,
1474                        (GFunc) g_free,
1475                        NULL);
1476       g_slist_free (recent_info->groups);
1477
1478       recent_info->groups = NULL;
1479     }
1480   
1481   if (recent_info->icon)
1482     g_object_unref (recent_info->icon);
1483
1484   g_free (recent_info);
1485 }
1486
1487 /**
1488  * gtk_recent_info_ref:
1489  * @info: a #GtkRecentInfo
1490  *
1491  * Increases the reference count of @recent_info by one.
1492  *
1493  * Return value: the recent info object with its reference count increased
1494  *   by one.
1495  *
1496  * Since: 2.10
1497  */
1498 GtkRecentInfo *
1499 gtk_recent_info_ref (GtkRecentInfo *info)
1500 {
1501   g_return_val_if_fail (info != NULL, NULL);
1502   g_return_val_if_fail (info->ref_count > 0, NULL);
1503   
1504   info->ref_count += 1;
1505     
1506   return info;
1507 }
1508
1509 /**
1510  * gtk_recent_info_unref:
1511  * @info: a #GtkRecentInfo
1512  *
1513  * Decreases the reference count of @info by one.  If the reference
1514  * count reaches zero, @info is deallocated, and the memory freed.
1515  *
1516  * Since: 2.10
1517  */
1518 void
1519 gtk_recent_info_unref (GtkRecentInfo *info)
1520 {
1521   g_return_if_fail (info != NULL);
1522   g_return_if_fail (info->ref_count > 0);
1523
1524   info->ref_count -= 1;
1525   
1526   if (info->ref_count == 0)
1527     gtk_recent_info_free (info);
1528 }
1529
1530 /**
1531  * gtk_recent_info_get_uri:
1532  * @info: a #GtkRecentInfo
1533  *
1534  * Gets the URI of the resource.
1535  *
1536  * Return value: the URI of the resource.  The returned string is
1537  *   owned by the recent manager, and should not be freed.
1538  *
1539  * Since: 2.10
1540  */
1541 G_CONST_RETURN gchar *
1542 gtk_recent_info_get_uri (GtkRecentInfo *info)
1543 {
1544   g_return_val_if_fail (info != NULL, NULL);
1545   
1546   return info->uri;
1547 }
1548
1549 /**
1550  * gtk_recent_info_get_display_name:
1551  * @info: a #GtkRecentInfo
1552  *
1553  * Gets the name of the resource.  If none has been defined, the basename
1554  * of the resource is obtained.
1555  *
1556  * Return value: the display name of the resource.  The returned string
1557  *   is owned by the recent manager, and should not be freed.
1558  *
1559  * Since: 2.10
1560  */
1561 G_CONST_RETURN gchar *
1562 gtk_recent_info_get_display_name (GtkRecentInfo *info)
1563 {
1564   g_return_val_if_fail (info != NULL, NULL);
1565   
1566   if (!info->display_name)
1567     info->display_name = gtk_recent_info_get_short_name (info);
1568   
1569   return info->display_name;
1570 }
1571
1572 /**
1573  * gtk_recent_info_get_description:
1574  * @info: a #GtkRecentInfo
1575  *
1576  * Gets the (short) description of the resource.
1577  *
1578  * Return value: the description of the resource.  The returned string
1579  *   is owned by the recent manager, and should not be freed.
1580  *
1581  * Since: 2.10
1582  **/
1583 G_CONST_RETURN gchar *
1584 gtk_recent_info_get_description (GtkRecentInfo *info)
1585 {
1586   g_return_val_if_fail (info != NULL, NULL);
1587   
1588   return info->description;
1589 }
1590
1591 /**
1592  * gtk_recent_info_get_mime_type:
1593  * @info: a #GtkRecentInfo
1594  *
1595  * Gets the MIME type of the resource.
1596  *
1597  * Return value: the MIME type of the resource.  The returned string
1598  *   is owned by the recent manager, and should not be freed.
1599  *
1600  * Since: 2.10
1601  */
1602 G_CONST_RETURN gchar *
1603 gtk_recent_info_get_mime_type (GtkRecentInfo *info)
1604 {
1605   g_return_val_if_fail (info != NULL, NULL);
1606   
1607   if (!info->mime_type)
1608     info->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
1609   
1610   return info->mime_type;
1611 }
1612
1613 /**
1614  * gtk_recent_info_get_added:
1615  * @info: a #GtkRecentInfo
1616  *
1617  * Gets the timestamp (seconds from system's Epoch) when the resource
1618  * was added to the recently used resources list.
1619  *
1620  * Return value: the number of seconds elapsed from system's Epoch when
1621  *   the resource was added to the list, or -1 on failure.
1622  *
1623  * Since: 2.10
1624  */
1625 time_t
1626 gtk_recent_info_get_added (GtkRecentInfo *info)
1627 {
1628   g_return_val_if_fail (info != NULL, (time_t) -1);
1629   
1630   return info->added;
1631 }
1632
1633 /**
1634  * gtk_recent_info_get_modified:
1635  * @info: a #GtkRecentInfo
1636  *
1637  * Gets the timestamp (seconds from system's Epoch) when the resource
1638  * was last modified.
1639  *
1640  * Return value: the number of seconds elapsed from system's Epoch when
1641  *   the resource was last modified, or -1 on failure.
1642  *
1643  * Since: 2.10
1644  */
1645 time_t
1646 gtk_recent_info_get_modified (GtkRecentInfo *info)
1647 {
1648   g_return_val_if_fail (info != NULL, (time_t) -1);
1649   
1650   return info->modified;
1651 }
1652
1653 /**
1654  * gtk_recent_info_get_visited:
1655  * @info: a #GtkRecentInfo
1656  *
1657  * Gets the timestamp (seconds from system's Epoch) when the resource
1658  * was last visited.
1659  *
1660  * Return value: the number of seconds elapsed from system's Epoch when
1661  *   the resource was last visited, or -1 on failure.
1662  *
1663  * Since: 2.10
1664  */
1665 time_t
1666 gtk_recent_info_get_visited (GtkRecentInfo *info)
1667 {
1668   g_return_val_if_fail (info != NULL, (time_t) -1);
1669   
1670   return info->visited;
1671 }
1672
1673 /**
1674  * gtk_recent_info_get_private_hint:
1675  * @info: a #GtkRecentInfo
1676  *
1677  * Gets the value of the "private" flag.  Resources in the recently used
1678  * list that have this flag set to %TRUE should only be displayed by the
1679  * applications that have registered them.
1680  *
1681  * Return value: %TRUE if the private flag was found, %FALSE otherwise.
1682  *
1683  * Since: 2.10
1684  */
1685 gboolean
1686 gtk_recent_info_get_private_hint (GtkRecentInfo *info)
1687 {
1688   g_return_val_if_fail (info != NULL, FALSE);
1689   
1690   return info->is_private;
1691 }
1692
1693
1694 static RecentAppInfo *
1695 recent_app_info_new (const gchar *app_name)
1696 {
1697   RecentAppInfo *app_info;
1698
1699   g_assert (app_name != NULL);
1700   
1701   app_info = g_slice_new0 (RecentAppInfo);
1702   app_info->name = g_strdup (app_name);
1703   app_info->exec = NULL;
1704   app_info->count = 1;
1705   app_info->stamp = 0; 
1706   
1707   return app_info;
1708 }
1709
1710 static void
1711 recent_app_info_free (RecentAppInfo *app_info)
1712 {
1713   if (!app_info)
1714     return;
1715   
1716   g_free (app_info->name);
1717   g_free (app_info->exec);
1718   
1719   g_slice_free (RecentAppInfo, app_info);
1720 }
1721
1722 /**
1723  * gtk_recent_info_get_application_info:
1724  * @info: a #GtkRecentInfo
1725  * @app_name: the name of the application that has registered this item
1726  * @app_exec: return location for the string containing the command line
1727  * @count: return location for the number of times this item was registered
1728  * @time_: return location for the timestamp this item was last registered
1729  *    for this application
1730  *
1731  * Gets the data regarding the application that has registered the resource
1732  * pointed by @info.
1733  *
1734  * If the command line contains any escape characters defined inside the
1735  * storage specification, they will be expanded.
1736  *
1737  * Return value: %TRUE if an application with @app_name has registered this
1738  *   resource inside the recently used list, or %FALSE otherwise. The
1739  *   @app_exec string is owned by the #GtkRecentInfo and should not be
1740  *   modified or freed
1741  *
1742  * Since: 2.10
1743  */
1744 gboolean
1745 gtk_recent_info_get_application_info (GtkRecentInfo  *info,
1746                                       const gchar    *app_name,
1747                                       const gchar   **app_exec,
1748                                       guint          *count,
1749                                       time_t         *time_)
1750 {
1751   RecentAppInfo *ai;
1752   
1753   g_return_val_if_fail (info != NULL, FALSE);
1754   g_return_val_if_fail (app_name != NULL, FALSE);
1755   
1756   ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
1757                                               app_name);
1758   if (!ai)
1759     {
1760       g_warning ("No registered application with name '%s' "
1761                  "for item with URI '%s' found",
1762                  app_name,
1763                  info->uri);
1764       return FALSE;
1765     }
1766   
1767   if (app_exec)
1768     *app_exec = ai->exec;
1769   
1770   if (count)
1771     *count = ai->count;
1772   
1773   if (time_)
1774     *time_ = ai->stamp;
1775
1776   return TRUE;
1777 }
1778
1779 /**
1780  * gtk_recent_info_get_applications:
1781  * @info: a #GtkRecentInfo
1782  * @length: return location for the length of the returned list, or %NULL
1783  *
1784  * Retrieves the list of applications that have registered this resource.
1785  *
1786  * Return value: a newly allocated %NULL-terminated array of strings.
1787  *   Use g_strfreev() to free it.
1788  *
1789  * Since: 2.10
1790  */                           
1791 gchar **
1792 gtk_recent_info_get_applications (GtkRecentInfo *info,
1793                                   gsize         *length)
1794 {
1795   GSList *l;
1796   gchar **retval;
1797   gsize n_apps, i;
1798   
1799   g_return_val_if_fail (info != NULL, NULL);
1800   
1801   if (!info->applications)
1802     {
1803       if (length)
1804         *length = 0;
1805       
1806       return NULL;    
1807     }
1808   
1809   n_apps = g_slist_length (info->applications);
1810   
1811   retval = g_new0 (gchar *, n_apps + 1);
1812   
1813   for (l = info->applications, i = 0;
1814        l != NULL;
1815        l = l->next)
1816     {
1817       RecentAppInfo *ai = (RecentAppInfo *) l->data;
1818       
1819       g_assert (ai != NULL);
1820       
1821       retval[i++] = g_strdup (ai->name);
1822     }
1823   retval[i] = NULL;
1824   
1825   if (length)
1826     *length = i;
1827   
1828   return retval;
1829 }
1830
1831 /**
1832  * gtk_recent_info_has_application:
1833  * @info: a #GtkRecentInfo
1834  * @app_name: a string containing an application name
1835  *
1836  * Checks whether an application registered this resource using @app_name.
1837  *
1838  * Return value: %TRUE if an application with name @app_name was found,
1839  *   %FALSE otherwise.
1840  *
1841  * Since: 2.10
1842  */
1843 gboolean
1844 gtk_recent_info_has_application (GtkRecentInfo *info,
1845                                  const gchar   *app_name)
1846 {
1847   g_return_val_if_fail (info != NULL, FALSE);
1848   g_return_val_if_fail (app_name != NULL, FALSE);
1849   
1850   return (NULL != g_hash_table_lookup (info->apps_lookup, app_name));
1851 }
1852
1853 /**
1854  * gtk_recent_info_last_application:
1855  * @info: a #GtkRecentInfo
1856  *
1857  * Gets the name of the last application that have registered the
1858  * recently used resource represented by @info.
1859  *
1860  * Return value: an application name.  Use g_free() to free it.
1861  *
1862  * Since: 2.10
1863  */
1864 gchar *
1865 gtk_recent_info_last_application (GtkRecentInfo  *info)
1866 {
1867   GSList *l;
1868   time_t last_stamp = (time_t) -1;
1869   gchar *name = NULL;
1870   
1871   g_return_val_if_fail (info != NULL, NULL);
1872   
1873   for (l = info->applications; l != NULL; l = l->next)
1874     {
1875       RecentAppInfo *ai = (RecentAppInfo *) l->data;
1876       
1877       if (ai->stamp > last_stamp)
1878         {
1879           name = ai->name;
1880           last_stamp = ai->stamp;
1881         }
1882     }
1883   
1884   return g_strdup (name);
1885 }
1886
1887 static GdkPixbuf *
1888 get_icon_for_mime_type (const char *mime_type,
1889                         gint        pixel_size)
1890 {
1891   GtkIconTheme *icon_theme;
1892   char *content_type;
1893   GIcon *icon;
1894   GtkIconInfo *info;
1895   GdkPixbuf *pixbuf;
1896
1897   icon_theme = gtk_icon_theme_get_default ();
1898
1899   content_type = g_content_type_from_mime_type (mime_type);
1900
1901   if (!content_type)
1902     return NULL;
1903
1904   icon = g_content_type_get_icon (content_type);
1905   info = gtk_icon_theme_lookup_by_gicon (icon_theme, 
1906                                          icon, 
1907                                          pixel_size, 
1908                                          GTK_ICON_LOOKUP_USE_BUILTIN);
1909   g_free (content_type);
1910   g_object_unref (icon);
1911
1912   if (!info)
1913     return NULL;
1914
1915   pixbuf = gtk_icon_info_load_icon (info, NULL);
1916   gtk_icon_info_free (info);
1917
1918   return pixbuf;
1919 }
1920
1921 static GdkPixbuf *
1922 get_icon_fallback (const gchar *icon_name,
1923                    gint         size)
1924 {
1925   GtkIconTheme *icon_theme;
1926   GdkPixbuf *retval;
1927
1928   icon_theme = gtk_icon_theme_get_default ();
1929   
1930   retval = gtk_icon_theme_load_icon (icon_theme, icon_name,
1931                                      size,
1932                                      GTK_ICON_LOOKUP_USE_BUILTIN,
1933                                      NULL);
1934   g_assert (retval != NULL);
1935   
1936   return retval; 
1937 }
1938
1939 /**
1940  * gtk_recent_info_get_icon:
1941  * @info: a #GtkRecentInfo
1942  * @size: the size of the icon in pixels
1943  *
1944  * Retrieves the icon of size @size associated to the resource MIME type.
1945  *
1946  * Return value: a #GdkPixbuf containing the icon, or %NULL. Use
1947  *   g_object_unref() when finished using the icon.
1948  *
1949  * Since: 2.10
1950  */
1951 GdkPixbuf *
1952 gtk_recent_info_get_icon (GtkRecentInfo *info,
1953                           gint           size)
1954 {
1955   GdkPixbuf *retval = NULL;
1956   
1957   g_return_val_if_fail (info != NULL, NULL);
1958   
1959   if (info->mime_type)
1960     retval = get_icon_for_mime_type (info->mime_type, size);
1961
1962   /* this function should never fail */  
1963   if (!retval)
1964     {
1965       if (info->mime_type &&
1966           strcmp (info->mime_type, "x-directory/normal") == 0)
1967         retval = get_icon_fallback (GTK_STOCK_DIRECTORY, size);
1968       else
1969         retval = get_icon_fallback (GTK_STOCK_FILE, size);
1970     }
1971   
1972   return retval;
1973 }
1974
1975 /**
1976  * gtk_recent_info_is_local:
1977  * @info: a #GtkRecentInfo
1978  *
1979  * Checks whether the resource is local or not by looking at the
1980  * scheme of its URI.
1981  *
1982  * Return value: %TRUE if the resource is local.
1983  *
1984  * Since: 2.10
1985  */
1986 gboolean
1987 gtk_recent_info_is_local (GtkRecentInfo *info)
1988 {
1989   g_return_val_if_fail (info != NULL, FALSE);
1990   
1991   return has_case_prefix (info->uri, "file:/");
1992 }
1993
1994 /**
1995  * gtk_recent_info_exists:
1996  * @info: a #GtkRecentInfo
1997  *
1998  * Checks whether the resource pointed by @info still exists.  At
1999  * the moment this check is done only on resources pointing to local files.
2000  *
2001  * Return value: %TRUE if the resource exists
2002  *
2003  * Since: 2.10
2004  */
2005 gboolean
2006 gtk_recent_info_exists (GtkRecentInfo *info)
2007 {
2008   gchar *filename;
2009   struct stat stat_buf;
2010   gboolean retval = FALSE;
2011   
2012   g_return_val_if_fail (info != NULL, FALSE);
2013   
2014   /* we guarantee only local resources */
2015   if (!gtk_recent_info_is_local (info))
2016     return FALSE;
2017   
2018   filename = g_filename_from_uri (info->uri, NULL, NULL);
2019   if (filename)
2020     {
2021       if (stat (filename, &stat_buf) == 0)
2022         retval = TRUE;
2023      
2024       g_free (filename);
2025     }
2026   
2027   return retval;
2028 }
2029
2030 /**
2031  * gtk_recent_info_match:
2032  * @info_a: a #GtkRecentInfo
2033  * @info_b: a #GtkRecentInfo
2034  *
2035  * Checks whether two #GtkRecentInfo structures point to the same
2036  * resource.
2037  *
2038  * Return value: %TRUE if both #GtkRecentInfo structures point to se same
2039  *   resource, %FALSE otherwise.
2040  *
2041  * Since: 2.10
2042  */
2043 gboolean
2044 gtk_recent_info_match (GtkRecentInfo *info_a,
2045                        GtkRecentInfo *info_b)
2046 {
2047   g_return_val_if_fail (info_a != NULL, FALSE);
2048   g_return_val_if_fail (info_b != NULL, FALSE);
2049   
2050   return (0 == strcmp (info_a->uri, info_b->uri));
2051 }
2052
2053 /* taken from gnome-vfs-uri.c */
2054 static const gchar *
2055 get_method_string (const gchar  *substring, 
2056                    gchar       **method_string)
2057 {
2058   const gchar *p;
2059   char *method;
2060         
2061   for (p = substring;
2062        g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
2063        p++)
2064     ;
2065
2066   if (*p == ':'
2067 #ifdef G_OS_WIN32
2068                 &&
2069       !(p == substring + 1 && g_ascii_isalpha (*substring))
2070 #endif
2071                                                            )
2072     {
2073       /* Found toplevel method specification.  */
2074       method = g_strndup (substring, p - substring);
2075       *method_string = g_ascii_strdown (method, -1);
2076       g_free (method);
2077       p++;
2078     }
2079   else
2080     {
2081       *method_string = g_strdup ("file");
2082       p = substring;
2083     }
2084   
2085   return p;
2086 }
2087
2088 /* Stolen from gnome_vfs_make_valid_utf8() */
2089 static char *
2090 make_valid_utf8 (const char *name)
2091 {
2092   GString *string;
2093   const char *remainder, *invalid;
2094   int remaining_bytes, valid_bytes;
2095
2096   string = NULL;
2097   remainder = name;
2098   remaining_bytes = name ? strlen (name) : 0;
2099
2100   while (remaining_bytes != 0)
2101     {
2102       if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2103         break;
2104       
2105       valid_bytes = invalid - remainder;
2106       
2107       if (string == NULL)
2108         string = g_string_sized_new (remaining_bytes);
2109       
2110       g_string_append_len (string, remainder, valid_bytes);
2111       g_string_append_c (string, '?');
2112       
2113       remaining_bytes -= valid_bytes + 1;
2114       remainder = invalid + 1;
2115     }
2116   
2117   if (string == NULL)
2118     return g_strdup (name);
2119
2120   g_string_append (string, remainder);
2121   g_assert (g_utf8_validate (string->str, -1, NULL));
2122
2123   return g_string_free (string, FALSE);
2124 }
2125
2126 static gchar *
2127 get_uri_shortname_for_display (const gchar *uri)
2128 {
2129   gchar *name = NULL;
2130   gboolean validated = FALSE;
2131
2132   if (has_case_prefix (uri, "file:/"))
2133     {
2134       gchar *local_file;
2135       
2136       local_file = g_filename_from_uri (uri, NULL, NULL);
2137       
2138       if (local_file)
2139         {
2140           name = g_filename_display_basename (local_file);
2141           validated = TRUE;
2142         }
2143                 
2144       g_free (local_file);
2145     } 
2146   
2147   if (!name)
2148     {
2149       gchar *method;
2150       gchar *local_file;
2151       const gchar *rest;
2152       
2153       rest = get_method_string (uri, &method);
2154       local_file = g_filename_display_basename (rest);
2155       
2156       name = g_strconcat (method, ": ", local_file, NULL);
2157       
2158       g_free (local_file);
2159       g_free (method);
2160     }
2161   
2162   g_assert (name != NULL);
2163   
2164   if (!validated && !g_utf8_validate (name, -1, NULL))
2165     {
2166       gchar *utf8_name;
2167       
2168       utf8_name = make_valid_utf8 (name);
2169       g_free (name);
2170       
2171       name = utf8_name;
2172     }
2173
2174   return name;
2175 }
2176
2177 /**
2178  * gtk_recent_info_get_short_name:
2179  * @info: an #GtkRecentInfo
2180  *
2181  * Computes a valid UTF-8 string that can be used as the name of the item in a
2182  * menu or list.  For example, calling this function on an item that refers to
2183  * "file:///foo/bar.txt" will yield "bar.txt".
2184  *
2185  * Return value: A newly-allocated string in UTF-8 encoding; free it with
2186  *   g_free().
2187  *
2188  * Since: 2.10
2189  */
2190 gchar *
2191 gtk_recent_info_get_short_name (GtkRecentInfo *info)
2192 {
2193   gchar *short_name;
2194
2195   g_return_val_if_fail (info != NULL, NULL);
2196
2197   if (info->uri == NULL)
2198     return NULL;
2199
2200   short_name = get_uri_shortname_for_display (info->uri);
2201
2202   return short_name;
2203 }
2204
2205 /**
2206  * gtk_recent_info_get_uri_display:
2207  * @info: a #GtkRecentInfo
2208  *
2209  * Gets a displayable version of the resource's URI.  If the resource
2210  * is local, it returns a local path; if the resource is not local,
2211  * it returns the UTF-8 encoded content of gtk_recent_info_get_uri().
2212  *
2213  * Return value: a newly allocated UTF-8 string containing the
2214  *   resource's URI or %NULL. Use g_free() when done using it.
2215  *
2216  * Since: 2.10
2217  */
2218 gchar *
2219 gtk_recent_info_get_uri_display (GtkRecentInfo *info)
2220 {
2221   gchar *retval;
2222   
2223   g_return_val_if_fail (info != NULL, NULL);
2224
2225   retval = NULL;
2226   if (gtk_recent_info_is_local (info))
2227     {
2228       gchar *filename;
2229
2230       filename = g_filename_from_uri (info->uri, NULL, NULL);
2231       if (!filename)
2232         return NULL;
2233       
2234       retval = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
2235       g_free (filename);
2236     }
2237   else
2238     {
2239       retval = make_valid_utf8 (info->uri);
2240     }
2241
2242   return retval;
2243 }
2244
2245 /**
2246  * gtk_recent_info_get_age:
2247  * @info: a #GtkRecentInfo
2248  *
2249  * Gets the number of days elapsed since the last update of the resource
2250  * pointed by @info.
2251  *
2252  * Return value: a positive integer containing the number of days elapsed
2253  *   since the time this resource was last modified.  
2254  *
2255  * Since: 2.10
2256  */
2257 gint
2258 gtk_recent_info_get_age (GtkRecentInfo *info)
2259 {
2260   time_t now, delta;
2261   gint retval;
2262
2263   g_return_val_if_fail (info != NULL, -1);
2264
2265   now = time (NULL);
2266   
2267   delta = now - info->modified;
2268   
2269   retval = (gint) (delta / (60 * 60 * 24));
2270   
2271   return retval;
2272 }
2273
2274 /**
2275  * gtk_recent_info_get_groups:
2276  * @info: a #GtkRecentInfo
2277  * @length: return location for the number of groups returned, or %NULL
2278  *
2279  * Returns all groups registered for the recently used item @info.  The
2280  * array of returned group names will be %NULL terminated, so length might
2281  * optionally be %NULL.
2282  *
2283  * Return value: a newly allocated %NULL terminated array of strings.  Use
2284  *   g_strfreev() to free it.
2285  *
2286  * Since: 2.10
2287  */
2288 gchar **
2289 gtk_recent_info_get_groups (GtkRecentInfo *info,
2290                             gsize         *length)
2291 {
2292   GSList *l;
2293   gchar **retval;
2294   gsize n_groups, i;
2295   
2296   g_return_val_if_fail (info != NULL, NULL);
2297   
2298   if (!info->groups)
2299     {
2300       if (length)
2301         *length = 0;
2302       
2303       return NULL;
2304     }
2305   
2306   n_groups = g_slist_length (info->groups);
2307   
2308   retval = g_new0 (gchar *, n_groups + 1);
2309   
2310   for (l = info->groups, i = 0;
2311        l != NULL;
2312        l = l->next)
2313     {
2314       gchar *group_name = (gchar *) l->data;
2315       
2316       g_assert (group_name != NULL);
2317       
2318       retval[i++] = g_strdup (group_name);
2319     }
2320   retval[i] = NULL;
2321   
2322   if (length)
2323     *length = i;
2324   
2325   return retval;
2326 }
2327
2328 /**
2329  * gtk_recent_info_has_group:
2330  * @info: a #GtkRecentInfo
2331  * @group_name: name of a group
2332  *
2333  * Checks whether @group_name appears inside the groups registered for the
2334  * recently used item @info.
2335  *
2336  * Return value: %TRUE if the group was found.
2337  *
2338  * Since: 2.10
2339  */
2340 gboolean
2341 gtk_recent_info_has_group (GtkRecentInfo *info,
2342                            const gchar   *group_name)
2343 {
2344   GSList *l;
2345   
2346   g_return_val_if_fail (info != NULL, FALSE);
2347   g_return_val_if_fail (group_name != NULL, FALSE);
2348
2349   if (!info->groups)
2350     return FALSE;
2351
2352   for (l = info->groups; l != NULL; l = l->next)
2353     {
2354       gchar *g = (gchar *) l->data;
2355
2356       if (strcmp (g, group_name) == 0)
2357         return TRUE;
2358     }
2359
2360   return FALSE;
2361 }
2362
2363 /*
2364  * _gtk_recent_manager_sync:
2365  * 
2366  * Private function for synchronising the recent manager singleton.
2367  */
2368 void
2369 _gtk_recent_manager_sync (void)
2370 {
2371   if (recent_manager_singleton)
2372     {
2373       /* force a dump of the contents of the recent manager singleton */
2374       recent_manager_singleton->priv->is_dirty = TRUE;
2375       gtk_recent_manager_real_changed (recent_manager_singleton);
2376     }
2377 }
2378
2379 #define __GTK_RECENT_MANAGER_C__
2380 #include "gtkaliasdef.c"