1 /* GTK - The GIMP Toolkit
2 * gtkrecentmanager.c: a manager for the recently used resources
4 * Copyright (C) 2006 Emmanuele Bassi
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.
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.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
21 * SECTION:gtkrecentmanager
22 * @Title: GtkRecentManager
23 * @short_description: Managing recently used files
24 * @See_Also: #GBookmarkFile, #GtkSettings, #GtkRecentChooser
26 * #GtkRecentManager provides a facility for adding, removing and
27 * looking up recently used files. Each recently used file is
28 * identified by its URI, and has meta-data associated to it, like
29 * the names and command lines of the applications that have
30 * registered it, the number of time each application has registered
31 * the same file, the mime type of the file and whether the file
32 * should be displayed only by the applications that have
35 * <note><para>The recently used files list is per user.</para></note>
37 * The #GtkRecentManager acts like a database of all the recently
38 * used files. You can create new #GtkRecentManager objects, but
39 * it is more efficient to use the default manager created by GTK+.
41 * Adding a new recently used file is as simple as:
44 * GtkRecentManager *manager;
46 * manager = gtk_recent_manager_get_default ();
47 * gtk_recent_manager_add_item (manager, file_uri);
50 * The #GtkRecentManager will try to gather all the needed information
51 * from the file itself through GIO.
53 * Looking up the meta-data associated with a recently used file
54 * given its URI requires calling gtk_recent_manager_lookup_item():
57 * GtkRecentManager *manager;
58 * GtkRecentInfo *info;
59 * GError *error = NULL;
61 * manager = gtk_recent_manager_get_default ();
62 * info = gtk_recent_manager_lookup_item (manager, file_uri, &error);
65 * g_warning ("Could not find the file: %s", error->message);
66 * g_error_free (error);
70 * /* Use the info object */
71 * gtk_recent_info_unref (info);
75 * In order to retrieve the list of recently used files, you can use
76 * gtk_recent_manager_get_items(), which returns a list of #GtkRecentInfo
79 * A #GtkRecentManager is the model used to populate the contents of
80 * one, or more #GtkRecentChooser implementations.
82 * <note><para>The maximum age of the recently used files list is
83 * controllable through the #GtkSettings:gtk-recent-files-max-age
84 * property.</para></note>
86 * Recently used files are supported since GTK+ 2.10.
91 #include <sys/types.h>
100 #include <glib/gstdio.h>
103 #include "gtkrecentmanager.h"
105 #include "gtksettings.h"
106 #include "gtkstock.h"
107 #include "gtkicontheme.h"
108 #include "gtktypebuiltins.h"
109 #include "gtkprivate.h"
110 #include "gtkmarshalers.h"
112 /* the file where we store the recently used items */
113 #define GTK_RECENTLY_USED_FILE "recently-used.xbel"
115 /* return all items by default */
116 #define DEFAULT_LIMIT -1
118 /* keep in sync with xdgmime */
119 #define GTK_RECENT_DEFAULT_MIME "application/octet-stream"
134 * #GtkRecentInfo is an opaque data structure
135 * whose members can only be accessed using the provided API.
137 * #GtkRecentInfo constains all the meta-data
138 * associated with an entry in the recently used files list.
142 struct _GtkRecentInfo
155 GSList *applications;
156 GHashTable *apps_lookup;
167 struct _GtkRecentManagerPrivate
175 GBookmarkFile *recent_items;
177 GFileMonitor *monitor;
179 guint changed_timeout;
192 static void gtk_recent_manager_dispose (GObject *object);
193 static void gtk_recent_manager_finalize (GObject *object);
194 static void gtk_recent_manager_set_property (GObject *object,
198 static void gtk_recent_manager_get_property (GObject *object,
202 static void gtk_recent_manager_add_item_query_info (GObject *source_object,
205 static void gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
208 GFileMonitorEvent event_type,
210 static void gtk_recent_manager_changed (GtkRecentManager *manager);
211 static void gtk_recent_manager_real_changed (GtkRecentManager *manager);
212 static void gtk_recent_manager_set_filename (GtkRecentManager *manager,
213 const gchar *filename);
214 static void gtk_recent_manager_clamp_to_age (GtkRecentManager *manager,
218 static void build_recent_items_list (GtkRecentManager *manager);
219 static void purge_recent_items_list (GtkRecentManager *manager,
222 static RecentAppInfo *recent_app_info_new (const gchar *app_name);
223 static void recent_app_info_free (RecentAppInfo *app_info);
225 static GtkRecentInfo *gtk_recent_info_new (const gchar *uri);
226 static void gtk_recent_info_free (GtkRecentInfo *recent_info);
228 static guint signal_changed = 0;
230 static GtkRecentManager *recent_manager_singleton = NULL;
232 G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT)
235 filename_warning (const gchar *format,
236 const gchar *filename,
237 const gchar *message)
239 gchar *utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
240 g_warning (format, utf8 ? utf8 : "(invalid filename)", message);
244 /* Test of haystack has the needle prefix, comparing case
245 * insensitive. haystack may be UTF-8, but needle must
246 * contain only lowercase ascii. */
248 has_case_prefix (const gchar *haystack,
253 /* Eat one character at a time. */
257 while (*n && *h && *n == g_ascii_tolower (*h))
267 gtk_recent_manager_error_quark (void)
269 return g_quark_from_static_string ("gtk-recent-manager-error-quark");
273 gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
275 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
277 gobject_class->set_property = gtk_recent_manager_set_property;
278 gobject_class->get_property = gtk_recent_manager_get_property;
279 gobject_class->dispose = gtk_recent_manager_dispose;
280 gobject_class->finalize = gtk_recent_manager_finalize;
283 * GtkRecentManager:filename
285 * The full path to the file to be used to store and read the recently
286 * used resources list
290 g_object_class_install_property (gobject_class,
292 g_param_spec_string ("filename",
294 P_("The full path to the file to be used to store and read the list"),
296 (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)));
299 * GtkRecentManager:size
301 * The size of the recently used resources list.
305 g_object_class_install_property (gobject_class,
307 g_param_spec_int ("size",
309 P_("The size of the recently used resources list"),
316 * GtkRecentManager::changed
317 * @recent_manager: the recent manager
319 * Emitted when the current recently used resources manager changes its
320 * contents, either by calling gtk_recent_manager_add_item() or by another
326 g_signal_new (I_("changed"),
327 G_TYPE_FROM_CLASS (klass),
329 G_STRUCT_OFFSET (GtkRecentManagerClass, changed),
331 g_cclosure_marshal_VOID__VOID,
334 klass->changed = gtk_recent_manager_real_changed;
336 g_type_class_add_private (klass, sizeof (GtkRecentManagerPrivate));
340 gtk_recent_manager_init (GtkRecentManager *manager)
342 GtkRecentManagerPrivate *priv;
344 manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
345 GTK_TYPE_RECENT_MANAGER,
346 GtkRecentManagerPrivate);
347 priv = manager->priv;
350 priv->filename = NULL;
354 gtk_recent_manager_set_property (GObject *object,
359 GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
364 gtk_recent_manager_set_filename (recent_manager, g_value_get_string (value));
367 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
373 gtk_recent_manager_get_property (GObject *object,
378 GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
383 g_value_set_string (value, recent_manager->priv->filename);
386 g_value_set_int (value, recent_manager->priv->size);
389 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
395 gtk_recent_manager_finalize (GObject *object)
397 GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
398 GtkRecentManagerPrivate *priv = manager->priv;
400 g_free (priv->filename);
402 if (priv->recent_items != NULL)
403 g_bookmark_file_free (priv->recent_items);
405 G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object);
409 gtk_recent_manager_dispose (GObject *gobject)
411 GtkRecentManager *manager = GTK_RECENT_MANAGER (gobject);
412 GtkRecentManagerPrivate *priv = manager->priv;
414 if (priv->monitor != NULL)
416 g_signal_handlers_disconnect_by_func (priv->monitor,
417 G_CALLBACK (gtk_recent_manager_monitor_changed),
419 g_object_unref (priv->monitor);
420 priv->monitor = NULL;
423 if (priv->changed_timeout != 0)
425 g_source_remove (priv->changed_timeout);
426 priv->changed_timeout = 0;
427 priv->changed_age = 0;
432 g_object_ref (manager);
433 g_signal_emit (manager, signal_changed, 0);
434 g_object_unref (manager);
437 G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (gobject);
441 gtk_recent_manager_real_changed (GtkRecentManager *manager)
443 GtkRecentManagerPrivate *priv = manager->priv;
445 g_object_freeze_notify (G_OBJECT (manager));
451 /* we are marked as dirty, so we dump the content of our
452 * recently used items list
454 g_assert (priv->filename != NULL);
456 if (!priv->recent_items)
458 /* if no container object has been defined, we create a new
459 * empty container, and dump it
461 priv->recent_items = g_bookmark_file_new ();
466 GtkSettings *settings = gtk_settings_get_default ();
469 g_object_get (G_OBJECT (settings), "gtk-recent-files-max-age", &age, NULL);
471 gtk_recent_manager_clamp_to_age (manager, age);
474 g_bookmark_file_free (priv->recent_items);
475 priv->recent_items = g_bookmark_file_new ();
480 g_bookmark_file_to_file (priv->recent_items, priv->filename, &write_error);
483 filename_warning ("Attempting to store changes into `%s', "
486 write_error->message);
487 g_error_free (write_error);
490 if (g_chmod (priv->filename, 0600) < 0)
492 filename_warning ("Attempting to set the permissions of `%s', "
498 /* mark us as clean */
499 priv->is_dirty = FALSE;
503 /* we are not marked as dirty, so we have been called
504 * because the recently used resources file has been
505 * changed (and not from us).
507 build_recent_items_list (manager);
510 g_object_thaw_notify (G_OBJECT (manager));
514 gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
517 GFileMonitorEvent event_type,
520 GtkRecentManager *manager = user_data;
524 case G_FILE_MONITOR_EVENT_CHANGED:
525 case G_FILE_MONITOR_EVENT_CREATED:
526 gdk_threads_enter ();
527 gtk_recent_manager_changed (manager);
528 gdk_threads_leave ();
531 case G_FILE_MONITOR_EVENT_DELETED:
540 get_default_filename (void)
542 return g_build_filename (g_get_user_data_dir (),
543 GTK_RECENTLY_USED_FILE,
548 gtk_recent_manager_set_filename (GtkRecentManager *manager,
549 const gchar *filename)
551 GtkRecentManagerPrivate *priv;
555 g_assert (GTK_IS_RECENT_MANAGER (manager));
557 priv = manager->priv;
559 /* if a filename is already set and filename is not NULL, then copy
560 * it and reset the monitor; otherwise, if it's NULL we're being
561 * called from the finalization sequence, so we simply disconnect the
562 * monitoring and return.
564 * if no filename is set and filename is NULL, use the default.
568 g_free (priv->filename);
572 g_signal_handlers_disconnect_by_func (priv->monitor,
573 G_CALLBACK (gtk_recent_manager_monitor_changed),
575 g_object_unref (priv->monitor);
576 priv->monitor = NULL;
579 if (!filename || *filename == '\0')
582 priv->filename = g_strdup (filename);
586 if (!filename || *filename == '\0')
587 priv->filename = get_default_filename ();
589 priv->filename = g_strdup (filename);
592 g_assert (priv->filename != NULL);
593 file = g_file_new_for_path (priv->filename);
596 priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
599 filename_warning ("Unable to monitor `%s': %s\n"
600 "The GtkRecentManager will not update its contents "
601 "if the file is changed from other instances",
604 g_error_free (error);
607 g_signal_connect (priv->monitor, "changed",
608 G_CALLBACK (gtk_recent_manager_monitor_changed),
611 g_object_unref (file);
613 priv->is_dirty = FALSE;
614 build_recent_items_list (manager);
617 /* reads the recently used resources file and builds the items list.
618 * we keep the items list inside the parser object, and build the
619 * RecentInfo object only on user's demand to avoid useless replication.
620 * this function resets the dirty bit of the manager.
623 build_recent_items_list (GtkRecentManager *manager)
625 GtkRecentManagerPrivate *priv = manager->priv;
629 g_assert (priv->filename != NULL);
631 if (!priv->recent_items)
633 priv->recent_items = g_bookmark_file_new ();
637 /* the file exists, and it's valid (we hope); if not, destroy the container
638 * object and hope for a better result when the next "changed" signal is
641 g_bookmark_file_load_from_file (priv->recent_items, priv->filename, &read_error);
644 /* if the file does not exist we just wait for the first write
645 * operation on this recent manager instance, to avoid creating
646 * empty files and leading to spurious file system events (Sabayon
647 * will not be happy about those)
649 if (read_error->domain == G_FILE_ERROR &&
650 read_error->code != G_FILE_ERROR_NOENT)
651 filename_warning ("Attempting to read the recently used resources "
652 "file at `%s', but the parser failed: %s.",
654 read_error->message);
656 g_bookmark_file_free (priv->recent_items);
657 priv->recent_items = NULL;
659 g_error_free (read_error);
663 size = g_bookmark_file_get_size (priv->recent_items);
664 if (priv->size != size)
668 g_object_notify (G_OBJECT (manager), "size");
672 priv->is_dirty = FALSE;
676 /********************
678 ********************/
682 * gtk_recent_manager_new:
684 * Creates a new recent manager object. Recent manager objects are used to
685 * handle the list of recently used resources. A #GtkRecentManager object
686 * monitors the recently used resources list, and emits the "changed" signal
687 * each time something inside the list changes.
689 * #GtkRecentManager objects are expensive: be sure to create them only when
690 * needed. You should use gtk_recent_manager_get_default() instead.
692 * Return value: A newly created #GtkRecentManager object.
697 gtk_recent_manager_new (void)
699 return g_object_new (GTK_TYPE_RECENT_MANAGER, NULL);
703 * gtk_recent_manager_get_default:
705 * Gets a unique instance of #GtkRecentManager, that you can share
706 * in your application without caring about memory management.
708 * Return value: (transfer none): A unique #GtkRecentManager. Do not ref or unref it.
713 gtk_recent_manager_get_default (void)
715 if (G_UNLIKELY (!recent_manager_singleton))
716 recent_manager_singleton = gtk_recent_manager_new ();
718 return recent_manager_singleton;
723 gtk_recent_manager_add_item_query_info (GObject *source_object,
727 GFile *file = G_FILE (source_object);
728 GtkRecentManager *manager = user_data;
729 GtkRecentData recent_data;
730 GFileInfo *file_info;
731 gchar *uri, *basename;
733 uri = g_file_get_uri (file);
735 file_info = g_file_query_info_finish (file, res, NULL); /* NULL-GError */
737 recent_data.display_name = NULL;
738 recent_data.description = NULL;
744 content_type = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
746 if (G_LIKELY (content_type))
747 recent_data.mime_type = g_content_type_get_mime_type (content_type);
749 recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
751 g_free (content_type);
752 g_object_unref (file_info);
756 basename = g_file_get_basename (file);
757 recent_data.mime_type = g_content_type_guess (basename, NULL, 0, NULL);
761 recent_data.app_name = g_strdup (g_get_application_name ());
762 recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL);
763 recent_data.groups = NULL;
764 recent_data.is_private = FALSE;
766 gdk_threads_enter ();
768 /* Ignore return value, this can't fail anyway since all required
770 gtk_recent_manager_add_full (manager, uri, &recent_data);
772 manager->priv->is_dirty = TRUE;
773 gtk_recent_manager_changed (manager);
775 gdk_threads_leave ();
777 g_free (recent_data.mime_type);
778 g_free (recent_data.app_name);
779 g_free (recent_data.app_exec);
781 g_object_unref (manager);
786 * gtk_recent_manager_add_item:
787 * @manager: a #GtkRecentManager
790 * Adds a new resource, pointed by @uri, into the recently used
793 * This function automatically retrieves some of the needed
794 * metadata and setting other metadata to common default values; it
795 * then feeds the data to gtk_recent_manager_add_full().
797 * See gtk_recent_manager_add_full() if you want to explicitly
798 * define the metadata for the resource pointed by @uri.
800 * Return value: %TRUE if the new item was successfully added
801 * to the recently used resources list
806 gtk_recent_manager_add_item (GtkRecentManager *manager,
811 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
812 g_return_val_if_fail (uri != NULL, FALSE);
814 file = g_file_new_for_uri (uri);
816 g_file_query_info_async (file,
817 G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
819 G_FILE_QUERY_INFO_NONE,
821 gtk_recent_manager_add_item_query_info,
822 g_object_ref (manager));
824 g_object_unref (file);
830 * gtk_recent_manager_add_full:
831 * @manager: a #GtkRecentManager
833 * @recent_data: metadata of the resource
835 * Adds a new resource, pointed by @uri, into the recently used
836 * resources list, using the metadata specified inside the #GtkRecentData
837 * structure passed in @recent_data.
839 * The passed URI will be used to identify this resource inside the
842 * In order to register the new recently used resource, metadata about
843 * the resource must be passed as well as the URI; the metadata is
844 * stored in a #GtkRecentData structure, which must contain the MIME
845 * type of the resource pointed by the URI; the name of the application
846 * that is registering the item, and a command line to be used when
847 * launching the item.
849 * Optionally, a #GtkRecentData structure might contain a UTF-8 string
850 * to be used when viewing the item instead of the last component of the
851 * URI; a short description of the item; whether the item should be
852 * considered private - that is, should be displayed only by the
853 * applications that have registered it.
855 * Return value: %TRUE if the new item was successfully added to the
856 * recently used resources list, %FALSE otherwise.
861 gtk_recent_manager_add_full (GtkRecentManager *manager,
863 const GtkRecentData *data)
865 GtkRecentManagerPrivate *priv;
867 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
868 g_return_val_if_fail (uri != NULL, FALSE);
869 g_return_val_if_fail (data != NULL, FALSE);
872 if ((data->display_name) &&
873 (!g_utf8_validate (data->display_name, -1, NULL)))
875 g_warning ("Attempting to add `%s' to the list of recently used "
876 "resources, but the display name is not a valid UTF-8 "
882 if ((data->description) &&
883 (!g_utf8_validate (data->description, -1, NULL)))
885 g_warning ("Attempting to add `%s' to the list of recently used "
886 "resources, but the description is not a valid UTF-8 "
893 if (!data->mime_type)
895 g_warning ("Attempting to add `%s' to the list of recently used "
896 "resources, but not MIME type was defined",
903 g_warning ("Attempting to add `%s' to the list of recently used "
904 "resources, but no name of the application that is "
905 "registering it was defined",
912 g_warning ("Attempting to add `%s' to the list of recently used "
913 "resources, but no command line for the application "
914 "that is registering it was defined",
919 priv = manager->priv;
921 if (!priv->recent_items)
923 priv->recent_items = g_bookmark_file_new ();
927 if (data->display_name)
928 g_bookmark_file_set_title (priv->recent_items, uri, data->display_name);
930 if (data->description)
931 g_bookmark_file_set_description (priv->recent_items, uri, data->description);
933 g_bookmark_file_set_mime_type (priv->recent_items, uri, data->mime_type);
935 if (data->groups && data->groups[0] != '\0')
939 for (j = 0; (data->groups)[j] != NULL; j++)
940 g_bookmark_file_add_group (priv->recent_items, uri, (data->groups)[j]);
943 /* register the application; this will take care of updating the
944 * registration count and time in case the application has
945 * already registered the same document inside the list
947 g_bookmark_file_add_application (priv->recent_items, uri,
951 g_bookmark_file_set_is_private (priv->recent_items, uri,
954 /* mark us as dirty, so that when emitting the "changed" signal we
955 * will dump our changes
957 priv->is_dirty = TRUE;
958 gtk_recent_manager_changed (manager);
964 * gtk_recent_manager_remove_item:
965 * @manager: a #GtkRecentManager
966 * @uri: the URI of the item you wish to remove
967 * @error: (allow-none): return location for a #GError, or %NULL
969 * Removes a resource pointed by @uri from the recently used resources
970 * list handled by a recent manager.
972 * Return value: %TRUE if the item pointed by @uri has been successfully
973 * removed by the recently used resources list, and %FALSE otherwise.
978 gtk_recent_manager_remove_item (GtkRecentManager *manager,
982 GtkRecentManagerPrivate *priv;
983 GError *remove_error = NULL;
985 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
986 g_return_val_if_fail (uri != NULL, FALSE);
987 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
989 priv = manager->priv;
991 if (!priv->recent_items)
993 priv->recent_items = g_bookmark_file_new ();
996 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
997 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
998 _("Unable to find an item with URI '%s'"),
1004 g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
1007 g_error_free (remove_error);
1009 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1010 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1011 _("Unable to find an item with URI '%s'"),
1017 priv->is_dirty = TRUE;
1018 gtk_recent_manager_changed (manager);
1024 * gtk_recent_manager_has_item:
1025 * @manager: a #GtkRecentManager
1028 * Checks whether there is a recently used resource registered
1029 * with @uri inside the recent manager.
1031 * Return value: %TRUE if the resource was found, %FALSE otherwise.
1036 gtk_recent_manager_has_item (GtkRecentManager *manager,
1039 GtkRecentManagerPrivate *priv;
1041 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
1042 g_return_val_if_fail (uri != NULL, FALSE);
1044 priv = manager->priv;
1045 g_return_val_if_fail (priv->recent_items != NULL, FALSE);
1047 return g_bookmark_file_has_item (priv->recent_items, uri);
1051 build_recent_info (GBookmarkFile *bookmarks,
1052 GtkRecentInfo *info)
1054 gchar **apps, **groups;
1055 gsize apps_len, groups_len, i;
1057 g_assert (bookmarks != NULL);
1058 g_assert (info != NULL);
1060 info->display_name = g_bookmark_file_get_title (bookmarks, info->uri, NULL);
1061 info->description = g_bookmark_file_get_description (bookmarks, info->uri, NULL);
1062 info->mime_type = g_bookmark_file_get_mime_type (bookmarks, info->uri, NULL);
1064 info->is_private = g_bookmark_file_get_is_private (bookmarks, info->uri, NULL);
1066 info->added = g_bookmark_file_get_added (bookmarks, info->uri, NULL);
1067 info->modified = g_bookmark_file_get_modified (bookmarks, info->uri, NULL);
1068 info->visited = g_bookmark_file_get_visited (bookmarks, info->uri, NULL);
1070 groups = g_bookmark_file_get_groups (bookmarks, info->uri, &groups_len, NULL);
1071 for (i = 0; i < groups_len; i++)
1073 gchar *group_name = g_strdup (groups[i]);
1075 info->groups = g_slist_append (info->groups, group_name);
1078 g_strfreev (groups);
1080 apps = g_bookmark_file_get_applications (bookmarks, info->uri, &apps_len, NULL);
1081 for (i = 0; i < apps_len; i++)
1083 gchar *app_name, *app_exec;
1086 RecentAppInfo *app_info;
1091 res = g_bookmark_file_get_app_info (bookmarks, info->uri, app_name,
1099 app_info = recent_app_info_new (app_name);
1100 app_info->exec = app_exec;
1101 app_info->count = count;
1102 app_info->stamp = stamp;
1104 info->applications = g_slist_prepend (info->applications, app_info);
1105 g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
1112 * gtk_recent_manager_lookup_item:
1113 * @manager: a #GtkRecentManager
1115 * @error: (allow-none): a return location for a #GError, or %NULL
1117 * Searches for a URI inside the recently used resources list, and
1118 * returns a structure containing informations about the resource
1119 * like its MIME type, or its display name.
1121 * Return value: a #GtkRecentInfo structure containing information
1122 * about the resource pointed by @uri, or %NULL if the URI was
1123 * not registered in the recently used resources list. Free with
1124 * gtk_recent_info_unref().
1129 gtk_recent_manager_lookup_item (GtkRecentManager *manager,
1133 GtkRecentManagerPrivate *priv;
1134 GtkRecentInfo *info = NULL;
1136 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
1137 g_return_val_if_fail (uri != NULL, NULL);
1138 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1140 priv = manager->priv;
1141 if (!priv->recent_items)
1143 priv->recent_items = g_bookmark_file_new ();
1146 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1147 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1148 _("Unable to find an item with URI '%s'"),
1154 if (!g_bookmark_file_has_item (priv->recent_items, uri))
1156 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1157 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1158 _("Unable to find an item with URI '%s'"),
1163 info = gtk_recent_info_new (uri);
1164 g_return_val_if_fail (info != NULL, NULL);
1166 /* fill the RecentInfo structure with the data retrieved by our
1167 * parser object from the storage file
1169 build_recent_info (priv->recent_items, info);
1175 * gtk_recent_manager_move_item:
1176 * @manager: a #GtkRecentManager
1177 * @uri: the URI of a recently used resource
1178 * @new_uri: (allow-none): the new URI of the recently used resource, or %NULL to
1179 * remove the item pointed by @uri in the list
1180 * @error: (allow-none): a return location for a #GError, or %NULL
1182 * Changes the location of a recently used resource from @uri to @new_uri.
1184 * Please note that this function will not affect the resource pointed
1185 * by the URIs, but only the URI used in the recently used resources list.
1187 * Return value: %TRUE on success.
1192 gtk_recent_manager_move_item (GtkRecentManager *recent_manager,
1194 const gchar *new_uri,
1197 GtkRecentManagerPrivate *priv;
1200 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
1201 g_return_val_if_fail (uri != NULL, FALSE);
1202 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1204 priv = recent_manager->priv;
1206 if (!priv->recent_items)
1208 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1209 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1210 _("Unable to find an item with URI '%s'"),
1215 if (!g_bookmark_file_has_item (priv->recent_items, uri))
1217 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
1218 GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
1219 _("Unable to find an item with URI '%s'"),
1225 if (!g_bookmark_file_move_item (priv->recent_items,
1230 g_error_free (move_error);
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'"),
1239 priv->is_dirty = TRUE;
1240 gtk_recent_manager_changed (recent_manager);
1246 * gtk_recent_manager_get_items:
1247 * @manager: a #GtkRecentManager
1249 * Gets the list of recently used resources.
1251 * Return value: (element-type GtkRecentInfo) (transfer full): a list of
1252 * newly allocated #GtkRecentInfo objects. Use
1253 * gtk_recent_info_unref() on each item inside the list, and then
1254 * free the list itself using g_list_free().
1259 gtk_recent_manager_get_items (GtkRecentManager *manager)
1261 GtkRecentManagerPrivate *priv;
1262 GList *retval = NULL;
1266 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
1268 priv = manager->priv;
1269 if (!priv->recent_items)
1272 uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len);
1273 for (i = 0; i < uris_len; i++)
1275 GtkRecentInfo *info;
1277 info = gtk_recent_info_new (uris[i]);
1278 build_recent_info (priv->recent_items, info);
1280 retval = g_list_prepend (retval, info);
1289 purge_recent_items_list (GtkRecentManager *manager,
1292 GtkRecentManagerPrivate *priv = manager->priv;
1294 if (priv->recent_items == NULL)
1297 g_bookmark_file_free (priv->recent_items);
1298 priv->recent_items = g_bookmark_file_new ();
1301 /* emit the changed signal, to ensure that the purge is written */
1302 priv->is_dirty = TRUE;
1303 gtk_recent_manager_changed (manager);
1307 * gtk_recent_manager_purge_items:
1308 * @manager: a #GtkRecentManager
1309 * @error: (allow-none): a return location for a #GError, or %NULL
1311 * Purges every item from the recently used resources list.
1313 * Return value: the number of items that have been removed from the
1314 * recently used resources list.
1319 gtk_recent_manager_purge_items (GtkRecentManager *manager,
1322 GtkRecentManagerPrivate *priv;
1325 g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), -1);
1327 priv = manager->priv;
1328 if (!priv->recent_items)
1331 count = g_bookmark_file_get_size (priv->recent_items);
1335 purge_recent_items_list (manager, error);
1337 purged = count - g_bookmark_file_get_size (priv->recent_items);
1343 emit_manager_changed (gpointer data)
1345 GtkRecentManager *manager = data;
1347 manager->priv->changed_age = 0;
1348 manager->priv->changed_timeout = 0;
1350 g_signal_emit (manager, signal_changed, 0);
1356 gtk_recent_manager_changed (GtkRecentManager *manager)
1358 /* coalesce consecutive changes
1360 * we schedule a write in 250 msecs immediately; if we get more than one
1361 * request per millisecond before the timeout has a chance to run, we
1362 * schedule an emission immediately.
1364 if (manager->priv->changed_timeout == 0)
1365 manager->priv->changed_timeout = gdk_threads_add_timeout (250, emit_manager_changed, manager);
1368 manager->priv->changed_age += 1;
1370 if (manager->priv->changed_age > 250)
1372 g_source_remove (manager->priv->changed_timeout);
1373 g_signal_emit (manager, signal_changed, 0);
1375 manager->priv->changed_age = 0;
1376 manager->priv->changed_timeout = 0;
1382 gtk_recent_manager_clamp_to_age (GtkRecentManager *manager,
1385 GtkRecentManagerPrivate *priv = manager->priv;
1390 if (G_UNLIKELY (!priv->recent_items))
1395 uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
1397 for (i = 0; i < n_uris; i++)
1399 const gchar *uri = uris[i];
1403 modified = g_bookmark_file_get_modified (priv->recent_items, uri, NULL);
1404 item_age = (gint) ((now - modified) / (60 * 60 * 24));
1406 g_bookmark_file_remove_item (priv->recent_items, uri, NULL);
1416 G_DEFINE_BOXED_TYPE (GtkRecentInfo, gtk_recent_info,
1417 gtk_recent_info_ref,
1418 gtk_recent_info_unref)
1420 static GtkRecentInfo *
1421 gtk_recent_info_new (const gchar *uri)
1423 GtkRecentInfo *info;
1425 g_assert (uri != NULL);
1427 info = g_new0 (GtkRecentInfo, 1);
1428 info->uri = g_strdup (uri);
1430 info->applications = NULL;
1431 info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
1433 info->groups = NULL;
1435 info->ref_count = 1;
1441 gtk_recent_info_free (GtkRecentInfo *recent_info)
1446 g_free (recent_info->uri);
1447 g_free (recent_info->display_name);
1448 g_free (recent_info->description);
1449 g_free (recent_info->mime_type);
1451 if (recent_info->applications)
1453 g_slist_foreach (recent_info->applications,
1454 (GFunc) recent_app_info_free,
1456 g_slist_free (recent_info->applications);
1458 recent_info->applications = NULL;
1461 if (recent_info->apps_lookup)
1462 g_hash_table_destroy (recent_info->apps_lookup);
1464 if (recent_info->groups)
1466 g_slist_foreach (recent_info->groups,
1469 g_slist_free (recent_info->groups);
1471 recent_info->groups = NULL;
1474 if (recent_info->icon)
1475 g_object_unref (recent_info->icon);
1477 g_free (recent_info);
1481 * gtk_recent_info_ref:
1482 * @info: a #GtkRecentInfo
1484 * Increases the reference count of @recent_info by one.
1486 * Return value: the recent info object with its reference count increased
1492 gtk_recent_info_ref (GtkRecentInfo *info)
1494 g_return_val_if_fail (info != NULL, NULL);
1495 g_return_val_if_fail (info->ref_count > 0, NULL);
1497 info->ref_count += 1;
1503 * gtk_recent_info_unref:
1504 * @info: a #GtkRecentInfo
1506 * Decreases the reference count of @info by one. If the reference
1507 * count reaches zero, @info is deallocated, and the memory freed.
1512 gtk_recent_info_unref (GtkRecentInfo *info)
1514 g_return_if_fail (info != NULL);
1515 g_return_if_fail (info->ref_count > 0);
1517 info->ref_count -= 1;
1519 if (info->ref_count == 0)
1520 gtk_recent_info_free (info);
1524 * gtk_recent_info_get_uri:
1525 * @info: a #GtkRecentInfo
1527 * Gets the URI of the resource.
1529 * Return value: the URI of the resource. The returned string is
1530 * owned by the recent manager, and should not be freed.
1535 gtk_recent_info_get_uri (GtkRecentInfo *info)
1537 g_return_val_if_fail (info != NULL, NULL);
1543 * gtk_recent_info_get_display_name:
1544 * @info: a #GtkRecentInfo
1546 * Gets the name of the resource. If none has been defined, the basename
1547 * of the resource is obtained.
1549 * Return value: the display name of the resource. The returned string
1550 * is owned by the recent manager, and should not be freed.
1555 gtk_recent_info_get_display_name (GtkRecentInfo *info)
1557 g_return_val_if_fail (info != NULL, NULL);
1559 if (!info->display_name)
1560 info->display_name = gtk_recent_info_get_short_name (info);
1562 return info->display_name;
1566 * gtk_recent_info_get_description:
1567 * @info: a #GtkRecentInfo
1569 * Gets the (short) description of the resource.
1571 * Return value: the description of the resource. The returned string
1572 * is owned by the recent manager, and should not be freed.
1577 gtk_recent_info_get_description (GtkRecentInfo *info)
1579 g_return_val_if_fail (info != NULL, NULL);
1581 return info->description;
1585 * gtk_recent_info_get_mime_type:
1586 * @info: a #GtkRecentInfo
1588 * Gets the MIME type of the resource.
1590 * Return value: the MIME type of the resource. The returned string
1591 * is owned by the recent manager, and should not be freed.
1596 gtk_recent_info_get_mime_type (GtkRecentInfo *info)
1598 g_return_val_if_fail (info != NULL, NULL);
1600 if (!info->mime_type)
1601 info->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
1603 return info->mime_type;
1607 * gtk_recent_info_get_added:
1608 * @info: a #GtkRecentInfo
1610 * Gets the timestamp (seconds from system's Epoch) when the resource
1611 * was added to the recently used resources list.
1613 * Return value: the number of seconds elapsed from system's Epoch when
1614 * the resource was added to the list, or -1 on failure.
1619 gtk_recent_info_get_added (GtkRecentInfo *info)
1621 g_return_val_if_fail (info != NULL, (time_t) -1);
1627 * gtk_recent_info_get_modified:
1628 * @info: a #GtkRecentInfo
1630 * Gets the timestamp (seconds from system's Epoch) when the resource
1631 * was last modified.
1633 * Return value: the number of seconds elapsed from system's Epoch when
1634 * the resource was last modified, or -1 on failure.
1639 gtk_recent_info_get_modified (GtkRecentInfo *info)
1641 g_return_val_if_fail (info != NULL, (time_t) -1);
1643 return info->modified;
1647 * gtk_recent_info_get_visited:
1648 * @info: a #GtkRecentInfo
1650 * Gets the timestamp (seconds from system's Epoch) when the resource
1653 * Return value: the number of seconds elapsed from system's Epoch when
1654 * the resource was last visited, or -1 on failure.
1659 gtk_recent_info_get_visited (GtkRecentInfo *info)
1661 g_return_val_if_fail (info != NULL, (time_t) -1);
1663 return info->visited;
1667 * gtk_recent_info_get_private_hint:
1668 * @info: a #GtkRecentInfo
1670 * Gets the value of the "private" flag. Resources in the recently used
1671 * list that have this flag set to %TRUE should only be displayed by the
1672 * applications that have registered them.
1674 * Return value: %TRUE if the private flag was found, %FALSE otherwise.
1679 gtk_recent_info_get_private_hint (GtkRecentInfo *info)
1681 g_return_val_if_fail (info != NULL, FALSE);
1683 return info->is_private;
1687 static RecentAppInfo *
1688 recent_app_info_new (const gchar *app_name)
1690 RecentAppInfo *app_info;
1692 g_assert (app_name != NULL);
1694 app_info = g_slice_new0 (RecentAppInfo);
1695 app_info->name = g_strdup (app_name);
1696 app_info->exec = NULL;
1697 app_info->count = 1;
1698 app_info->stamp = 0;
1704 recent_app_info_free (RecentAppInfo *app_info)
1709 g_free (app_info->name);
1710 g_free (app_info->exec);
1712 g_slice_free (RecentAppInfo, app_info);
1716 * gtk_recent_info_get_application_info:
1717 * @info: a #GtkRecentInfo
1718 * @app_name: the name of the application that has registered this item
1719 * @app_exec: (transfer none) (out): return location for the string containing the command line
1720 * @count: (out): return location for the number of times this item was registered
1721 * @time_: (out): return location for the timestamp this item was last registered
1722 * for this application
1724 * Gets the data regarding the application that has registered the resource
1727 * If the command line contains any escape characters defined inside the
1728 * storage specification, they will be expanded.
1730 * Return value: %TRUE if an application with @app_name has registered this
1731 * resource inside the recently used list, or %FALSE otherwise. The
1732 * @app_exec string is owned by the #GtkRecentInfo and should not be
1738 gtk_recent_info_get_application_info (GtkRecentInfo *info,
1739 const gchar *app_name,
1740 const gchar **app_exec,
1746 g_return_val_if_fail (info != NULL, FALSE);
1747 g_return_val_if_fail (app_name != NULL, FALSE);
1749 ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
1753 g_warning ("No registered application with name '%s' "
1754 "for item with URI '%s' found",
1761 *app_exec = ai->exec;
1773 * gtk_recent_info_get_applications:
1774 * @info: a #GtkRecentInfo
1775 * @length: (out) (allow-none): return location for the length of the returned list
1777 * Retrieves the list of applications that have registered this resource.
1779 * Return value: (array length=length zero-terminated=1) (transfer full):
1780 * a newly allocated %NULL-terminated array of strings.
1781 * Use g_strfreev() to free it.
1786 gtk_recent_info_get_applications (GtkRecentInfo *info,
1793 g_return_val_if_fail (info != NULL, NULL);
1795 if (!info->applications)
1803 n_apps = g_slist_length (info->applications);
1805 retval = g_new0 (gchar *, n_apps + 1);
1807 for (l = info->applications, i = 0;
1811 RecentAppInfo *ai = (RecentAppInfo *) l->data;
1813 g_assert (ai != NULL);
1815 retval[i++] = g_strdup (ai->name);
1826 * gtk_recent_info_has_application:
1827 * @info: a #GtkRecentInfo
1828 * @app_name: a string containing an application name
1830 * Checks whether an application registered this resource using @app_name.
1832 * Return value: %TRUE if an application with name @app_name was found,
1838 gtk_recent_info_has_application (GtkRecentInfo *info,
1839 const gchar *app_name)
1841 g_return_val_if_fail (info != NULL, FALSE);
1842 g_return_val_if_fail (app_name != NULL, FALSE);
1844 return (NULL != g_hash_table_lookup (info->apps_lookup, app_name));
1848 * gtk_recent_info_last_application:
1849 * @info: a #GtkRecentInfo
1851 * Gets the name of the last application that have registered the
1852 * recently used resource represented by @info.
1854 * Return value: an application name. Use g_free() to free it.
1859 gtk_recent_info_last_application (GtkRecentInfo *info)
1862 time_t last_stamp = (time_t) -1;
1865 g_return_val_if_fail (info != NULL, NULL);
1867 for (l = info->applications; l != NULL; l = l->next)
1869 RecentAppInfo *ai = (RecentAppInfo *) l->data;
1871 if (ai->stamp > last_stamp)
1874 last_stamp = ai->stamp;
1878 return g_strdup (name);
1882 get_icon_for_mime_type (const char *mime_type,
1885 GtkIconTheme *icon_theme;
1891 icon_theme = gtk_icon_theme_get_default ();
1893 content_type = g_content_type_from_mime_type (mime_type);
1898 icon = g_content_type_get_icon (content_type);
1899 info = gtk_icon_theme_lookup_by_gicon (icon_theme,
1902 GTK_ICON_LOOKUP_USE_BUILTIN);
1903 g_free (content_type);
1904 g_object_unref (icon);
1909 pixbuf = gtk_icon_info_load_icon (info, NULL);
1910 gtk_icon_info_free (info);
1916 get_icon_fallback (const gchar *icon_name,
1919 GtkIconTheme *icon_theme;
1922 icon_theme = gtk_icon_theme_get_default ();
1924 retval = gtk_icon_theme_load_icon (icon_theme, icon_name,
1926 GTK_ICON_LOOKUP_USE_BUILTIN,
1928 g_assert (retval != NULL);
1934 * gtk_recent_info_get_icon:
1935 * @info: a #GtkRecentInfo
1936 * @size: the size of the icon in pixels
1938 * Retrieves the icon of size @size associated to the resource MIME type.
1940 * Return value: (transfer full): a #GdkPixbuf containing the icon,
1941 * or %NULL. Use g_object_unref() when finished using the icon.
1946 gtk_recent_info_get_icon (GtkRecentInfo *info,
1949 GdkPixbuf *retval = NULL;
1951 g_return_val_if_fail (info != NULL, NULL);
1953 if (info->mime_type)
1954 retval = get_icon_for_mime_type (info->mime_type, size);
1956 /* this function should never fail */
1959 if (info->mime_type &&
1960 strcmp (info->mime_type, "x-directory/normal") == 0)
1961 retval = get_icon_fallback ("folder", size);
1963 retval = get_icon_fallback ("text-x-generic", size);
1970 * gtk_recent_info_get_gicon:
1971 * @info: a #GtkRecentInfo
1973 * Retrieves the icon associated to the resource MIME type.
1975 * Return value: (transfer full): a #GIcon containing the icon, or %NULL. Use
1976 * g_object_unref() when finished using the icon
1981 gtk_recent_info_get_gicon (GtkRecentInfo *info)
1984 gchar *content_type;
1986 g_return_val_if_fail (info != NULL, NULL);
1988 if (info->mime_type != NULL &&
1989 (content_type = g_content_type_from_mime_type (info->mime_type)) != NULL)
1991 icon = g_content_type_get_icon (content_type);
1992 g_free (content_type);
1999 * gtk_recent_info_is_local:
2000 * @info: a #GtkRecentInfo
2002 * Checks whether the resource is local or not by looking at the
2003 * scheme of its URI.
2005 * Return value: %TRUE if the resource is local.
2010 gtk_recent_info_is_local (GtkRecentInfo *info)
2012 g_return_val_if_fail (info != NULL, FALSE);
2014 return has_case_prefix (info->uri, "file:/");
2018 * gtk_recent_info_exists:
2019 * @info: a #GtkRecentInfo
2021 * Checks whether the resource pointed by @info still exists. At
2022 * the moment this check is done only on resources pointing to local files.
2024 * Return value: %TRUE if the resource exists
2029 gtk_recent_info_exists (GtkRecentInfo *info)
2033 gboolean retval = FALSE;
2035 g_return_val_if_fail (info != NULL, FALSE);
2037 /* we guarantee only local resources */
2038 if (!gtk_recent_info_is_local (info))
2041 filename = g_filename_from_uri (info->uri, NULL, NULL);
2044 if (g_stat (filename, &stat_buf) == 0)
2054 * gtk_recent_info_match:
2055 * @info_a: a #GtkRecentInfo
2056 * @info_b: a #GtkRecentInfo
2058 * Checks whether two #GtkRecentInfo structures point to the same
2061 * Return value: %TRUE if both #GtkRecentInfo structures point to se same
2062 * resource, %FALSE otherwise.
2067 gtk_recent_info_match (GtkRecentInfo *info_a,
2068 GtkRecentInfo *info_b)
2070 g_return_val_if_fail (info_a != NULL, FALSE);
2071 g_return_val_if_fail (info_b != NULL, FALSE);
2073 return (0 == strcmp (info_a->uri, info_b->uri));
2076 /* taken from gnome-vfs-uri.c */
2077 static const gchar *
2078 get_method_string (const gchar *substring,
2079 gchar **method_string)
2085 g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
2092 !(p == substring + 1 && g_ascii_isalpha (*substring))
2096 /* Found toplevel method specification. */
2097 method = g_strndup (substring, p - substring);
2098 *method_string = g_ascii_strdown (method, -1);
2104 *method_string = g_strdup ("file");
2111 /* Stolen from gnome_vfs_make_valid_utf8() */
2113 make_valid_utf8 (const char *name)
2116 const char *remainder, *invalid;
2117 int remaining_bytes, valid_bytes;
2121 remaining_bytes = name ? strlen (name) : 0;
2123 while (remaining_bytes != 0)
2125 if (g_utf8_validate (remainder, remaining_bytes, &invalid))
2128 valid_bytes = invalid - remainder;
2131 string = g_string_sized_new (remaining_bytes);
2133 g_string_append_len (string, remainder, valid_bytes);
2134 g_string_append_c (string, '?');
2136 remaining_bytes -= valid_bytes + 1;
2137 remainder = invalid + 1;
2141 return g_strdup (name);
2143 g_string_append (string, remainder);
2144 g_assert (g_utf8_validate (string->str, -1, NULL));
2146 return g_string_free (string, FALSE);
2150 get_uri_shortname_for_display (const gchar *uri)
2153 gboolean validated = FALSE;
2155 if (has_case_prefix (uri, "file:/"))
2159 local_file = g_filename_from_uri (uri, NULL, NULL);
2163 name = g_filename_display_basename (local_file);
2167 g_free (local_file);
2176 rest = get_method_string (uri, &method);
2177 local_file = g_filename_display_basename (rest);
2179 name = g_strconcat (method, ": ", local_file, NULL);
2181 g_free (local_file);
2185 g_assert (name != NULL);
2187 if (!validated && !g_utf8_validate (name, -1, NULL))
2191 utf8_name = make_valid_utf8 (name);
2201 * gtk_recent_info_get_short_name:
2202 * @info: an #GtkRecentInfo
2204 * Computes a valid UTF-8 string that can be used as the name of the item in a
2205 * menu or list. For example, calling this function on an item that refers to
2206 * "file:///foo/bar.txt" will yield "bar.txt".
2208 * Return value: A newly-allocated string in UTF-8 encoding; free it with
2214 gtk_recent_info_get_short_name (GtkRecentInfo *info)
2218 g_return_val_if_fail (info != NULL, NULL);
2220 if (info->uri == NULL)
2223 short_name = get_uri_shortname_for_display (info->uri);
2229 * gtk_recent_info_get_uri_display:
2230 * @info: a #GtkRecentInfo
2232 * Gets a displayable version of the resource's URI. If the resource
2233 * is local, it returns a local path; if the resource is not local,
2234 * it returns the UTF-8 encoded content of gtk_recent_info_get_uri().
2236 * Return value: a newly allocated UTF-8 string containing the
2237 * resource's URI or %NULL. Use g_free() when done using it.
2242 gtk_recent_info_get_uri_display (GtkRecentInfo *info)
2246 g_return_val_if_fail (info != NULL, NULL);
2249 if (gtk_recent_info_is_local (info))
2253 filename = g_filename_from_uri (info->uri, NULL, NULL);
2257 retval = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
2262 retval = make_valid_utf8 (info->uri);
2269 * gtk_recent_info_get_age:
2270 * @info: a #GtkRecentInfo
2272 * Gets the number of days elapsed since the last update of the resource
2275 * Return value: a positive integer containing the number of days elapsed
2276 * since the time this resource was last modified.
2281 gtk_recent_info_get_age (GtkRecentInfo *info)
2286 g_return_val_if_fail (info != NULL, -1);
2290 delta = now - info->modified;
2292 retval = (gint) (delta / (60 * 60 * 24));
2298 * gtk_recent_info_get_groups:
2299 * @info: a #GtkRecentInfo
2300 * @length: (out) (allow-none): return location for the number of groups returned
2302 * Returns all groups registered for the recently used item @info. The
2303 * array of returned group names will be %NULL terminated, so length might
2304 * optionally be %NULL.
2306 * Return value: (array length=length zero-terminated=1) (transfer full):
2307 * a newly allocated %NULL terminated array of strings.
2308 * Use g_strfreev() to free it.
2313 gtk_recent_info_get_groups (GtkRecentInfo *info,
2320 g_return_val_if_fail (info != NULL, NULL);
2330 n_groups = g_slist_length (info->groups);
2332 retval = g_new0 (gchar *, n_groups + 1);
2334 for (l = info->groups, i = 0;
2338 gchar *group_name = (gchar *) l->data;
2340 g_assert (group_name != NULL);
2342 retval[i++] = g_strdup (group_name);
2353 * gtk_recent_info_has_group:
2354 * @info: a #GtkRecentInfo
2355 * @group_name: name of a group
2357 * Checks whether @group_name appears inside the groups registered for the
2358 * recently used item @info.
2360 * Return value: %TRUE if the group was found.
2365 gtk_recent_info_has_group (GtkRecentInfo *info,
2366 const gchar *group_name)
2370 g_return_val_if_fail (info != NULL, FALSE);
2371 g_return_val_if_fail (group_name != NULL, FALSE);
2376 for (l = info->groups; l != NULL; l = l->next)
2378 gchar *g = (gchar *) l->data;
2380 if (strcmp (g, group_name) == 0)
2388 * gtk_recent_info_create_app_info:
2389 * @info: a #GtkRecentInfo
2390 * @app_name: (allow-none): the name of the application that should
2391 * be mapped to a #GAppInfo; if %NULL is used then the default
2392 * application for the MIME type is used
2393 * @error: (allow-none): return location for a #GError, or %NULL
2395 * Creates a #GAppInfo for the specified #GtkRecentInfo
2397 * Return value: (transfer full): the newly created #GAppInfo, or %NULL.
2398 * In case of error, @error will be set either with a
2399 * %GTK_RECENT_MANAGER_ERROR or a %G_IO_ERROR
2402 gtk_recent_info_create_app_info (GtkRecentInfo *info,
2403 const gchar *app_name,
2408 GError *internal_error = NULL;
2410 g_return_val_if_fail (info != NULL, NULL);
2412 if (app_name == NULL || *app_name == '\0')
2416 if (info->mime_type == NULL)
2419 content_type = g_content_type_from_mime_type (info->mime_type);
2420 if (content_type == NULL)
2423 app_info = g_app_info_get_default_for_type (content_type, TRUE);
2424 g_free (content_type);
2429 ai = g_hash_table_lookup (info->apps_lookup, app_name);
2432 g_set_error (error, GTK_RECENT_MANAGER_ERROR,
2433 GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
2434 _("No registered application with name '%s' for item with URI '%s' found"),
2440 internal_error = NULL;
2441 app_info = g_app_info_create_from_commandline (ai->exec, ai->name,
2442 G_APP_INFO_CREATE_NONE,
2444 if (internal_error != NULL)
2446 g_propagate_error (error, internal_error);
2454 * _gtk_recent_manager_sync:
2456 * Private function for synchronising the recent manager singleton.
2459 _gtk_recent_manager_sync (void)
2461 if (recent_manager_singleton)
2463 /* force a dump of the contents of the recent manager singleton */
2464 recent_manager_singleton->priv->is_dirty = TRUE;
2465 gtk_recent_manager_real_changed (recent_manager_singleton);