* 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * 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.
+ *
+ * <note><para>The recently used files list is per user.</para></note>
+ *
+ * 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.
+ *
+ * <note><para>The maximum age of the recently used files list is
+ * controllable through the #GtkSettings:gtk-recent-files-max-age
+ * property.</para></note>
+ *
+ * Recently used files are supported since GTK+ 2.10.
*/
#include "config.h"
#include <stdlib.h>
#include <glib.h>
#include <glib/gstdio.h>
+#include <gio/gio.h>
#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"
-
-#ifdef G_OS_UNIX
-#define XDG_PREFIX _gtk_xdg
-#include "xdgmime/xdgmime.h"
-#endif
/* the file where we store the recently used items */
-#define GTK_RECENTLY_USED_FILE ".recently-used.xbel"
-
-/* a poll every two seconds should be enough */
-#define POLL_DELTA 2000
+#define GTK_RECENTLY_USED_FILE "recently-used.xbel"
/* return all items by default */
#define DEFAULT_LIMIT -1
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;
{
gchar *filename;
- guint is_screen_singleton : 1;
guint is_dirty : 1;
- guint write_in_progress : 1;
- guint read_in_progress : 1;
- gint limit;
gint size;
- GdkScreen *screen;
-
GBookmarkFile *recent_items;
-
- time_t last_mtime;
- guint poll_timeout;
+
+ GFileMonitor *monitor;
+
+ guint changed_timeout;
+ guint changed_age;
};
enum
PROP_SIZE
};
-static void gtk_recent_manager_finalize (GObject *object);
-
-static void gtk_recent_manager_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_recent_manager_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-static void gtk_recent_manager_changed (GtkRecentManager *manager);
-
-static void gtk_recent_manager_real_changed (GtkRecentManager *manager);
-static gboolean gtk_recent_manager_poll_timeout (gpointer data);
-static void gtk_recent_manager_set_filename (GtkRecentManager *manager,
- const gchar *filename);
-
-static void build_recent_items_list (GtkRecentManager *manager);
-static void purge_recent_items_list (GtkRecentManager *manager,
- GError **error);
-
-static RecentAppInfo *recent_app_info_new (const gchar *app_name);
-static void recent_app_info_free (RecentAppInfo *app_info);
-
-static GtkRecentInfo *gtk_recent_info_new (const gchar *uri);
-static void gtk_recent_info_free (GtkRecentInfo *recent_info);
+static void gtk_recent_manager_dispose (GObject *object);
+static void gtk_recent_manager_finalize (GObject *object);
+static void gtk_recent_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_manager_add_item_query_info (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data);
+static void gtk_recent_manager_changed (GtkRecentManager *manager);
+static void gtk_recent_manager_real_changed (GtkRecentManager *manager);
+static void gtk_recent_manager_set_filename (GtkRecentManager *manager,
+ 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);
+static void purge_recent_items_list (GtkRecentManager *manager,
+ GError **error);
+
+static RecentAppInfo *recent_app_info_new (const gchar *app_name);
+static void recent_app_info_free (RecentAppInfo *app_info);
+
+static GtkRecentInfo *gtk_recent_info_new (const gchar *uri);
+static void gtk_recent_info_free (GtkRecentInfo *recent_info);
static guint signal_changed = 0;
+static GtkRecentManager *recent_manager_singleton = NULL;
+
G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT)
static void
return g_quark_from_static_string ("gtk-recent-manager-error-quark");
}
-
static void
gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- gtk_recent_manager_parent_class = g_type_class_peek_parent (klass);
-
gobject_class->set_property = gtk_recent_manager_set_property;
gobject_class->get_property = gtk_recent_manager_get_property;
+ gobject_class->dispose = gtk_recent_manager_dispose;
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
P_("Filename"),
P_("The full path to the file to be used to store and read the list"),
NULL,
- (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_WRITABLE)));
- /**
- * 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));
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)));
+
/**
- * GtkRecentManager:size
+ * GtkRecentManager:size:
*
* The size of the recently used resources list.
*
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
*/
gtk_recent_manager_init (GtkRecentManager *manager)
{
GtkRecentManagerPrivate *priv;
-
- priv = g_type_instance_get_private ((GTypeInstance *) manager,
- GTK_TYPE_RECENT_MANAGER);
- manager->priv = priv;
-
- priv->limit = DEFAULT_LIMIT;
- priv->size = 0;
-
- priv->is_screen_singleton = FALSE;
- priv->is_dirty = FALSE;
- priv->write_in_progress = FALSE;
- priv->read_in_progress = FALSE;
+ GtkSettings *settings;
- priv->screen = NULL;
+ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+ GTK_TYPE_RECENT_MANAGER,
+ GtkRecentManagerPrivate);
+ priv = manager->priv;
- priv->filename = g_build_filename (g_get_home_dir (),
- GTK_RECENTLY_USED_FILE,
- NULL);
- priv->poll_timeout = g_timeout_add (POLL_DELTA,
- gtk_recent_manager_poll_timeout,
- manager);
+ priv->size = 0;
+ priv->filename = NULL;
- build_recent_items_list (manager);
+ 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
{
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);
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;
GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
GtkRecentManagerPrivate *priv = manager->priv;
- /* remove the poll timeout */
- if (priv->poll_timeout)
- g_source_remove (priv->poll_timeout);
-
- if (priv->filename)
- g_free (priv->filename);
-
- if (priv->recent_items)
+ g_free (priv->filename);
+
+ if (priv->recent_items != NULL)
g_bookmark_file_free (priv->recent_items);
-
- /* chain up parent's finalize method */
+
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),
+ manager);
+ g_object_unref (priv->monitor);
+ priv->monitor = NULL;
+ }
+
+ 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_enabled_changed (GtkRecentManager *manager)
+{
+ manager->priv->is_dirty = TRUE;
+ gtk_recent_manager_changed (manager);
+}
+
static void
gtk_recent_manager_real_changed (GtkRecentManager *manager)
{
if (priv->is_dirty)
{
GError *write_error;
- struct stat stat_buf;
/* we are marked as dirty, so we dump the content of our
* recently used items list
*/
g_assert (priv->filename != NULL);
- priv->write_in_progress = TRUE;
-
- /* if no container object has been defined, we create a new
- * empty container, and dump it
- */
if (!priv->recent_items)
{
+ /* if no container object has been defined, we create a new
+ * empty container, and dump it
+ */
priv->recent_items = g_bookmark_file_new ();
priv->size = 0;
}
+ else
+ {
+ GtkSettings *settings = gtk_settings_get_default ();
+ gint age = 30;
+ gboolean enabled;
+
+ 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 = g_bookmark_file_new ();
+ }
+ else if (age > 0)
+ gtk_recent_manager_clamp_to_age (manager, age);
+ }
write_error = NULL;
- g_bookmark_file_to_file (priv->recent_items,
- priv->filename,
- &write_error);
-
+ g_bookmark_file_to_file (priv->recent_items, priv->filename, &write_error);
if (write_error)
{
filename_warning ("Attempting to store changes into `%s', "
g_error_free (write_error);
}
- priv->write_in_progress = FALSE;
-
- /* we have sync'ed our list with the storage file, so we
- * update the file mtime in order to skip the timed check
- * and spare us from a re-read.
- */
- if (g_stat (priv->filename, &stat_buf) < 0)
- {
- filename_warning ("Unable to stat() the recently used resources file "
- "at `%s': %s.",
- priv->filename,
- g_strerror (errno));
-
- g_object_thaw_notify (G_OBJECT (manager));
+ if (g_chmod (priv->filename, 0600) < 0)
+ {
+ filename_warning ("Attempting to set the permissions of `%s', "
+ "but failed: %s",
+ priv->filename,
+ g_strerror (errno));
+ }
- return;
- }
-
- priv->last_mtime = stat_buf.st_mtime;
-
/* mark us as clean */
priv->is_dirty = FALSE;
}
g_object_thaw_notify (G_OBJECT (manager));
}
-/* timed poll()-ing of the recently used resources file.
- * an event-based system would be more efficient.
- */
-static gboolean
-gtk_recent_manager_poll_timeout (gpointer data)
+static void
+gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
{
- GtkRecentManager *manager = GTK_RECENT_MANAGER (data);
- GtkRecentManagerPrivate *priv = manager->priv;
- struct stat stat_buf;
- int stat_res;
-
- /* wait for the next timeout if we have a read/write in progress */
- if (priv->write_in_progress || priv->read_in_progress)
- return TRUE;
+ GtkRecentManager *manager = user_data;
- stat_res = g_stat (priv->filename, &stat_buf);
- if (stat_res < 0)
+ switch (event_type)
{
- /* the file does not exist, yet, so we wait */
- if (errno == ENOENT)
- return TRUE;
-
- filename_warning ("Unable to stat() the recently used resources file "
- "at `%s': %s.",
- priv->filename,
- g_strerror (errno));
-
- return TRUE;
+ 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:
+ break;
+
+ default:
+ break;
}
+}
- /* the file didn't change from the last poll(), so we bail out */
- if (stat_buf.st_mtime == priv->last_mtime)
- return TRUE;
+static gchar *
+get_default_filename (void)
+{
+ if (g_mkdir_with_parents (g_get_user_data_dir (), 0755) == -1)
+ {
+ int saved_errno = errno;
- /* the file has been changed, hence we emit the "changed" signal */
- gtk_recent_manager_changed (manager);
+ 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 TRUE;
+ return NULL;
+ }
+
+ return g_build_filename (g_get_user_data_dir (),
+ GTK_RECENTLY_USED_FILE,
+ NULL);
}
static void
const gchar *filename)
{
GtkRecentManagerPrivate *priv;
+ GFile *file;
+ GError *error;
g_assert (GTK_IS_RECENT_MANAGER (manager));
+
priv = manager->priv;
-
- if (!filename || filename[0] == '\0')
- return;
-
- g_free (manager->priv->filename);
- if (manager->priv->poll_timeout)
+ /* if a filename is already set and filename is not NULL, then copy
+ * it and reset the monitor; otherwise, if it's NULL we're being
+ * called from the finalization sequence, so we simply disconnect the
+ * monitoring and return.
+ *
+ * if no filename is set and filename is NULL, use the default.
+ */
+ if (priv->filename)
+ {
+ g_free (priv->filename);
+
+ if (priv->monitor)
+ {
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (gtk_recent_manager_monitor_changed),
+ manager);
+ g_object_unref (priv->monitor);
+ priv->monitor = NULL;
+ }
+
+ if (!filename || *filename == '\0')
+ return;
+ else
+ priv->filename = g_strdup (filename);
+ }
+ else
{
- g_source_remove (manager->priv->poll_timeout);
- manager->priv->poll_timeout = 0;
+ if (!filename || *filename == '\0')
+ priv->filename = get_default_filename ();
+ else
+ priv->filename = g_strdup (filename);
}
- priv->filename = g_strdup (filename);
- priv->poll_timeout = g_timeout_add (POLL_DELTA,
- gtk_recent_manager_poll_timeout,
- manager);
+ g_assert (priv->filename != NULL);
+ file = g_file_new_for_path (priv->filename);
+
+ error = NULL;
+ priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+ if (error)
+ {
+ filename_warning ("Unable to monitor `%s': %s\n"
+ "The GtkRecentManager will not update its contents "
+ "if the file is changed from other instances",
+ priv->filename,
+ error->message);
+ g_error_free (error);
+ }
+ else
+ g_signal_connect (priv->monitor, "changed",
+ G_CALLBACK (gtk_recent_manager_monitor_changed),
+ manager);
+
+ g_object_unref (file);
- /* mark us clean, so that we can re-read the list
- * of recently used resources
- */
priv->is_dirty = FALSE;
build_recent_items_list (manager);
}
static void
build_recent_items_list (GtkRecentManager *manager)
{
- GtkRecentManagerPrivate *priv;
- struct stat stat_buf;
- int stat_res;
- gboolean res;
+ GtkRecentManagerPrivate *priv = manager->priv;
GError *read_error;
gint size;
- priv = manager->priv;
g_assert (priv->filename != NULL);
if (!priv->recent_items)
priv->size = 0;
}
- stat_res = g_stat (priv->filename, &stat_buf);
- if (stat_res < 0)
- {
- /* the file doesn't exists, so we bail out and wait for the first
- * write operation
- */
-
- if (errno == ENOENT)
- return;
- else
- {
- filename_warning ("Attempting to read the recently used resources file "
- "at `%s', but an error occurred: %s. Aborting.",
- priv->filename,
- g_strerror (errno));
-
- return;
- }
- }
-
- /* record the last mtime, for later use */
- priv->last_mtime = stat_buf.st_mtime;
-
- priv->read_in_progress = TRUE;
-
/* the file exists, and it's valid (we hope); if not, destroy the container
* object and hope for a better result when the next "changed" signal is
* fired. */
read_error = NULL;
- res = g_bookmark_file_load_from_file (priv->recent_items,
- priv->filename,
- &read_error);
+ g_bookmark_file_load_from_file (priv->recent_items, priv->filename, &read_error);
if (read_error)
{
- filename_warning ("Attempting to read the recently used resources file "
- "at `%s', but the parser failed: %s.",
- priv->filename,
- read_error->message);
+ /* if the file does not exist we just wait for the first write
+ * operation on this recent manager instance, to avoid creating
+ * empty files and leading to spurious file system events (Sabayon
+ * will not be happy about those)
+ */
+ if (read_error->domain == G_FILE_ERROR &&
+ read_error->code != G_FILE_ERROR_NOENT)
+ filename_warning ("Attempting to read the recently used resources "
+ "file at `%s', but the parser failed: %s.",
+ priv->filename,
+ read_error->message);
g_bookmark_file_free (priv->recent_items);
priv->recent_items = NULL;
g_error_free (read_error);
}
-
- size = g_bookmark_file_get_size (priv->recent_items);
- if (priv->size != size)
+ else
{
- priv->size = size;
-
- g_object_notify (G_OBJECT (manager), "size");
+ size = g_bookmark_file_get_size (priv->recent_items);
+ if (priv->size != size)
+ {
+ priv->size = size;
+
+ g_object_notify (G_OBJECT (manager), "size");
+ }
}
- priv->read_in_progress = FALSE;
priv->is_dirty = FALSE;
}
* monitors the recently used resources list, and emits the "changed" signal
* each time something inside the list changes.
*
- * #GtkRecentManager objects are expansive: be sure to create them only when
- * needed. You should use the gtk_recent_manager_new_for_screen() or the
- * gtk_recent_manager_get_default() functions instead.
+ * #GtkRecentManager objects are expensive: be sure to create them only when
+ * needed. You should use gtk_recent_manager_get_default() instead.
*
* Return value: A newly created #GtkRecentManager object.
*
/**
* gtk_recent_manager_get_default:
*
- * Gets the recent manager for the default screen. See
- * gtk_recent_manager_get_for_screen().
+ * Gets a unique instance of #GtkRecentManager, that you can share
+ * in your application without caring about memory management.
*
- * Return value: A unique #GtkRecentManager associated with the
- * default screen. This recent manager is associated to the
- * screen and can be used as long as the screen is open.
- * Do no ref or unref it.
+ * Return value: (transfer none): A unique #GtkRecentManager. Do not ref or unref it.
*
* Since: 2.10
*/
GtkRecentManager *
gtk_recent_manager_get_default (void)
{
- return gtk_recent_manager_get_for_screen (gdk_screen_get_default ());
-}
-
-/**
- * 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.
- *
- * Since: 2.10
- */
-GtkRecentManager *
-gtk_recent_manager_get_for_screen (GdkScreen *screen)
-{
- GtkRecentManager *manager;
-
- g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
- g_return_val_if_fail (!screen->closed, NULL);
-
- manager = g_object_get_data (G_OBJECT (screen), "gtk-recent-manager-default");
- if (!manager)
- {
- GtkRecentManagerPrivate *priv;
-
- manager = gtk_recent_manager_new ();
- gtk_recent_manager_set_screen (manager, screen);
-
- priv = manager->priv;
- priv->is_screen_singleton = TRUE;
-
- g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-default"), manager);
- }
+ if (G_UNLIKELY (!recent_manager_singleton))
+ recent_manager_singleton = gtk_recent_manager_new ();
- return manager;
+ return recent_manager_singleton;
}
+
static void
-display_closed (GdkDisplay *display,
- gboolean is_error,
- GtkRecentManager *manager)
+gtk_recent_manager_add_item_query_info (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
{
- GtkRecentManagerPrivate *priv = manager->priv;
- GdkScreen *screen = priv->screen;
- gboolean was_screen_singleton = priv->is_screen_singleton;
+ GFile *file = G_FILE (source_object);
+ GtkRecentManager *manager = user_data;
+ GtkRecentData recent_data;
+ GFileInfo *file_info;
+ gchar *uri, *basename;
- if (was_screen_singleton)
- {
- g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-default"), NULL);
- priv->is_screen_singleton = FALSE;
- }
+ uri = g_file_get_uri (file);
- gtk_recent_manager_set_screen (manager, NULL);
+ file_info = g_file_query_info_finish (file, res, NULL); /* NULL-GError */
- if (was_screen_singleton)
- g_object_unref (manager);
-}
-
-static void
-unset_screen (GtkRecentManager *manager)
-{
- GtkRecentManagerPrivate *priv = manager->priv;
- GdkDisplay *display;
+ recent_data.display_name = NULL;
+ recent_data.description = NULL;
- if (priv->screen)
+ if (file_info)
{
- display = gdk_screen_get_display (priv->screen);
+ gchar *content_type;
- g_signal_handlers_disconnect_by_func (display,
- (gpointer) display_closed,
- manager);
+ content_type = g_file_info_get_attribute_as_string (file_info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
- priv->screen = NULL;
- }
-}
-
-/**
- * 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
- */
-void
-gtk_recent_manager_set_screen (GtkRecentManager *manager,
- GdkScreen *screen)
-{
- GtkRecentManagerPrivate *priv;
- GdkDisplay *display;
+ if (G_LIKELY (content_type))
+ recent_data.mime_type = g_content_type_get_mime_type (content_type);
+ else
+ recent_data.mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
- g_return_if_fail (GTK_IS_RECENT_MANAGER (manager));
- g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
+ g_free (content_type);
+ g_object_unref (file_info);
+ }
+ else
+ {
+ basename = g_file_get_basename (file);
+ recent_data.mime_type = g_content_type_guess (basename, NULL, 0, NULL);
+ g_free (basename);
+ }
- priv = manager->priv;
+ 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;
- unset_screen (manager);
+ gdk_threads_enter ();
- if (screen)
- {
- display = gdk_screen_get_display (screen);
+ /* Ignore return value, this can't fail anyway since all required
+ * fields are set */
+ gtk_recent_manager_add_full (manager, uri, &recent_data);
- priv->screen = screen;
+ manager->priv->is_dirty = TRUE;
+ gtk_recent_manager_changed (manager);
- g_signal_connect (display, "closed",
- G_CALLBACK (display_closed), manager);
- }
-}
+ gdk_threads_leave ();
-/**
- * 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;
-}
+ g_free (recent_data.mime_type);
+ g_free (recent_data.app_name);
+ g_free (recent_data.app_exec);
-/**
- * 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;
+ g_object_unref (manager);
+ g_free (uri);
}
/**
* Adds a new resource, pointed by @uri, into the recently used
* resources list.
*
- * This function automatically retrieving some of the needed
+ * This function automatically retrieves some of the needed
* metadata and setting other metadata to common default values; it
* then feeds the data to gtk_recent_manager_add_full().
- *
- * See gtk_recent_manager_add_full() if you want to explicitely
+ *
+ * See gtk_recent_manager_add_full() if you want to explicitly
* define the metadata for the resource pointed by @uri.
*
* Return value: %TRUE if the new item was successfully added
gtk_recent_manager_add_item (GtkRecentManager *manager,
const gchar *uri)
{
- GtkRecentData *recent_data;
- GError *add_error;
- gboolean retval;
+ GFile* file;
g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), FALSE);
g_return_val_if_fail (uri != NULL, FALSE);
- recent_data = g_slice_new (GtkRecentData);
-
- recent_data->display_name = NULL;
- recent_data->description = NULL;
-
-#ifdef G_OS_UNIX
- if (has_case_prefix (uri, "file:/"))
- {
- gchar *filename;
- const gchar *mime_type;
-
- filename = g_filename_from_uri (uri, NULL, NULL);
- mime_type = xdg_mime_get_mime_type_for_file (filename, NULL);
- if (!mime_type)
- recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
- else
- recent_data->mime_type = g_strdup (mime_type);
-
- g_free (filename);
- }
- else
-#endif
- recent_data->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
-
- 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;
-
- add_error = NULL;
- retval = gtk_recent_manager_add_full (manager, uri, recent_data);
-
- g_free (recent_data->mime_type);
- g_free (recent_data->app_name);
- g_free (recent_data->app_exec);
+ file = g_file_new_for_uri (uri);
- g_slice_free (GtkRecentData, recent_data);
-
- return retval;
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
+ G_PRIORITY_DEFAULT,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ gtk_recent_manager_add_item_query_info,
+ g_object_ref (manager));
+
+ g_object_unref (file);
+
+ return TRUE;
}
/**
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);
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)
* will dump our changes
*/
priv->is_dirty = TRUE;
-
gtk_recent_manager_changed (manager);
return TRUE;
* 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.
g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
if (remove_error)
{
- g_propagate_error (error, remove_error);
+ g_error_free (remove_error);
+
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+ _("Unable to find an item with URI '%s'"),
+ uri);
return FALSE;
}
priv->is_dirty = TRUE;
-
gtk_recent_manager_changed (manager);
return TRUE;
return g_bookmark_file_has_item (priv->recent_items, uri);
}
-static gboolean
+static void
build_recent_info (GBookmarkFile *bookmarks,
GtkRecentInfo *info)
{
app_info->count = count;
app_info->stamp = stamp;
- info->applications = g_slist_append (info->applications,
- app_info);
+ info->applications = g_slist_prepend (info->applications, app_info);
g_hash_table_replace (info->apps_lookup, app_info->name, app_info);
}
g_strfreev (apps);
-
- return TRUE;
}
/**
* 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
{
GtkRecentManagerPrivate *priv;
GtkRecentInfo *info = NULL;
- gboolean res;
g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
g_return_val_if_fail (uri != NULL, NULL);
/* fill the RecentInfo structure with the data retrieved by our
* parser object from the storage file
*/
- res = build_recent_info (priv->recent_items, info);
- if (!res)
- {
- gtk_recent_info_free (info);
-
- return NULL;
- }
-
- return gtk_recent_info_ref (info);
+ build_recent_info (priv->recent_items, info);
+
+ return info;
}
/**
* 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.
*
* 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);
+ 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_propagate_error (error, 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);
return FALSE;
}
-
- priv->is_dirty = TRUE;
+ priv->is_dirty = TRUE;
gtk_recent_manager_changed (recent_manager);
-
+
return TRUE;
}
*
* 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().
*
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;
- gboolean res;
info = gtk_recent_info_new (uris[i]);
- res = build_recent_info (priv->recent_items, info);
- if (!res)
- {
- g_warning ("Unable to create a RecentInfo object for "
- "item with URI `%s'",
- uris[i]);
- gtk_recent_info_free (info);
-
- continue;
- }
+ build_recent_info (priv->recent_items, info);
retval = g_list_prepend (retval, info);
}
g_strfreev (uris);
-
- /* clamp the list, if a limit is present */
- if ((priv->limit != -1) &&
- (g_list_length (retval) > priv->limit))
- {
- GList *clamp, *l;
-
- clamp = g_list_nth (retval, priv->limit - 1);
-
- if (!clamp)
- return retval;
-
- l = clamp->next;
- clamp->next = NULL;
-
- g_list_foreach (l, (GFunc) gtk_recent_info_free, NULL);
- g_list_free (l);
- }
return retval;
}
{
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.
*
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
+gtk_recent_manager_clamp_to_age (GtkRecentManager *manager,
+ gint age)
+{
+ GtkRecentManagerPrivate *priv = manager->priv;
+ gchar **uris;
+ gsize n_uris, i;
+ time_t now;
+
+ if (G_UNLIKELY (!priv->recent_items))
+ return;
+
+ now = time (NULL);
+
+ uris = g_bookmark_file_get_uris (priv->recent_items, &n_uris);
+
+ for (i = 0; i < n_uris; i++)
+ {
+ const gchar *uri = uris[i];
+ time_t modified;
+ gint item_age;
+
+ modified = g_bookmark_file_get_modified (priv->recent_items, uri, NULL);
+ item_age = (gint) ((now - modified) / (60 * 60 * 24));
+ if (item_age > age)
+ g_bookmark_file_remove_item (priv->recent_items, uri, NULL);
+ }
+
+ g_strfreev (uris);
}
/*****************
* GtkRecentInfo *
*****************/
-GType
-gtk_recent_info_get_type (void)
-{
- static GType info_type = 0;
-
- if (!info_type)
- info_type = g_boxed_type_register_static ("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)
*
* Since: 2.10
*/
-G_CONST_RETURN gchar *
+const gchar *
gtk_recent_info_get_uri (GtkRecentInfo *info)
{
g_return_val_if_fail (info != NULL, NULL);
*
* 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);
*
* Since: 2.10
**/
-G_CONST_RETURN gchar *
+const gchar *
gtk_recent_info_get_description (GtkRecentInfo *info)
{
g_return_val_if_fail (info != NULL, NULL);
*
* 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);
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;
}
if (!app_info)
return;
- if (app_info->name)
- g_free (app_info->name);
-
- if (app_info->exec)
- g_free (app_info->exec);
+ 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
* 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_)
{
/**
* 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)
return g_strdup (name);
}
-typedef struct
-{
- gint size;
- GdkPixbuf *pixbuf;
-} IconCacheElement;
-
-static void
-icon_cache_element_free (IconCacheElement *element)
-{
- if (element->pixbuf)
- g_object_unref (element->pixbuf);
- g_free (element);
-}
-
-static void
-icon_theme_changed (GtkIconTheme *icon_theme)
-{
- GHashTable *cache;
-
- /* Difference from the initial creation is that we don't
- * reconnect the signal
- */
- cache = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify)g_free,
- (GDestroyNotify)icon_cache_element_free);
- g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
- cache, (GDestroyNotify)g_hash_table_destroy);
-}
-
-/* TODO: use the GtkFileChooser's icon cache instead of our own to reduce
- * the memory footprint
- */
static GdkPixbuf *
-get_cached_icon (const gchar *name,
- gint pixel_size)
+get_icon_for_mime_type (const char *mime_type,
+ gint pixel_size)
{
GtkIconTheme *icon_theme;
- GHashTable *cache;
- IconCacheElement *element;
+ char *content_type;
+ GIcon *icon;
+ GtkIconInfo *info;
+ GdkPixbuf *pixbuf;
icon_theme = gtk_icon_theme_get_default ();
- cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-recent-icon-cache");
-
- if (!cache)
- {
- cache = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify)g_free,
- (GDestroyNotify)icon_cache_element_free);
-
- g_object_set_data_full (G_OBJECT (icon_theme), "gtk-recent-icon-cache",
- cache, (GDestroyNotify)g_hash_table_destroy);
- g_signal_connect (icon_theme, "changed",
- G_CALLBACK (icon_theme_changed), NULL);
- }
-
- element = g_hash_table_lookup (cache, name);
- if (!element)
- {
- element = g_new0 (IconCacheElement, 1);
- g_hash_table_insert (cache, g_strdup (name), element);
- }
- if (element->size != pixel_size)
- {
- if (element->pixbuf)
- g_object_unref (element->pixbuf);
-
- element->size = pixel_size;
- element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
- pixel_size, 0, NULL);
- }
+ content_type = g_content_type_from_mime_type (mime_type);
- return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
-}
-
-
-static GdkPixbuf *
-get_icon_for_mime_type (const char *mime_type,
- gint pixel_size)
-{
- const char *separator;
- GString *icon_name;
- GdkPixbuf *pixbuf;
+ if (!content_type)
+ return NULL;
- separator = strchr (mime_type, '/');
- if (!separator)
- return NULL; /* maybe we should return a GError with "invalid MIME-type" */
+ 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);
- 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 = get_cached_icon (icon_name->str, pixel_size);
- g_string_free (icon_name, TRUE);
- if (pixbuf)
- return pixbuf;
+ if (!info)
+ return NULL;
- icon_name = g_string_new ("gnome-mime-");
- g_string_append_len (icon_name, mime_type, separator - mime_type);
- pixbuf = get_cached_icon (icon_name->str, pixel_size);
- g_string_free (icon_name, TRUE);
+ pixbuf = gtk_icon_info_load_icon (info, NULL);
+ gtk_icon_info_free (info);
return pixbuf;
}
*
* Retrieves the icon of size @size associated to the resource MIME type.
*
- * Return value: a #GdkPixbuf containing the icon, or %NULL.
+ * Return value: (transfer full): a #GdkPixbuf containing the icon,
+ * or %NULL. Use g_object_unref() when finished using the icon.
*
* Since: 2.10
*/
if (info->mime_type)
retval = get_icon_for_mime_type (info->mime_type, size);
- /* this should never fail */
+ /* this function should never fail */
if (!retval)
- retval = get_icon_fallback (GTK_STOCK_FILE, size);
+ {
+ if (info->mime_type &&
+ strcmp (info->mime_type, "x-directory/normal") == 0)
+ retval = get_icon_fallback ("folder", size);
+ else
+ 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
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);
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);
rest = get_method_string (uri, &method);
local_file = g_filename_display_basename (rest);
- name = g_strdup_printf ("%s: %s", method, local_file);
+ name = g_strconcat (method, ": ", local_file, NULL);
g_free (local_file);
g_free (method);
* is local, it returns a local path; if the resource is not local,
* it returns the UTF-8 encoded content of gtk_recent_info_get_uri().
*
- * Return value: a UTF-8 string containing the resource's URI or %NULL
+ * Return value: a newly allocated UTF-8 string containing the
+ * resource's URI or %NULL. Use g_free() when done using it.
*
* Since: 2.10
*/
/**
* 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
*/
return FALSE;
}
-#define __GTK_RECENT_MANAGER_C__
-#include "gtkaliasdef.c"
+/**
+ * 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:
+ *
+ * Private function for synchronising the recent manager singleton.
+ */
+void
+_gtk_recent_manager_sync (void)
+{
+ if (recent_manager_singleton)
+ {
+ /* force a dump of the contents of the recent manager singleton */
+ recent_manager_singleton->priv->is_dirty = TRUE;
+ gtk_recent_manager_real_changed (recent_manager_singleton);
+ }
+}