]> Pileus Git - ~andy/gtk/blob - gtk/gtkrecentmanager.c
[introspection] Merge in Gtk-custom.c annotations
[~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: (transfer none): 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:  (element-type GtkRecentInfo) (transfer full): a list of
1277  *   newly allocated #GtkRecentInfo objects. Use
1278  *   gtk_recent_info_unref() on each item inside the list, and then
1279  *   free the list itself using g_list_free().
1280  *
1281  * Since: 2.10
1282  */
1283 GList *
1284 gtk_recent_manager_get_items (GtkRecentManager *manager)
1285 {
1286   GtkRecentManagerPrivate *priv;
1287   GList *retval = NULL;
1288   gchar **uris;
1289   gsize uris_len, i;
1290   
1291   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
1292   
1293   priv = manager->priv;
1294   if (!priv->recent_items)
1295     return NULL;
1296
1297   if (priv->limit == 0)
1298     return NULL;
1299   
1300   uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
1301   for (i = 0; i < uris_len; i++)
1302     {
1303       GtkRecentInfo *info;
1304       
1305       if (priv->limit != -1 && i == priv->limit)
1306         break;
1307       
1308       info = gtk_recent_info_new (uris[i]);
1309       build_recent_info (priv->recent_items, info);
1310       
1311       retval = g_list_prepend (retval, info);
1312     }
1313   
1314   g_strfreev (uris);
1315   
1316   return retval;
1317 }
1318
1319 static void
1320 purge_recent_items_list (GtkRecentManager  *manager,
1321                          GError           **error)
1322 {
1323   GtkRecentManagerPrivate *priv = manager->priv;
1324
1325   if (!priv->recent_items)
1326     return;
1327   
1328   g_bookmark_file_free (priv->recent_items);
1329   priv->recent_items = NULL;
1330       
1331   priv->recent_items = g_bookmark_file_new ();
1332   priv->size = 0;
1333   priv->is_dirty = TRUE;
1334       
1335   /* emit the changed signal, to ensure that the purge is written */
1336   gtk_recent_manager_changed (manager);
1337 }
1338
1339 /**
1340  * gtk_recent_manager_purge_items:
1341  * @manager: a #GtkRecentManager
1342  * @error: a return location for a #GError, or %NULL
1343  *
1344  * Purges every item from the recently used resources list.
1345  *
1346  * Return value: the number of items that have been removed from the
1347  *   recently used resources list.
1348  *
1349  * Since: 2.10
1350  */
1351 gint
1352 gtk_recent_manager_purge_items (GtkRecentManager  *manager,
1353                                 GError           **error)
1354 {
1355   GtkRecentManagerPrivate *priv;
1356   gint count, purged;
1357   
1358   g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), -1);
1359
1360   priv = manager->priv;
1361   if (!priv->recent_items)
1362     return 0;
1363   
1364   count = g_bookmark_file_get_size (priv->recent_items);
1365   if (!count)
1366     return 0;
1367   
1368   purge_recent_items_list (manager, error);
1369   
1370   purged = count - g_bookmark_file_get_size (priv->recent_items);
1371
1372   return purged;
1373 }
1374
1375 static void
1376 gtk_recent_manager_changed (GtkRecentManager *recent_manager)
1377 {
1378   g_signal_emit (recent_manager, signal_changed, 0);
1379 }
1380
1381 static void
1382 gtk_recent_manager_clamp_to_age (GtkRecentManager *manager,
1383                                  gint              age)
1384 {
1385   GtkRecentManagerPrivate *priv = manager->priv;
1386   gchar **uris;
1387   gsize n_uris, i;
1388   time_t now;
1389
1390   if (G_UNLIKELY (!priv->recent_items))
1391     return;
1392
1393   now = time (NULL);
1394
1395   uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
1396
1397   for (i = 0; i < n_uris; i++)
1398     {
1399       const gchar *uri = uris[i];
1400       time_t modified;
1401       gint item_age;
1402
1403       modified = g_bookmark_file_get_modified (priv->recent_items, uri, NULL);
1404       item_age = (gint) ((now - modified) / (60 * 60 * 24));
1405       if (item_age > age)
1406         g_bookmark_file_remove_item (priv->recent_items, uri, NULL);
1407     }
1408
1409   g_strfreev (uris);
1410 }
1411
1412 /*****************
1413  * GtkRecentInfo *
1414  *****************/
1415  
1416 GType
1417 gtk_recent_info_get_type (void)
1418 {
1419   static GType info_type = 0;
1420   
1421   if (!info_type)
1422     info_type = g_boxed_type_register_static (I_("GtkRecentInfo"),
1423                                               (GBoxedCopyFunc) gtk_recent_info_ref,
1424                                               (GBoxedFreeFunc) gtk_recent_info_unref);
1425   return info_type;
1426 }
1427
1428 static GtkRecentInfo *
1429 gtk_recent_info_new (const gchar *uri)
1430 {
1431   GtkRecentInfo *info;
1432
1433   g_assert (uri != NULL);
1434
1435   info = g_new0 (GtkRecentInfo, 1);
1436   info->uri = g_strdup (uri);
1437   
1438   info->applications = NULL;
1439   info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
1440   
1441   info->groups = NULL;
1442   
1443   info->ref_count = 1;
1444
1445   return info;
1446 }
1447
1448 static void
1449 gtk_recent_info_free (GtkRecentInfo *recent_info)
1450 {
1451   if (!recent_info)
1452     return;
1453
1454   g_free (recent_info->uri);
1455   g_free (recent_info->display_name);
1456   g_free (recent_info->description);
1457   g_free (recent_info->mime_type);
1458   
1459   if (recent_info->applications)
1460     {
1461       g_slist_foreach (recent_info->applications,
1462                        (GFunc) recent_app_info_free,
1463                        NULL);
1464       g_slist_free (recent_info->applications);
1465       
1466       recent_info->applications = NULL;
1467     }
1468   
1469   if (recent_info->apps_lookup)
1470     g_hash_table_destroy (recent_info->apps_lookup);
1471
1472   if (recent_info->groups)
1473     {
1474       g_slist_foreach (recent_info->groups,
1475                        (GFunc) g_free,
1476                        NULL);
1477       g_slist_free (recent_info->groups);
1478
1479       recent_info->groups = NULL;
1480     }
1481   
1482   if (recent_info->icon)
1483     g_object_unref (recent_info->icon);
1484
1485   g_free (recent_info);
1486 }
1487
1488 /**
1489  * gtk_recent_info_ref:
1490  * @info: a #GtkRecentInfo
1491  *
1492  * Increases the reference count of @recent_info by one.
1493  *
1494  * Return value: the recent info object with its reference count increased
1495  *   by one.
1496  *
1497  * Since: 2.10
1498  */
1499 GtkRecentInfo *
1500 gtk_recent_info_ref (GtkRecentInfo *info)
1501 {
1502   g_return_val_if_fail (info != NULL, NULL);
1503   g_return_val_if_fail (info->ref_count > 0, NULL);
1504   
1505   info->ref_count += 1;
1506     
1507   return info;
1508 }
1509
1510 /**
1511  * gtk_recent_info_unref:
1512  * @info: a #GtkRecentInfo
1513  *
1514  * Decreases the reference count of @info by one.  If the reference
1515  * count reaches zero, @info is deallocated, and the memory freed.
1516  *
1517  * Since: 2.10
1518  */
1519 void
1520 gtk_recent_info_unref (GtkRecentInfo *info)
1521 {
1522   g_return_if_fail (info != NULL);
1523   g_return_if_fail (info->ref_count > 0);
1524
1525   info->ref_count -= 1;
1526   
1527   if (info->ref_count == 0)
1528     gtk_recent_info_free (info);
1529 }
1530
1531 /**
1532  * gtk_recent_info_get_uri:
1533  * @info: a #GtkRecentInfo
1534  *
1535  * Gets the URI of the resource.
1536  *
1537  * Return value: the URI of the resource.  The returned string is
1538  *   owned by the recent manager, and should not be freed.
1539  *
1540  * Since: 2.10
1541  */
1542 G_CONST_RETURN gchar *
1543 gtk_recent_info_get_uri (GtkRecentInfo *info)
1544 {
1545   g_return_val_if_fail (info != NULL, NULL);
1546   
1547   return info->uri;
1548 }
1549
1550 /**
1551  * gtk_recent_info_get_display_name:
1552  * @info: a #GtkRecentInfo
1553  *
1554  * Gets the name of the resource.  If none has been defined, the basename
1555  * of the resource is obtained.
1556  *
1557  * Return value: the display name of the resource.  The returned string
1558  *   is owned by the recent manager, and should not be freed.
1559  *
1560  * Since: 2.10
1561  */
1562 G_CONST_RETURN gchar *
1563 gtk_recent_info_get_display_name (GtkRecentInfo *info)
1564 {
1565   g_return_val_if_fail (info != NULL, NULL);
1566   
1567   if (!info->display_name)
1568     info->display_name = gtk_recent_info_get_short_name (info);
1569   
1570   return info->display_name;
1571 }
1572
1573 /**
1574  * gtk_recent_info_get_description:
1575  * @info: a #GtkRecentInfo
1576  *
1577  * Gets the (short) description of the resource.
1578  *
1579  * Return value: the description of the resource.  The returned string
1580  *   is owned by the recent manager, and should not be freed.
1581  *
1582  * Since: 2.10
1583  **/
1584 G_CONST_RETURN gchar *
1585 gtk_recent_info_get_description (GtkRecentInfo *info)
1586 {
1587   g_return_val_if_fail (info != NULL, NULL);
1588   
1589   return info->description;
1590 }
1591
1592 /**
1593  * gtk_recent_info_get_mime_type:
1594  * @info: a #GtkRecentInfo
1595  *
1596  * Gets the MIME type of the resource.
1597  *
1598  * Return value: the MIME type of the resource.  The returned string
1599  *   is owned by the recent manager, and should not be freed.
1600  *
1601  * Since: 2.10
1602  */
1603 G_CONST_RETURN gchar *
1604 gtk_recent_info_get_mime_type (GtkRecentInfo *info)
1605 {
1606   g_return_val_if_fail (info != NULL, NULL);
1607   
1608   if (!info->mime_type)
1609     info->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
1610   
1611   return info->mime_type;
1612 }
1613
1614 /**
1615  * gtk_recent_info_get_added:
1616  * @info: a #GtkRecentInfo
1617  *
1618  * Gets the timestamp (seconds from system's Epoch) when the resource
1619  * was added to the recently used resources list.
1620  *
1621  * Return value: the number of seconds elapsed from system's Epoch when
1622  *   the resource was added to the list, or -1 on failure.
1623  *
1624  * Since: 2.10
1625  */
1626 time_t
1627 gtk_recent_info_get_added (GtkRecentInfo *info)
1628 {
1629   g_return_val_if_fail (info != NULL, (time_t) -1);
1630   
1631   return info->added;
1632 }
1633
1634 /**
1635  * gtk_recent_info_get_modified:
1636  * @info: a #GtkRecentInfo
1637  *
1638  * Gets the timestamp (seconds from system's Epoch) when the resource
1639  * was last modified.
1640  *
1641  * Return value: the number of seconds elapsed from system's Epoch when
1642  *   the resource was last modified, or -1 on failure.
1643  *
1644  * Since: 2.10
1645  */
1646 time_t
1647 gtk_recent_info_get_modified (GtkRecentInfo *info)
1648 {
1649   g_return_val_if_fail (info != NULL, (time_t) -1);
1650   
1651   return info->modified;
1652 }
1653
1654 /**
1655  * gtk_recent_info_get_visited:
1656  * @info: a #GtkRecentInfo
1657  *
1658  * Gets the timestamp (seconds from system's Epoch) when the resource
1659  * was last visited.
1660  *
1661  * Return value: the number of seconds elapsed from system's Epoch when
1662  *   the resource was last visited, or -1 on failure.
1663  *
1664  * Since: 2.10
1665  */
1666 time_t
1667 gtk_recent_info_get_visited (GtkRecentInfo *info)
1668 {
1669   g_return_val_if_fail (info != NULL, (time_t) -1);
1670   
1671   return info->visited;
1672 }
1673
1674 /**
1675  * gtk_recent_info_get_private_hint:
1676  * @info: a #GtkRecentInfo
1677  *
1678  * Gets the value of the "private" flag.  Resources in the recently used
1679  * list that have this flag set to %TRUE should only be displayed by the
1680  * applications that have registered them.
1681  *
1682  * Return value: %TRUE if the private flag was found, %FALSE otherwise.
1683  *
1684  * Since: 2.10
1685  */
1686 gboolean
1687 gtk_recent_info_get_private_hint (GtkRecentInfo *info)
1688 {
1689   g_return_val_if_fail (info != NULL, FALSE);
1690   
1691   return info->is_private;
1692 }
1693
1694
1695 static RecentAppInfo *
1696 recent_app_info_new (const gchar *app_name)
1697 {
1698   RecentAppInfo *app_info;
1699
1700   g_assert (app_name != NULL);
1701   
1702   app_info = g_slice_new0 (RecentAppInfo);
1703   app_info->name = g_strdup (app_name);
1704   app_info->exec = NULL;
1705   app_info->count = 1;
1706   app_info->stamp = 0; 
1707   
1708   return app_info;
1709 }
1710
1711 static void
1712 recent_app_info_free (RecentAppInfo *app_info)
1713 {
1714   if (!app_info)
1715     return;
1716   
1717   g_free (app_info->name);
1718   g_free (app_info->exec);
1719   
1720   g_slice_free (RecentAppInfo, app_info);
1721 }
1722
1723 /**
1724  * gtk_recent_info_get_application_info:
1725  * @info: a #GtkRecentInfo
1726  * @app_name: the name of the application that has registered this item
1727  * @app_exec: (transfer none) (out): return location for the string containing the command line
1728  * @count: (out): return location for the number of times this item was registered
1729  * @time_: (out): return location for the timestamp this item was last registered
1730  *    for this application
1731  *
1732  * Gets the data regarding the application that has registered the resource
1733  * pointed by @info.
1734  *
1735  * If the command line contains any escape characters defined inside the
1736  * storage specification, they will be expanded.
1737  *
1738  * Return value: %TRUE if an application with @app_name has registered this
1739  *   resource inside the recently used list, or %FALSE otherwise. The
1740  *   @app_exec string is owned by the #GtkRecentInfo and should not be
1741  *   modified or freed
1742  *
1743  * Since: 2.10
1744  */
1745 gboolean
1746 gtk_recent_info_get_application_info (GtkRecentInfo  *info,
1747                                       const gchar    *app_name,
1748                                       const gchar   **app_exec,
1749                                       guint          *count,
1750                                       time_t         *time_)
1751 {
1752   RecentAppInfo *ai;
1753   
1754   g_return_val_if_fail (info != NULL, FALSE);
1755   g_return_val_if_fail (app_name != NULL, FALSE);
1756   
1757   ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
1758                                               app_name);
1759   if (!ai)
1760     {
1761       g_warning ("No registered application with name '%s' "
1762                  "for item with URI '%s' found",
1763                  app_name,
1764                  info->uri);
1765       return FALSE;
1766     }
1767   
1768   if (app_exec)
1769     *app_exec = ai->exec;
1770   
1771   if (count)
1772     *count = ai->count;
1773   
1774   if (time_)
1775     *time_ = ai->stamp;
1776
1777   return TRUE;
1778 }
1779
1780 /**
1781  * gtk_recent_info_get_applications:
1782  * @info: a #GtkRecentInfo
1783  * @length: (out) (allow-none): return location for the length of the returned list
1784  *
1785  * Retrieves the list of applications that have registered this resource.
1786  *
1787  * Return value: (array length=length zero-terminated=1): a newly allocated
1788  *  %NULL-terminated array of strings. Use g_strfreev() to free it.
1789  *
1790  * Since: 2.10
1791  */
1792 gchar **
1793 gtk_recent_info_get_applications (GtkRecentInfo *info,
1794                                   gsize         *length)
1795 {
1796   GSList *l;
1797   gchar **retval;
1798   gsize n_apps, i;
1799   
1800   g_return_val_if_fail (info != NULL, NULL);
1801   
1802   if (!info->applications)
1803     {
1804       if (length)
1805         *length = 0;
1806       
1807       return NULL;    
1808     }
1809   
1810   n_apps = g_slist_length (info->applications);
1811   
1812   retval = g_new0 (gchar *, n_apps + 1);
1813   
1814   for (l = info->applications, i = 0;
1815        l != NULL;
1816        l = l->next)
1817     {
1818       RecentAppInfo *ai = (RecentAppInfo *) l->data;
1819       
1820       g_assert (ai != NULL);
1821       
1822       retval[i++] = g_strdup (ai->name);
1823     }
1824   retval[i] = NULL;
1825   
1826   if (length)
1827     *length = i;
1828   
1829   return retval;
1830 }
1831
1832 /**
1833  * gtk_recent_info_has_application:
1834  * @info: a #GtkRecentInfo
1835  * @app_name: a string containing an application name
1836  *
1837  * Checks whether an application registered this resource using @app_name.
1838  *
1839  * Return value: %TRUE if an application with name @app_name was found,
1840  *   %FALSE otherwise.
1841  *
1842  * Since: 2.10
1843  */
1844 gboolean
1845 gtk_recent_info_has_application (GtkRecentInfo *info,
1846                                  const gchar   *app_name)
1847 {
1848   g_return_val_if_fail (info != NULL, FALSE);
1849   g_return_val_if_fail (app_name != NULL, FALSE);
1850   
1851   return (NULL != g_hash_table_lookup (info->apps_lookup, app_name));
1852 }
1853
1854 /**
1855  * gtk_recent_info_last_application:
1856  * @info: a #GtkRecentInfo
1857  *
1858  * Gets the name of the last application that have registered the
1859  * recently used resource represented by @info.
1860  *
1861  * Return value: an application name.  Use g_free() to free it.
1862  *
1863  * Since: 2.10
1864  */
1865 gchar *
1866 gtk_recent_info_last_application (GtkRecentInfo  *info)
1867 {
1868   GSList *l;
1869   time_t last_stamp = (time_t) -1;
1870   gchar *name = NULL;
1871   
1872   g_return_val_if_fail (info != NULL, NULL);
1873   
1874   for (l = info->applications; l != NULL; l = l->next)
1875     {
1876       RecentAppInfo *ai = (RecentAppInfo *) l->data;
1877       
1878       if (ai->stamp > last_stamp)
1879         {
1880           name = ai->name;
1881           last_stamp = ai->stamp;
1882         }
1883     }
1884   
1885   return g_strdup (name);
1886 }
1887
1888 static GdkPixbuf *
1889 get_icon_for_mime_type (const char *mime_type,
1890                         gint        pixel_size)
1891 {
1892   GtkIconTheme *icon_theme;
1893   char *content_type;
1894   GIcon *icon;
1895   GtkIconInfo *info;
1896   GdkPixbuf *pixbuf;
1897
1898   icon_theme = gtk_icon_theme_get_default ();
1899
1900   content_type = g_content_type_from_mime_type (mime_type);
1901
1902   if (!content_type)
1903     return NULL;
1904
1905   icon = g_content_type_get_icon (content_type);
1906   info = gtk_icon_theme_lookup_by_gicon (icon_theme, 
1907                                          icon, 
1908                                          pixel_size, 
1909                                          GTK_ICON_LOOKUP_USE_BUILTIN);
1910   g_free (content_type);
1911   g_object_unref (icon);
1912
1913   if (!info)
1914     return NULL;
1915
1916   pixbuf = gtk_icon_info_load_icon (info, NULL);
1917   gtk_icon_info_free (info);
1918
1919   return pixbuf;
1920 }
1921
1922 static GdkPixbuf *
1923 get_icon_fallback (const gchar *icon_name,
1924                    gint         size)
1925 {
1926   GtkIconTheme *icon_theme;
1927   GdkPixbuf *retval;
1928
1929   icon_theme = gtk_icon_theme_get_default ();
1930   
1931   retval = gtk_icon_theme_load_icon (icon_theme, icon_name,
1932                                      size,
1933                                      GTK_ICON_LOOKUP_USE_BUILTIN,
1934                                      NULL);
1935   g_assert (retval != NULL);
1936   
1937   return retval; 
1938 }
1939
1940 /**
1941  * gtk_recent_info_get_icon:
1942  * @info: a #GtkRecentInfo
1943  * @size: the size of the icon in pixels
1944  *
1945  * Retrieves the icon of size @size associated to the resource MIME type.
1946  *
1947  * Return value: a #GdkPixbuf containing the icon, or %NULL. Use
1948  *   g_object_unref() when finished using the icon.
1949  *
1950  * Since: 2.10
1951  */
1952 GdkPixbuf *
1953 gtk_recent_info_get_icon (GtkRecentInfo *info,
1954                           gint           size)
1955 {
1956   GdkPixbuf *retval = NULL;
1957   
1958   g_return_val_if_fail (info != NULL, NULL);
1959   
1960   if (info->mime_type)
1961     retval = get_icon_for_mime_type (info->mime_type, size);
1962
1963   /* this function should never fail */  
1964   if (!retval)
1965     {
1966       if (info->mime_type &&
1967           strcmp (info->mime_type, "x-directory/normal") == 0)
1968         retval = get_icon_fallback (GTK_STOCK_DIRECTORY, size);
1969       else
1970         retval = get_icon_fallback (GTK_STOCK_FILE, size);
1971     }
1972   
1973   return retval;
1974 }
1975
1976 /**
1977  * gtk_recent_info_is_local:
1978  * @info: a #GtkRecentInfo
1979  *
1980  * Checks whether the resource is local or not by looking at the
1981  * scheme of its URI.
1982  *
1983  * Return value: %TRUE if the resource is local.
1984  *
1985  * Since: 2.10
1986  */
1987 gboolean
1988 gtk_recent_info_is_local (GtkRecentInfo *info)
1989 {
1990   g_return_val_if_fail (info != NULL, FALSE);
1991   
1992   return has_case_prefix (info->uri, "file:/");
1993 }
1994
1995 /**
1996  * gtk_recent_info_exists:
1997  * @info: a #GtkRecentInfo
1998  *
1999  * Checks whether the resource pointed by @info still exists.  At
2000  * the moment this check is done only on resources pointing to local files.
2001  *
2002  * Return value: %TRUE if the resource exists
2003  *
2004  * Since: 2.10
2005  */
2006 gboolean
2007 gtk_recent_info_exists (GtkRecentInfo *info)
2008 {
2009   gchar *filename;
2010   struct stat stat_buf;
2011   gboolean retval = FALSE;
2012   
2013   g_return_val_if_fail (info != NULL, FALSE);
2014   
2015   /* we guarantee only local resources */
2016   if (!gtk_recent_info_is_local (info))
2017     return FALSE;
2018   
2019   filename = g_filename_from_uri (info->uri, NULL, NULL);
2020   if (filename)
2021     {
2022       if (stat (filename, &stat_buf) == 0)
2023         retval = TRUE;
2024      
2025       g_free (filename);
2026     }
2027   
2028   return retval;
2029 }
2030
2031 /**
2032  * gtk_recent_info_match:
2033  * @info_a: a #GtkRecentInfo
2034  * @info_b: a #GtkRecentInfo
2035  *
2036  * Checks whether two #GtkRecentInfo structures point to the same
2037  * resource.
2038  *
2039  * Return value: %TRUE if both #GtkRecentInfo structures point to se same
2040  *   resource, %FALSE otherwise.
2041  *
2042  * Since: 2.10
2043  */
2044 gboolean
2045 gtk_recent_info_match (GtkRecentInfo *info_a,
2046                        GtkRecentInfo *info_b)
2047 {
2048   g_return_val_if_fail (info_a != NULL, FALSE);
2049   g_return_val_if_fail (info_b != NULL, FALSE);
2050   
2051   return (0 == strcmp (info_a->uri, info_b->uri));
2052 }
2053
2054 /* taken from gnome-vfs-uri.c */
2055 static const gchar *
2056 get_method_string (const gchar  *substring, 
2057                    gchar       **method_string)
2058 {
2059   const gchar *p;
2060   char *method;
2061         
2062   for (p = substring;
2063        g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
2064        p++)
2065     ;
2066
2067   if (*p == ':'
2068 #ifdef G_OS_WIN32
2069                 &&
2070       !(p == substring + 1 && g_ascii_isalpha (*substring))
2071 #endif
2072                                                            )
2073     {
2074       /* Found toplevel method specification.  */
2075       method = g_strndup (substring, p - substring);
2076       *method_string = g_ascii_strdown (method, -1);
2077       g_free (method);
2078       p++;
2079     }
2080   else
2081     {
2082       *method_string = g_strdup ("file");
2083       p = substring;
2084     }
2085   
2086   return p;
2087 }
2088
2089 /* Stolen from gnome_vfs_make_valid_utf8() */
2090 static char *
2091 make_valid_utf8 (const char *name)
2092 {
2093   GString *string;
2094   const char *remainder, *invalid;
2095   int remaining_bytes, valid_bytes;
2096
2097   string = NULL;
2098   remainder = name;
2099   remaining_bytes = name ? strlen (name) : 0;
2100
2101   while (remaining_bytes != 0)
2102     {
2103       if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2104         break;
2105       
2106       valid_bytes = invalid - remainder;
2107       
2108       if (string == NULL)
2109         string = g_string_sized_new (remaining_bytes);
2110       
2111       g_string_append_len (string, remainder, valid_bytes);
2112       g_string_append_c (string, '?');
2113       
2114       remaining_bytes -= valid_bytes + 1;
2115       remainder = invalid + 1;
2116     }
2117   
2118   if (string == NULL)
2119     return g_strdup (name);
2120
2121   g_string_append (string, remainder);
2122   g_assert (g_utf8_validate (string->str, -1, NULL));
2123
2124   return g_string_free (string, FALSE);
2125 }
2126
2127 static gchar *
2128 get_uri_shortname_for_display (const gchar *uri)
2129 {
2130   gchar *name = NULL;
2131   gboolean validated = FALSE;
2132
2133   if (has_case_prefix (uri, "file:/"))
2134     {
2135       gchar *local_file;
2136       
2137       local_file = g_filename_from_uri (uri, NULL, NULL);
2138       
2139       if (local_file)
2140         {
2141           name = g_filename_display_basename (local_file);
2142           validated = TRUE;
2143         }
2144                 
2145       g_free (local_file);
2146     } 
2147   
2148   if (!name)
2149     {
2150       gchar *method;
2151       gchar *local_file;
2152       const gchar *rest;
2153       
2154       rest = get_method_string (uri, &method);
2155       local_file = g_filename_display_basename (rest);
2156       
2157       name = g_strconcat (method, ": ", local_file, NULL);
2158       
2159       g_free (local_file);
2160       g_free (method);
2161     }
2162   
2163   g_assert (name != NULL);
2164   
2165   if (!validated && !g_utf8_validate (name, -1, NULL))
2166     {
2167       gchar *utf8_name;
2168       
2169       utf8_name = make_valid_utf8 (name);
2170       g_free (name);
2171       
2172       name = utf8_name;
2173     }
2174
2175   return name;
2176 }
2177
2178 /**
2179  * gtk_recent_info_get_short_name:
2180  * @info: an #GtkRecentInfo
2181  *
2182  * Computes a valid UTF-8 string that can be used as the name of the item in a
2183  * menu or list.  For example, calling this function on an item that refers to
2184  * "file:///foo/bar.txt" will yield "bar.txt".
2185  *
2186  * Return value: A newly-allocated string in UTF-8 encoding; free it with
2187  *   g_free().
2188  *
2189  * Since: 2.10
2190  */
2191 gchar *
2192 gtk_recent_info_get_short_name (GtkRecentInfo *info)
2193 {
2194   gchar *short_name;
2195
2196   g_return_val_if_fail (info != NULL, NULL);
2197
2198   if (info->uri == NULL)
2199     return NULL;
2200
2201   short_name = get_uri_shortname_for_display (info->uri);
2202
2203   return short_name;
2204 }
2205
2206 /**
2207  * gtk_recent_info_get_uri_display:
2208  * @info: a #GtkRecentInfo
2209  *
2210  * Gets a displayable version of the resource's URI.  If the resource
2211  * is local, it returns a local path; if the resource is not local,
2212  * it returns the UTF-8 encoded content of gtk_recent_info_get_uri().
2213  *
2214  * Return value: a newly allocated UTF-8 string containing the
2215  *   resource's URI or %NULL. Use g_free() when done using it.
2216  *
2217  * Since: 2.10
2218  */
2219 gchar *
2220 gtk_recent_info_get_uri_display (GtkRecentInfo *info)
2221 {
2222   gchar *retval;
2223   
2224   g_return_val_if_fail (info != NULL, NULL);
2225
2226   retval = NULL;
2227   if (gtk_recent_info_is_local (info))
2228     {
2229       gchar *filename;
2230
2231       filename = g_filename_from_uri (info->uri, NULL, NULL);
2232       if (!filename)
2233         return NULL;
2234       
2235       retval = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
2236       g_free (filename);
2237     }
2238   else
2239     {
2240       retval = make_valid_utf8 (info->uri);
2241     }
2242
2243   return retval;
2244 }
2245
2246 /**
2247  * gtk_recent_info_get_age:
2248  * @info: a #GtkRecentInfo
2249  *
2250  * Gets the number of days elapsed since the last update of the resource
2251  * pointed by @info.
2252  *
2253  * Return value: a positive integer containing the number of days elapsed
2254  *   since the time this resource was last modified.  
2255  *
2256  * Since: 2.10
2257  */
2258 gint
2259 gtk_recent_info_get_age (GtkRecentInfo *info)
2260 {
2261   time_t now, delta;
2262   gint retval;
2263
2264   g_return_val_if_fail (info != NULL, -1);
2265
2266   now = time (NULL);
2267   
2268   delta = now - info->modified;
2269   
2270   retval = (gint) (delta / (60 * 60 * 24));
2271   
2272   return retval;
2273 }
2274
2275 /**
2276  * gtk_recent_info_get_groups:
2277  * @info: a #GtkRecentInfo
2278  * @length: (out) (allow-none): return location for the number of groups returned
2279  *
2280  * Returns all groups registered for the recently used item @info.  The
2281  * array of returned group names will be %NULL terminated, so length might
2282  * optionally be %NULL.
2283  *
2284  * Return value:  (array length=length zero-terminated=1): a newly allocated
2285  * %NULL terminated array of strings.  Use g_strfreev() to free it.
2286  *
2287  * Since: 2.10
2288  */
2289 gchar **
2290 gtk_recent_info_get_groups (GtkRecentInfo *info,
2291                             gsize         *length)
2292 {
2293   GSList *l;
2294   gchar **retval;
2295   gsize n_groups, i;
2296   
2297   g_return_val_if_fail (info != NULL, NULL);
2298   
2299   if (!info->groups)
2300     {
2301       if (length)
2302         *length = 0;
2303       
2304       return NULL;
2305     }
2306   
2307   n_groups = g_slist_length (info->groups);
2308   
2309   retval = g_new0 (gchar *, n_groups + 1);
2310   
2311   for (l = info->groups, i = 0;
2312        l != NULL;
2313        l = l->next)
2314     {
2315       gchar *group_name = (gchar *) l->data;
2316       
2317       g_assert (group_name != NULL);
2318       
2319       retval[i++] = g_strdup (group_name);
2320     }
2321   retval[i] = NULL;
2322   
2323   if (length)
2324     *length = i;
2325   
2326   return retval;
2327 }
2328
2329 /**
2330  * gtk_recent_info_has_group:
2331  * @info: a #GtkRecentInfo
2332  * @group_name: name of a group
2333  *
2334  * Checks whether @group_name appears inside the groups registered for the
2335  * recently used item @info.
2336  *
2337  * Return value: %TRUE if the group was found.
2338  *
2339  * Since: 2.10
2340  */
2341 gboolean
2342 gtk_recent_info_has_group (GtkRecentInfo *info,
2343                            const gchar   *group_name)
2344 {
2345   GSList *l;
2346   
2347   g_return_val_if_fail (info != NULL, FALSE);
2348   g_return_val_if_fail (group_name != NULL, FALSE);
2349
2350   if (!info->groups)
2351     return FALSE;
2352
2353   for (l = info->groups; l != NULL; l = l->next)
2354     {
2355       gchar *g = (gchar *) l->data;
2356
2357       if (strcmp (g, group_name) == 0)
2358         return TRUE;
2359     }
2360
2361   return FALSE;
2362 }
2363
2364 /*
2365  * _gtk_recent_manager_sync:
2366  * 
2367  * Private function for synchronising the recent manager singleton.
2368  */
2369 void
2370 _gtk_recent_manager_sync (void)
2371 {
2372   if (recent_manager_singleton)
2373     {
2374       /* force a dump of the contents of the recent manager singleton */
2375       recent_manager_singleton->priv->is_dirty = TRUE;
2376       gtk_recent_manager_real_changed (recent_manager_singleton);
2377     }
2378 }
2379
2380 #define __GTK_RECENT_MANAGER_C__
2381 #include "gtkaliasdef.c"