X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkrecentmanager.c;h=310efe077126223be5bb33dbbcedb2a548909a4d;hb=HEAD;hp=39a3242e9daecf57277ef9cb436c36797b6940e6;hpb=21284ce95f92f1711ce1db2bce1cc93aa8212af2;p=~andy%2Fgtk diff --git a/gtk/gtkrecentmanager.c b/gtk/gtkrecentmanager.c index 39a3242e9..310efe077 100644 --- a/gtk/gtkrecentmanager.c +++ b/gtk/gtkrecentmanager.c @@ -14,8 +14,76 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * License along with this library. If not, see . + */ + +/** + * SECTION:gtkrecentmanager + * @Title: GtkRecentManager + * @short_description: Managing recently used files + * @See_Also: #GBookmarkFile, #GtkSettings, #GtkRecentChooser + * + * #GtkRecentManager provides a facility for adding, removing and + * looking up recently used files. Each recently used file is + * identified by its URI, and has meta-data associated to it, like + * the names and command lines of the applications that have + * registered it, the number of time each application has registered + * the same file, the mime type of the file and whether the file + * should be displayed only by the applications that have + * registered it. + * + * The recently used files list is per user. + * + * The #GtkRecentManager acts like a database of all the recently + * used files. You can create new #GtkRecentManager objects, but + * it is more efficient to use the default manager created by GTK+. + * + * Adding a new recently used file is as simple as: + * + * |[ + * GtkRecentManager *manager; + * + * manager = gtk_recent_manager_get_default (); + * gtk_recent_manager_add_item (manager, file_uri); + * ]| + * + * The #GtkRecentManager will try to gather all the needed information + * from the file itself through GIO. + * + * Looking up the meta-data associated with a recently used file + * given its URI requires calling gtk_recent_manager_lookup_item(): + * + * |[ + * GtkRecentManager *manager; + * GtkRecentInfo *info; + * GError *error = NULL; + * + * manager = gtk_recent_manager_get_default (); + * info = gtk_recent_manager_lookup_item (manager, file_uri, &error); + * if (error) + * { + * g_warning ("Could not find the file: %s", error->message); + * g_error_free (error); + * } + * else + * { + * /* Use the info object */ + * gtk_recent_info_unref (info); + * } + * ]| + * + * In order to retrieve the list of recently used files, you can use + * gtk_recent_manager_get_items(), which returns a list of #GtkRecentInfo + * structures. + * + * A #GtkRecentManager is the model used to populate the contents of + * one, or more #GtkRecentChooser implementations. + * + * The maximum age of the recently used files list is + * controllable through the #GtkSettings:gtk-recent-files-max-age + * property. + * + * Recently used files are supported since GTK+ 2.10. */ #include "config.h" @@ -34,15 +102,15 @@ #include "gtkrecentmanager.h" #include "gtkintl.h" +#include "gtksettings.h" #include "gtkstock.h" #include "gtkicontheme.h" #include "gtktypebuiltins.h" #include "gtkprivate.h" #include "gtkmarshalers.h" -#include "gtkalias.h" /* the file where we store the recently used items */ -#define GTK_RECENTLY_USED_FILE ".recently-used.xbel" +#define GTK_RECENTLY_USED_FILE "recently-used.xbel" /* return all items by default */ #define DEFAULT_LIMIT -1 @@ -60,6 +128,17 @@ typedef struct time_t stamp; } RecentAppInfo; +/** + * GtkRecentInfo: + * + * #GtkRecentInfo is an opaque data structure + * whose members can only be accessed using the provided API. + * + * #GtkRecentInfo constains all the meta-data + * associated with an entry in the recently used files list. + * + * Since: 2.10 + */ struct _GtkRecentInfo { gchar *uri; @@ -85,20 +164,20 @@ struct _GtkRecentInfo gint ref_count; }; -#define GTK_RECENT_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerPrivate)) - struct _GtkRecentManagerPrivate { gchar *filename; guint is_dirty : 1; - gint limit; gint size; GBookmarkFile *recent_items; GFileMonitor *monitor; + + guint changed_timeout; + guint changed_age; }; enum @@ -134,6 +213,7 @@ static void gtk_recent_manager_set_filename (GtkRecentManager *manag const gchar *filename); static void gtk_recent_manager_clamp_to_age (GtkRecentManager *manager, gint age); +static void gtk_recent_manager_enabled_changed (GtkRecentManager *manager); static void build_recent_items_list (GtkRecentManager *manager); @@ -201,7 +281,7 @@ gtk_recent_manager_class_init (GtkRecentManagerClass *klass) gobject_class->finalize = gtk_recent_manager_finalize; /** - * GtkRecentManager:filename + * GtkRecentManager:filename: * * The full path to the file to be used to store and read the recently * used resources list @@ -215,25 +295,9 @@ gtk_recent_manager_class_init (GtkRecentManagerClass *klass) P_("The full path to the file to be used to store and read the list"), NULL, (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE))); + /** - * GtkRecentManager:limit - * - * The maximum number of items to be returned by the - * gtk_recent_manager_get_items() function. - * - * Since: 2.10 - */ - g_object_class_install_property (gobject_class, - PROP_LIMIT, - g_param_spec_int ("limit", - P_("Limit"), - P_("The maximum number of items to be returned by gtk_recent_manager_get_items()"), - -1, - G_MAXINT, - DEFAULT_LIMIT, - G_PARAM_READWRITE)); - /** - * GtkRecentManager:size + * GtkRecentManager:size: * * The size of the recently used resources list. * @@ -250,11 +314,12 @@ gtk_recent_manager_class_init (GtkRecentManagerClass *klass) G_PARAM_READABLE)); /** - * GtkRecentManager::changed + * GtkRecentManager::changed: * @recent_manager: the recent manager * * Emitted when the current recently used resources manager changes its - * contents. + * contents, either by calling gtk_recent_manager_add_item() or by another + * application. * * Since: 2.10 */ @@ -276,13 +341,19 @@ static void gtk_recent_manager_init (GtkRecentManager *manager) { GtkRecentManagerPrivate *priv; + GtkSettings *settings; - manager->priv = priv = GTK_RECENT_MANAGER_GET_PRIVATE (manager); - - priv->limit = DEFAULT_LIMIT; - priv->size = 0; + manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, + GTK_TYPE_RECENT_MANAGER, + GtkRecentManagerPrivate); + priv = manager->priv; + priv->size = 0; priv->filename = NULL; + + settings = gtk_settings_get_default (); + g_signal_connect_swapped (settings, "notify::gtk-recent-files-enabled", + G_CALLBACK (gtk_recent_manager_enabled_changed), manager); } static void @@ -297,9 +368,6 @@ gtk_recent_manager_set_property (GObject *object, { case PROP_FILENAME: gtk_recent_manager_set_filename (recent_manager, g_value_get_string (value)); - break; - case PROP_LIMIT: - gtk_recent_manager_set_limit (recent_manager, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -320,9 +388,6 @@ gtk_recent_manager_get_property (GObject *object, case PROP_FILENAME: g_value_set_string (value, recent_manager->priv->filename); break; - case PROP_LIMIT: - g_value_set_int (value, recent_manager->priv->limit); - break; case PROP_SIZE: g_value_set_int (value, recent_manager->priv->size); break; @@ -333,12 +398,26 @@ gtk_recent_manager_get_property (GObject *object, } static void -gtk_recent_manager_dispose (GObject *object) +gtk_recent_manager_finalize (GObject *object) { GtkRecentManager *manager = GTK_RECENT_MANAGER (object); GtkRecentManagerPrivate *priv = manager->priv; - if (priv->monitor) + g_free (priv->filename); + + if (priv->recent_items != NULL) + g_bookmark_file_free (priv->recent_items); + + G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object); +} + +static void +gtk_recent_manager_dispose (GObject *gobject) +{ + GtkRecentManager *manager = GTK_RECENT_MANAGER (gobject); + GtkRecentManagerPrivate *priv = manager->priv; + + if (priv->monitor != NULL) { g_signal_handlers_disconnect_by_func (priv->monitor, G_CALLBACK (gtk_recent_manager_monitor_changed), @@ -347,21 +426,28 @@ gtk_recent_manager_dispose (GObject *object) priv->monitor = NULL; } - G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (object); + if (priv->changed_timeout != 0) + { + g_source_remove (priv->changed_timeout); + priv->changed_timeout = 0; + priv->changed_age = 0; + } + + if (priv->is_dirty) + { + g_object_ref (manager); + g_signal_emit (manager, signal_changed, 0); + g_object_unref (manager); + } + + G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (gobject); } static void -gtk_recent_manager_finalize (GObject *object) +gtk_recent_manager_enabled_changed (GtkRecentManager *manager) { - GtkRecentManager *manager = GTK_RECENT_MANAGER (object); - GtkRecentManagerPrivate *priv = manager->priv; - - g_free (priv->filename); - - if (priv->recent_items) - g_bookmark_file_free (priv->recent_items); - - G_OBJECT_CLASS (gtk_recent_manager_parent_class)->finalize (object); + manager->priv->is_dirty = TRUE; + gtk_recent_manager_changed (manager); } static void @@ -392,17 +478,20 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager) { GtkSettings *settings = gtk_settings_get_default (); gint age = 30; + gboolean enabled; - g_object_get (G_OBJECT (settings), "gtk-recent-files-max-age", &age, NULL); - if (age > 0) - gtk_recent_manager_clamp_to_age (manager, age); - else if (age == 0) + g_object_get (G_OBJECT (settings), + "gtk-recent-files-max-age", &age, + "gtk-recent-files-enabled", &enabled, + NULL); + + if (age == 0 || !enabled) { g_bookmark_file_free (priv->recent_items); - priv->recent_items = NULL; - priv->recent_items = g_bookmark_file_new (); } + else if (age > 0) + gtk_recent_manager_clamp_to_age (manager, age); } write_error = NULL; @@ -416,6 +505,14 @@ gtk_recent_manager_real_changed (GtkRecentManager *manager) g_error_free (write_error); } + if (g_chmod (priv->filename, 0600) < 0) + { + filename_warning ("Attempting to set the permissions of `%s', " + "but failed: %s", + priv->filename, + g_strerror (errno)); + } + /* mark us as clean */ priv->is_dirty = FALSE; } @@ -444,7 +541,9 @@ gtk_recent_manager_monitor_changed (GFileMonitor *monitor, { case G_FILE_MONITOR_EVENT_CHANGED: case G_FILE_MONITOR_EVENT_CREATED: + gdk_threads_enter (); gtk_recent_manager_changed (manager); + gdk_threads_leave (); break; case G_FILE_MONITOR_EVENT_DELETED: @@ -455,6 +554,26 @@ gtk_recent_manager_monitor_changed (GFileMonitor *monitor, } } +static gchar * +get_default_filename (void) +{ + if (g_mkdir_with_parents (g_get_user_data_dir (), 0755) == -1) + { + int saved_errno = errno; + + g_critical ("Unable to create user data directory '%s' for storing " + "the recently used files list: %s", + g_get_user_data_dir (), + g_strerror (saved_errno)); + + return NULL; + } + + return g_build_filename (g_get_user_data_dir (), + GTK_RECENTLY_USED_FILE, + NULL); +} + static void gtk_recent_manager_set_filename (GtkRecentManager *manager, const gchar *filename) @@ -495,9 +614,7 @@ gtk_recent_manager_set_filename (GtkRecentManager *manager, else { if (!filename || *filename == '\0') - priv->filename = g_build_filename (g_get_home_dir (), - GTK_RECENTLY_USED_FILE, - NULL); + priv->filename = get_default_filename (); else priv->filename = g_strdup (filename); } @@ -616,10 +733,9 @@ gtk_recent_manager_new (void) * gtk_recent_manager_get_default: * * Gets a unique instance of #GtkRecentManager, that you can share - * in your application without caring about memory management. The - * returned instance will be freed when you application terminates. + * in your application without caring about memory management. * - * Return value: A unique #GtkRecentManager. Do not ref or unref it. + * Return value: (transfer none): A unique #GtkRecentManager. Do not ref or unref it. * * Since: 2.10 */ @@ -632,102 +748,6 @@ gtk_recent_manager_get_default (void) return recent_manager_singleton; } -/** - * gtk_recent_manager_get_for_screen: - * @screen: a #GdkScreen - * - * Gets the recent manager object associated with @screen; if this - * function has not previously been called for the given screen, - * a new recent manager object will be created and associated with - * the screen. Recent manager objects are fairly expensive to create, - * so using this function is usually a better choice than calling - * gtk_recent_manager_new() and setting the screen yourself; by using - * this function a single recent manager object will be shared between - * users. - * - * Return value: A unique #GtkRecentManager associated with the given - * screen. This recent manager is associated to the with the screen - * and can be used as long as the screen is open. Do not ref or - * unref it. - * - * Deprecated: 2.12: This function has been deprecated and should - * not be used in newly written code. Calling this function is - * equivalent to calling gtk_recent_manager_get_default(). - * - * Since: 2.10 - */ -GtkRecentManager * -gtk_recent_manager_get_for_screen (GdkScreen *screen) -{ - return gtk_recent_manager_get_default (); -} - -/** - * gtk_recent_manager_set_screen: - * @manager: a #GtkRecentManager - * @screen: a #GdkScreen - * - * Sets the screen for a recent manager; the screen is used to - * track the user's currently configured recently used documents - * storage. - * - * Since: 2.10 - * - * Deprecated: 2.12: This function has been deprecated and should - * not be used in newly written code. Calling this function has - * no effect. - */ -void -gtk_recent_manager_set_screen (GtkRecentManager *manager, - GdkScreen *screen) -{ - -} - -/** - * gtk_recent_manager_set_limit: - * @manager: a #GtkRecentManager - * @limit: the maximum number of items to return, or -1. - * - * Sets the maximum number of item that the gtk_recent_manager_get_items() - * function should return. If @limit is set to -1, then return all the - * items. - * - * Since: 2.10 - */ -void -gtk_recent_manager_set_limit (GtkRecentManager *manager, - gint limit) -{ - GtkRecentManagerPrivate *priv; - - g_return_if_fail (GTK_IS_RECENT_MANAGER (manager)); - - priv = manager->priv; - priv->limit = limit; -} - -/** - * gtk_recent_manager_get_limit: - * @manager: a #GtkRecentManager - * - * Gets the maximum number of items that the gtk_recent_manager_get_items() - * function should return. - * - * Return value: the number of items to return, or -1 for every item. - * - * Since: 2.10 - */ -gint -gtk_recent_manager_get_limit (GtkRecentManager *manager) -{ - GtkRecentManagerPrivate *priv; - - g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), DEFAULT_LIMIT); - - priv = manager->priv; - return priv->limit; -} static void gtk_recent_manager_add_item_query_info (GObject *source_object, @@ -738,21 +758,11 @@ gtk_recent_manager_add_item_query_info (GObject *source_object, GtkRecentManager *manager = user_data; GtkRecentData recent_data; GFileInfo *file_info; - gchar *uri; - GError *error; + gchar *uri, *basename; uri = g_file_get_uri (file); - error = NULL; - file_info = g_file_query_info_finish (file, res, &error); - if (error) - { - g_warning ("Unable to retrieve the file info for `%s': %s", - uri, - error->message); - g_error_free (error); - goto out; - } + file_info = g_file_query_info_finish (file, res, NULL); /* NULL-GError */ recent_data.display_name = NULL; recent_data.description = NULL; @@ -772,13 +782,19 @@ gtk_recent_manager_add_item_query_info (GObject *source_object, g_object_unref (file_info); } else - recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME); + { + basename = g_file_get_basename (file); + recent_data.mime_type = g_content_type_guess (basename, NULL, 0, NULL); + g_free (basename); + } recent_data.app_name = g_strdup (g_get_application_name ()); recent_data.app_exec = g_strjoin (" ", g_get_prgname (), "%u", NULL); recent_data.groups = NULL; recent_data.is_private = FALSE; + gdk_threads_enter (); + /* Ignore return value, this can't fail anyway since all required * fields are set */ gtk_recent_manager_add_full (manager, uri, &recent_data); @@ -786,11 +802,12 @@ gtk_recent_manager_add_item_query_info (GObject *source_object, manager->priv->is_dirty = TRUE; gtk_recent_manager_changed (manager); + gdk_threads_leave (); + g_free (recent_data.mime_type); g_free (recent_data.app_name); g_free (recent_data.app_exec); -out: g_object_unref (manager); g_free (uri); } @@ -876,6 +893,8 @@ gtk_recent_manager_add_full (GtkRecentManager *manager, const GtkRecentData *data) { GtkRecentManagerPrivate *priv; + GtkSettings *settings; + gboolean enabled; g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE); g_return_val_if_fail (uri != NULL, FALSE); @@ -928,7 +947,12 @@ gtk_recent_manager_add_full (GtkRecentManager *manager, uri); return FALSE; } - + + settings = gtk_settings_get_default (); + g_object_get (G_OBJECT (settings), "gtk-recent-files-enabled", &enabled, NULL); + if (!enabled) + return TRUE; + priv = manager->priv; if (!priv->recent_items) @@ -968,7 +992,6 @@ gtk_recent_manager_add_full (GtkRecentManager *manager, * will dump our changes */ priv->is_dirty = TRUE; - gtk_recent_manager_changed (manager); return TRUE; @@ -978,7 +1001,7 @@ gtk_recent_manager_add_full (GtkRecentManager *manager, * gtk_recent_manager_remove_item: * @manager: a #GtkRecentManager * @uri: the URI of the item you wish to remove - * @error: return location for a #GError, or %NULL + * @error: (allow-none): return location for a #GError, or %NULL * * Removes a resource pointed by @uri from the recently used resources * list handled by a recent manager. @@ -1029,7 +1052,6 @@ gtk_recent_manager_remove_item (GtkRecentManager *manager, } priv->is_dirty = TRUE; - gtk_recent_manager_changed (manager); return TRUE; @@ -1127,7 +1149,7 @@ build_recent_info (GBookmarkFile *bookmarks, * gtk_recent_manager_lookup_item: * @manager: a #GtkRecentManager * @uri: a URI - * @error: a return location for a #GError, or %NULL + * @error: (allow-none): a return location for a #GError, or %NULL * * Searches for a URI inside the recently used resources list, and * returns a structure containing informations about the resource @@ -1190,9 +1212,9 @@ gtk_recent_manager_lookup_item (GtkRecentManager *manager, * gtk_recent_manager_move_item: * @manager: a #GtkRecentManager * @uri: the URI of a recently used resource - * @new_uri: the new URI of the recently used resource, or %NULL to + * @new_uri: (allow-none): the new URI of the recently used resource, or %NULL to * remove the item pointed by @uri in the list - * @error: a return location for a #GError, or %NULL + * @error: (allow-none): a return location for a #GError, or %NULL * * Changes the location of a recently used resource from @uri to @new_uri. * @@ -1202,60 +1224,58 @@ gtk_recent_manager_lookup_item (GtkRecentManager *manager, * Return value: %TRUE on success. * * Since: 2.10 - */ + */ gboolean gtk_recent_manager_move_item (GtkRecentManager *recent_manager, - const gchar *uri, - const gchar *new_uri, - GError **error) + const gchar *uri, + const gchar *new_uri, + GError **error) { GtkRecentManagerPrivate *priv; GError *move_error; - gboolean res; - + g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE); g_return_val_if_fail (uri != NULL, FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - + priv = recent_manager->priv; if (!priv->recent_items) { g_set_error (error, GTK_RECENT_MANAGER_ERROR, - GTK_RECENT_MANAGER_ERROR_NOT_FOUND, - _("Unable to find an item with URI '%s'"), - uri); + GTK_RECENT_MANAGER_ERROR_NOT_FOUND, + _("Unable to find an item with URI '%s'"), + uri); return FALSE; } if (!g_bookmark_file_has_item (priv->recent_items, uri)) { g_set_error (error, GTK_RECENT_MANAGER_ERROR, - GTK_RECENT_MANAGER_ERROR_NOT_FOUND, - _("Unable to find an item with URI '%s'"), - uri); + GTK_RECENT_MANAGER_ERROR_NOT_FOUND, + _("Unable to find an item with URI '%s'"), + uri); return FALSE; } - + move_error = NULL; - res = g_bookmark_file_move_item (priv->recent_items, - uri, new_uri, - &move_error); - if (move_error) + if (!g_bookmark_file_move_item (priv->recent_items, + uri, + new_uri, + &move_error)) { g_error_free (move_error); g_set_error (error, GTK_RECENT_MANAGER_ERROR, - GTK_RECENT_MANAGER_ERROR_NOT_FOUND, - _("Unable to find an item with URI '%s'"), - uri); + GTK_RECENT_MANAGER_ERROR_NOT_FOUND, + _("Unable to find an item with URI '%s'"), + uri); return FALSE; } - - priv->is_dirty = TRUE; + priv->is_dirty = TRUE; gtk_recent_manager_changed (recent_manager); - + return TRUE; } @@ -1265,7 +1285,8 @@ gtk_recent_manager_move_item (GtkRecentManager *recent_manager, * * Gets the list of recently used resources. * - * Return value: a list of newly allocated #GtkRecentInfo objects. Use + * Return value: (element-type GtkRecentInfo) (transfer full): a list of + * newly allocated #GtkRecentInfo objects. Use * gtk_recent_info_unref() on each item inside the list, and then * free the list itself using g_list_free(). * @@ -1285,17 +1306,11 @@ gtk_recent_manager_get_items (GtkRecentManager *manager) if (!priv->recent_items) return NULL; - if (priv->limit == 0) - return NULL; - uris = g_bookmark_file_get_uris (priv->recent_items, &uris_len); for (i = 0; i < uris_len; i++) { GtkRecentInfo *info; - if (priv->limit != -1 && i == priv->limit) - break; - info = gtk_recent_info_new (uris[i]); build_recent_info (priv->recent_items, info); @@ -1313,24 +1328,22 @@ purge_recent_items_list (GtkRecentManager *manager, { GtkRecentManagerPrivate *priv = manager->priv; - if (!priv->recent_items) + if (priv->recent_items == NULL) return; - + g_bookmark_file_free (priv->recent_items); - priv->recent_items = NULL; - priv->recent_items = g_bookmark_file_new (); priv->size = 0; - priv->is_dirty = TRUE; - + /* emit the changed signal, to ensure that the purge is written */ + priv->is_dirty = TRUE; gtk_recent_manager_changed (manager); } /** * gtk_recent_manager_purge_items: * @manager: a #GtkRecentManager - * @error: a return location for a #GError, or %NULL + * @error: (allow-none): a return location for a #GError, or %NULL * * Purges every item from the recently used resources list. * @@ -1363,10 +1376,43 @@ gtk_recent_manager_purge_items (GtkRecentManager *manager, return purged; } +static gboolean +emit_manager_changed (gpointer data) +{ + GtkRecentManager *manager = data; + + manager->priv->changed_age = 0; + manager->priv->changed_timeout = 0; + + g_signal_emit (manager, signal_changed, 0); + + return FALSE; +} + static void -gtk_recent_manager_changed (GtkRecentManager *recent_manager) +gtk_recent_manager_changed (GtkRecentManager *manager) { - g_signal_emit (recent_manager, signal_changed, 0); + /* coalesce consecutive changes + * + * we schedule a write in 250 msecs immediately; if we get more than one + * request per millisecond before the timeout has a chance to run, we + * schedule an emission immediately. + */ + if (manager->priv->changed_timeout == 0) + manager->priv->changed_timeout = gdk_threads_add_timeout (250, emit_manager_changed, manager); + else + { + manager->priv->changed_age += 1; + + if (manager->priv->changed_age > 250) + { + g_source_remove (manager->priv->changed_timeout); + g_signal_emit (manager, signal_changed, 0); + + manager->priv->changed_age = 0; + manager->priv->changed_timeout = 0; + } + } } static void @@ -1404,17 +1450,9 @@ gtk_recent_manager_clamp_to_age (GtkRecentManager *manager, * GtkRecentInfo * *****************/ -GType -gtk_recent_info_get_type (void) -{ - static GType info_type = 0; - - if (!info_type) - info_type = g_boxed_type_register_static (I_("GtkRecentInfo"), - (GBoxedCopyFunc) gtk_recent_info_ref, - (GBoxedFreeFunc) gtk_recent_info_unref); - return info_type; -} +G_DEFINE_BOXED_TYPE (GtkRecentInfo, gtk_recent_info, + gtk_recent_info_ref, + gtk_recent_info_unref) static GtkRecentInfo * gtk_recent_info_new (const gchar *uri) @@ -1530,7 +1568,7 @@ gtk_recent_info_unref (GtkRecentInfo *info) * * Since: 2.10 */ -G_CONST_RETURN gchar * +const gchar * gtk_recent_info_get_uri (GtkRecentInfo *info) { g_return_val_if_fail (info != NULL, NULL); @@ -1550,7 +1588,7 @@ gtk_recent_info_get_uri (GtkRecentInfo *info) * * Since: 2.10 */ -G_CONST_RETURN gchar * +const gchar * gtk_recent_info_get_display_name (GtkRecentInfo *info) { g_return_val_if_fail (info != NULL, NULL); @@ -1572,7 +1610,7 @@ gtk_recent_info_get_display_name (GtkRecentInfo *info) * * Since: 2.10 **/ -G_CONST_RETURN gchar * +const gchar * gtk_recent_info_get_description (GtkRecentInfo *info) { g_return_val_if_fail (info != NULL, NULL); @@ -1591,7 +1629,7 @@ gtk_recent_info_get_description (GtkRecentInfo *info) * * Since: 2.10 */ -G_CONST_RETURN gchar * +const gchar * gtk_recent_info_get_mime_type (GtkRecentInfo *info) { g_return_val_if_fail (info != NULL, NULL); @@ -1690,11 +1728,11 @@ recent_app_info_new (const gchar *app_name) g_assert (app_name != NULL); - app_info = g_new0 (RecentAppInfo, 1); + app_info = g_slice_new0 (RecentAppInfo); app_info->name = g_strdup (app_name); app_info->exec = NULL; app_info->count = 1; - app_info->stamp = time (NULL); + app_info->stamp = 0; return app_info; } @@ -1706,19 +1744,18 @@ recent_app_info_free (RecentAppInfo *app_info) return; g_free (app_info->name); - g_free (app_info->exec); - g_free (app_info); + g_slice_free (RecentAppInfo, app_info); } /** * gtk_recent_info_get_application_info: * @info: a #GtkRecentInfo * @app_name: the name of the application that has registered this item - * @app_exec: return location for the string containing the command line - * @count: return location for the number of times this item was registered - * @time_: return location for the timestamp this item was last registered + * @app_exec: (transfer none) (out): return location for the string containing the command line + * @count: (out): return location for the number of times this item was registered + * @time_: (out): return location for the timestamp this item was last registered * for this application * * Gets the data regarding the application that has registered the resource @@ -1728,15 +1765,16 @@ recent_app_info_free (RecentAppInfo *app_info) * storage specification, they will be expanded. * * Return value: %TRUE if an application with @app_name has registered this - * resource inside the recently used list, or %FALSE otherwise. You should - * free the returned command line using g_free(). + * resource inside the recently used list, or %FALSE otherwise. The + * @app_exec string is owned by the #GtkRecentInfo and should not be + * modified or freed * * Since: 2.10 */ gboolean gtk_recent_info_get_application_info (GtkRecentInfo *info, const gchar *app_name, - gchar **app_exec, + const gchar **app_exec, guint *count, time_t *time_) { @@ -1771,15 +1809,16 @@ gtk_recent_info_get_application_info (GtkRecentInfo *info, /** * gtk_recent_info_get_applications: * @info: a #GtkRecentInfo - * @length: return location for the length of the returned list, or %NULL + * @length: (out) (allow-none): return location for the length of the returned list * * Retrieves the list of applications that have registered this resource. * - * Return value: a newly allocated %NULL-terminated array of strings. - * Use g_strfreev() to free it. + * Return value: (array length=length zero-terminated=1) (transfer full): + * a newly allocated %NULL-terminated array of strings. + * Use g_strfreev() to free it. * * Since: 2.10 - */ + */ gchar ** gtk_recent_info_get_applications (GtkRecentInfo *info, gsize *length) @@ -1881,52 +1920,31 @@ get_icon_for_mime_type (const char *mime_type, gint pixel_size) { GtkIconTheme *icon_theme; - const char *separator; - GString *icon_name; + char *content_type; + GIcon *icon; + GtkIconInfo *info; GdkPixbuf *pixbuf; - separator = strchr (mime_type, '/'); - if (!separator) + icon_theme = gtk_icon_theme_get_default (); + + content_type = g_content_type_from_mime_type (mime_type); + + if (!content_type) return NULL; - icon_theme = gtk_icon_theme_get_default (); + icon = g_content_type_get_icon (content_type); + info = gtk_icon_theme_lookup_by_gicon (icon_theme, + icon, + pixel_size, + GTK_ICON_LOOKUP_USE_BUILTIN); + g_free (content_type); + g_object_unref (icon); + + if (!info) + return NULL; - /* try with the three icon name variants for MIME types */ - - /* canonicalize MIME type: foo/x-bar -> foo-x-bar */ - icon_name = g_string_new (NULL); - g_string_append_len (icon_name, mime_type, separator - mime_type); - g_string_append_c (icon_name, '-'); - g_string_append (icon_name, separator + 1); - pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, - pixel_size, - 0, - NULL); - g_string_free (icon_name, TRUE); - if (pixbuf) - return pixbuf; - - /* canonicalize MIME type, and prepend "gnome-mime-" */ - icon_name = g_string_new ("gnome-mime-"); - g_string_append_len (icon_name, mime_type, separator - mime_type); - g_string_append_c (icon_name, '-'); - g_string_append (icon_name, separator + 1); - pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, - pixel_size, - 0, - NULL); - g_string_free (icon_name, TRUE); - if (pixbuf) - return pixbuf; - - /* try the MIME family icon */ - icon_name = g_string_new ("gnome-mime-"); - g_string_append_len (icon_name, mime_type, separator - mime_type); - pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, - pixel_size, - 0, - NULL); - g_string_free (icon_name, TRUE); + pixbuf = gtk_icon_info_load_icon (info, NULL); + g_object_unref (info); return pixbuf; } @@ -1956,8 +1974,8 @@ get_icon_fallback (const gchar *icon_name, * * Retrieves the icon of size @size associated to the resource MIME type. * - * Return value: a #GdkPixbuf containing the icon, or %NULL. Use - * g_object_unref() when finished using the icon. + * Return value: (transfer full): a #GdkPixbuf containing the icon, + * or %NULL. Use g_object_unref() when finished using the icon. * * Since: 2.10 */ @@ -1977,14 +1995,43 @@ gtk_recent_info_get_icon (GtkRecentInfo *info, { if (info->mime_type && strcmp (info->mime_type, "x-directory/normal") == 0) - retval = get_icon_fallback (GTK_STOCK_DIRECTORY, size); + retval = get_icon_fallback ("folder", size); else - retval = get_icon_fallback (GTK_STOCK_FILE, size); + retval = get_icon_fallback ("text-x-generic", size); } return retval; } +/** + * gtk_recent_info_get_gicon: + * @info: a #GtkRecentInfo + * + * Retrieves the icon associated to the resource MIME type. + * + * Return value: (transfer full): a #GIcon containing the icon, or %NULL. Use + * g_object_unref() when finished using the icon + * + * Since: 2.22 + */ +GIcon * +gtk_recent_info_get_gicon (GtkRecentInfo *info) +{ + GIcon *icon = NULL; + gchar *content_type; + + g_return_val_if_fail (info != NULL, NULL); + + if (info->mime_type != NULL && + (content_type = g_content_type_from_mime_type (info->mime_type)) != NULL) + { + icon = g_content_type_get_icon (content_type); + g_free (content_type); + } + + return icon; +} + /** * gtk_recent_info_is_local: * @info: a #GtkRecentInfo @@ -2019,7 +2066,7 @@ gboolean gtk_recent_info_exists (GtkRecentInfo *info) { gchar *filename; - struct stat stat_buf; + GStatBuf stat_buf; gboolean retval = FALSE; g_return_val_if_fail (info != NULL, FALSE); @@ -2031,7 +2078,7 @@ gtk_recent_info_exists (GtkRecentInfo *info) filename = g_filename_from_uri (info->uri, NULL, NULL); if (filename) { - if (stat (filename, &stat_buf) == 0) + if (g_stat (filename, &stat_buf) == 0) retval = TRUE; g_free (filename); @@ -2287,14 +2334,15 @@ gtk_recent_info_get_age (GtkRecentInfo *info) /** * gtk_recent_info_get_groups: * @info: a #GtkRecentInfo - * @length: return location for the number of groups returned, or %NULL + * @length: (out) (allow-none): return location for the number of groups returned * * Returns all groups registered for the recently used item @info. The * array of returned group names will be %NULL terminated, so length might * optionally be %NULL. * - * Return value: a newly allocated %NULL terminated array of strings. Use - * g_strfreev() to free it. + * Return value: (array length=length zero-terminated=1) (transfer full): + * a newly allocated %NULL terminated array of strings. + * Use g_strfreev() to free it. * * Since: 2.10 */ @@ -2373,6 +2421,72 @@ gtk_recent_info_has_group (GtkRecentInfo *info, return FALSE; } +/** + * gtk_recent_info_create_app_info: + * @info: a #GtkRecentInfo + * @app_name: (allow-none): the name of the application that should + * be mapped to a #GAppInfo; if %NULL is used then the default + * application for the MIME type is used + * @error: (allow-none): return location for a #GError, or %NULL + * + * Creates a #GAppInfo for the specified #GtkRecentInfo + * + * Return value: (transfer full): the newly created #GAppInfo, or %NULL. + * In case of error, @error will be set either with a + * %GTK_RECENT_MANAGER_ERROR or a %G_IO_ERROR + */ +GAppInfo * +gtk_recent_info_create_app_info (GtkRecentInfo *info, + const gchar *app_name, + GError **error) +{ + RecentAppInfo *ai; + GAppInfo *app_info; + GError *internal_error = NULL; + + g_return_val_if_fail (info != NULL, NULL); + + if (app_name == NULL || *app_name == '\0') + { + char *content_type; + + if (info->mime_type == NULL) + return NULL; + + content_type = g_content_type_from_mime_type (info->mime_type); + if (content_type == NULL) + return NULL; + + app_info = g_app_info_get_default_for_type (content_type, TRUE); + g_free (content_type); + + return app_info; + } + + ai = g_hash_table_lookup (info->apps_lookup, app_name); + if (ai == NULL) + { + g_set_error (error, GTK_RECENT_MANAGER_ERROR, + GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED, + _("No registered application with name '%s' for item with URI '%s' found"), + app_name, + info->uri); + return NULL; + } + + internal_error = NULL; + app_info = g_app_info_create_from_commandline (ai->exec, ai->name, + G_APP_INFO_CREATE_NONE, + &internal_error); + if (internal_error != NULL) + { + g_propagate_error (error, internal_error); + return NULL; + } + + return app_info; +} + /* * _gtk_recent_manager_sync: * @@ -2388,6 +2502,3 @@ _gtk_recent_manager_sync (void) gtk_recent_manager_real_changed (recent_manager_singleton); } } - -#define __GTK_RECENT_MANAGER_C__ -#include "gtkaliasdef.c"