summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@cvs.gnome.org>2006-03-29 20:16:44 +0000
committerEmmanuele Bassi <ebassi@src.gnome.org>2006-03-29 20:16:44 +0000
commit056f6db533b418e3571d2614c349bb412900dbc1 (patch)
treee3126a3d1d0300f497f9f99ee6e781327d92a8f4
parent5f5de68e8267cd3c3f4effa2e552b66a03951cce (diff)
downloadgtk+-056f6db533b418e3571d2614c349bb412900dbc1.tar.gz
Import GtkRecent* from libegg.
2006-03-29 Emmanuele Bassi <ebassi@cvs.gnome.org> Import GtkRecent* from libegg. * gtk/gtkrecentmanager.[ch]: Add GtkRecentManager, an object for managing a list of recently used resources. * gtk/gtkrecentchooser.[ch]: * gtk/gtkrecentchooserdefault.[ch]: * gtk/gtkrecentchooserdialog.[ch]: * gtk/gtkrecentchoosermenu.[ch]: * gtk/gtkrecentchooserprivate.h: * gtk/gtkrecentchooserutils.[ch]: * gtk/gtkrecentchooserwidget.[ch]: Add GtkRecentChooser, a GTypeInterface for widgets implementing viewers for recently used resources. * gtk/gtkrecentfilter.[ch]: GtkRecentFilter, a filter object for GtkRecentChooser implementations. * gtk/gtk.h: * gtk/gtk.symbols: * gtk/Makefile.am: Build glue for GtkRecent*.
-rw-r--r--gtk/Makefile.am17
-rw-r--r--gtk/gtk.h6
-rw-r--r--gtk/gtk.symbols128
-rw-r--r--gtk/gtkrecentchooser.c966
-rw-r--r--gtk/gtkrecentchooser.h186
-rw-r--r--gtk/gtkrecentchooserdefault.c2073
-rw-r--r--gtk/gtkrecentchooserdefault.h42
-rw-r--r--gtk/gtkrecentchooserdialog.c376
-rw-r--r--gtk/gtkrecentchooserdialog.h70
-rw-r--r--gtk/gtkrecentchoosermenu.c1225
-rw-r--r--gtk/gtkrecentchoosermenu.h70
-rw-r--r--gtk/gtkrecentchooserprivate.h42
-rw-r--r--gtk/gtkrecentchooserutils.c298
-rw-r--r--gtk/gtkrecentchooserutils.h63
-rw-r--r--gtk/gtkrecentchooserwidget.c194
-rw-r--r--gtk/gtkrecentchooserwidget.h60
-rw-r--r--gtk/gtkrecentfilter.c579
-rw-r--r--gtk/gtkrecentfilter.h90
-rw-r--r--gtk/gtkrecentmanager.c2424
-rw-r--r--gtk/gtkrecentmanager.h214
20 files changed, 9123 insertions, 0 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4d24311b87..1a9f19a197 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -228,6 +228,12 @@ gtk_public_h_sources = \
gtkradiotoolbutton.h \
gtkrange.h \
gtkrc.h \
+ gtkrecentchooser.h \
+ gtkrecentchooserdialog.h \
+ gtkrecentchoosermenu.h \
+ gtkrecentchooserwidget.h \
+ gtkrecentfilter.h \
+ gtkrecentmanager.h \
gtkruler.h \
gtkscale.h \
gtkscrollbar.h \
@@ -310,6 +316,9 @@ gtk_private_h_sources = \
gtkpathbar.h \
gtkplugprivate.h \
gtkrbtree.h \
+ gtkrecentchooserdefault.h \
+ gtkrecentchooserprivate.h \
+ gtkrecentchooserutils.h \
gtksequence.h \
gtksocketprivate.h \
gtktextbtree.h \
@@ -459,6 +468,14 @@ gtk_c_sources = \
gtkrange.c \
gtkrbtree.c \
gtkrc.c \
+ gtkrecentchooserdefault.c \
+ gtkrecentchooserdialog.c \
+ gtkrecentchoosermenu.c \
+ gtkrecentchooserwidget.c \
+ gtkrecentchooserutils.c \
+ gtkrecentchooser.c \
+ gtkrecentfilter.c \
+ gtkrecentmanager.c \
gtkruler.c \
gtkscale.c \
gtkscrollbar.c \
diff --git a/gtk/gtk.h b/gtk/gtk.h
index e99aa165c7..2575238e7a 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -140,6 +140,12 @@
#include <gtk/gtkradiotoolbutton.h>
#include <gtk/gtkrange.h>
#include <gtk/gtkrc.h>
+#include <gtk/gtkrecentchooser.h>
+#include <gtk/gtkrecentchooserdialog.h>
+#include <gtk/gtkrecentchoosermenu.h>
+#include <gtk/gtkrecentchooserwidget.h>
+#include <gtk/gtkrecentfilter.h>
+#include <gtk/gtkrecentmanager.h>
#include <gtk/gtkruler.h>
#include <gtk/gtkscale.h>
#include <gtk/gtkscrollbar.h>
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 24231aa871..0403fa2b47 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -2621,6 +2621,134 @@ gtk_rc_style_unref
#endif
#endif
+#if IN_HEADER(__GTK_RECENT_CHOOSER_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_C__))
+gtk_recent_chooser_set_show_private
+gtk_recent_chooser_get_show_private
+gtk_recent_chooser_set_show_not_found
+gtk_recent_chooser_get_show_not_found
+gtk_recent_chooser_set_show_icons
+gtk_recent_chooser_get_show_icons
+gtk_recent_chooser_set_select_multiple
+gtk_recent_chooser_get_select_multiple
+gtk_recent_chooser_set_local_only
+gtk_recent_chooser_get_local_only
+gtk_recent_chooser_set_limit
+gtk_recent_chooser_get_limit
+gtk_recent_chooser_set_show_tips
+gtk_recent_chooser_get_show_tips
+gtk_recent_chooser_set_show_numbers
+gtk_recent_chooser_get_show_numbers
+gtk_recent_chooser_set_sort_type
+gtk_recent_chooser_get_sort_type
+gtk_recent_chooser_set_sort_func
+gtk_recent_chooser_set_current_uri
+gtk_recent_chooser_get_current_uri
+gtk_recent_chooser_get_current_item
+gtk_recent_chooser_select_uri
+gtk_recent_chooser_unselect_uri
+gtk_recent_chooser_select_all
+gtk_recent_chooser_unselect_all
+gtk_recent_chooser_get_items
+gtk_recent_chooser_get_uris
+gtk_recent_chooser_add_filter
+gtk_recent_chooser_remove_filter
+gtk_recent_chooser_list_filters
+gtk_recent_chooser_set_filter
+gtk_recent_chooser_get_filter
+gtk_recent_chooser_get_type G_GNUC_CONST
+gtk_recent_chooser_error_quark
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_DIALOG_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_DIALOG_C__)
+gtk_recent_chooser_dialog_get_type G_GNUC_CONST
+gtk_recent_chooser_dialog_new G_GNUC_NULL_TERMINATED
+gtk_recent_chooser_dialog_new_for_manager G_GNUC_NULL_TERMINATED
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_MENU_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_MENU_C__)
+gtk_recent_chooser_menu_get_type G_GNUC_CONST
+gtk_recent_chooser_menu_new
+gtk_recent_chooser_menu_new_for_manager
+gtk_recent_chooser_menu_get_show_numbers
+gtk_recent_chooser_menu_set_show_numbers
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_CHOOSER_WIDGET_H__)
+#if IN_FILE(__GTK_RECENT_CHOOSER_WIDGET_C__)
+gtk_recent_chooser_widget_get_type G_GNUC_CONST
+gtk_recent_chooser_widget_new
+gtk_recent_chooser_widget_new_for_manager
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_FILTER_H__)
+#if IN_FILE(__GTK_RECENT_FILTER_C__)
+gtk_recent_filter_get_type G_GNUC_CONST
+gtk_recent_filter_new
+gtk_recent_filter_set_name
+gtk_recent_filter_get_name
+gtk_recent_filter_add_mime_type
+gtk_recent_filter_add_pattern
+gtk_recent_filter_add_pixbuf_formats
+gtk_recent_filter_add_application
+gtk_recent_filter_add_group
+gtk_recent_filter_add_age
+gtk_recent_filter_add_custom
+gtk_recent_filter_get_needed
+gtk_recent_filter_filter
+#endif
+#endif
+
+#if IN_HEADER(__GTK_RECENT_MANAGER_H__)
+#if IN_FILE(__GTK_RECENT_MANAGER_C__)
+gtk_recent_manager_error_quark
+gtk_recent_manager_get_type G_GNUC_CONST
+gtk_recent_manager_new
+gtk_recent_manager_get_default
+gtk_recent_manager_get_for_screen
+gtk_recent_manager_set_screen
+gtk_recent_manager_add_item
+gtk_recent_manager_add_full
+gtk_recent_manager_remove_item
+gtk_recent_manager_lookup_item
+gtk_recent_manager_has_item
+gtk_recent_manager_move_item
+gtk_recent_manager_set_limit
+gtk_recent_manager_get_limit
+gtk_recent_manager_purge_items
+gtk_recent_info_get_type G_GNUC_CONST
+gtk_recent_info_ref
+gtk_recent_info_unref
+gtk_recent_info_get_uri
+gtk_recent_info_get_display_name
+gtk_recent_info_get_description
+gtk_recent_info_get_mime_type
+gtk_recent_info_get_added
+gtk_recent_info_get_modified
+gtk_recent_info_get_visited
+gtk_recent_info_get_private_hint
+gtk_recent_info_get_application_info
+gtk_recent_info_get_applications G_GNUC_MALLOC
+gtk_recent_info_last_application G_GNUC_MALLOC
+gtk_recent_info_has_application
+gtk_recent_info_get_groups G_GNUC_MALLOC
+gtk_recent_info_has_group
+gtk_recent_info_get_icon
+gtk_recent_info_get_short_name G_GNUC_MALLOC
+gtk_recent_info_get_uri_display G_GNUC_MALLOC
+gtk_recent_info_get_age
+gtk_recent_info_is_local
+gtk_recent_info_exists
+gtk_recent_info_match
+#endif
+#endif
+
#if IN_HEADER(__GTK_TEXT_BUFFER_RICH_TEXT_H__)
#if IN_FILE(__GTK_TEXT_BUFFER_RICH_TEXT_C__)
gtk_text_buffer_deserialize
diff --git a/gtk/gtkrecentchooser.c b/gtk/gtkrecentchooser.c
new file mode 100644
index 0000000000..873eee1d46
--- /dev/null
+++ b/gtk/gtkrecentchooser.c
@@ -0,0 +1,966 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooser.c - Abstract interface for recent file selectors GUIs
+ *
+ * Copyright (C) 2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentmanager.h"
+#include "gtkintl.h"
+#include "gtktypebuiltins.h"
+#include "gtkprivate.h"
+#include "gtkmarshalers.h"
+#include "gtkalias.h"
+
+
+enum
+{
+ ITEM_ACTIVATED,
+ SELECTION_CHANGED,
+
+ LAST_SIGNAL
+};
+
+static void gtk_recent_chooser_class_init (gpointer g_iface);
+
+static guint chooser_signals[LAST_SIGNAL] = { 0, };
+
+GType
+gtk_recent_chooser_get_type (void)
+{
+ static GType chooser_type = 0;
+
+ if (!chooser_type)
+ {
+ static const GTypeInfo chooser_info =
+ {
+ sizeof (GtkRecentChooserIface),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) gtk_recent_chooser_class_init,
+ };
+
+ chooser_type = g_type_register_static (G_TYPE_INTERFACE,
+ I_("GtkRecentChooser"),
+ &chooser_info, 0);
+
+ g_type_interface_add_prerequisite (chooser_type, GTK_TYPE_OBJECT);
+ }
+
+ return chooser_type;
+}
+
+static void
+gtk_recent_chooser_class_init (gpointer g_iface)
+{
+ GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+ /**
+ * GtkRecentChooser::selection-changed
+ * @chooser: the object which received the signal
+ *
+ * This signal is emitted when there is a change in the set of
+ * selected recently used resources. This can happen when a user
+ * modifies the selection with the mouse or the keyboard, or when
+ * explicitely calling functions to change the selection.
+ *
+ * Since: 2.10
+ */
+ chooser_signals[SELECTION_CHANGED] =
+ g_signal_new (I_("selection-changed"),
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkRecentChooserIface, selection_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * GtkRecentChooser::item-activated
+ * @chooser: the object which received the signal
+ *
+ * This signal is emitted when the user "activates" a recent item
+ * in the recent chooser. This can happen by double-clicking on an item
+ * in the recently used resources list, or by pressing
+ * <keycap>Enter</keycap>.
+ *
+ * Since: 2.10
+ */
+ chooser_signals[ITEM_ACTIVATED] =
+ g_signal_new (I_("item-activated"),
+ iface_type,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkRecentChooserIface, item_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("recent-manager",
+ P_("Recent Manager"),
+ P_("The RecentManager object to use"),
+ GTK_TYPE_RECENT_MANAGER,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("show-private",
+ P_("Show Private"),
+ P_("Whether the private items should be displayed"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("show-tips",
+ P_("Show Tooltips"),
+ P_("Whether there should be a tooltip on the item"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("show-icons",
+ P_("Show Icons"),
+ P_("Whether there should be an icon near the item"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("show-not-found",
+ P_("Show Not Found"),
+ P_("Whether the items pointing to unavailable resources should be displayed"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("select-multiple",
+ P_("Select Multiple"),
+ P_("Whether to allow multiple items to be selected"),
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_boolean ("local-only",
+ P_("Local only"),
+ P_("Whether the selected resource(s) should be limited to local file: URIs"),
+ TRUE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_int ("limit",
+ P_("Limit"),
+ P_("The maximum number of items to be displayed"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_enum ("sort-type",
+ P_("Sort Type"),
+ P_("The sorting order of the items displayed"),
+ GTK_TYPE_RECENT_SORT_TYPE,
+ GTK_RECENT_SORT_NONE,
+ G_PARAM_READWRITE));
+ g_object_interface_install_property (g_iface,
+ g_param_spec_object ("filter",
+ P_("Filter"),
+ P_("The current filter for selecting which resources are displayed"),
+ GTK_TYPE_RECENT_FILTER,
+ G_PARAM_READWRITE));
+}
+
+GQuark
+gtk_recent_chooser_error_quark (void)
+{
+ static GQuark error_quark = 0;
+ if (!error_quark)
+ error_quark = g_quark_from_static_string ("gtk-recent-chooser-error-quark");
+ return error_quark;
+}
+
+/**
+ * _gtk_recent_chooser_get_recent_manager:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentManager used by @chooser.
+ *
+ * Return value: the recent manager for @chooser.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_recent_manager (chooser);
+}
+
+/**
+ * gtk_recent_chooser_set_show_private:
+ * @chooser: a #GtkRecentChooser
+ * @show_private: %TRUE to show private items, %FALSE otherwise
+ *
+ * Whether to show recently used resources marked registered as private.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_private (GtkRecentChooser *chooser,
+ gboolean show_private)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "show-private", show_private, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_private:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Returns whether @chooser should display recently used resources
+ * registered as private.
+ *
+ * Return value: %TRUE if the recent chooser should show private items,
+ * %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_private (GtkRecentChooser *chooser)
+{
+ gboolean show_private;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "show-private", &show_private, NULL);
+
+ return show_private;
+}
+
+/**
+ * gtk_recent_chooser_set_show_not_found:
+ * @chooser: a #GtkRecentChooser
+ * @show_not_found: whether to show the local items we didn't find
+ *
+ * Sets whether @chooser should display the recently used resources that
+ * it didn't find. This only applies to local resources.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_not_found (GtkRecentChooser *chooser,
+ gboolean show_not_found)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "show-not-found", show_not_found, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_not_found:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Retrieves whether @chooser should show the recently used resources that
+ * were not found.
+ *
+ * Return value: %TRUE if the resources not found should be displayed, and
+ * %FALSE otheriwse.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_not_found (GtkRecentChooser *chooser)
+{
+ gboolean show_not_found;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "show-not-found", &show_not_found, NULL);
+
+ return show_not_found;
+}
+
+/**
+ * gtk_recent_chooser_set_show_icons:
+ * @chooser: a #GtkRecentChooser
+ * @show_icons: whether to show an icon near the resource
+ *
+ * Sets whether @chooser should show an icon near the resource when
+ * displaying it.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_icons (GtkRecentChooser *chooser,
+ gboolean show_icons)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "show-icons", show_icons, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_icons:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Retrieves whether @chooser should show an icon near the resource.
+ *
+ * Return value: %TRUE if the icons should be displayed, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_icons (GtkRecentChooser *chooser)
+{
+ gboolean show_icons;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "show-icons", &show_icons, NULL);
+
+ return show_icons;
+}
+
+/**
+ * gtk_recent_chooser_set_select_multiple:
+ * @chooser: a #GtkRecentChooser
+ * @select_multiple: %TRUE if @chooser can select more than one item
+ *
+ * Sets whether @chooser can select multiple items.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_select_multiple (GtkRecentChooser *chooser,
+ gboolean select_multiple)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "select-multiple", select_multiple, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_select_multiple:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether @chooser can select multiple items.
+ *
+ * Return value: %TRUE if @chooser can select more than one item.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_select_multiple (GtkRecentChooser *chooser)
+{
+ gboolean select_multiple;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "select-multiple", &select_multiple, NULL);
+
+ return select_multiple;
+}
+
+/**
+ * gtk_recent_chooser_set_local_only:
+ * @chooser: a #GtkRecentChooser
+ * @local_only: %TRUE if only local files can be shown
+ *
+ * Sets whether only local resources, that is resources using the file:// URI
+ * scheme, should be shown in the recently used resources selector. If
+ * @local_only is %TRUE (the default) then the shown resources are guaranteed
+ * to be accessible through the operating system native file system.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_local_only (GtkRecentChooser *chooser,
+ gboolean local_only)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "local-only", local_only, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_local_only:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether only local resources should be shown in the recently used
+ * resources selector. See gtk_recent_chooser_set_local_only()
+ *
+ * Return value: %TRUE if only local resources should be shown.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_local_only (GtkRecentChooser *chooser)
+{
+ gboolean local_only;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "local-only", &local_only, NULL);
+
+ return local_only;
+}
+
+/**
+ * gtk_recent_chooser_set_limit:
+ * @chooser: a #GtkRecentChooser
+ * @limit: a positive integer, or -1 for all items
+ *
+ * Sets the number of items that should be returned by
+ * gtk_recent_chooser_get_items() and gtk_recent_chooser_get_uris().
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_limit (GtkRecentChooser *chooser,
+ gint limit)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "limit", limit, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_limit:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the number of items returned by gtk_recent_chooser_get_items()
+ * and gtk_recent_chooser_get_uris().
+ *
+ * Return value: A positive integer, or -1 meaning that all items are
+ * returned.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_chooser_get_limit (GtkRecentChooser *chooser)
+{
+ gint limit;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), 10);
+
+ g_object_get (chooser, "limit", &limit, NULL);
+
+ return limit;
+}
+
+/**
+ * gtk_recent_chooser_set_show_tips:
+ * @chooser: a #GtkRecentChooser
+ * @show_tips: %TRUE if tooltips should be shown
+ *
+ * Sets whether to show a tooltips on the widget.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_tips (GtkRecentChooser *chooser,
+ gboolean show_tips)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "show-tips", show_tips, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_tips:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets whether @chooser should display tooltips.
+ *
+ * Return value: %TRUE if the recent chooser should show tooltips,
+ * %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_tips (GtkRecentChooser *chooser)
+{
+ gboolean show_tips;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "show-tips", &show_tips, NULL);
+
+ return show_tips;
+}
+
+/**
+ * gtk_recent_chooser_set_show_numbers:
+ * @chooser: a #GtkRecentChooser
+ * @show_private: %TRUE to show numbers, %FALSE otherwise
+ *
+ * Whether to show recently used resources prepended by a unique number.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_show_numbers (GtkRecentChooser *chooser,
+ gboolean show_numbers)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "show-numbers", show_numbers, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_show_numbers:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Returns whether @chooser should display recently used resources
+ * prepended by a unique number.
+ *
+ * Return value: %TRUE if the recent chooser should show display numbers,
+ * %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_get_show_numbers (GtkRecentChooser *chooser)
+{
+ gboolean show_numbers;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ g_object_get (chooser, "show-numbers", &show_numbers, NULL);
+
+ return show_numbers;
+}
+
+
+/**
+ * gtk_recent_chooser_set_sort_type:
+ * @chooser: a #GtkRecentChooser
+ * @sort_type: sort order that the chooser should use
+ *
+ * Changes the sorting order of the recently used resources list displayed by
+ * @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_sort_type (GtkRecentChooser *chooser,
+ GtkRecentSortType sort_type)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (chooser, "sort-type", sort_type, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_sort_type:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the value set by gtk_recent_chooser_set_sort_type().
+ *
+ * Return value: the sorting order of the @chooser.
+ *
+ * Since: 2.10
+ */
+GtkRecentSortType
+gtk_recent_chooser_get_sort_type (GtkRecentChooser *chooser)
+{
+ GtkRecentSortType sort_type;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), GTK_RECENT_SORT_NONE);
+
+ g_object_get (chooser, "sort-type", &sort_type, NULL);
+
+ return sort_type;
+}
+
+/**
+ * gtk_recent_chooser_set_sort_func:
+ * @chooser: a #GtkRecentChooser
+ * @sort_func: the comparison function
+ * @sort_data: user data to pass to @sort_func, or %NULL
+ * @destroy_data: destroy notifier for @sort_data, or %NULL
+ *
+ * Sets the comparison function used when sorting to be @sort_func. If
+ * the @chooser has the sort type set to #GTK_RECENT_SORT_CUSTOM then
+ * the chooser will sort using this function.
+ *
+ * To the comparison function will be passed two #GtkRecentInfo structs and
+ * @sort_data; @sort_func should return a positive integer if the first
+ * item comes before the second, zero if the two items are equal and
+ * a negative integer if the first item comes after the second.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->set_sort_func (chooser,
+ sort_func,
+ sort_data,
+ data_destroy);
+}
+
+/**
+ * gtk_recent_chooser_set_current_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Sets @uri as the current URI for @chooser.
+ *
+ * Return value: %TRUE if the URI was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->set_current_uri (chooser, uri, error);
+}
+
+/**
+ * gtk_recent_chooser_get_current_uri:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the URI currently selected by @chooser.
+ *
+ * Return value: a newly allocated string holding a URI.
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_chooser_get_current_uri (GtkRecentChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_current_uri (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_current_item:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentInfo currently selected by @chooser.
+ *
+ * Return value: a #GtkRecentInfo. Use gtk_recent_info_unref() when
+ * when you have finished using it.
+ *
+ * Since: 2.10
+ */
+GtkRecentInfo *
+gtk_recent_chooser_get_current_item (GtkRecentChooser *chooser)
+{
+ GtkRecentManager *manager;
+ GtkRecentInfo *retval;
+ gchar *uri;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ uri = gtk_recent_chooser_get_current_uri (chooser);
+ if (!uri)
+ return NULL;
+
+ manager = _gtk_recent_chooser_get_recent_manager (chooser);
+ retval = gtk_recent_manager_lookup_item (manager, uri, NULL);
+ g_free (uri);
+
+ return retval;
+}
+
+/**
+ * gtk_recent_chooser_select_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Selects @uri inside @chooser.
+ *
+ * Return value: %TRUE if @uri was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), FALSE);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->select_uri (chooser, uri, error);
+}
+
+/**
+ * gtk_recent_chooser_unselect_uri:
+ * @chooser: a #GtkRecentChooser
+ * @uri: a URI
+ *
+ * Unselects @uri inside @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->unselect_uri (chooser, uri);
+}
+
+/**
+ * gtk_recent_chooser_select_all:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Selects all the items inside @chooser, if the @chooser supports
+ * multiple selection.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_select_all (GtkRecentChooser *chooser)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->select_all (chooser);
+}
+
+/**
+ * gtk_recent_chooser_unselect_all:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Unselects all the items inside @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_unselect_all (GtkRecentChooser *chooser)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->unselect_all (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_items:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the list of recently used resources in form of #GtkRecentInfo objects.
+ *
+ * The return value of this function is affected by the "sort-type" and
+ * "limit" properties of @chooser.
+ *
+ * Return value: A newly allocated list of #GtkRecentInfo objects. You should
+ * use gtk_recent_info_unref() on every item of the list, and then free
+ * the list itself using g_list_free().
+ *
+ * Since: 2.10
+ */
+GList *
+gtk_recent_chooser_get_items (GtkRecentChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->get_items (chooser);
+}
+
+/**
+ * gtk_recent_chooser_get_uris:
+ * @chooser: a #GtkRecentChooser
+ * @length: return location for a the length of the URI list, or %NULL
+ *
+ * Gets the URI of the recently used resources.
+ *
+ * The return value of this function is affected by the "sort-type" and "limit"
+ * properties of @chooser.
+ *
+ * Since the returned array is %NULL terminated, @length may be %NULL.
+ *
+ * Return value: A newly allocated, %NULL terminated array of strings. Use
+ * g_strfreev() to free it.
+ *
+ * Since: 2.10
+ */
+gchar **
+gtk_recent_chooser_get_uris (GtkRecentChooser *chooser,
+ gsize *length)
+{
+ GList *items, *l;
+ gchar **retval;
+ gsize n_items, i;
+
+ items = gtk_recent_chooser_get_items (chooser);
+ if (!items)
+ return NULL;
+
+ n_items = g_list_length (items);
+ retval = g_new0 (gchar *, n_items + 1);
+
+ for (l = items, i = 0; l != NULL; l = l->next)
+ {
+ GtkRecentInfo *info = (GtkRecentInfo *) l->data;
+ const gchar *uri;
+
+ g_assert (info != NULL);
+
+ uri = gtk_recent_info_get_uri (info);
+ g_assert (uri != NULL);
+
+ retval[i++] = g_strdup (uri);
+ }
+ retval[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ g_list_foreach (items,
+ (GFunc) gtk_recent_info_unref,
+ NULL);
+ g_list_free (items);
+
+ return retval;
+}
+
+/**
+ * gtk_recent_chooser_add_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Adds @filter to the list of #GtkRecentFilter objects held by @chooser.
+ *
+ * If no previous filter objects were defined, this function will call
+ * gtk_recent_chooser_set_filter().
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->add_filter (chooser, filter);
+}
+
+/**
+ * gtk_recent_chooser_remove_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Removes @filter from the list of #GtkRecentFilter objects held by @chooser.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ GTK_RECENT_CHOOSER_GET_IFACE (chooser)->remove_filter (chooser, filter);
+}
+
+/**
+ * gtk_recent_chooser_list_filters:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentFilter objects held by @chooser.
+ *
+ * Return value: A singly linked list of #GtkRecentFilter objects. You
+ * should just free the returned list using g_slist_free().
+ *
+ * Since: 2.10
+ */
+GSList *
+gtk_recent_chooser_list_filters (GtkRecentChooser *chooser)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ return GTK_RECENT_CHOOSER_GET_IFACE (chooser)->list_filters (chooser);
+}
+
+/**
+ * gtk_recent_chooser_set_filter:
+ * @chooser: a #GtkRecentChooser
+ * @filter: a #GtkRecentFilter
+ *
+ * Sets @filter as the current #GtkRecentFilter object used by @chooser
+ * to affect the displayed recently used resources.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_set_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_object_set (G_OBJECT (chooser), "filter", filter, NULL);
+}
+
+/**
+ * gtk_recent_chooser_get_filter:
+ * @chooser: a #GtkRecentChooser
+ *
+ * Gets the #GtkRecentFilter object currently used by @chooser to affect
+ * the display of the recently used resources.
+ *
+ * Return value: a #GtkRecentFilter object.
+ *
+ * Since: 2.10
+ */
+GtkRecentFilter *
+gtk_recent_chooser_get_filter (GtkRecentChooser *chooser)
+{
+ GtkRecentFilter *filter;
+
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
+
+ g_object_get (G_OBJECT (chooser), "filter", &filter, NULL);
+
+ /* we need this hack because g_object_get() increases the refcount
+ * of the returned object; see also gtk_file_chooser_get_filter()
+ * inside gtkfilechooser.c
+ */
+ if (filter)
+ g_object_unref (filter);
+
+ return filter;
+}
+
+void
+_gtk_recent_chooser_item_activated (GtkRecentChooser *chooser)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_signal_emit (chooser, chooser_signals[ITEM_ACTIVATED], 0);
+}
+
+void
+_gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (chooser));
+
+ g_signal_emit (chooser, chooser_signals[SELECTION_CHANGED], 0);
+}
+
+#define __GTK_RECENT_CHOOSER_H__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooser.h b/gtk/gtkrecentchooser.h
new file mode 100644
index 0000000000..2362667a95
--- /dev/null
+++ b/gtk/gtkrecentchooser.h
@@ -0,0 +1,186 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooser.h - Abstract interface for recent file selectors GUIs
+ *
+ * Copyright (C) 2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_H__
+#define __GTK_RECENT_CHOOSER_H__
+
+#include <gtk/gtkwidget.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER (gtk_recent_chooser_get_type ())
+#define GTK_RECENT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER, GtkRecentChooser))
+#define GTK_IS_RECENT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER))
+#define GTK_RECENT_CHOOSER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_RECENT_CHOOSER, GtkRecentChooserIface))
+
+/**
+ * GtkRecentSortType:
+ * @GTK_RECENT_SORT_NONE: Do not sort the returned list of recently used
+ * resources.
+ * @GTK_RECENT_SORT_MRU: Sort the returned list with the most recently used
+ * items first.
+ * @GTK_RECENT_SORT_LRU: Sort the returned list with the least recently used
+ * items first.
+ * @GTK_RECENT_SORT_CUSTOM: Sort the returned list using a custom sorting
+ * function passed using gtk_recent_manager_set_sort_func().
+ *
+ * Used to specify the sorting method to be applyed to the recently
+ * used resource list.
+ **/
+typedef enum
+{
+ GTK_RECENT_SORT_NONE = 0,
+ GTK_RECENT_SORT_MRU,
+ GTK_RECENT_SORT_LRU,
+ GTK_RECENT_SORT_CUSTOM
+} GtkRecentSortType;
+
+typedef gint (*GtkRecentSortFunc) (GtkRecentInfo *a,
+ GtkRecentInfo *b,
+ gpointer user_data);
+
+
+typedef struct _GtkRecentChooser GtkRecentChooser; /* dummy */
+typedef struct _GtkRecentChooserIface GtkRecentChooserIface;
+
+#define GTK_RECENT_CHOOSER_ERROR (gtk_recent_chooser_error_quark ())
+
+typedef enum
+{
+ GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+ GTK_RECENT_CHOOSER_ERROR_INVALID_URI
+} GtkRecentChooserError;
+
+GQuark gtk_recent_chooser_error_quark (void);
+
+
+struct _GtkRecentChooserIface
+{
+ GTypeInterface base_iface;
+
+ /*
+ * Methods
+ */
+ gboolean (* set_current_uri) (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+ gchar * (* get_current_uri) (GtkRecentChooser *chooser);
+ gboolean (* select_uri) (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+ void (* unselect_uri) (GtkRecentChooser *chooser,
+ const gchar *uri);
+ void (* select_all) (GtkRecentChooser *chooser);
+ void (* unselect_all) (GtkRecentChooser *chooser);
+ GList * (* get_items) (GtkRecentChooser *chooser);
+ GtkRecentManager *(* get_recent_manager) (GtkRecentChooser *chooser);
+ void (* add_filter) (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+ void (* remove_filter) (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+ GSList * (* list_filters) (GtkRecentChooser *chooser);
+ void (* set_sort_func) (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer data,
+ GDestroyNotify destroy);
+
+ /*
+ * Signals
+ */
+ void (* item_activated) (GtkRecentChooser *chooser);
+ void (* selection_changed) (GtkRecentChooser *chooser);
+};
+
+GType gtk_recent_chooser_get_type (void) G_GNUC_CONST;
+
+/*
+ * Configuration
+ */
+void gtk_recent_chooser_set_show_private (GtkRecentChooser *chooser,
+ gboolean show_private);
+gboolean gtk_recent_chooser_get_show_private (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_show_not_found (GtkRecentChooser *chooser,
+ gboolean show_not_found);
+gboolean gtk_recent_chooser_get_show_not_found (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_select_multiple (GtkRecentChooser *chooser,
+ gboolean select_multiple);
+gboolean gtk_recent_chooser_get_select_multiple (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_limit (GtkRecentChooser *chooser,
+ gint limit);
+gint gtk_recent_chooser_get_limit (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_local_only (GtkRecentChooser *chooser,
+ gboolean local_only);
+gboolean gtk_recent_chooser_get_local_only (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_show_tips (GtkRecentChooser *chooser,
+ gboolean show_tips);
+gboolean gtk_recent_chooser_get_show_tips (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_show_numbers (GtkRecentChooser *chooser,
+ gboolean show_numbers);
+gboolean gtk_recent_chooser_get_show_numbers (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_show_icons (GtkRecentChooser *chooser,
+ gboolean show_icons);
+gboolean gtk_recent_chooser_get_show_icons (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_sort_type (GtkRecentChooser *chooser,
+ GtkRecentSortType sort_type);
+GtkRecentSortType gtk_recent_chooser_get_sort_type (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy);
+
+/*
+ * Items handling
+ */
+gboolean gtk_recent_chooser_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+gchar * gtk_recent_chooser_get_current_uri (GtkRecentChooser *chooser);
+GtkRecentInfo *gtk_recent_chooser_get_current_item (GtkRecentChooser *chooser);
+gboolean gtk_recent_chooser_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+void gtk_recent_chooser_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri);
+void gtk_recent_chooser_select_all (GtkRecentChooser *chooser);
+void gtk_recent_chooser_unselect_all (GtkRecentChooser *chooser);
+GList * gtk_recent_chooser_get_items (GtkRecentChooser *chooser);
+gchar ** gtk_recent_chooser_get_uris (GtkRecentChooser *chooser,
+ gsize *length);
+
+/*
+ * Filters
+ */
+void gtk_recent_chooser_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+void gtk_recent_chooser_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+GSList * gtk_recent_chooser_list_filters (GtkRecentChooser *chooser);
+void gtk_recent_chooser_set_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+GtkRecentFilter *gtk_recent_chooser_get_filter (GtkRecentChooser *chooser);
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_H__ */
diff --git a/gtk/gtkrecentchooserdefault.c b/gtk/gtkrecentchooserdefault.c
new file mode 100644
index 0000000000..e830e96cbf
--- /dev/null
+++ b/gtk/gtkrecentchooserdefault.c
@@ -0,0 +1,2073 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdefault.c
+ * Copyright (C) 2005-2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gdk/gdkscreen.h>
+
+#include "gtkstock.h"
+#include "gtkicontheme.h"
+#include "gtkiconfactory.h"
+#include "gtksettings.h"
+#include "gtktreeview.h"
+#include "gtkliststore.h"
+#include "gtkbutton.h"
+#include "gtkcelllayout.h"
+#include "gtkcellrendererpixbuf.h"
+#include "gtkcellrenderertext.h"
+#include "gtkcheckmenuitem.h"
+#include "gtkclipboard.h"
+#include "gtkcombobox.h"
+#include "gtkentry.h"
+#include "gtkeventbox.h"
+#include "gtkexpander.h"
+#include "gtkframe.h"
+#include "gtkhbox.h"
+#include "gtkhpaned.h"
+#include "gtkimage.h"
+#include "gtkimagemenuitem.h"
+#include "gtkintl.h"
+#include "gtklabel.h"
+#include "gtkmenuitem.h"
+#include "gtkmessagedialog.h"
+#include "gtkscrolledwindow.h"
+#include "gtkseparatormenuitem.h"
+#include "gtksizegroup.h"
+#include "gtktable.h"
+#include "gtktreeview.h"
+#include "gtktreemodelsort.h"
+#include "gtktreemodelfilter.h"
+#include "gtktreeselection.h"
+#include "gtktreestore.h"
+#include "gtktooltips.h"
+#include "gtktypebuiltins.h"
+#include "gtkvbox.h"
+#include "gtkalias.h"
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentchooserdefault.h"
+
+
+
+struct _GtkRecentChooserDefault
+{
+ GtkVBox parent_instance;
+
+ GtkRecentManager *manager;
+ gulong manager_changed_id;
+ guint local_manager : 1;
+
+ gint icon_size;
+
+ /* RecentChooser properties */
+ gint limit;
+ GtkRecentSortType sort_type;
+ guint show_private : 1;
+ guint show_not_found : 1;
+ guint select_multiple : 1;
+ guint show_numbers : 1;
+ guint show_tips : 1;
+ guint show_icons : 1;
+ guint local_only : 1;
+
+ GSList *filters;
+ GtkRecentFilter *current_filter;
+ GtkWidget *filter_combo_hbox;
+ GtkWidget *filter_combo;
+
+ GtkRecentSortFunc sort_func;
+ gpointer sort_data;
+ GDestroyNotify sort_data_destroy;
+
+ GtkTooltips *tooltips;
+
+ GtkIconTheme *icon_theme;
+
+ GtkWidget *recent_view;
+ GtkListStore *recent_store;
+ GtkTreeModel *recent_store_filter;
+ GtkTreeViewColumn *icon_column;
+ GtkTreeViewColumn *meta_column;
+ GtkCellRenderer *meta_renderer;
+ GtkTreeSelection *selection;
+
+ GtkWidget *recent_popup_menu;
+ GtkWidget *recent_popup_menu_copy_item;
+ GtkWidget *recent_popup_menu_remove_item;
+ GtkWidget *recent_popup_menu_clear_item;
+ GtkWidget *recent_popup_menu_show_private_item;
+
+ guint load_id;
+ GList *recent_items;
+ gint n_recent_items;
+ gint loaded_items;
+ guint load_state;
+};
+
+typedef struct _GtkRecentChooserDefaultClass
+{
+ GtkVBoxClass parent_class;
+} GtkRecentChooserDefaultClass;
+
+enum {
+ RECENT_URI_COLUMN,
+ RECENT_DISPLAY_NAME_COLUMN,
+ RECENT_INFO_COLUMN,
+
+ N_RECENT_COLUMNS
+};
+
+enum {
+ LOAD_EMPTY, /* initial state: the model is empty */
+ LOAD_PRELOAD, /* the model is loading and not inserted in the tree yet */
+ LOAD_LOADING, /* the model is fully loaded but not inserted */
+ LOAD_FINISHED /* the model is fully loaded and inserted */
+};
+
+enum {
+ TEXT_URI_LIST
+};
+
+/* Target types for DnD from the file list */
+static const GtkTargetEntry recent_list_source_targets[] = {
+ { "text/uri-list", 0, TEXT_URI_LIST }
+};
+
+static const int num_recent_list_source_targets = (sizeof (recent_list_source_targets)
+ / sizeof (recent_list_source_targets[0]));
+
+/* Icon size for if we can't get it from the theme */
+#define FALLBACK_ICON_SIZE 48
+#define FALLBACK_ITEM_LIMIT 20
+
+#define NUM_CHARS 40
+#define NUM_LINES 9
+
+
+
+/* GObject */
+static void gtk_recent_chooser_default_class_init (GtkRecentChooserDefaultClass *klass);
+static void gtk_recent_chooser_default_init (GtkRecentChooserDefault *impl);
+static GObject *gtk_recent_chooser_default_constructor (GType type,
+ guint n_construct_prop,
+ GObjectConstructParam *construct_params);
+static void gtk_recent_chooser_default_finalize (GObject *object);
+static void gtk_recent_chooser_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_chooser_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* GtkRecentChooserIface */
+static void gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface);
+static gboolean gtk_recent_chooser_default_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static gchar * gtk_recent_chooser_default_get_current_uri (GtkRecentChooser *chooser);
+static gboolean gtk_recent_chooser_default_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static void gtk_recent_chooser_default_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri);
+static void gtk_recent_chooser_default_select_all (GtkRecentChooser *chooser);
+static void gtk_recent_chooser_default_unselect_all (GtkRecentChooser *chooser);
+static GList * gtk_recent_chooser_default_get_items (GtkRecentChooser *chooser);
+static GtkRecentManager *gtk_recent_chooser_default_get_recent_manager (GtkRecentChooser *chooser);
+static void gtk_recent_chooser_default_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy);
+static void gtk_recent_chooser_default_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static void gtk_recent_chooser_default_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static GSList * gtk_recent_chooser_default_list_filters (GtkRecentChooser *chooser);
+
+
+static void gtk_recent_chooser_default_map (GtkWidget *widget);
+static void gtk_recent_chooser_default_show_all (GtkWidget *widget);
+
+static void set_current_filter (GtkRecentChooserDefault *impl,
+ GtkRecentFilter *filter);
+
+static GtkIconTheme *get_icon_theme_for_widget (GtkWidget *widget);
+static gint get_icon_size_for_widget (GtkWidget *widget,
+ GtkIconSize icon_size);
+
+static void reload_recent_items (GtkRecentChooserDefault *impl);
+static void chooser_set_model (GtkRecentChooserDefault *impl);
+
+static void set_recent_manager (GtkRecentChooserDefault *impl,
+ GtkRecentManager *manager);
+
+static void chooser_set_sort_type (GtkRecentChooserDefault *impl,
+ GtkRecentSortType sort_type);
+
+static gboolean recent_store_filter_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+static void recent_manager_changed_cb (GtkRecentManager *manager,
+ gpointer user_data);
+static void recent_icon_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data);
+static void recent_meta_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+static void selection_changed_cb (GtkTreeSelection *z,
+ gpointer user_data);
+static void row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *tree_path,
+ GtkTreeViewColumn *tree_column,
+ gpointer user_data);
+static void filter_combo_changed_cb (GtkComboBox *combo_box,
+ gpointer user_data);
+
+static void remove_all_activated_cb (GtkMenuItem *menu_item,
+ gpointer user_data);
+static void remove_item_activated_cb (GtkMenuItem *menu_item,
+ gpointer user_data);
+static void show_private_toggled_cb (GtkCheckMenuItem *menu_item,
+ gpointer user_data);
+
+static gboolean recent_view_popup_menu_cb (GtkWidget *widget,
+ gpointer user_data);
+static gboolean recent_view_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+
+static void recent_view_drag_begin_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer user_data);
+static void recent_view_drag_data_get_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time_,
+ gpointer data);
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDefault,
+ gtk_recent_chooser_default,
+ GTK_TYPE_VBOX,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+ gtk_recent_chooser_iface_init));
+
+
+
+
+static void
+gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
+{
+ iface->set_current_uri = gtk_recent_chooser_default_set_current_uri;
+ iface->get_current_uri = gtk_recent_chooser_default_get_current_uri;
+ iface->select_uri = gtk_recent_chooser_default_select_uri;
+ iface->unselect_uri = gtk_recent_chooser_default_unselect_uri;
+ iface->select_all = gtk_recent_chooser_default_select_all;
+ iface->unselect_all = gtk_recent_chooser_default_unselect_all;
+ iface->get_items = gtk_recent_chooser_default_get_items;
+ iface->get_recent_manager = gtk_recent_chooser_default_get_recent_manager;
+ iface->set_sort_func = gtk_recent_chooser_default_set_sort_func;
+ iface->add_filter = gtk_recent_chooser_default_add_filter;
+ iface->remove_filter = gtk_recent_chooser_default_remove_filter;
+ iface->list_filters = gtk_recent_chooser_default_list_filters;
+}
+
+static void
+gtk_recent_chooser_default_class_init (GtkRecentChooserDefaultClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->constructor = gtk_recent_chooser_default_constructor;
+ gobject_class->finalize = gtk_recent_chooser_default_finalize;
+ gobject_class->set_property = gtk_recent_chooser_default_set_property;
+ gobject_class->get_property = gtk_recent_chooser_default_get_property;
+
+ widget_class->map = gtk_recent_chooser_default_map;
+ widget_class->show_all = gtk_recent_chooser_default_show_all;
+
+ _gtk_recent_chooser_install_properties (gobject_class);
+}
+
+static void
+gtk_recent_chooser_default_init (GtkRecentChooserDefault *impl)
+{
+ gtk_box_set_spacing (GTK_BOX (impl), 12);
+
+ /* by default, we use the global manager */
+ impl->local_manager = FALSE;
+
+ impl->limit = FALLBACK_ITEM_LIMIT;
+ impl->sort_type = GTK_RECENT_SORT_NONE;
+
+ impl->show_icons = TRUE;
+ impl->show_private = FALSE;
+ impl->show_not_found = FALSE;
+ impl->show_tips = TRUE;
+ impl->select_multiple = FALSE;
+ impl->local_only = TRUE;
+
+ impl->icon_size = FALLBACK_ICON_SIZE;
+ impl->icon_theme = NULL;
+
+ impl->current_filter = NULL;
+
+ impl->tooltips = gtk_tooltips_new ();
+ g_object_ref_sink (impl->tooltips);
+
+ impl->recent_items = NULL;
+ impl->n_recent_items = 0;
+ impl->loaded_items = 0;
+
+ impl->load_state = LOAD_EMPTY;
+}
+
+static GObject *
+gtk_recent_chooser_default_constructor (GType type,
+ guint n_construct_prop,
+ GObjectConstructParam *construct_params)
+{
+ GtkRecentChooserDefault *impl;
+ GObject *object;
+
+ GtkWidget *scrollw;
+ GtkCellRenderer *renderer;
+
+ object = G_OBJECT_CLASS (gtk_recent_chooser_default_parent_class)->constructor (type, n_construct_prop, construct_params);
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+
+ g_assert (impl->manager);
+
+ gtk_widget_push_composite_child ();
+
+ scrollw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollw),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollw),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (impl), scrollw, TRUE, TRUE, 0);
+ gtk_widget_show (scrollw);
+
+ impl->recent_view = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->recent_view), FALSE);
+ g_signal_connect (impl->recent_view, "row-activated",
+ G_CALLBACK (row_activated_cb), impl);
+ g_signal_connect (impl->recent_view, "popup-menu",
+ G_CALLBACK (recent_view_popup_menu_cb), impl);
+ g_signal_connect (impl->recent_view, "button-press-event",
+ G_CALLBACK (recent_view_button_press_cb), impl);
+ g_signal_connect (impl->recent_view, "drag-begin",
+ G_CALLBACK (recent_view_drag_begin_cb), impl);
+ g_signal_connect (impl->recent_view, "drag-data-get",
+ G_CALLBACK (recent_view_drag_data_get_cb), impl);
+
+ g_object_set_data (G_OBJECT (impl->recent_view), "GtkRecentChooserDefault", impl);
+
+ gtk_container_add (GTK_CONTAINER (scrollw), impl->recent_view);
+ gtk_widget_show (impl->recent_view);
+
+ impl->icon_column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (impl->icon_column, FALSE);
+ gtk_tree_view_column_set_resizable (impl->icon_column, FALSE);
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (impl->icon_column, renderer, FALSE);
+ gtk_tree_view_column_set_cell_data_func (impl->icon_column,
+ renderer,
+ recent_icon_data_func,
+ impl,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (impl->recent_view),
+ impl->icon_column);
+
+ impl->meta_column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (impl->meta_column, TRUE);
+ gtk_tree_view_column_set_resizable (impl->meta_column, FALSE);
+
+ impl->meta_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (G_OBJECT (impl->meta_renderer),
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+ gtk_tree_view_column_pack_start (impl->meta_column, impl->meta_renderer, TRUE);
+ gtk_tree_view_column_set_cell_data_func (impl->meta_column,
+ impl->meta_renderer,
+ recent_meta_data_func,
+ impl,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (impl->recent_view),
+ impl->meta_column);
+
+ impl->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->recent_view));
+ gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_SINGLE);
+ g_signal_connect (impl->selection, "changed", G_CALLBACK (selection_changed_cb), impl);
+
+ /* drag and drop */
+ gtk_drag_source_set (impl->recent_view,
+ GDK_BUTTON1_MASK,
+ recent_list_source_targets,
+ num_recent_list_source_targets,
+ GDK_ACTION_COPY);
+
+ impl->filter_combo_hbox = gtk_hbox_new (FALSE, 12);
+
+ impl->filter_combo = gtk_combo_box_new_text ();
+ gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (impl->filter_combo), FALSE);
+ g_signal_connect (impl->filter_combo, "changed",
+ G_CALLBACK (filter_combo_changed_cb), impl);
+ gtk_tooltips_set_tip (impl->tooltips,
+ impl->filter_combo,
+ _("Select which type of documents are shown"),
+ NULL);
+
+ gtk_box_pack_end (GTK_BOX (impl->filter_combo_hbox),
+ impl->filter_combo,
+ FALSE, FALSE, 0);
+ gtk_widget_show (impl->filter_combo);
+
+ gtk_box_pack_end (GTK_BOX (impl), impl->filter_combo_hbox, FALSE, FALSE, 0);
+
+ gtk_widget_pop_composite_child ();
+
+ impl->recent_store = gtk_list_store_new (N_RECENT_COLUMNS,
+ G_TYPE_STRING, /* uri */
+ G_TYPE_STRING, /* display_name */
+ GTK_TYPE_RECENT_INFO /* info */);
+
+ return object;
+}
+
+static void
+gtk_recent_chooser_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+ set_recent_manager (impl, g_value_get_object (value));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+ impl->show_private = g_value_get_boolean (value);
+
+ if (impl->recent_store && impl->recent_store_filter)
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+
+ if (impl->recent_popup_menu_show_private_item)
+ {
+ GtkCheckMenuItem *item = GTK_CHECK_MENU_ITEM (impl->recent_popup_menu_show_private_item);
+ g_signal_handlers_block_by_func (item, G_CALLBACK (show_private_toggled_cb), impl);
+ gtk_check_menu_item_set_active (item, impl->show_private);
+ g_signal_handlers_unblock_by_func (item, G_CALLBACK (show_private_toggled_cb), impl);
+ }
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+ impl->show_not_found = g_value_get_boolean (value);
+
+ if (impl->recent_store && impl->recent_store_filter)
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+ impl->show_tips = g_value_get_boolean (value);
+
+ if (impl->show_tips)
+ gtk_tooltips_enable (impl->tooltips);
+ else
+ gtk_tooltips_disable (impl->tooltips);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+ impl->show_icons = g_value_get_boolean (value);
+ gtk_tree_view_column_set_visible (impl->icon_column, impl->show_icons);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+ impl->select_multiple = g_value_get_boolean (value);
+
+ if (impl->select_multiple)
+ gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_MULTIPLE);
+ else
+ gtk_tree_selection_set_mode (impl->selection, GTK_SELECTION_SINGLE);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+ impl->local_only = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LIMIT:
+ impl->limit = g_value_get_int (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+ chooser_set_sort_type (impl, g_value_get_enum (value));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_FILTER:
+ set_current_filter (impl, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_recent_chooser_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+
+ switch (prop_id)
+ {
+ case GTK_RECENT_CHOOSER_PROP_LIMIT:
+ g_value_set_int (value, impl->limit);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+ g_value_set_enum (value, impl->sort_type);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+ g_value_set_boolean (value, impl->show_private);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+ g_value_set_boolean (value, impl->show_icons);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+ g_value_set_boolean (value, impl->show_not_found);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+ g_value_set_boolean (value, impl->show_tips);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+ g_value_set_boolean (value, impl->local_only);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+ g_value_set_boolean (value, impl->select_multiple);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_FILTER:
+ g_value_set_object (value, impl->current_filter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_recent_chooser_default_finalize (GObject *object)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (object);
+
+ if (impl->recent_items)
+ {
+ g_list_foreach (impl->recent_items,
+ (GFunc) gtk_recent_info_unref,
+ NULL);
+ g_list_free (impl->recent_items);
+ impl->recent_items = NULL;
+ }
+
+ if (impl->manager_changed_id)
+ {
+ g_signal_handler_disconnect (impl->manager, impl->manager_changed_id);
+ impl->manager_changed_id = 0;
+ }
+
+ impl->manager = NULL;
+
+ if (impl->sort_data_destroy)
+ {
+ impl->sort_data_destroy (impl->sort_data);
+
+ impl->sort_data_destroy = NULL;
+ impl->sort_data = NULL;
+ impl->sort_func = NULL;
+ }
+
+ if (impl->filters)
+ {
+ g_slist_foreach (impl->filters,
+ (GFunc) g_object_unref,
+ NULL);
+ g_slist_free (impl->filters);
+ }
+
+ if (impl->current_filter)
+ g_object_unref (impl->current_filter);
+
+ if (impl->recent_store_filter)
+ g_object_unref (impl->recent_store_filter);
+
+ if (impl->recent_store)
+ g_object_unref (impl->recent_store);
+
+ if (impl->tooltips)
+ g_object_unref (impl->tooltips);
+
+ G_OBJECT_CLASS (gtk_recent_chooser_default_parent_class)->finalize (object);
+}
+
+/* override GtkWidget::show_all since we have internal widgets we wish to keep
+ * hidden unless we decide otherwise, like the filter combo box.
+ */
+static void
+gtk_recent_chooser_default_show_all (GtkWidget *widget)
+{
+ gtk_widget_show (widget);
+}
+
+
+
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow *parent,
+ const gchar *msg,
+ const gchar *detail)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", detail);
+
+ if (parent->group)
+ gtk_window_group_add_window (parent->group, GTK_WINDOW (dialog));
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (!GTK_WIDGET_TOPLEVEL (toplevel))
+ return NULL;
+ else
+ return GTK_WINDOW (toplevel);
+}
+
+/* Shows an error dialog for the file chooser */
+static void
+error_message (GtkRecentChooserDefault *impl,
+ const gchar *msg,
+ const gchar *detail)
+{
+ error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
+
+static void
+set_busy_cursor (GtkRecentChooserDefault *impl,
+ gboolean show_busy_cursor)
+{
+ GtkWindow *toplevel;
+ GdkDisplay *display;
+ GdkCursor *cursor;
+
+ toplevel = get_toplevel (GTK_WIDGET (impl));
+ if (!toplevel || !GTK_WIDGET_REALIZED (toplevel))
+ return;
+
+ display = gtk_widget_get_display (GTK_WIDGET (toplevel));
+
+ cursor = NULL;
+ if (show_busy_cursor)
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+
+ gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor);
+ gdk_display_flush (display);
+
+ if (cursor)
+ gdk_cursor_unref (cursor);
+}
+
+static void
+chooser_set_model (GtkRecentChooserDefault *impl)
+{
+ g_assert (impl->recent_store != NULL);
+ g_assert (impl->recent_store_filter == NULL);
+ g_assert (impl->load_state == LOAD_LOADING);
+
+ impl->recent_store_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->recent_store), NULL);
+ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->recent_store_filter),
+ recent_store_filter_func,
+ impl,
+ NULL);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->recent_view),
+ impl->recent_store_filter);
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->recent_view));
+ gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->recent_view), TRUE);
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->recent_view),
+ RECENT_DISPLAY_NAME_COLUMN);
+
+ impl->load_state = LOAD_FINISHED;
+}
+
+static gboolean
+load_recent_items (gpointer user_data)
+{
+ GtkRecentChooserDefault *impl;
+ GtkRecentInfo *info;
+ GtkTreeIter iter;
+ const gchar *uri, *name;
+ gboolean retval;
+
+ GDK_THREADS_ENTER ();
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ g_assert ((impl->load_state == LOAD_EMPTY) ||
+ (impl->load_state == LOAD_PRELOAD));
+
+ /* store the items for multiple runs */
+ if (!impl->recent_items)
+ {
+ impl->recent_items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (impl));
+ if (!impl->recent_items)
+ {
+ GDK_THREADS_LEAVE ();
+
+ impl->load_state = LOAD_FINISHED;
+
+ return FALSE;
+ }
+
+ impl->n_recent_items = g_list_length (impl->recent_items);
+ impl->loaded_items = 0;
+ impl->load_state = LOAD_PRELOAD;
+ }
+
+ info = (GtkRecentInfo *) g_list_nth_data (impl->recent_items,
+ impl->loaded_items);
+ g_assert (info);
+
+ uri = gtk_recent_info_get_uri (info);
+ name = gtk_recent_info_get_display_name (info);
+
+ /* at this point, everything goes inside the model; operations on the
+ * visualization of items inside the model are done in the cell data
+ * funcs (remember that there are two of those: one for the icon and
+ * one for the text), while the filtering is done only when a filter
+ * is actually loaded. */
+ gtk_list_store_append (impl->recent_store, &iter);
+ gtk_list_store_set (impl->recent_store, &iter,
+ RECENT_URI_COLUMN, uri, /* uri */
+ RECENT_DISPLAY_NAME_COLUMN, name, /* display_name */
+ RECENT_INFO_COLUMN, info, /* info */
+ -1);
+
+ impl->loaded_items += 1;
+
+ if (impl->loaded_items == impl->n_recent_items)
+ {
+ /* we have finished loading, so we remove the items cache */
+ impl->load_state = LOAD_LOADING;
+
+ g_list_foreach (impl->recent_items,
+ (GFunc) gtk_recent_info_unref,
+ NULL);
+ g_list_free (impl->recent_items);
+
+ impl->recent_items = NULL;
+ impl->n_recent_items = 0;
+ impl->loaded_items = 0;
+
+ if (impl->recent_store_filter)
+ {
+ g_object_unref (impl->recent_store_filter);
+ impl->recent_store_filter = NULL;
+ }
+
+ /* load the filled up model */
+ chooser_set_model (impl);
+
+ retval = FALSE;
+ }
+ else
+ {
+ /* we did not finish, so continue loading */
+ retval = TRUE;
+ }
+
+ GDK_THREADS_LEAVE ();
+
+ return retval;
+}
+
+static void
+cleanup_after_load (gpointer user_data)
+{
+ GtkRecentChooserDefault *impl;
+
+ GDK_THREADS_ENTER ();
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ if (impl->load_id != 0)
+ {
+ g_assert ((impl->load_state == LOAD_PRELOAD) ||
+ (impl->load_state == LOAD_LOADING) ||
+ (impl->load_state == LOAD_FINISHED));
+
+ /* we have officialy finished loading all the items,
+ * so we can reset the state machine
+ */
+ g_source_remove (impl->load_id);
+ impl->load_id = 0;
+ impl->load_state = LOAD_EMPTY;
+ }
+ else
+ g_assert ((impl->load_state == LOAD_EMPTY) ||
+ (impl->load_state == LOAD_LOADING) ||
+ (impl->load_state == LOAD_FINISHED));
+
+ set_busy_cursor (impl, FALSE);
+
+ GDK_THREADS_LEAVE ();
+}
+
+/* clears the current model and reloads the recently used resources */
+static void
+reload_recent_items (GtkRecentChooserDefault *impl)
+{
+ /* reload is already in progress - do not disturb */
+ if (impl->load_id)
+ return;
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->recent_view), NULL);
+ gtk_list_store_clear (impl->recent_store);
+
+ if (!impl->icon_theme)
+ impl->icon_theme = get_icon_theme_for_widget (GTK_WIDGET (impl));
+
+ impl->icon_size = get_icon_size_for_widget (GTK_WIDGET (impl),
+ GTK_ICON_SIZE_BUTTON);
+
+ set_busy_cursor (impl, TRUE);
+
+ impl->load_state = LOAD_EMPTY;
+ impl->load_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 30,
+ load_recent_items,
+ impl,
+ cleanup_after_load);
+}
+
+/* taken form gtkfilechooserdialog.c */
+static void
+set_default_size (GtkRecentChooserDefault *impl)
+{
+ GtkWidget *widget;
+ gint width, height;
+ gint font_size;
+ GdkScreen *screen;
+ gint monitor_num;
+ GtkRequisition req;
+ GdkRectangle monitor;
+
+ widget = GTK_WIDGET (impl);
+
+ /* Size based on characters and the icon size */
+ font_size = pango_font_description_get_size (widget->style->font_desc);
+ font_size = PANGO_PIXELS (font_size);
+
+ width = impl->icon_size + font_size * NUM_CHARS;
+ height = (impl->icon_size + font_size) * NUM_LINES;
+
+ /* Use at least the requisition size... */
+ gtk_widget_size_request (widget, &req);
+ width = MAX (width, req.width);
+ height = MAX (height, req.height);
+
+ /* ... but no larger than the monitor */
+ screen = gtk_widget_get_screen (widget);
+ monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
+
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ width = MIN (width, monitor.width * 3 / 4);
+ height = MIN (height, monitor.height * 3 / 4);
+
+ /* Set size */
+ gtk_widget_set_size_request (impl->recent_view, width, height);
+}
+
+static void
+gtk_recent_chooser_default_map (GtkWidget *widget)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (widget);
+
+ if (GTK_WIDGET_CLASS (gtk_recent_chooser_default_parent_class)->map)
+ GTK_WIDGET_CLASS (gtk_recent_chooser_default_parent_class)->map (widget);
+
+ /* reloads everything */
+ reload_recent_items (impl);
+
+ set_default_size (impl);
+}
+
+static void
+recent_icon_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+ GtkRecentInfo *info = NULL;
+ GdkPixbuf *pixbuf;
+
+ gtk_tree_model_get (model, iter,
+ RECENT_INFO_COLUMN, &info,
+ -1);
+ g_assert (info != NULL);
+
+ pixbuf = gtk_recent_info_get_icon (info, impl->icon_size);
+
+ g_object_set (cell,
+ "pixbuf", pixbuf,
+ NULL);
+
+ if (pixbuf)
+ g_object_unref (pixbuf);
+}
+
+static void
+recent_meta_data_func (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkRecentInfo *info = NULL;
+ gchar *uri;
+ gchar *name;
+ GString *data;
+
+ data = g_string_new (NULL);
+
+ gtk_tree_model_get (model, iter,
+ RECENT_DISPLAY_NAME_COLUMN, &name,
+ RECENT_INFO_COLUMN, &info,
+ -1);
+ g_assert (info != NULL);
+
+ uri = gtk_recent_info_get_uri_display (info);
+
+ if (!name)
+ name = gtk_recent_info_get_short_name (info);
+
+ g_string_append_printf (data,
+ "<b>%s</b>\n"
+ "<small>Location: %s</small>",
+ name,
+ uri);
+
+ g_object_set (cell,
+ "markup", data->str,
+ "sensitive", gtk_recent_info_exists (info),
+ NULL);
+
+ g_string_free (data, TRUE);
+ g_free (uri);
+ g_free (name);
+ gtk_recent_info_unref (info);
+}
+
+
+static gchar *
+gtk_recent_chooser_default_get_current_uri (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ g_assert (impl->selection != NULL);
+
+ if (!impl->select_multiple)
+ {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *uri = NULL;
+
+ if (!gtk_tree_selection_get_selected (impl->selection, &model, &iter))
+ return NULL;
+
+ gtk_tree_model_get (model, &iter, RECENT_URI_COLUMN, &uri, -1);
+
+ return uri;
+ }
+
+ return NULL;
+}
+
+typedef struct
+{
+ guint found : 1;
+ guint do_select : 1;
+ guint do_activate : 1;
+
+ gchar *uri;
+
+ GtkRecentChooserDefault *impl;
+} SelectURIData;
+
+static gboolean
+scan_for_uri_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ SelectURIData *select_data = (SelectURIData *) user_data;
+ gchar *uri;
+
+ if (!select_data)
+ return TRUE;
+
+ if (select_data->found)
+ return TRUE;
+
+ gtk_tree_model_get (model, iter, RECENT_URI_COLUMN, &uri, -1);
+ if (uri && (0 == strcmp (uri, select_data->uri)))
+ {
+ select_data->found = TRUE;
+
+ if (select_data->do_activate)
+ {
+ gtk_tree_view_row_activated (GTK_TREE_VIEW (select_data->impl->recent_view),
+ path,
+ select_data->impl->meta_column);
+
+ return TRUE;
+ }
+
+ if (select_data->do_select)
+ gtk_tree_selection_select_iter (select_data->impl->selection, iter);
+ else
+ gtk_tree_selection_unselect_iter (select_data->impl->selection, iter);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gtk_recent_chooser_default_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+ SelectURIData *data;
+
+ data = g_new0 (SelectURIData, 1);
+ data->uri = g_strdup (uri);
+ data->impl = impl;
+ data->found = FALSE;
+ data->do_activate = TRUE;
+ data->do_select = TRUE;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+ scan_for_uri_cb,
+ data);
+
+ if (!data->found)
+ {
+ g_free (data->uri);
+ g_free (data);
+
+ g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+ GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+ _("No item for URI '%s' found"),
+ uri);
+ return FALSE;
+ }
+
+ g_free (data->uri);
+ g_free (data);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_recent_chooser_default_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+ SelectURIData *data;
+
+ data = g_new0 (SelectURIData, 1);
+ data->uri = g_strdup (uri);
+ data->impl = impl;
+ data->found = FALSE;
+ data->do_activate = FALSE;
+ data->do_select = TRUE;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+ scan_for_uri_cb,
+ data);
+
+ if (!data->found)
+ {
+ g_free (data->uri);
+ g_free (data);
+
+ g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+ GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+ _("No item for URI '%s' found"),
+ uri);
+ return FALSE;
+ }
+
+ g_free (data->uri);
+ g_free (data);
+
+ return TRUE;
+}
+
+static void
+gtk_recent_chooser_default_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+ SelectURIData *data;
+
+ data = g_new0 (SelectURIData, 1);
+ data->uri = g_strdup (uri);
+ data->impl = impl;
+ data->found = FALSE;
+ data->do_activate = FALSE;
+ data->do_select = FALSE;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (impl->recent_store),
+ scan_for_uri_cb,
+ data);
+
+ g_free (data->uri);
+ g_free (data);
+}
+
+static void
+gtk_recent_chooser_default_select_all (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ if (!impl->select_multiple)
+ return;
+
+ gtk_tree_selection_select_all (impl->selection);
+}
+
+static void
+gtk_recent_chooser_default_unselect_all (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ gtk_tree_selection_unselect_all (impl->selection);
+}
+
+static void
+gtk_recent_chooser_default_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ if (impl->sort_data_destroy)
+ {
+ impl->sort_data_destroy (impl->sort_data);
+
+ impl->sort_func = NULL;
+ impl->sort_data = NULL;
+ impl->sort_data_destroy = NULL;
+ }
+
+ if (sort_func)
+ {
+ impl->sort_func = sort_func;
+ impl->sort_data = sort_data;
+ impl->sort_data_destroy = data_destroy;
+ }
+}
+
+static gint
+sort_recent_items_mru (GtkRecentInfo *a,
+ GtkRecentInfo *b,
+ gpointer unused)
+{
+ g_assert (a != NULL && b != NULL);
+
+ return (gtk_recent_info_get_modified (a) < gtk_recent_info_get_modified (b));
+}
+
+static gint
+sort_recent_items_lru (GtkRecentInfo *a,
+ GtkRecentInfo *b,
+ gpointer unused)
+{
+ g_assert (a != NULL && b != NULL);
+
+ return (gtk_recent_info_get_modified (a) > gtk_recent_info_get_modified (b));
+}
+
+/* our proxy sorting function */
+static gint
+sort_recent_items_proxy (gpointer *a,
+ gpointer *b,
+ gpointer user_data)
+{
+ GtkRecentInfo *info_a = (GtkRecentInfo *) a;
+ GtkRecentInfo *info_b = (GtkRecentInfo *) b;
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ if (impl->sort_func)
+ return (* impl->sort_func) (info_a,
+ info_b,
+ impl->sort_data);
+
+ /* fallback */
+ return 0;
+}
+
+static void
+chooser_set_sort_type (GtkRecentChooserDefault *impl,
+ GtkRecentSortType sort_type)
+{
+ if (impl->sort_type == sort_type)
+ return;
+
+ impl->sort_type = sort_type;
+}
+
+static GList *
+gtk_recent_chooser_default_get_items (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl;
+ gint limit;
+ GtkRecentSortType sort_type;
+ GList *items;
+ GCompareDataFunc compare_func;
+ gint length;
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ if (!impl->manager)
+ return NULL;
+
+ items = gtk_recent_manager_get_items (impl->manager);
+ if (!items)
+ return NULL;
+
+ limit = gtk_recent_chooser_get_limit (chooser);
+ sort_type = gtk_recent_chooser_get_sort_type (chooser);
+
+ switch (sort_type)
+ {
+ case GTK_RECENT_SORT_NONE:
+ compare_func = NULL;
+ break;
+ case GTK_RECENT_SORT_MRU:
+ compare_func = (GCompareDataFunc) sort_recent_items_mru;
+ break;
+ case GTK_RECENT_SORT_LRU:
+ compare_func = (GCompareDataFunc) sort_recent_items_lru;
+ break;
+ case GTK_RECENT_SORT_CUSTOM:
+ compare_func = (GCompareDataFunc) sort_recent_items_proxy;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /* sort the items; the filtering will be dealt with using
+ * the treeview's own filter object
+ */
+ if (compare_func)
+ items = g_list_sort_with_data (items, compare_func, impl);
+
+ length = g_list_length (items);
+ if ((limit != -1) && (length > limit))
+ {
+ GList *clamp, *l;
+
+ clamp = g_list_nth (items, limit - 1);
+
+ if (!clamp)
+ return items;
+
+ l = clamp->next;
+ clamp->next = NULL;
+
+ g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (l);
+ }
+
+ return items;
+}
+
+static GtkRecentManager *
+gtk_recent_chooser_default_get_recent_manager (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ return impl->manager;
+}
+
+static void
+show_filters (GtkRecentChooserDefault *impl,
+ gboolean show)
+{
+ if (show)
+ gtk_widget_show (impl->filter_combo_hbox);
+ else
+ gtk_widget_hide (impl->filter_combo_hbox);
+}
+
+static void
+gtk_recent_chooser_default_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ GtkRecentChooserDefault *impl;
+ const gchar *name;
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ if (g_slist_find (impl->filters, filter))
+ {
+ g_warning ("gtk_recent_chooser_add_filter() called on filter already in list\n");
+ return;
+ }
+
+ g_object_ref_sink (filter);
+ impl->filters = g_slist_append (impl->filters, filter);
+
+ /* display new filter */
+ name = gtk_recent_filter_get_name (filter);
+ if (!name)
+ name = "Untitled filter";
+
+ gtk_combo_box_append_text (GTK_COMBO_BOX (impl->filter_combo), name);
+
+ if (!g_slist_find (impl->filters, impl->current_filter))
+ set_current_filter (impl, filter);
+
+ show_filters (impl, TRUE);
+}
+
+static void
+gtk_recent_chooser_default_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint filter_idx;
+
+ filter_idx = g_slist_index (impl->filters, filter);
+
+ if (filter_idx < 0)
+ {
+ g_warning ("gtk_recent_chooser_remove_filter() called on filter not in list\n");
+ return;
+ }
+
+ impl->filters = g_slist_remove (impl->filters, filter);
+
+ if (filter == impl->current_filter)
+ {
+ if (impl->filters)
+ set_current_filter (impl, impl->filters->data);
+ else
+ set_current_filter (impl, NULL);
+ }
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->filter_combo));
+ gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_idx);
+ gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
+
+ g_object_unref (filter);
+
+ if (!impl->filters)
+ show_filters (impl, FALSE);
+}
+
+static GSList *
+gtk_recent_chooser_default_list_filters (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (chooser);
+
+ return g_slist_copy (impl->filters);
+}
+
+static gboolean
+get_is_recent_filtered (GtkRecentChooserDefault *impl,
+ GtkRecentInfo *info)
+{
+ GtkRecentFilter *current_filter;
+ GtkRecentFilterInfo filter_info;
+ GtkRecentFilterFlags needed;
+ gboolean retval;
+
+ g_assert (info != NULL);
+
+ if (!impl->current_filter)
+ return FALSE;
+
+ current_filter = impl->current_filter;
+ needed = gtk_recent_filter_get_needed (current_filter);
+
+ filter_info.contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE;
+
+ filter_info.uri = gtk_recent_info_get_uri (info);
+ filter_info.mime_type = gtk_recent_info_get_mime_type (info);
+
+ if (needed & GTK_RECENT_FILTER_DISPLAY_NAME)
+ {
+ filter_info.display_name = gtk_recent_info_get_display_name (info);
+ filter_info.contains |= GTK_RECENT_FILTER_DISPLAY_NAME;
+ }
+ else
+ filter_info.uri = NULL;
+
+ if (needed & GTK_RECENT_FILTER_APPLICATION)
+ {
+ filter_info.applications = (const gchar **) gtk_recent_info_get_applications (info, NULL);
+ filter_info.contains |= GTK_RECENT_FILTER_APPLICATION;
+ }
+ else
+ filter_info.applications = NULL;
+
+ if (needed & GTK_RECENT_FILTER_GROUP)
+ {
+ filter_info.groups = (const gchar **) gtk_recent_info_get_groups (info, NULL);
+ filter_info.contains |= GTK_RECENT_FILTER_GROUP;
+ }
+ else
+ filter_info.groups = NULL;
+
+ if (needed & GTK_RECENT_FILTER_AGE)
+ {
+ filter_info.age = gtk_recent_info_get_age (info);
+ filter_info.contains |= GTK_RECENT_FILTER_AGE;
+ }
+ else
+ filter_info.age = -1;
+
+ retval = gtk_recent_filter_filter (current_filter, &filter_info);
+
+ /* this we own */
+ if (filter_info.applications)
+ g_strfreev ((gchar **) filter_info.applications);
+
+ return !retval;
+}
+
+static gboolean
+recent_store_filter_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+ GtkRecentInfo *info = NULL;
+
+ if (!impl->current_filter)
+ return TRUE;
+
+ gtk_tree_model_get (model, iter,
+ RECENT_INFO_COLUMN, &info,
+ -1);
+ if (!info)
+ return TRUE;
+
+ if (get_is_recent_filtered (impl, info))
+ return FALSE;
+
+ if (impl->local_only && !gtk_recent_info_is_local (info))
+ return FALSE;
+
+ if ((!impl->show_private) && gtk_recent_info_get_private_hint (info))
+ return FALSE;
+
+ if ((!impl->show_not_found) && !gtk_recent_info_exists (info))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+set_current_filter (GtkRecentChooserDefault *impl,
+ GtkRecentFilter *filter)
+{
+ if (impl->current_filter != filter)
+ {
+ gint filter_idx;
+
+ filter_idx = g_slist_index (impl->filters, filter);
+ if (impl->filters && filter && filter_idx < 0)
+ return;
+
+ if (impl->current_filter)
+ g_object_unref (impl->current_filter);
+
+ impl->current_filter = filter;
+
+ if (impl->current_filter)
+ {
+ g_object_ref_sink (impl->current_filter);
+ }
+
+ if (impl->filters)
+ gtk_combo_box_set_active (GTK_COMBO_BOX (impl->filter_combo),
+ filter_idx);
+
+ if (impl->recent_store && impl->recent_store_filter)
+ {
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->recent_store_filter));
+ }
+
+ g_object_notify (G_OBJECT (impl), "filter");
+ }
+}
+
+static GtkIconTheme *
+get_icon_theme_for_widget (GtkWidget *widget)
+{
+ if (gtk_widget_has_screen (widget))
+ return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
+
+ return gtk_icon_theme_get_default ();
+}
+
+static gint
+get_icon_size_for_widget (GtkWidget *widget,
+ GtkIconSize icon_size)
+{
+ GtkSettings *settings;
+ gint width, height;
+
+ if (gtk_widget_has_screen (widget))
+ settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
+ else
+ settings = gtk_settings_get_default ();
+
+ if (gtk_icon_size_lookup_for_settings (settings, icon_size,
+ &width, &height))
+ return MAX (width, height);
+
+ return FALLBACK_ICON_SIZE;
+}
+
+
+static void
+recent_manager_changed_cb (GtkRecentManager *manager,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ reload_recent_items (impl);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ _gtk_recent_chooser_selection_changed (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+row_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *tree_path,
+ GtkTreeViewColumn *tree_column,
+ gpointer user_data)
+{
+ _gtk_recent_chooser_item_activated (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+filter_combo_changed_cb (GtkComboBox *combo_box,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl;
+ gint new_index;
+ GtkRecentFilter *filter;
+
+ impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ new_index = gtk_combo_box_get_active (combo_box);
+ filter = g_slist_nth_data (impl->filters, new_index);
+
+ set_current_filter (impl, filter);
+}
+
+static GdkPixbuf *
+get_drag_pixbuf (GtkRecentChooserDefault *impl)
+{
+ GtkRecentInfo *info;
+ GdkPixbuf *retval;
+ gint size;
+
+ g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+
+ info = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (impl));
+ if (!info)
+ return NULL;
+
+ size = get_icon_size_for_widget (GTK_WIDGET (impl), GTK_ICON_SIZE_DND);
+
+ retval = gtk_recent_info_get_icon (info, size);
+ gtk_recent_info_unref (info);
+
+ return retval;
+}
+
+static void
+recent_view_drag_begin_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+ GdkPixbuf *pixbuf;
+
+ pixbuf = get_drag_pixbuf (impl);
+ if (pixbuf)
+ {
+ gtk_drag_set_icon_pixbuf (context, pixbuf, 0, 0);
+ g_object_unref (pixbuf);
+ }
+ else
+ gtk_drag_set_icon_default (context);
+}
+
+typedef struct
+{
+ gchar **uri_list;
+ gsize next_pos;
+} DragData;
+
+static void
+append_uri_to_urilist (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ DragData *drag_data = (DragData *) user_data;
+ GtkTreeModel *child_model;
+ GtkTreeIter child_iter;
+ gchar *uri = NULL;
+ gsize pos;
+
+ child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
+ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model),
+ &child_iter,
+ iter);
+ gtk_tree_model_get (child_model, &child_iter,
+ RECENT_URI_COLUMN, &uri,
+ -1);
+ g_assert (uri != NULL);
+
+ pos = drag_data->next_pos;
+ drag_data->uri_list[pos] = g_strdup (uri);
+ drag_data->next_pos = pos + 1;
+}
+
+static void
+recent_view_drag_data_get_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time_,
+ gpointer data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (data);
+ DragData *drag_data;
+ gsize n_uris;
+
+ n_uris = gtk_tree_selection_count_selected_rows (impl->selection);
+ if (n_uris == 0)
+ return;
+
+ drag_data = g_new (DragData, 1);
+ drag_data->uri_list = g_new0 (gchar *, n_uris + 1);
+ drag_data->next_pos = 0;
+
+ gtk_tree_selection_selected_foreach (impl->selection,
+ append_uri_to_urilist,
+ drag_data);
+
+ gtk_selection_data_set_uris (selection_data, drag_data->uri_list);
+
+ g_strfreev (drag_data->uri_list);
+ g_free (drag_data);
+}
+
+
+
+static void
+remove_selected_from_list (GtkRecentChooserDefault *impl)
+{
+ gchar *uri;
+ GError *err;
+
+ if (impl->select_multiple)
+ return;
+
+ uri = gtk_recent_chooser_get_current_uri (GTK_RECENT_CHOOSER (impl));
+ if (!uri)
+ return;
+
+ err = NULL;
+ if (!gtk_recent_manager_remove_item (impl->manager, uri, &err))
+ {
+ gchar *msg;
+
+ msg = strdup (_("Could not remove item"));
+ error_message (impl, msg, err->message);
+
+ g_free (msg);
+ g_error_free (err);
+ }
+
+ g_free (uri);
+}
+
+static void
+copy_activated_cb (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+ GtkRecentInfo *info;
+ gchar *utf8_uri;
+
+ info = gtk_recent_chooser_get_current_item (GTK_RECENT_CHOOSER (impl));
+ if (!info)
+ return;
+
+ utf8_uri = gtk_recent_info_get_uri_display (info);
+
+ gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (impl),
+ GDK_SELECTION_CLIPBOARD),
+ utf8_uri, -1);
+
+ g_free (utf8_uri);
+}
+
+static void
+remove_all_activated_cb (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+ GError *err = NULL;
+
+ gtk_recent_manager_purge_items (impl->manager, &err);
+ if (err)
+ {
+ gchar *msg;
+
+ msg = g_strdup (_("Could not clear list"));
+
+ error_message (impl, msg, err->message);
+
+ g_free (msg);
+ g_error_free (err);
+ }
+}
+
+static void
+remove_item_activated_cb (GtkMenuItem *menu_item,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ remove_selected_from_list (impl);
+}
+
+static void
+show_private_toggled_cb (GtkCheckMenuItem *menu_item,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ g_object_set (G_OBJECT (impl),
+ "show-private", gtk_check_menu_item_get_active (menu_item),
+ NULL);
+}
+
+static void
+recent_popup_menu_detach_cb (GtkWidget *attach_widget,
+ GtkMenu *menu)
+{
+ GtkRecentChooserDefault *impl;
+
+ impl = g_object_get_data (G_OBJECT (attach_widget), "GtkRecentChooserDefault");
+ g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+
+ impl->recent_popup_menu = NULL;
+ impl->recent_popup_menu_remove_item = NULL;
+ impl->recent_popup_menu_copy_item = NULL;
+ impl->recent_popup_menu_clear_item = NULL;
+ impl->recent_popup_menu_show_private_item = NULL;
+}
+
+static void
+recent_view_menu_ensure_state (GtkRecentChooserDefault *impl)
+{
+ gint count;
+
+ g_assert (GTK_IS_RECENT_CHOOSER_DEFAULT (impl));
+ g_assert (impl->recent_popup_menu != NULL);
+
+ if (!impl->manager)
+ count = 0;
+ else
+ g_object_get (G_OBJECT (impl->manager), "size", &count, NULL);
+
+ if (count == 0)
+ {
+ gtk_widget_set_sensitive (impl->recent_popup_menu_remove_item, FALSE);
+ gtk_widget_set_sensitive (impl->recent_popup_menu_copy_item, FALSE);
+ gtk_widget_set_sensitive (impl->recent_popup_menu_clear_item, FALSE);
+ gtk_widget_set_sensitive (impl->recent_popup_menu_show_private_item, FALSE);
+ }
+}
+
+static void
+recent_view_menu_build (GtkRecentChooserDefault *impl)
+{
+ GtkWidget *item;
+
+ if (impl->recent_popup_menu)
+ {
+ recent_view_menu_ensure_state (impl);
+
+ return;
+ }
+
+ impl->recent_popup_menu = gtk_menu_new ();
+ gtk_menu_attach_to_widget (GTK_MENU (impl->recent_popup_menu),
+ impl->recent_view,
+ recent_popup_menu_detach_cb);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("Copy _Location"));
+ impl->recent_popup_menu_copy_item = item;
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+ gtk_image_new_from_stock (GTK_STOCK_COPY, GTK_ICON_SIZE_MENU));
+ g_signal_connect (item, "activate",
+ G_CALLBACK (copy_activated_cb), impl);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Remove From List"));
+ impl->recent_popup_menu_remove_item = item;
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+ gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU));
+ g_signal_connect (item, "activate",
+ G_CALLBACK (remove_item_activated_cb), impl);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ item = gtk_image_menu_item_new_with_mnemonic (_("_Clear List"));
+ impl->recent_popup_menu_clear_item = item;
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+ gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU));
+ g_signal_connect (item, "activate",
+ G_CALLBACK (remove_all_activated_cb), impl);
+
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ item = gtk_check_menu_item_new_with_mnemonic (_("Show _Private Resources"));
+ impl->recent_popup_menu_show_private_item = item;
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), impl->show_private);
+ g_signal_connect (item, "toggled",
+ G_CALLBACK (show_private_toggled_cb), impl);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (impl->recent_popup_menu), item);
+
+ recent_view_menu_ensure_state (impl);
+}
+
+/* taken from gtkfilechooserdefault.c */
+static void
+popup_position_func (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget = GTK_WIDGET (user_data);
+ GdkScreen *screen = gtk_widget_get_screen (widget);
+ GtkRequisition req;
+ gint monitor_num;
+ GdkRectangle monitor;
+
+ if (G_UNLIKELY (!GTK_WIDGET_REALIZED (widget)))
+ return;
+
+ gdk_window_get_origin (widget->window, x, y);
+
+ gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+ *x += (widget->allocation.width - req.width) / 2;
+ *y += (widget->allocation.height - req.height) / 2;
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+
+ *push_in = FALSE;
+}
+
+
+static void
+recent_view_menu_popup (GtkRecentChooserDefault *impl,
+ GdkEventButton *event)
+{
+ recent_view_menu_build (impl);
+
+ if (event)
+ gtk_menu_popup (GTK_MENU (impl->recent_popup_menu),
+ NULL, NULL, NULL, NULL,
+ event->button, event->time);
+ else
+ {
+ gtk_menu_popup (GTK_MENU (impl->recent_popup_menu),
+ NULL, NULL,
+ popup_position_func, impl->recent_view,
+ 0, GDK_CURRENT_TIME);
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->recent_popup_menu),
+ FALSE);
+ }
+}
+
+static gboolean
+recent_view_popup_menu_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ recent_view_menu_popup (GTK_RECENT_CHOOSER_DEFAULT (user_data), NULL);
+ return TRUE;
+}
+
+static gboolean
+recent_view_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GtkRecentChooserDefault *impl = GTK_RECENT_CHOOSER_DEFAULT (user_data);
+
+ if (event->button == 3)
+ {
+ GtkTreePath *path;
+ gboolean res;
+
+ if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (impl->recent_view)))
+ return FALSE;
+
+ res = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (impl->recent_view),
+ event->x, event->y,
+ &path,
+ NULL, NULL, NULL);
+ if (!res)
+ return FALSE;
+
+ /* select the path before creating the popup menu */
+ gtk_tree_selection_select_path (impl->selection, path);
+ gtk_tree_path_free (path);
+
+ recent_view_menu_popup (impl, event);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+set_recent_manager (GtkRecentChooserDefault *impl,
+ GtkRecentManager *manager)
+{
+ if (impl->manager)
+ {
+ g_signal_handler_disconnect (impl, impl->manager_changed_id);
+ impl->manager_changed_id = 0;
+
+ impl->manager = NULL;
+ }
+
+ if (manager)
+ impl->manager = manager;
+ else
+ impl->manager = gtk_recent_manager_get_default ();
+
+ if (impl->manager)
+ impl->manager_changed_id = g_signal_connect (impl->manager, "changed",
+ G_CALLBACK (recent_manager_changed_cb),
+ impl);
+}
+
+GtkWidget *
+_gtk_recent_chooser_default_new (GtkRecentManager *manager)
+{
+ return g_object_new (GTK_TYPE_RECENT_CHOOSER_DEFAULT,
+ "recent-manager", manager,
+ NULL);
+}
+
+#define __GTK_RECENT_CHOOSER_DEFAULT_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserdefault.h b/gtk/gtkrecentchooserdefault.h
new file mode 100644
index 0000000000..852f4014a1
--- /dev/null
+++ b/gtk/gtkrecentchooserdefault.h
@@ -0,0 +1,42 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdefault.h
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_DEFAULT_H__
+#define __GTK_RECENT_CHOOSER_DEFAULT_H__
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_RECENT_CHOOSER_DEFAULT (gtk_recent_chooser_default_get_type ())
+#define GTK_RECENT_CHOOSER_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_DEFAULT, GtkRecentChooserDefault))
+#define GTK_IS_RECENT_CHOOSER_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_DEFAULT))
+
+
+typedef struct _GtkRecentChooserDefault GtkRecentChooserDefault;
+
+GType _gtk_recent_chooser_default_get_type (void) G_GNUC_CONST;
+GtkWidget *_gtk_recent_chooser_default_new (GtkRecentManager *recent_manager);
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_DEFAULT_H__ */
diff --git a/gtk/gtkrecentchooserdialog.c b/gtk/gtkrecentchooserdialog.c
new file mode 100644
index 0000000000..a047789a9a
--- /dev/null
+++ b/gtk/gtkrecentchooserdialog.c
@@ -0,0 +1,376 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdialog.c: Recent files selector dialog
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gtkrecentchooserdialog.h"
+#include "gtkrecentchooserwidget.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentmanager.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+#include <stdarg.h>
+
+struct _GtkRecentChooserDialogPrivate
+{
+ GtkRecentManager *manager;
+
+ GtkWidget *chooser;
+};
+
+#define GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE(obj) (GTK_RECENT_CHOOSER_DIALOG (obj)->priv)
+
+static void gtk_recent_chooser_dialog_class_init (GtkRecentChooserDialogClass *klass);
+static void gtk_recent_chooser_dialog_init (GtkRecentChooserDialog *dialog);
+static void gtk_recent_chooser_dialog_finalize (GObject *object);
+
+static GObject *gtk_recent_chooser_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params);
+
+static void gtk_recent_chooser_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_chooser_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_recent_chooser_dialog_map (GtkWidget *widget);
+static void gtk_recent_chooser_dialog_unmap (GtkWidget *widget);
+static void gtk_recent_chooser_dialog_style_set (GtkWidget *widget,
+ GtkStyle *old_style);
+
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserDialog,
+ gtk_recent_chooser_dialog,
+ GTK_TYPE_DIALOG,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+ _gtk_recent_chooser_delegate_iface_init));
+
+static void
+gtk_recent_chooser_dialog_class_init (GtkRecentChooserDialogClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->set_property = gtk_recent_chooser_dialog_set_property;
+ gobject_class->get_property = gtk_recent_chooser_dialog_get_property;
+ gobject_class->constructor = gtk_recent_chooser_dialog_constructor;
+ gobject_class->finalize = gtk_recent_chooser_dialog_finalize;
+
+ widget_class->map = gtk_recent_chooser_dialog_map;
+ widget_class->unmap = gtk_recent_chooser_dialog_unmap;
+ widget_class->style_set = gtk_recent_chooser_dialog_style_set;
+
+ _gtk_recent_chooser_install_properties (gobject_class);
+
+ g_type_class_add_private (klass, sizeof (GtkRecentChooserDialogPrivate));
+}
+
+static void
+gtk_recent_chooser_dialog_init (GtkRecentChooserDialog *dialog)
+{
+ GtkRecentChooserDialogPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog,
+ GTK_TYPE_RECENT_CHOOSER_DIALOG,
+ GtkRecentChooserDialogPrivate);
+
+ dialog->priv = priv;
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+}
+
+/* we intercept the GtkRecentChooser::item_activated signal and try to
+ * make the dialog emit a valid response signal
+ */
+static void
+gtk_recent_chooser_item_activated_cb (GtkRecentChooser *chooser,
+ gpointer user_data)
+{
+ GtkRecentChooserDialog *dialog;
+ GList *children, *l;
+
+ dialog = GTK_RECENT_CHOOSER_DIALOG (user_data);
+
+ if (gtk_window_activate_default (GTK_WINDOW (dialog)))
+ return;
+
+ children = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
+
+ for (l = children; l; l = l->next)
+ {
+ GtkWidget *widget;
+ gint response_id;
+
+ widget = GTK_WIDGET (l->data);
+ response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget);
+
+ if (response_id == GTK_RESPONSE_ACCEPT ||
+ response_id == GTK_RESPONSE_OK ||
+ response_id == GTK_RESPONSE_YES ||
+ response_id == GTK_RESPONSE_APPLY)
+ {
+ g_list_free (children);
+
+ gtk_dialog_response (GTK_DIALOG (dialog), response_id);
+
+ return;
+ }
+ }
+
+ g_list_free (children);
+}
+
+static GObject *
+gtk_recent_chooser_dialog_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GObject *object;
+ GtkRecentChooserDialogPrivate *priv;
+
+ object = G_OBJECT_CLASS (gtk_recent_chooser_dialog_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ gtk_widget_push_composite_child ();
+
+ if (priv->manager)
+ priv->chooser = g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET,
+ "recent-manager", priv->manager,
+ NULL);
+ else
+ priv->chooser = g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET, NULL);
+
+ g_signal_connect (priv->chooser, "item-activated",
+ G_CALLBACK (gtk_recent_chooser_item_activated_cb),
+ object);
+
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (object)->vbox),
+ priv->chooser, TRUE, TRUE, 0);
+ gtk_widget_show (priv->chooser);
+
+ _gtk_recent_chooser_set_delegate (GTK_RECENT_CHOOSER (object),
+ GTK_RECENT_CHOOSER (priv->chooser));
+
+ gtk_widget_pop_composite_child ();
+
+ return object;
+}
+
+static void
+gtk_recent_chooser_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserDialogPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ switch (prop_id)
+ {
+ case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+ priv->manager = g_value_get_object (value);
+ break;
+ default:
+ g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value);
+ break;
+ }
+}
+
+static void
+gtk_recent_chooser_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserDialogPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_DIALOG_GET_PRIVATE (object);
+
+ g_object_get_property (G_OBJECT (priv->chooser), pspec->name, value);
+}
+
+static void
+gtk_recent_chooser_dialog_finalize (GObject *object)
+{
+ GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (object);
+
+ dialog->priv->manager = NULL;
+
+ G_OBJECT_CLASS (gtk_recent_chooser_dialog_parent_class)->finalize (object);
+}
+
+static void
+gtk_recent_chooser_dialog_map (GtkWidget *widget)
+{
+ GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+ GtkRecentChooserDialogPrivate *priv = dialog->priv;
+
+ if (!GTK_WIDGET_MAPPED (priv->chooser))
+ gtk_widget_map (priv->chooser);
+
+ GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->map (widget);
+}
+
+static void
+gtk_recent_chooser_dialog_unmap (GtkWidget *widget)
+{
+ GtkRecentChooserDialog *dialog = GTK_RECENT_CHOOSER_DIALOG (widget);
+ GtkRecentChooserDialogPrivate *priv = dialog->priv;
+
+ GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->unmap (widget);
+
+ gtk_widget_unmap (priv->chooser);
+}
+
+/* taken from gtkfilechooserdialog.c */
+static void
+gtk_recent_chooser_dialog_style_set (GtkWidget *widget,
+ GtkStyle *old_style)
+{
+ GtkDialog *dialog;
+
+ dialog = GTK_DIALOG (widget);
+
+ /* Override the style properties with HIG-compliant spacings. Ugh.
+ * http://developer.gnome.org/projects/gup/hig/1.0/layout.html#layout-dialogs
+ * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-spacing
+ */
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox), 12);
+ gtk_box_set_spacing (GTK_BOX (dialog->vbox), 24);
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog->action_area), 0);
+ gtk_box_set_spacing (GTK_BOX (dialog->action_area), 6);
+
+ if (GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->style_set)
+ GTK_WIDGET_CLASS (gtk_recent_chooser_dialog_parent_class)->style_set (widget, old_style);
+}
+
+static GtkWidget *
+gtk_recent_chooser_dialog_new_valist (const gchar *title,
+ GtkWindow *parent,
+ GtkRecentManager *manager,
+ const gchar *first_button_text,
+ va_list varargs)
+{
+ GtkWidget *result;
+ const char *button_text = first_button_text;
+ gint response_id;
+
+ result = g_object_new (GTK_TYPE_RECENT_CHOOSER_DIALOG,
+ "title", title,
+ "recent-manager", manager,
+ NULL);
+
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (result), parent);
+
+ while (button_text)
+ {
+ response_id = va_arg (varargs, gint);
+ gtk_dialog_add_button (GTK_DIALOG (result), button_text, response_id);
+ button_text = va_arg (varargs, const gchar *);
+ }
+
+ return result;
+}
+
+/**
+ * gtk_recent_chooser_dialog_new:
+ * @title: Title of the dialog, or %NULL
+ * @parent: Transient parent of the dialog, or %NULL,
+ * @first_button_text: stock ID or text to go in the first button, or %NULL
+ * @Varargs: response ID for the first button, then additional (button, id)
+ * pairs, ending with %NULL
+ *
+ * Creates a new #GtkRecentChooserDialog. This function is analogous to
+ * gtk_dialog_new_with_buttons().
+ *
+ * Return value: a new #GtkRecentChooserDialog
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *result;
+ va_list varargs;
+
+ va_start (varargs, first_button_text);
+ result = gtk_recent_chooser_dialog_new_valist (title,
+ parent,
+ NULL,
+ first_button_text,
+ varargs);
+ va_end (varargs);
+
+ return result;
+}
+
+/**
+ * gtk_recent_chooser_dialog_new_for_manager:
+ * @title: Title of the dialog, or %NULL
+ * @parent: Transient parent of the dialog, or %NULL,
+ * @manager: a #GtkRecentManager
+ * @first_button_text: stock ID or text to go in the first button, or %NULL
+ * @Varargs: response ID for the first button, then additional (button, id)
+ * pairs, ending with %NULL
+ *
+ * Creates a new #GtkRecentChooserDialog with a specified recent manager.
+ *
+ * This is useful if you have implemented your own recent manager, or if you
+ * have a customized instance of a #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserDialog
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_dialog_new_for_manager (const gchar *title,
+ GtkWindow *parent,
+ GtkRecentManager *manager,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *result;
+ va_list varargs;
+
+ va_start (varargs, first_button_text);
+ result = gtk_recent_chooser_dialog_new_valist (title,
+ parent,
+ manager,
+ first_button_text,
+ varargs);
+ va_end (varargs);
+
+ return result;
+}
+
+#define __GTK_RECENT_CHOOSER_DIALOG_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserdialog.h b/gtk/gtkrecentchooserdialog.h
new file mode 100644
index 0000000000..22be218d0b
--- /dev/null
+++ b/gtk/gtkrecentchooserdialog.h
@@ -0,0 +1,70 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserdialog.h: Recent files selector dialog
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_DIALOG_H__
+#define __GTK_RECENT_CHOOSER_DIALOG_H__
+
+#include <gtk/gtkdialog.h>
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_DIALOG (gtk_recent_chooser_dialog_get_type ())
+#define GTK_RECENT_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialog))
+#define GTK_IS_RECENT_CHOOSER_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG))
+#define GTK_RECENT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialogClass))
+#define GTK_IS_RECENT_CHOOSER_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_DIALOG))
+#define GTK_RECENT_CHOOSER_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_DIALOG, GtkRecentChooserDialogClass))
+
+typedef struct _GtkRecentChooserDialog GtkRecentChooserDialog;
+typedef struct _GtkRecentChooserDialogClass GtkRecentChooserDialogClass;
+
+typedef struct _GtkRecentChooserDialogPrivate GtkRecentChooserDialogPrivate;
+
+
+struct _GtkRecentChooserDialog
+{
+ /*< private >*/
+ GtkDialog parent_instance;
+
+ GtkRecentChooserDialogPrivate *priv;
+};
+
+struct _GtkRecentChooserDialogClass
+{
+ GtkDialogClass parent_class;
+};
+
+
+GType gtk_recent_chooser_dialog_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gtk_recent_chooser_dialog_new (const gchar *title,
+ GtkWindow *parent,
+ const gchar *first_button_text,
+ ...) G_GNUC_NULL_TERMINATED;
+GtkWidget *gtk_recent_chooser_dialog_new_for_manager (const gchar *title,
+ GtkWindow *parent,
+ GtkRecentManager *manager,
+ const gchar *first_button_text,
+ ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_DIALOG_H__ */
diff --git a/gtk/gtkrecentchoosermenu.c b/gtk/gtkrecentchoosermenu.c
new file mode 100644
index 0000000000..90542d91b5
--- /dev/null
+++ b/gtk/gtkrecentchoosermenu.c
@@ -0,0 +1,1225 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchoosermenu.c - Recently used items menu widget
+ * Copyright (C) 2005, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <gdk/gdkscreen.h>
+
+#include "gtkstock.h"
+#include "gtkicontheme.h"
+#include "gtkiconfactory.h"
+#include "gtkintl.h"
+#include "gtksettings.h"
+#include "gtkmenushell.h"
+#include "gtkmenuitem.h"
+#include "gtkimagemenuitem.h"
+#include "gtkseparatormenuitem.h"
+#include "gtkmenu.h"
+#include "gtkimage.h"
+#include "gtkobject.h"
+#include "gtktooltips.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentfilter.h"
+#include "gtkrecentchooser.h"
+#include "gtkrecentchooserutils.h"
+#include "gtkrecentchooserprivate.h"
+#include "gtkrecentchoosermenu.h"
+
+struct _GtkRecentChooserMenuPrivate
+{
+ /* the recent manager object */
+ GtkRecentManager *manager;
+
+ /* size of the icons of the menu items */
+ gint icon_size;
+
+ /* RecentChooser properties */
+ gint limit;
+ guint show_private : 1;
+ guint show_not_found : 1;
+ guint show_tips : 1;
+ guint show_icons : 1;
+ guint local_only : 1;
+
+ guint show_numbers : 1;
+
+ GtkRecentSortType sort_type;
+ GtkRecentSortFunc sort_func;
+ gpointer sort_data;
+ GDestroyNotify sort_data_destroy;
+
+ GSList *filters;
+ GtkRecentFilter *current_filter;
+
+ guint local_manager : 1;
+ gulong manager_changed_id;
+
+ /* tooltips for our bookmark items*/
+ GtkTooltips *tooltips;
+};
+
+enum {
+ PROP_0,
+
+ PROP_SHOW_NUMBERS
+};
+
+#define FALLBACK_ICON_SIZE 32
+#define FALLBACK_ITEM_LIMIT 10
+
+#define GTK_RECENT_CHOOSER_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuPrivate))
+
+static void gtk_recent_chooser_menu_finalize (GObject *object);
+static GObject *gtk_recent_chooser_menu_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params);
+
+static void gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface);
+
+static void gtk_recent_chooser_menu_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_chooser_menu_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static gboolean gtk_recent_chooser_menu_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static gchar * gtk_recent_chooser_menu_get_current_uri (GtkRecentChooser *chooser);
+static gboolean gtk_recent_chooser_menu_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static void gtk_recent_chooser_menu_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri);
+static void gtk_recent_chooser_menu_select_all (GtkRecentChooser *chooser);
+static void gtk_recent_chooser_menu_unselect_all (GtkRecentChooser *chooser);
+static GList * gtk_recent_chooser_menu_get_items (GtkRecentChooser *chooser);
+static GtkRecentManager *gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser *chooser);
+static void gtk_recent_chooser_menu_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy);
+static void gtk_recent_chooser_menu_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static void gtk_recent_chooser_menu_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static GSList * gtk_recent_chooser_menu_list_filters (GtkRecentChooser *chooser);
+static void gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
+ GtkRecentFilter *filter);
+
+static void gtk_recent_chooser_menu_map (GtkWidget *widget);
+static void gtk_recent_chooser_menu_set_show_tips (GtkRecentChooserMenu *menu,
+ gboolean show_tips);
+
+static void set_recent_manager (GtkRecentChooserMenu *menu,
+ GtkRecentManager *manager);
+
+static void chooser_set_sort_type (GtkRecentChooserMenu *menu,
+ GtkRecentSortType sort_type);
+
+static gint get_icon_size_for_widget (GtkWidget *widget);
+
+static void item_activate_cb (GtkWidget *widget,
+ gpointer user_data);
+static void manager_changed_cb (GtkRecentManager *manager,
+ gpointer user_data);
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserMenu,
+ gtk_recent_chooser_menu,
+ GTK_TYPE_MENU,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+ gtk_recent_chooser_iface_init));
+
+
+static void
+gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
+{
+ iface->set_current_uri = gtk_recent_chooser_menu_set_current_uri;
+ iface->get_current_uri = gtk_recent_chooser_menu_get_current_uri;
+ iface->select_uri = gtk_recent_chooser_menu_select_uri;
+ iface->unselect_uri = gtk_recent_chooser_menu_unselect_uri;
+ iface->select_all = gtk_recent_chooser_menu_select_all;
+ iface->unselect_all = gtk_recent_chooser_menu_unselect_all;
+ iface->get_items = gtk_recent_chooser_menu_get_items;
+ iface->get_recent_manager = gtk_recent_chooser_menu_get_recent_manager;
+ iface->set_sort_func = gtk_recent_chooser_menu_set_sort_func;
+ iface->add_filter = gtk_recent_chooser_menu_add_filter;
+ iface->remove_filter = gtk_recent_chooser_menu_remove_filter;
+ iface->list_filters = gtk_recent_chooser_menu_list_filters;
+}
+
+static void
+gtk_recent_chooser_menu_class_init (GtkRecentChooserMenuClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->constructor = gtk_recent_chooser_menu_constructor;
+ gobject_class->finalize = gtk_recent_chooser_menu_finalize;
+ gobject_class->set_property = gtk_recent_chooser_menu_set_property;
+ gobject_class->get_property = gtk_recent_chooser_menu_get_property;
+
+ widget_class->map = gtk_recent_chooser_menu_map;
+
+ _gtk_recent_chooser_install_properties (gobject_class);
+
+ /**
+ * GtkRecentChooserMenu:show-numbers
+ *
+ * Whether the first ten items in the menu should be prepended by
+ * a number acting as a unique mnemonic.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_SHOW_NUMBERS,
+ g_param_spec_boolean ("show-numbers",
+ P_("Show Numbers"),
+ P_("Whether the items should be displayed with a number"),
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (GtkRecentChooserMenuPrivate));
+}
+
+static void
+gtk_recent_chooser_menu_init (GtkRecentChooserMenu *menu)
+{
+ GtkRecentChooserMenuPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_MENU_GET_PRIVATE (menu);
+
+ menu->priv = priv;
+
+ priv->show_icons= TRUE;
+ priv->show_numbers = FALSE;
+ priv->show_tips = FALSE;
+ priv->show_not_found = FALSE;
+ priv->show_private = FALSE;
+ priv->local_only = TRUE;
+
+ priv->limit = FALLBACK_ITEM_LIMIT;
+
+ priv->sort_type = GTK_RECENT_SORT_NONE;
+
+ priv->icon_size = FALLBACK_ICON_SIZE;
+
+ priv->current_filter = NULL;
+
+ priv->tooltips = gtk_tooltips_new ();
+ g_object_ref_sink (priv->tooltips);
+}
+
+static void
+gtk_recent_chooser_menu_finalize (GObject *object)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+ GtkRecentChooserMenuPrivate *priv = menu->priv;
+
+ g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+ priv->manager_changed_id = 0;
+
+ priv->manager = NULL;
+
+ if (priv->sort_data_destroy)
+ {
+ priv->sort_data_destroy (priv->sort_data);
+
+ priv->sort_data_destroy = NULL;
+ priv->sort_data = NULL;
+ priv->sort_func = NULL;
+ }
+
+ if (priv->tooltips)
+ g_object_unref (priv->tooltips);
+
+ if (priv->current_filter)
+ g_object_unref (priv->current_filter);
+
+ G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->finalize;
+}
+
+static GObject *
+gtk_recent_chooser_menu_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GtkRecentChooserMenu *menu;
+ GObject *object;
+
+ object = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ menu = GTK_RECENT_CHOOSER_MENU (object);
+
+ g_assert (menu->priv->manager);
+
+ return object;
+}
+
+static void
+gtk_recent_chooser_menu_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+
+ switch (prop_id)
+ {
+ case PROP_SHOW_NUMBERS:
+ menu->priv->show_numbers = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+ set_recent_manager (menu, g_value_get_object (value));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+ menu->priv->show_private = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+ menu->priv->show_not_found = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+ gtk_recent_chooser_menu_set_show_tips (menu, g_value_get_boolean (value));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+ menu->priv->show_icons = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+ g_warning ("%s: RecentChoosers of type `%s' do not support "
+ "selecting multiple items.",
+ G_STRFUNC,
+ G_OBJECT_TYPE_NAME (object));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+ menu->priv->local_only = g_value_get_boolean (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LIMIT:
+ menu->priv->limit = g_value_get_int (value);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+ chooser_set_sort_type (menu, g_value_get_enum (value));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_FILTER:
+ gtk_recent_chooser_menu_set_current_filter (menu, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_recent_chooser_menu_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (object);
+
+ switch (prop_id)
+ {
+ case PROP_SHOW_NUMBERS:
+ g_value_set_boolean (value, menu->priv->show_numbers);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
+ g_value_set_boolean (value, menu->priv->show_tips);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LIMIT:
+ g_value_set_int (value, menu->priv->limit);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
+ g_value_set_boolean (value, menu->priv->local_only);
+ case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
+ g_value_set_enum (value, menu->priv->sort_type);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
+ g_value_set_boolean (value, menu->priv->show_private);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
+ g_value_set_boolean (value, menu->priv->show_not_found);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
+ g_value_set_boolean (value, menu->priv->show_icons);
+ break;
+ case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
+ g_warning ("%s: Recent Choosers of type `%s' do not support "
+ "selecting multiple items.",
+ G_STRFUNC,
+ G_OBJECT_TYPE_NAME (object));
+ break;
+ case GTK_RECENT_CHOOSER_PROP_FILTER:
+ g_value_set_object (value, menu->priv->current_filter);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+gtk_recent_chooser_menu_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+ GList *children, *l;
+ GtkWidget *menu_item = NULL;
+ gboolean found = FALSE;
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkRecentInfo *info;
+
+ menu_item = GTK_WIDGET (l->data);
+
+ info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+ if (!info)
+ continue;
+
+ if (0 == strcmp (uri, gtk_recent_info_get_uri (info)))
+ found = TRUE;
+ }
+
+ g_list_free (children);
+
+ if (!found)
+ {
+ g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+ GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+ _("No recently used resource found with URI `%s'"),
+ uri);
+ return FALSE;
+ }
+ else
+ {
+ gtk_menu_shell_activate_item (GTK_MENU_SHELL (menu), menu_item, TRUE);
+
+ return TRUE;
+ }
+}
+
+static gchar *
+gtk_recent_chooser_menu_get_current_uri (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+ GtkWidget *menu_item;
+ GtkRecentInfo *info;
+
+ menu_item = gtk_menu_get_active (GTK_MENU (menu));
+ if (!menu_item)
+ return NULL;
+
+ info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+ if (!info)
+ return NULL;
+
+ return g_strdup (gtk_recent_info_get_uri (info));
+}
+
+static gboolean
+gtk_recent_chooser_menu_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+ GList *children, *l;
+ GtkWidget *menu_item = NULL;
+ gboolean found = FALSE;
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkRecentInfo *info;
+
+ menu_item = GTK_WIDGET (l->data);
+
+ info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+ if (!info)
+ continue;
+
+ if (0 == strcmp (uri, gtk_recent_info_get_uri (info)))
+ found = TRUE;
+ }
+
+ g_list_free (children);
+
+ if (!found)
+ {
+ g_set_error (error, GTK_RECENT_CHOOSER_ERROR,
+ GTK_RECENT_CHOOSER_ERROR_NOT_FOUND,
+ _("No recently used resource found with URI `%s'"),
+ uri);
+ return FALSE;
+ }
+ else
+ {
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), menu_item);
+
+ return TRUE;
+ }
+}
+
+static void
+gtk_recent_chooser_menu_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+
+ gtk_menu_shell_deselect (GTK_MENU_SHELL (menu));
+}
+
+static void
+gtk_recent_chooser_menu_select_all (GtkRecentChooser *chooser)
+{
+ g_warning (_("This function is not implemented for "
+ "widgets of class '%s'"),
+ g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_unselect_all (GtkRecentChooser *chooser)
+{
+ g_warning (_("This function is not implemented for "
+ "widgets of class '%s'."),
+ g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+ GtkRecentChooserMenuPrivate *priv = menu->priv;
+
+ if (priv->sort_data_destroy)
+ {
+ priv->sort_data_destroy (priv->sort_data);
+
+ priv->sort_func = NULL;
+ priv->sort_data = NULL;
+ priv->sort_data_destroy = NULL;
+ }
+
+ if (sort_func)
+ {
+ priv->sort_func = sort_func;
+ priv->sort_data = sort_data;
+ priv->sort_data_destroy = data_destroy;
+ }
+}
+
+static gint
+sort_recent_items_mru (GtkRecentInfo *a,
+ GtkRecentInfo *b,
+ gpointer unused)
+{
+ g_assert (a != NULL && b != NULL);
+
+ return (gtk_recent_info_get_modified (a) < gtk_recent_info_get_modified (b));
+}
+
+static gint
+sort_recent_items_lru (GtkRecentInfo *a,
+ GtkRecentInfo *b,
+ gpointer unused)
+{
+ g_assert (a != NULL && b != NULL);
+
+ return (gtk_recent_info_get_modified (a) > gtk_recent_info_get_modified (b));
+}
+
+/* our proxy sorting function */
+static gint
+sort_recent_items_proxy (gpointer *a,
+ gpointer *b,
+ gpointer user_data)
+{
+ GtkRecentInfo *info_a = (GtkRecentInfo *) a;
+ GtkRecentInfo *info_b = (GtkRecentInfo *) b;
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (user_data);
+
+ if (menu->priv->sort_func)
+ return (* menu->priv->sort_func) (info_a,
+ info_b,
+ menu->priv->sort_data);
+
+ /* fallback */
+ return 0;
+}
+
+static void
+chooser_set_sort_type (GtkRecentChooserMenu *menu,
+ GtkRecentSortType sort_type)
+{
+ if (menu->priv->sort_type == sort_type)
+ return;
+
+ menu->priv->sort_type = sort_type;
+}
+
+
+static GList *
+gtk_recent_chooser_menu_get_items (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (chooser);
+ GtkRecentChooserMenuPrivate *priv;
+ gint limit;
+ GtkRecentSortType sort_type;
+ GList *items;
+ GCompareDataFunc compare_func;
+ gint length;
+
+ priv = menu->priv;
+
+ if (!priv->manager)
+ return NULL;
+
+ limit = gtk_recent_chooser_get_limit (chooser);
+ sort_type = gtk_recent_chooser_get_sort_type (chooser);
+
+ switch (sort_type)
+ {
+ case GTK_RECENT_SORT_NONE:
+ compare_func = NULL;
+ break;
+ case GTK_RECENT_SORT_MRU:
+ compare_func = (GCompareDataFunc) sort_recent_items_mru;
+ break;
+ case GTK_RECENT_SORT_LRU:
+ compare_func = (GCompareDataFunc) sort_recent_items_lru;
+ break;
+ case GTK_RECENT_SORT_CUSTOM:
+ compare_func = (GCompareDataFunc) sort_recent_items_proxy;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ items = gtk_recent_manager_get_items (priv->manager);
+ if (!items)
+ return NULL;
+
+ if (compare_func)
+ items = g_list_sort_with_data (items, compare_func, menu);
+
+ length = g_list_length (items);
+ if ((limit != -1) && (length > limit))
+ {
+ GList *clamp, *l;
+
+ clamp = g_list_nth (items, limit - 1);
+
+ l = clamp->next;
+ clamp->next = NULL;
+
+ g_list_foreach (l, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (l);
+ }
+
+ return items;
+}
+
+static GtkRecentManager *
+gtk_recent_chooser_menu_get_recent_manager (GtkRecentChooser *chooser)
+{
+ GtkRecentChooserMenuPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_MENU (chooser)->priv;
+
+ return priv->manager;
+}
+
+static void
+gtk_recent_chooser_menu_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ g_warning (_("This function is not implemented for "
+ "widgets of class '%s'"),
+ g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static void
+gtk_recent_chooser_menu_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ g_warning (_("This function is not implemented for "
+ "widgets of class '%s'"),
+ g_type_name (G_OBJECT_TYPE (chooser)));
+}
+
+static GSList *
+gtk_recent_chooser_menu_list_filters (GtkRecentChooser *chooser)
+{
+ g_warning (_("This function is not implemented for "
+ "widgets of class '%s'"),
+ g_type_name (G_OBJECT_TYPE (chooser)));
+
+ return NULL;
+}
+
+static void
+gtk_recent_chooser_menu_set_current_filter (GtkRecentChooserMenu *menu,
+ GtkRecentFilter *filter)
+{
+ GtkRecentChooserMenuPrivate *priv;
+
+ priv = menu->priv;
+
+ if (priv->current_filter)
+ g_object_unref (G_OBJECT (priv->current_filter));
+
+ priv->current_filter = filter;
+ g_object_ref_sink (priv->current_filter);
+
+ g_object_notify (G_OBJECT (menu), "filter");
+}
+
+static gboolean
+get_is_recent_filtered (GtkRecentChooserMenu *menu,
+ GtkRecentInfo *info)
+{
+ GtkRecentChooserMenuPrivate *priv;
+ GtkRecentFilter *current_filter;
+ GtkRecentFilterInfo filter_info;
+ GtkRecentFilterFlags needed;
+ gboolean retval;
+
+ g_assert (info != NULL);
+
+ priv = menu->priv;
+
+ if (!priv->current_filter)
+ return FALSE;
+
+ current_filter = priv->current_filter;
+ needed = gtk_recent_filter_get_needed (current_filter);
+
+ filter_info.contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE;
+
+ filter_info.uri = gtk_recent_info_get_uri (info);
+ filter_info.mime_type = gtk_recent_info_get_mime_type (info);
+
+ if (needed & GTK_RECENT_FILTER_DISPLAY_NAME)
+ {
+ filter_info.display_name = gtk_recent_info_get_display_name (info);
+ filter_info.contains |= GTK_RECENT_FILTER_DISPLAY_NAME;
+ }
+ else
+ filter_info.uri = NULL;
+
+ if (needed & GTK_RECENT_FILTER_APPLICATION)
+ {
+ filter_info.applications = (const gchar **) gtk_recent_info_get_applications (info, NULL);
+ filter_info.contains |= GTK_RECENT_FILTER_APPLICATION;
+ }
+ else
+ filter_info.applications = NULL;
+
+ if (needed & GTK_RECENT_FILTER_GROUP)
+ {
+ filter_info.groups = (const gchar **) gtk_recent_info_get_groups (info, NULL);
+ filter_info.contains |= GTK_RECENT_FILTER_GROUP;
+ }
+ else
+ filter_info.groups = NULL;
+
+ if (needed & GTK_RECENT_FILTER_AGE)
+ {
+ filter_info.age = gtk_recent_info_get_age (info);
+ filter_info.contains |= GTK_RECENT_FILTER_AGE;
+ }
+ else
+ filter_info.age = -1;
+
+ retval = gtk_recent_filter_filter (current_filter, &filter_info);
+
+ /* this we own */
+ if (filter_info.applications)
+ g_strfreev ((gchar **) filter_info.applications);
+
+ return !retval;
+}
+
+/* taken from libeel/eel-strings.c */
+static gchar *
+escape_underscores (const gchar *string)
+{
+ gint underscores;
+ const gchar *p;
+ gchar *q;
+ gchar *escaped;
+
+ if (!string)
+ return NULL;
+
+ underscores = 0;
+ for (p = string; *p != '\0'; p++)
+ underscores += (*p == '_');
+
+ if (underscores == 0)
+ return g_strdup (string);
+
+ escaped = g_new (char, strlen (string) + underscores + 1);
+ for (p = string, q = escaped; *p != '\0'; p++, q++)
+ {
+ /* Add an extra underscore. */
+ if (*p == '_')
+ *q++ = '_';
+
+ *q = *p;
+ }
+
+ *q = '\0';
+
+ return escaped;
+}
+
+static void
+gtk_recent_chooser_menu_add_tip (GtkRecentChooserMenu *menu,
+ GtkRecentInfo *info,
+ GtkWidget *item)
+{
+ GtkRecentChooserMenuPrivate *priv;
+ gchar *path, *tip_text;
+
+ g_assert (info != NULL);
+ g_assert (item != NULL);
+
+ priv = menu->priv;
+
+ if (!priv->tooltips)
+ return;
+
+ path = gtk_recent_info_get_uri_display (info);
+
+ tip_text = g_strdup_printf (_("Open '%s'"), path);
+
+ gtk_tooltips_set_tip (priv->tooltips,
+ item,
+ tip_text,
+ NULL);
+
+ g_free (path);
+ g_free (tip_text);
+}
+
+static GtkWidget *
+gtk_recent_chooser_menu_create_item (GtkRecentChooserMenu *menu,
+ GtkRecentInfo *info,
+ gint count)
+{
+ GtkRecentChooserMenuPrivate *priv;
+ gchar *label;
+ GtkWidget *item, *image;
+ GdkPixbuf *icon;
+
+ g_assert (info != NULL);
+
+ priv = menu->priv;
+
+ if (priv->show_numbers)
+ {
+ gchar *name, *escaped;
+
+ name = g_strdup (gtk_recent_info_get_display_name (info));
+ if (!name)
+ name = g_strdup (_("Unknown item"));
+
+ escaped = escape_underscores (name);
+
+ /* avoid clashing mnemonics */
+ if (count <= 10)
+ label = g_strdup_printf ("_%d. %s", count, escaped);
+ else
+ label = g_strdup_printf ("%d. %s", count, escaped);
+
+ item = gtk_image_menu_item_new_with_mnemonic (label);
+
+ g_free (escaped);
+ g_free (name);
+ }
+ else
+ {
+ label = g_strdup (gtk_recent_info_get_display_name (info));
+ item = gtk_image_menu_item_new_with_label (label);
+ }
+
+ if (priv->show_icons)
+ {
+ icon = gtk_recent_info_get_icon (info, priv->icon_size);
+
+ image = gtk_image_new_from_pixbuf (icon);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ }
+
+ if (!gtk_recent_info_exists (info))
+ {
+ gtk_widget_set_sensitive (item, FALSE);
+
+ goto out;
+ }
+
+ g_signal_connect (item, "activate",
+ G_CALLBACK (item_activate_cb),
+ menu);
+
+out:
+ g_free (label);
+
+ return item;
+}
+
+/* removes the items we own from the menu */
+static void
+gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
+{
+ GList *children, *l;
+
+ children = gtk_container_get_children (GTK_CONTAINER (menu));
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkWidget *menu_item = GTK_WIDGET (l->data);
+ gint mark = 0;
+
+ /* check for our mark, in order to remove just the items we own */
+ mark = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item),
+ "gtk-recent-menu-mark"));
+ if (mark == 1)
+ {
+ GtkRecentInfo *info;
+
+ /* destroy the attached RecentInfo struct, if found */
+ info = g_object_get_data (G_OBJECT (menu_item), "gtk-recent-info");
+ if (info)
+ g_object_set_data_full (G_OBJECT (menu_item), "gtk-recent-info",
+ NULL, NULL);
+
+ /* and finally remove the item from the menu */
+ gtk_container_remove (GTK_CONTAINER (menu), menu_item);
+ }
+ }
+
+ g_list_free (children);
+}
+
+/* GtkWidget::map method override
+ *
+ * We override this method in order to populate the menu with our
+ * menu items linked to the recently used resources.
+ */
+static void
+gtk_recent_chooser_menu_map (GtkWidget *widget)
+{
+ GtkRecentChooserMenu *menu = GTK_RECENT_CHOOSER_MENU (widget);
+ GtkRecentChooserMenuPrivate *priv = menu->priv;
+ GList *items, *l;
+ gint count;
+ gboolean has_items = FALSE;
+
+ if (GTK_WIDGET_CLASS (gtk_recent_chooser_menu_parent_class)->map)
+ GTK_WIDGET_CLASS (gtk_recent_chooser_menu_parent_class)->map (widget);
+
+ priv->icon_size = get_icon_size_for_widget (widget);
+
+ /* dispose our menu items first */
+ gtk_recent_chooser_menu_dispose_items (menu);
+
+ items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (menu));
+
+ count = g_list_length (items);
+ items = g_list_reverse (items);
+
+ for (l = items; l != NULL; l = l->next)
+ {
+ GtkRecentInfo *info = (GtkRecentInfo *) l->data;
+ GtkWidget *item;
+
+ g_assert (info != NULL);
+
+ /* skip non-local items on request */
+ if (priv->local_only && !gtk_recent_info_is_local (info))
+ continue;
+
+ /* skip private items on request */
+ if (!priv->show_private && gtk_recent_info_get_private_hint (info))
+ continue;
+
+ /* skip non-existing items on request */
+ if (!priv->show_not_found && !gtk_recent_info_exists (info))
+ continue;
+
+ /* filter items based on the currently set filter object */
+ if (get_is_recent_filtered (menu, info))
+ continue;
+
+ item = gtk_recent_chooser_menu_create_item (menu, info, count);
+ if (!item)
+ continue;
+
+ gtk_recent_chooser_menu_add_tip (menu, info, item);
+
+ /* FIXME
+ *
+ * We should really place our items taking into account user
+ * defined menu items; this would also remove the need of
+ * reverting the scan order.
+ */
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+
+ /* mark the menu item as one of our own */
+ g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
+ GINT_TO_POINTER (1));
+
+ /* attach the RecentInfo object to the menu item, and own a reference
+ * to it, so that it will be destroyed with the menu item when it's
+ * not needed anymore.
+ */
+ g_object_set_data_full (G_OBJECT (item), "gtk-recent-info",
+ gtk_recent_info_ref (info),
+ (GDestroyNotify) gtk_recent_info_unref);
+
+ /* we have at least one item */
+ if (!has_items)
+ has_items = TRUE;
+ }
+
+ /* now, the RecentInfo objects are bound to the lifetime of the menu */
+ if (items)
+ {
+ g_list_foreach (items,
+ (GFunc) gtk_recent_info_unref,
+ NULL);
+ g_list_free (items);
+ }
+
+ /* no recently used resources were found, or they were filtered out, so
+ * we build an item stating that no recently used resources were found
+ * (as night follows the day...).
+ */
+ if (!has_items)
+ {
+ GtkWidget *item;
+
+ item = gtk_menu_item_new_with_label ("No items found");
+ gtk_widget_set_sensitive (item, FALSE);
+
+ /* we also mark this item, so that it gets removed when rebuilding
+ * the menu on the next map event
+ */
+ g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
+ GINT_TO_POINTER (1));
+
+ gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show (item);
+ }
+}
+
+/* bounce activate signal from the recent menu item widget
+ * to the recent menu widget
+ */
+static void
+item_activate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkRecentChooser *chooser = GTK_RECENT_CHOOSER (user_data);
+
+ _gtk_recent_chooser_item_activated (chooser);
+}
+
+/* we force a redraw if the manager changes when we are showing */
+static void
+manager_changed_cb (GtkRecentManager *manager,
+ gpointer user_data)
+{
+ gtk_widget_queue_draw (GTK_WIDGET (user_data));
+}
+
+static void
+set_recent_manager (GtkRecentChooserMenu *menu,
+ GtkRecentManager *manager)
+{
+ if (menu->priv->manager)
+ g_signal_handler_disconnect (menu, menu->priv->manager_changed_id);
+
+ menu->priv->manager = NULL;
+
+ if (manager)
+ menu->priv->manager = manager;
+ else
+ menu->priv->manager = gtk_recent_manager_get_default ();
+
+ if (menu->priv->manager)
+ menu->priv->manager_changed_id = g_signal_connect (menu->priv->manager, "changed",
+ G_CALLBACK (manager_changed_cb),
+ menu);
+}
+
+static gint
+get_icon_size_for_widget (GtkWidget *widget)
+{
+ GtkSettings *settings;
+ gint width, height;
+
+ if (gtk_widget_has_screen (widget))
+ settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
+ else
+ settings = gtk_settings_get_default ();
+
+ if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
+ &width, &height))
+ return MAX (width, height);
+
+ return FALLBACK_ICON_SIZE;
+}
+
+static void
+gtk_recent_chooser_menu_set_show_tips (GtkRecentChooserMenu *menu,
+ gboolean show_tips)
+{
+ if (menu->priv->show_tips == show_tips)
+ return;
+
+ g_assert (menu->priv->tooltips != NULL);
+
+ if (show_tips)
+ gtk_tooltips_enable (menu->priv->tooltips);
+ else
+ gtk_tooltips_disable (menu->priv->tooltips);
+
+ menu->priv->show_tips = show_tips;
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * gtk_recent_chooser_menu_new:
+ *
+ * Creates a new #GtkRecentChooserMenu widget.
+ *
+ * This kind of widget shows the list of recently used resources as
+ * a menu, each item as a menu item. Each item inside the menu might
+ * have an icon, representing its MIME type, and a number, for mnemonic
+ * access.
+ *
+ * This widget implements the #GtkRecentChooser interface.
+ *
+ * This widget creates its own #GtkRecentManager object. See the
+ * gtk_recent_chooser_menu_new_for_manager() function to know how to create
+ * a #GtkRecentChooserMenu widget bound to another #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserMenu
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_menu_new (void)
+{
+ return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
+ "recent-manager", NULL,
+ NULL);
+}
+
+/**
+ * gtk_recent_chooser_menu_new_for_manager:
+ * @manager: a #GtkRecentManager
+ *
+ * Creates a new #GtkRecentChooserMenu widget using @manager as
+ * the underlying recently used resources manager.
+ *
+ * This is useful if you have implemented your own recent manager,
+ * or if you have a customized instance of a #GtkRecentManager
+ * object or if you wish to share a common #GtkRecentManager object
+ * among multiple #GtkRecentChooser widgets.
+ *
+ * Return value: a new #GtkRecentChooserMenu, bound to @manager.
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_menu_new_for_manager (GtkRecentManager *manager)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
+
+ return g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
+ "recent-manager", manager,
+ NULL);
+}
+
+/**
+ * gtk_recent_chooser_menu_get_show_numbers:
+ * @menu: a #GtkRecentChooserMenu
+ *
+ * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
+ *
+ * Return value: %TRUE if numbers should be shown.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_chooser_menu_get_show_numbers (GtkRecentChooserMenu *menu)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu), FALSE);
+
+ return menu->priv->show_numbers;
+}
+
+/**
+ * gtk_recent_chooser_menu_set_show_numbers:
+ * @menu: a #GtkRecentChooserMenu
+ * @show_numbers: whether to show numbers
+ *
+ * Sets whether a number should be added to the items of @menu. The
+ * numbers are shown to provide a unique character for a mnemonic to
+ * be used inside the menu item's label. Only the first the items
+ * get a number to avoid clashes.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_chooser_menu_set_show_numbers (GtkRecentChooserMenu *menu,
+ gboolean show_numbers)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER_MENU (menu));
+
+ if (menu->priv->show_numbers == show_numbers)
+ return;
+
+ menu->priv->show_numbers = show_numbers;
+ g_object_notify (G_OBJECT (menu), "show-numbers");
+}
+
+#define __GTK_RECENT_CHOOSER_MENU_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchoosermenu.h b/gtk/gtkrecentchoosermenu.h
new file mode 100644
index 0000000000..200233f9ee
--- /dev/null
+++ b/gtk/gtkrecentchoosermenu.h
@@ -0,0 +1,70 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchoosermenu.h - Recently used items menu widget
+ * Copyright (C) 2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_MENU_H__
+#define __GTK_RECENT_CHOOSER_MENU_H__
+
+#include <gtk/gtkmenu.h>
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_MENU (gtk_recent_chooser_menu_get_type ())
+#define GTK_RECENT_CHOOSER_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenu))
+#define GTK_IS_RECENT_CHOOSER_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_MENU))
+#define GTK_RECENT_CHOOSER_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuClass))
+#define GTK_IS_RECENT_CHOOSER_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_MENU))
+#define GTK_RECENT_CHOOSER_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_MENU, GtkRecentChooserMenuClass))
+
+typedef struct _GtkRecentChooserMenu GtkRecentChooserMenu;
+typedef struct _GtkRecentChooserMenuClass GtkRecentChooserMenuClass;
+typedef struct _GtkRecentChooserMenuPrivate GtkRecentChooserMenuPrivate;
+
+struct _GtkRecentChooserMenu
+{
+ /*< private >*/
+ GtkMenu parent_instance;
+
+ GtkRecentChooserMenuPrivate *priv;
+};
+
+struct _GtkRecentChooserMenuClass
+{
+ GtkMenuClass parent_class;
+
+ /* padding for future expansion */
+ void (* gtk_recent1) (void);
+ void (* gtk_recent2) (void);
+ void (* gtk_recent3) (void);
+ void (* gtk_recent4) (void);
+};
+
+GType gtk_recent_chooser_menu_get_type (void) G_GNUC_CONST;
+
+GtkWidget *gtk_recent_chooser_menu_new (void);
+GtkWidget *gtk_recent_chooser_menu_new_for_manager (GtkRecentManager *manager);
+
+gboolean gtk_recent_chooser_menu_get_show_numbers (GtkRecentChooserMenu *menu);
+void gtk_recent_chooser_menu_set_show_numbers (GtkRecentChooserMenu *menu,
+ gboolean show_numbers);
+
+G_END_DECLS
+
+#endif /* ! __GTK_RECENT_CHOOSER_MENU_H__ */
diff --git a/gtk/gtkrecentchooserprivate.h b/gtk/gtkrecentchooserprivate.h
new file mode 100644
index 0000000000..fcd3c832d6
--- /dev/null
+++ b/gtk/gtkrecentchooserprivate.h
@@ -0,0 +1,42 @@
+/* gtkrecentprivatechooser.h - Interface definitions for recent selectors UI
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_PRIVATE_H__
+#define __GTK_RECENT_CHOOSER_PRIVATE_H__
+
+#include <glib-object.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkrecentchooser.h"
+
+G_BEGIN_DECLS
+
+#define GTK_DEFAULT_RECENT_MANAGER "gtk-recent-manager-default"
+
+GtkRecentManager *_gtk_recent_chooser_get_recent_manager (GtkRecentChooser *chooser);
+
+void _gtk_recent_chooser_item_activated (GtkRecentChooser *chooser);
+void _gtk_recent_chooser_selection_changed (GtkRecentChooser *chooser);
+
+G_END_DECLS
+
+#endif /* ! __GTK_RECENT_CHOOSER_PRIVATE_H__ */
diff --git a/gtk/gtkrecentchooserutils.c b/gtk/gtkrecentchooserutils.c
new file mode 100644
index 0000000000..b7fa81d5a0
--- /dev/null
+++ b/gtk/gtkrecentchooserutils.c
@@ -0,0 +1,298 @@
+/* gtkrecentchooserutils.h - Private utility functions for implementing a
+ * GtkRecentChooser interface
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Based on gtkfilechooserutils.c:
+ * Copyright (C) 2003 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include "gtkrecentchooserutils.h"
+
+/* Methods */
+static void delegate_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy);
+static void delegate_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static void delegate_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter);
+static GSList *delegate_list_filters (GtkRecentChooser *chooser);
+static gboolean delegate_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static void delegate_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri);
+static GList *delegate_get_items (GtkRecentChooser *chooser);
+static GtkRecentManager *delegate_get_recent_manager (GtkRecentChooser *chooser);
+static void delegate_select_all (GtkRecentChooser *chooser);
+static void delegate_unselect_all (GtkRecentChooser *chooser);
+static gboolean delegate_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error);
+static gchar * delegate_get_current_uri (GtkRecentChooser *chooser);
+
+/* Signals */
+static void delegate_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data);
+static void delegate_selection_changed (GtkRecentChooser *receiver,
+ gpointer user_data);
+static void delegate_item_activated (GtkRecentChooser *receiver,
+ gpointer user_data);
+
+/**
+ * _gtk_recent_chooser_install_properties:
+ * @klass: the class structure for a type deriving from #GObject
+ *
+ * Installs the necessary properties for a class implementing
+ * #GtkRecentChooser. A #GtkParamSpecOverride property is installed
+ * for each property, using the values from the #GtkRecentChooserProp
+ * enumeration. The caller must make sure itself that the enumeration
+ * values don't collide with some other property values they
+ * are using.
+ */
+void
+_gtk_recent_chooser_install_properties (GObjectClass *klass)
+{
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER,
+ "recent-manager");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE,
+ "show-private");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SHOW_TIPS,
+ "show-tips");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SHOW_ICONS,
+ "show-icons");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND,
+ "show-not-found");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE,
+ "select-multiple");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_LIMIT,
+ "limit");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY,
+ "local-only");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_SORT_TYPE,
+ "sort-type");
+ g_object_class_override_property (klass,
+ GTK_RECENT_CHOOSER_PROP_FILTER,
+ "filter");
+}
+
+/**
+ * _gtk_recent_chooser_delegate_iface_init:
+ * @iface: a #GtkRecentChooserIface
+ *
+ * An interface-initialization function for use in cases where
+ * an object is simply delegating the methods, signals of
+ * the #GtkRecentChooser interface to another object.
+ * _gtk_recent_chooser_set_delegate() must be called on each
+ * instance of the object so that the delegate object can
+ * be found.
+ */
+void
+_gtk_recent_chooser_delegate_iface_init (GtkRecentChooserIface *iface)
+{
+ iface->set_current_uri = delegate_set_current_uri;
+ iface->get_current_uri = delegate_get_current_uri;
+ iface->select_uri = delegate_select_uri;
+ iface->unselect_uri = delegate_unselect_uri;
+ iface->select_all = delegate_select_all;
+ iface->unselect_all = delegate_unselect_all;
+ iface->get_items = delegate_get_items;
+ iface->get_recent_manager = delegate_get_recent_manager;
+ iface->set_sort_func = delegate_set_sort_func;
+ iface->add_filter = delegate_add_filter;
+ iface->remove_filter = delegate_remove_filter;
+ iface->list_filters = delegate_list_filters;
+}
+
+/**
+ * _gtk_recent_chooser_set_delegate:
+ * @receiver: a #GObject implementing #GtkRecentChooser
+ * @delegate: another #GObject implementing #GtkRecentChooser
+ *
+ * Establishes that calls on @receiver for #GtkRecentChooser
+ * methods should be delegated to @delegate, and that
+ * #GtkRecentChooser signals emitted on @delegate should be
+ * forwarded to @receiver. Must be used in conjunction with
+ * _gtk_recent_chooser_delegate_iface_init().
+ */
+void
+_gtk_recent_chooser_set_delegate (GtkRecentChooser *receiver,
+ GtkRecentChooser *delegate)
+{
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (receiver));
+ g_return_if_fail (GTK_IS_RECENT_CHOOSER (delegate));
+
+ g_object_set_data (G_OBJECT (receiver),
+ "gtk-recent-chooser-delegate", delegate);
+
+ g_signal_connect (delegate, "notify",
+ G_CALLBACK (delegate_notify), receiver);
+ g_signal_connect (delegate, "selection-changed",
+ G_CALLBACK (delegate_selection_changed), receiver);
+ g_signal_connect (delegate, "item-activated",
+ G_CALLBACK (delegate_item_activated), receiver);
+}
+
+GQuark
+_gtk_recent_chooser_delegate_get_quark (void)
+{
+ static GQuark quark = 0;
+
+ if (G_UNLIKELY (quark == 0))
+ quark = g_quark_from_static_string ("gtk-recent-chooser-delegate");
+
+ return quark;
+}
+
+static GtkRecentChooser *
+get_delegate (GtkRecentChooser *receiver)
+{
+ return g_object_get_qdata (G_OBJECT (receiver),
+ GTK_RECENT_CHOOSER_DELEGATE_QUARK);
+}
+
+static void
+delegate_set_sort_func (GtkRecentChooser *chooser,
+ GtkRecentSortFunc sort_func,
+ gpointer sort_data,
+ GDestroyNotify data_destroy)
+{
+ gtk_recent_chooser_set_sort_func (get_delegate (chooser),
+ sort_func,
+ sort_data,
+ data_destroy);
+}
+
+static void
+delegate_add_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ gtk_recent_chooser_add_filter (get_delegate (chooser), filter);
+}
+
+static void
+delegate_remove_filter (GtkRecentChooser *chooser,
+ GtkRecentFilter *filter)
+{
+ gtk_recent_chooser_remove_filter (get_delegate (chooser), filter);
+}
+
+static GSList *
+delegate_list_filters (GtkRecentChooser *chooser)
+{
+ return gtk_recent_chooser_list_filters (get_delegate (chooser));
+}
+
+static gboolean
+delegate_select_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ return gtk_recent_chooser_select_uri (get_delegate (chooser), uri, error);
+}
+
+static void
+delegate_unselect_uri (GtkRecentChooser *chooser,
+ const gchar *uri)
+{
+ return gtk_recent_chooser_unselect_uri (get_delegate (chooser), uri);
+}
+
+static GList *
+delegate_get_items (GtkRecentChooser *chooser)
+{
+ return gtk_recent_chooser_get_items (get_delegate (chooser));
+}
+
+static GtkRecentManager *
+delegate_get_recent_manager (GtkRecentChooser *chooser)
+{
+ return _gtk_recent_chooser_get_recent_manager (get_delegate (chooser));
+}
+
+static void
+delegate_select_all (GtkRecentChooser *chooser)
+{
+ gtk_recent_chooser_select_all (get_delegate (chooser));
+}
+
+static void
+delegate_unselect_all (GtkRecentChooser *chooser)
+{
+ gtk_recent_chooser_unselect_all (get_delegate (chooser));
+}
+
+static gboolean
+delegate_set_current_uri (GtkRecentChooser *chooser,
+ const gchar *uri,
+ GError **error)
+{
+ return gtk_recent_chooser_set_current_uri (get_delegate (chooser), uri, error);
+}
+
+static gchar *
+delegate_get_current_uri (GtkRecentChooser *chooser)
+{
+ return gtk_recent_chooser_get_current_uri (get_delegate (chooser));
+}
+
+static void
+delegate_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ gpointer iface;
+
+ iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (object)),
+ gtk_recent_chooser_get_type ());
+ if (g_object_interface_find_property (iface, pspec->name))
+ g_object_notify (user_data, pspec->name);
+}
+
+static void
+delegate_selection_changed (GtkRecentChooser *receiver,
+ gpointer user_data)
+{
+ _gtk_recent_chooser_selection_changed (GTK_RECENT_CHOOSER (user_data));
+}
+
+static void
+delegate_item_activated (GtkRecentChooser *receiver,
+ gpointer user_data)
+{
+ _gtk_recent_chooser_item_activated (GTK_RECENT_CHOOSER (user_data));
+}
+
+#define __GTK_RECENT_CHOOSER_UTILS_H__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserutils.h b/gtk/gtkrecentchooserutils.h
new file mode 100644
index 0000000000..79adca301a
--- /dev/null
+++ b/gtk/gtkrecentchooserutils.h
@@ -0,0 +1,63 @@
+/* gtkrecentchooserutils.h - Private utility functions for implementing a
+ * GtkRecentChooser interface
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * All rights reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Based on gtkfilechooserutils.h:
+ * Copyright (C) 2003 Red Hat, Inc.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_UTILS_H__
+#define __GTK_RECENT_CHOOSER_UTILS_H__
+
+#include "gtkrecentchooserprivate.h"
+
+G_BEGIN_DECLS
+
+
+#define GTK_RECENT_CHOOSER_DELEGATE_QUARK (_gtk_recent_chooser_delegate_get_quark ())
+
+typedef enum {
+ GTK_RECENT_CHOOSER_PROP_FIRST = 0x3000,
+ GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER,
+ GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE,
+ GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND,
+ GTK_RECENT_CHOOSER_PROP_SHOW_TIPS,
+ GTK_RECENT_CHOOSER_PROP_SHOW_ICONS,
+ GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE,
+ GTK_RECENT_CHOOSER_PROP_LIMIT,
+ GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY,
+ GTK_RECENT_CHOOSER_PROP_SORT_TYPE,
+ GTK_RECENT_CHOOSER_PROP_FILTER,
+ GTK_RECENT_CHOOSER_PROP_LAST
+} GtkRecentChooserProp;
+
+void _gtk_recent_chooser_install_properties (GObjectClass *klass);
+
+void _gtk_recent_chooser_delegate_iface_init (GtkRecentChooserIface *iface);
+void _gtk_recent_chooser_set_delegate (GtkRecentChooser *receiver,
+ GtkRecentChooser *delegate);
+
+GQuark _gtk_recent_chooser_delegate_get_quark (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_UTILS_H__ */
diff --git a/gtk/gtkrecentchooserwidget.c b/gtk/gtkrecentchooserwidget.c
new file mode 100644
index 0000000000..b3c2115694
--- /dev/null
+++ b/gtk/gtkrecentchooserwidget.c
@@ -0,0 +1,194 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserwidget.c: embeddable recently used resources chooser widget
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "gtkrecentchooserwidget.h"
+#include "gtkrecentchooserdefault.h"
+#include "gtkrecentchooserutils.h"
+#include "gtktypebuiltins.h"
+#include "gtkalias.h"
+
+struct _GtkRecentChooserWidgetPrivate
+{
+ GtkRecentManager *manager;
+
+ GtkWidget *chooser;
+};
+
+#define GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE(obj) (GTK_RECENT_CHOOSER_WIDGET (obj)->priv)
+
+static GObject *gtk_recent_chooser_widget_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam *params);
+static void gtk_recent_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_chooser_widget_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_WITH_CODE (GtkRecentChooserWidget,
+ gtk_recent_chooser_widget,
+ GTK_TYPE_VBOX,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
+ _gtk_recent_chooser_delegate_iface_init));
+
+static void
+gtk_recent_chooser_widget_class_init (GtkRecentChooserWidgetClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructor = gtk_recent_chooser_widget_constructor;
+ gobject_class->set_property = gtk_recent_chooser_widget_set_property;
+ gobject_class->get_property = gtk_recent_chooser_widget_get_property;
+ gobject_class->finalize = gtk_recent_chooser_widget_finalize;
+
+ _gtk_recent_chooser_install_properties (gobject_class);
+
+ g_type_class_add_private (klass, sizeof (GtkRecentChooserWidgetPrivate));
+}
+
+
+static void
+gtk_recent_chooser_widget_init (GtkRecentChooserWidget *widget)
+{
+ widget->priv = G_TYPE_INSTANCE_GET_PRIVATE (widget, GTK_TYPE_RECENT_CHOOSER_WIDGET,
+ GtkRecentChooserWidgetPrivate);
+}
+
+static GObject *
+gtk_recent_chooser_widget_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam *params)
+{
+ GObject *object;
+ GtkRecentChooserWidgetPrivate *priv;
+
+ object = G_OBJECT_CLASS (gtk_recent_chooser_widget_parent_class)->constructor (type,
+ n_params,
+ params);
+
+ priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+ priv->chooser = _gtk_recent_chooser_default_new (priv->manager);
+
+
+ gtk_container_add (GTK_CONTAINER (object), priv->chooser);
+ gtk_widget_show (priv->chooser);
+ _gtk_recent_chooser_set_delegate (GTK_RECENT_CHOOSER (object),
+ GTK_RECENT_CHOOSER (priv->chooser));
+
+ return object;
+}
+
+static void
+gtk_recent_chooser_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserWidgetPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+
+ switch (prop_id)
+ {
+ case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
+ priv->manager = g_value_get_object (value);
+ break;
+ default:
+ g_object_set_property (G_OBJECT (priv->chooser), pspec->name, value);
+ break;
+ }
+}
+
+static void
+gtk_recent_chooser_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentChooserWidgetPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+
+ g_object_get_property (G_OBJECT (priv->chooser), pspec->name, value);
+}
+
+static void
+gtk_recent_chooser_widget_finalize (GObject *object)
+{
+ GtkRecentChooserWidgetPrivate *priv;
+
+ priv = GTK_RECENT_CHOOSER_WIDGET_GET_PRIVATE (object);
+ priv->manager = NULL;
+
+ G_OBJECT_CLASS (gtk_recent_chooser_widget_parent_class)->finalize (object);
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * gtk_recent_chooser_widget_new:
+ *
+ * Creates a new #GtkRecentChooserWidget object. This is an embeddable widget
+ * used to access the recently used resources list.
+ *
+ * Return value: a new #GtkRecentChooserWidget
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_widget_new (void)
+{
+ return g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET, NULL);
+}
+
+/**
+ * gtk_recent_chooser_widget_new_for_manager:
+ * @manager: a #GtkRecentManager
+ *
+ * Creates a new #GtkRecentChooserWidget with a specified recent manager.
+ *
+ * This is useful if you have implemented your own recent manager, or if you
+ * have a customized instance of a #GtkRecentManager object.
+ *
+ * Return value: a new #GtkRecentChooserWidget
+ *
+ * Since: 2.10
+ */
+GtkWidget *
+gtk_recent_chooser_widget_new_for_manager (GtkRecentManager *manager)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (manager), NULL);
+
+ return g_object_new (GTK_TYPE_RECENT_CHOOSER_WIDGET,
+ "recent-manager", manager,
+ NULL);
+}
+
+#define __GTK_RECENT_CHOOSER_WIDGET_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentchooserwidget.h b/gtk/gtkrecentchooserwidget.h
new file mode 100644
index 0000000000..96aea95f1f
--- /dev/null
+++ b/gtk/gtkrecentchooserwidget.h
@@ -0,0 +1,60 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentchooserwidget.h: embeddable recently used resources chooser widget
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_CHOOSER_WIDGET_H__
+#define __GTK_RECENT_CHOOSER_WIDGET_H__
+
+#include "gtkrecentchooser.h"
+#include <gtk/gtkvbox.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_CHOOSER_WIDGET (gtk_recent_chooser_widget_get_type ())
+#define GTK_RECENT_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidget))
+#define GTK_IS_RECENT_CHOOSER_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET))
+#define GTK_RECENT_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidgetClass))
+#define GTK_IS_RECENT_CHOOSER_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_CHOOSER_WIDGET))
+#define GTK_RECENT_CHOOSER_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_CHOOSER_WIDGET, GtkRecentChooserWidgetClass))
+
+typedef struct _GtkRecentChooserWidget GtkRecentChooserWidget;
+typedef struct _GtkRecentChooserWidgetClass GtkRecentChooserWidgetClass;
+
+typedef struct _GtkRecentChooserWidgetPrivate GtkRecentChooserWidgetPrivate;
+
+struct _GtkRecentChooserWidget
+{
+ /*< private >*/
+ GtkVBox parent_instance;
+
+ GtkRecentChooserWidgetPrivate *priv;
+};
+
+struct _GtkRecentChooserWidgetClass
+{
+ GtkVBoxClass parent_class;
+};
+
+GType gtk_recent_chooser_widget_get_type (void) G_GNUC_CONST;
+GtkWidget *gtk_recent_chooser_widget_new (void);
+GtkWidget *gtk_recent_chooser_widget_new_for_manager (GtkRecentManager *manager);
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_CHOOSER_WIDGET_H__ */
diff --git a/gtk/gtkrecentfilter.c b/gtk/gtkrecentfilter.c
new file mode 100644
index 0000000000..58476dead8
--- /dev/null
+++ b/gtk/gtkrecentfilter.c
@@ -0,0 +1,579 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentfilter.h - Filter object for recently used resources
+ * Copyright (C) 2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <string.h>
+
+#include "gtkrecentfilter.h"
+#include "gtkobject.h"
+#include "gtkintl.h"
+#include "gtkprivate.h"
+
+#include "gtkalias.h"
+
+#ifdef G_OS_UNIX
+#define XDG_PREFIX _gtk_xdg
+#include "xdgmime/xdgmime.h"
+#endif
+
+typedef struct _GtkRecentFilterClass GtkRecentFilterClass;
+typedef struct _FilterRule FilterRule;
+
+typedef enum {
+ FILTER_RULE_URI,
+ FILTER_RULE_DISPLAY_NAME,
+ FILTER_RULE_MIME_TYPE,
+ FILTER_RULE_PIXBUF_FORMATS,
+ FILTER_RULE_APPLICATION,
+ FILTER_RULE_AGE,
+ FILTER_RULE_GROUP,
+ FILTER_RULE_CUSTOM,
+} FilterRuleType;
+
+struct _GtkRecentFilter
+{
+ GtkObject parent_instance;
+
+ gchar *name;
+ GSList *rules;
+
+ GtkRecentFilterFlags needed;
+};
+
+struct _GtkRecentFilterClass
+{
+ GtkObjectClass parent_class;
+};
+
+struct _FilterRule
+{
+ FilterRuleType type;
+ GtkRecentFilterFlags needed;
+
+ union {
+ gchar *uri;
+ gchar *pattern;
+ gchar *mime_type;
+ GSList *pixbuf_formats;
+ gchar *application;
+ gchar *group;
+ gint age;
+ struct {
+ GtkRecentFilterFunc func;
+ gpointer data;
+ GDestroyNotify data_destroy;
+ } custom;
+ } u;
+};
+
+G_DEFINE_TYPE (GtkRecentFilter, gtk_recent_filter, GTK_TYPE_OBJECT);
+
+
+static void
+filter_rule_free (FilterRule *rule)
+{
+ switch (rule->type)
+ {
+ case FILTER_RULE_MIME_TYPE:
+ g_free (rule->u.mime_type);
+ break;
+ case FILTER_RULE_URI:
+ g_free (rule->u.uri);
+ break;
+ case FILTER_RULE_DISPLAY_NAME:
+ g_free (rule->u.pattern);
+ break;
+ case FILTER_RULE_PIXBUF_FORMATS:
+ g_slist_free (rule->u.pixbuf_formats);
+ break;
+ case FILTER_RULE_AGE:
+ break;
+ case FILTER_RULE_APPLICATION:
+ g_free (rule->u.application);
+ break;
+ case FILTER_RULE_GROUP:
+ g_free (rule->u.group);
+ break;
+ case FILTER_RULE_CUSTOM:
+ if (rule->u.custom.data_destroy)
+ rule->u.custom.data_destroy (rule->u.custom.data);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (rule);
+}
+
+static void
+gtk_recent_filter_finalize (GObject *object)
+{
+ GtkRecentFilter *filter = GTK_RECENT_FILTER (object);
+
+ if (filter->name)
+ g_free (filter->name);
+
+ if (filter->rules)
+ {
+ g_slist_foreach (filter->rules,
+ (GFunc) filter_rule_free,
+ NULL);
+ g_slist_free (filter->rules);
+ }
+
+ G_OBJECT_CLASS (gtk_recent_filter_parent_class)->finalize (object);
+}
+
+static void
+gtk_recent_filter_class_init (GtkRecentFilterClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gtk_recent_filter_finalize;
+}
+
+static void
+gtk_recent_filter_init (GtkRecentFilter *filter)
+{
+
+}
+
+/*
+ * Public API
+ */
+
+/**
+ * gtk_recent_filter_new:
+ *
+ * Creates a new #GtkRecentFilter with no rules added to it.
+ * Such filter does not accept any recently used resources, so is not
+ * particularly useful until you add rules with
+ * gtk_recent_filter_add_pattern(), gtk_recent_filter_add_mime_type(),
+ * gtk_recent_filter_add_application(), gtk_recent_filter_add_age().
+ * To create a filter that accepts any recently used resource, use:
+ *
+ * <informalexample><programlisting>
+ * GtkRecentFilter *filter = gtk_recent_filter_new (<!-- -->);
+ * gtk_recent_filter_add_pattern (filter, "*");
+ * </programlisting></informalexample>
+ *
+ * Return value: a new #GtkRecentFilter
+ *
+ * Since: 2.10
+ */
+GtkRecentFilter *
+gtk_recent_filter_new (void)
+{
+ return g_object_new (GTK_TYPE_RECENT_FILTER, NULL);
+}
+
+/**
+ * gtk_recent_filter_set_name:
+ * @filter: a #GtkRecentFilter
+ * @name: then human readable name of @filter
+ *
+ * Sets the human-readable name of the filter; this is the string
+ * that will be displayed in the recently used resources selector
+ * user interface if there is a selectable list of filters.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_set_name (GtkRecentFilter *filter,
+ const gchar *name)
+{
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+
+ if (filter->name)
+ g_free (filter->name);
+
+ if (name)
+ filter->name = g_strdup (name);
+}
+
+/**
+ * gtk_recent_filter_get_name:
+ * @filter: a #GtkRecentFilter
+ *
+ * Gets the human-readable name for the filter.
+ * See gtk_recent_filter_set_name().
+ *
+ * Return value: the name of the filter, or %NULL. The returned string
+ * is owned by the filter object and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_filter_get_name (GtkRecentFilter *filter)
+{
+ g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), NULL);
+
+ return filter->name;
+}
+
+/**
+ * gtk_recent_filter_get_needed:
+ * @filter: a #GtkRecentFilter
+ *
+ * Gets the fields that need to be filled in for the structure
+ * passed to gtk_recent_filter_filter()
+ *
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkRecentChooser.
+ *
+ * Return value: bitfield of flags indicating needed fields when
+ * calling gtk_recent_filter_filter()
+ *
+ * Since: 2.10
+ */
+GtkRecentFilterFlags
+gtk_recent_filter_get_needed (GtkRecentFilter *filter)
+{
+ return filter->needed;
+}
+
+static void
+recent_filter_add_rule (GtkRecentFilter *filter,
+ FilterRule *rule)
+{
+ filter->needed |= rule->needed;
+ filter->rules = g_slist_append (filter->rules, rule);
+}
+
+/**
+ * gtk_recent_filter_add_mime_type:
+ * @filter: a #GtkRecentFilter
+ * @mime_type: a MIME type
+ *
+ * Adds a rule that allows resources based on their registered MIME type.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_mime_type (GtkRecentFilter *filter,
+ const gchar *mime_type)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+ g_return_if_fail (mime_type != NULL);
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_MIME_TYPE;
+ rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
+ rule->u.mime_type = g_strdup (mime_type);
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_pattern:
+ * @filter: a #GtkRecentFilter
+ * @pattern: a file pattern
+ *
+ * Adds a rule that allows resources based on a pattern matching their
+ * display name.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_pattern (GtkRecentFilter *filter,
+ const gchar *pattern)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+ g_return_if_fail (pattern != NULL);
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_DISPLAY_NAME;
+ rule->needed = GTK_RECENT_FILTER_DISPLAY_NAME;
+ rule->u.pattern = g_strdup (pattern);
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_pixbuf_formats:
+ * @filter: a #GtkRecentFilter
+ *
+ * Adds a rule allowing image files in the formats supported
+ * by GdkPixbuf.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter *filter)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_PIXBUF_FORMATS;
+ rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
+ rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_application:
+ * @filter: a #GtkRecentFilter
+ * @application: an application name
+ *
+ * Adds a rule that allows resources based on the name of the application
+ * that has registered them.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_application (GtkRecentFilter *filter,
+ const gchar *application)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+ g_return_if_fail (application != NULL);
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_APPLICATION;
+ rule->needed = GTK_RECENT_FILTER_APPLICATION;
+ rule->u.application = g_strdup (application);
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_group:
+ * @filter: a #GtkRecentFilter
+ * @group: a group name
+ *
+ * Adds a rule that allows resources based on the name of the group
+ * to which they belong
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_group (GtkRecentFilter *filter,
+ const gchar *group)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+ g_return_if_fail (group != NULL);
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_GROUP;
+ rule->needed = GTK_RECENT_FILTER_GROUP;
+ rule->u.group = g_strdup (group);
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_mime_type:
+ * @filter: a #GtkRecentFilter
+ * @days: number of days
+ *
+ * Adds a rule that allows resources based on their age - that is, the number
+ * of days elapsed since they were last modified.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_filter_add_age (GtkRecentFilter *filter,
+ gint days)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_AGE;
+ rule->needed = GTK_RECENT_FILTER_AGE;
+ rule->u.age = days;
+
+ recent_filter_add_rule (filter, rule);
+}
+
+/**
+ * gtk_recent_filter_add_custom:
+ * @filter: a #GtkRecentFilter
+ * @needed: bitfield of flags indicating the information that the custom
+ * filter function needs.
+ * @func: callback function; if the function returns %TRUE, then
+ * the file will be displayed.
+ * @data: data to pass to @func
+ * @data_destroy: function to call to free @data when it is no longer needed.
+ *
+ * Adds a rule to a filter that allows resources based on a custom callback
+ * function. The bitfield @needed which is passed in provides information
+ * about what sorts of information that the filter function needs;
+ * this allows GTK+ to avoid retrieving expensive information when
+ * it isn't needed by the filter.
+ *
+ * Since: 2.10
+ **/
+void
+gtk_recent_filter_add_custom (GtkRecentFilter *filter,
+ GtkRecentFilterFlags needed,
+ GtkRecentFilterFunc func,
+ gpointer data,
+ GDestroyNotify data_destroy)
+{
+ FilterRule *rule;
+
+ g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
+ g_return_if_fail (func != NULL);
+
+ rule = g_new0 (FilterRule, 1);
+ rule->type = FILTER_RULE_CUSTOM;
+ rule->needed = needed;
+ rule->u.custom.func = func;
+ rule->u.custom.data = data;
+ rule->u.custom.data_destroy = data_destroy;
+
+ recent_filter_add_rule (filter, rule);
+}
+
+
+/**
+ * gtk_recent_filter_filter:
+ * @filter: a #GtkRecentFilter
+ * @filter_info: a #GtkRecentFilterInfo structure containing information
+ * about a recently used resource
+ *
+ * Tests whether a file should be displayed according to @filter.
+ * The #GtkRecentFilterInfo structure @filter_info should include
+ * the fields returned from gtk_recent_filter_get_needed().
+ *
+ * This function will not typically be used by applications; it
+ * is intended principally for use in the implementation of
+ * #GtkRecentChooser.
+ *
+ * Return value: %TRUE if the file should be displayed
+ */
+gboolean
+gtk_recent_filter_filter (GtkRecentFilter *filter,
+ const GtkRecentFilterInfo *filter_info)
+{
+ GSList *l;
+
+ g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), FALSE);
+ g_return_val_if_fail (filter_info != NULL, FALSE);
+
+ for (l = filter->rules; l != NULL; l = l->next)
+ {
+ FilterRule *rule = (FilterRule *) l->data;
+
+ if ((filter_info->contains & rule->needed) != rule->needed)
+ continue;
+
+ switch (rule->type)
+ {
+ case FILTER_RULE_MIME_TYPE:
+ if ((filter_info->mime_type != NULL)
+#ifdef G_OS_UNIX
+ && (xdg_mime_mime_type_subclass (filter_info->mime_type, rule->u.mime_type)))
+#else
+ && (strcmp (filter_info->mime_type, rule->u.mime_type) == 0))
+#endif
+ return TRUE;
+ break;
+ case FILTER_RULE_APPLICATION:
+ if (filter_info->applications)
+ {
+ gint i;
+
+ for (i = 0; filter_info->applications[i] != NULL; i++)
+ {
+ if (strcmp (filter_info->applications[i], rule->u.application) == 0)
+ return TRUE;
+ }
+ }
+ break;
+ case FILTER_RULE_GROUP:
+ if (filter_info->groups)
+ {
+ gint i;
+
+ for (i = 0; filter_info->groups[i] != NULL; i++)
+ {
+ if (strcmp (filter_info->groups[i], rule->u.group) == 0)
+ return TRUE;
+ }
+ }
+ break;
+ case FILTER_RULE_PIXBUF_FORMATS:
+ {
+ GSList *list;
+ if (!filter_info->mime_type)
+ break;
+
+ for (list = rule->u.pixbuf_formats; list; list = list->next)
+ {
+ gint i;
+ gchar **mime_types;
+
+ mime_types = gdk_pixbuf_format_get_mime_types (list->data);
+
+ for (i = 0; mime_types[i] != NULL; i++)
+ {
+ if (strcmp (mime_types[i], filter_info->mime_type) == 0)
+ {
+ g_strfreev (mime_types);
+ return TRUE;
+ }
+ }
+
+ g_strfreev (mime_types);
+ }
+ break;
+ }
+ case FILTER_RULE_URI:
+ if ((filter_info->uri != NULL) &&
+ _gtk_fnmatch (rule->u.uri, filter_info->uri, FALSE))
+ return TRUE;
+ break;
+ case FILTER_RULE_DISPLAY_NAME:
+ if ((filter_info->display_name != NULL) &&
+ _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
+ return TRUE;
+ break;
+ case FILTER_RULE_AGE:
+ if ((filter_info->age != -1) &&
+ (filter_info->age < rule->u.age))
+ return TRUE;
+ break;
+ case FILTER_RULE_CUSTOM:
+ if (rule->u.custom.func (filter_info, rule->u.custom.data))
+ return TRUE;
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+#define __GTK_RECENT_FILTER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentfilter.h b/gtk/gtkrecentfilter.h
new file mode 100644
index 0000000000..28f6fe7aa7
--- /dev/null
+++ b/gtk/gtkrecentfilter.h
@@ -0,0 +1,90 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentfilter.h - Filter object for recently used resources
+ * Copyright (C) 2006, Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_RECENT_FILTER_H__
+#define __GTK_RECENT_FILTER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_FILTER (gtk_recent_filter_get_type ())
+#define GTK_RECENT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_FILTER, GtkRecentFilter))
+#define GTK_IS_RECENT_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_FILTER))
+
+typedef struct _GtkRecentFilter GtkRecentFilter;
+typedef struct _GtkRecentFilterInfo GtkRecentFilterInfo;
+
+typedef enum {
+ GTK_RECENT_FILTER_URI = 1 << 0,
+ GTK_RECENT_FILTER_DISPLAY_NAME = 1 << 1,
+ GTK_RECENT_FILTER_MIME_TYPE = 1 << 2,
+ GTK_RECENT_FILTER_APPLICATION = 1 << 3,
+ GTK_RECENT_FILTER_GROUP = 1 << 4,
+ GTK_RECENT_FILTER_AGE = 1 << 5
+} GtkRecentFilterFlags;
+
+typedef gboolean (*GtkRecentFilterFunc) (const GtkRecentFilterInfo *filter_info,
+ gpointer user_data);
+
+struct _GtkRecentFilterInfo
+{
+ GtkRecentFilterFlags contains;
+
+ const gchar *uri;
+ const gchar *display_name;
+ const gchar *mime_type;
+ const gchar **applications;
+ const gchar **groups;
+
+ gint age;
+};
+
+GType gtk_recent_filter_get_type (void) G_GNUC_CONST;
+
+GtkRecentFilter * gtk_recent_filter_new (void);
+void gtk_recent_filter_set_name (GtkRecentFilter *filter,
+ const gchar *name);
+G_CONST_RETURN gchar *gtk_recent_filter_get_name (GtkRecentFilter *filter);
+
+void gtk_recent_filter_add_mime_type (GtkRecentFilter *filter,
+ const gchar *mime_type);
+void gtk_recent_filter_add_pattern (GtkRecentFilter *filter,
+ const gchar *pattern);
+void gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter *filter);
+void gtk_recent_filter_add_application (GtkRecentFilter *filter,
+ const gchar *application);
+void gtk_recent_filter_add_group (GtkRecentFilter *filter,
+ const gchar *group);
+void gtk_recent_filter_add_age (GtkRecentFilter *filter,
+ gint days);
+void gtk_recent_filter_add_custom (GtkRecentFilter *filter,
+ GtkRecentFilterFlags needed,
+ GtkRecentFilterFunc func,
+ gpointer data,
+ GDestroyNotify data_destroy);
+
+GtkRecentFilterFlags gtk_recent_filter_get_needed (GtkRecentFilter *filter);
+gboolean gtk_recent_filter_filter (GtkRecentFilter *filter,
+ const GtkRecentFilterInfo *filter_info);
+
+G_END_DECLS
+
+#endif /* ! __GTK_RECENT_FILTER_H__ */
diff --git a/gtk/gtkrecentmanager.c b/gtk/gtkrecentmanager.c
new file mode 100644
index 0000000000..82e3760fc2
--- /dev/null
+++ b/gtk/gtkrecentmanager.c
@@ -0,0 +1,2424 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentmanager.c: a manager for the recently used resources
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gtkrecentmanager.h"
+#include "gtkintl.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 per second should be enough */
+#define POLL_DELTA 1000
+
+/* return all items by default */
+#define DEFAULT_LIMIT -1
+
+/* keep in sync with xdgmime */
+#define GTK_RECENT_DEFAULT_MIME "application/octet-stream"
+
+typedef struct
+{
+ gchar *name;
+ gchar *exec;
+
+ guint count;
+
+ time_t stamp;
+} RecentAppInfo;
+
+struct _GtkRecentInfo
+{
+ gchar *uri;
+
+ gchar *display_name;
+ gchar *description;
+
+ time_t added;
+ time_t modified;
+ time_t visited;
+
+ gchar *mime_type;
+
+ GSList *applications;
+ GHashTable *apps_lookup;
+
+ GSList *groups;
+
+ gboolean is_private;
+
+ GdkPixbuf *icon;
+
+ gint ref_count;
+};
+
+struct _GtkRecentManagerPrivate
+{
+ 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;
+};
+
+enum
+{
+ PROP_0,
+
+ PROP_FILENAME,
+ PROP_LIMIT,
+ 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 guint signal_changed = 0;
+
+G_DEFINE_TYPE (GtkRecentManager, gtk_recent_manager, G_TYPE_OBJECT);
+
+GQuark
+gtk_recent_manager_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (quark == 0)
+ quark = g_quark_from_static_string ("gtk-recent-manager-error-quark");
+ return 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->finalize = gtk_recent_manager_finalize;
+
+ /**
+ * GtkRecentManager:filename
+ *
+ * The full path to the file to be used to store and read the recently
+ * used resources list
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_FILENAME,
+ g_param_spec_string ("filename",
+ 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));
+ /**
+ * GtkRecentManager:size
+ *
+ * The size of the recently used resources list.
+ *
+ * Since: 2.10
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_SIZE,
+ g_param_spec_int ("size",
+ P_("Size"),
+ P_("The size of the recently used resources list"),
+ -1,
+ G_MAXINT,
+ 0,
+ G_PARAM_READABLE));
+
+ /**
+ * GtkRecentManager::changed
+ * @recent_manager: the recent manager
+ *
+ * Emitted when the current recently used resources manager changes its
+ * contents.
+ *
+ * Since: 2.10
+ */
+ signal_changed =
+ g_signal_new (I_("changed"),
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkRecentManagerClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ klass->changed = gtk_recent_manager_real_changed;
+
+ g_type_class_add_private (klass, sizeof (GtkRecentManagerPrivate));
+}
+
+static void
+gtk_recent_manager_init (GtkRecentManager *manager)
+{
+ GtkRecentManagerPrivate *priv;
+
+ priv = g_type_instance_get_private ((GTypeInstance *) manager,
+ GTK_TYPE_RECENT_MANAGER);
+ manager->priv = priv;
+
+ priv->filename = g_build_filename (g_get_home_dir (),
+ GTK_RECENTLY_USED_FILE,
+ NULL);
+
+ 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;
+
+ priv->screen = NULL;
+}
+
+static void
+gtk_recent_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
+
+ switch (prop_id)
+ {
+ 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);
+ break;
+ }
+}
+
+static void
+gtk_recent_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkRecentManager *recent_manager = GTK_RECENT_MANAGER (object);
+
+ switch (prop_id)
+ {
+ 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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_recent_manager_finalize (GObject *object)
+{
+ 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_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_real_changed (GtkRecentManager *manager)
+{
+ GtkRecentManagerPrivate *priv = manager->priv;
+
+ g_object_freeze_notify (G_OBJECT (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)
+ {
+ priv->recent_items = g_bookmark_file_new ();
+ priv->size = 0;
+ }
+
+ write_error = NULL;
+ g_bookmark_file_to_file (priv->recent_items,
+ priv->filename,
+ &write_error);
+ if (write_error)
+ {
+ g_warning ("Attempting to store changes into `%s', "
+ "but failed: %s",
+ priv->filename,
+ write_error->message);
+ g_error_free (write_error);
+ }
+
+ /* 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)
+ {
+ g_warning ("Unable to stat() the recently used resources file "
+ "at `%s': %s.",
+ priv->filename,
+ g_strerror (errno));
+
+ priv->write_in_progress = FALSE;
+
+ g_object_thaw_notify (G_OBJECT (manager));
+
+ return;
+ }
+
+ priv->last_mtime = stat_buf.st_mtime;
+
+ /* mark us as clean */
+ priv->is_dirty = FALSE;
+ }
+ else
+ {
+ /* we are not marked as dirty, so we have been called
+ * because the recently used resources file has been
+ * changed (and not from us).
+ */
+ build_recent_items_list (manager);
+ }
+
+ 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)
+{
+ 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;
+
+ stat_res = g_stat (priv->filename, &stat_buf);
+ if (stat_res < 0)
+ {
+ /* the file does not exist, yet, so we wait */
+ if (errno == ENOENT)
+ return TRUE;
+
+ g_warning ("Unable to stat() the recently used resources file "
+ "at `%s': %s.",
+ priv->filename,
+ g_strerror (errno));
+
+ return TRUE;
+ }
+
+ /* the file didn't change from the last poll(), so we bail out */
+ if (stat_buf.st_mtime == priv->last_mtime)
+ return TRUE;
+
+ /* the file has been changed, hence we emit the "changed" signal */
+ gtk_recent_manager_changed (manager);
+
+ return TRUE;
+}
+
+static void
+gtk_recent_manager_set_filename (GtkRecentManager *manager,
+ const gchar *filename)
+{
+ GtkRecentManagerPrivate *priv;
+
+ 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)
+ {
+ g_source_remove (manager->priv->poll_timeout);
+ manager->priv->poll_timeout = 0;
+ }
+
+ build_recent_items_list (manager);
+
+ priv->filename = g_strdup (filename);
+ priv->poll_timeout = g_timeout_add (POLL_DELTA,
+ gtk_recent_manager_poll_timeout,
+ manager);
+
+ priv->is_dirty = FALSE;
+}
+
+/* reads the recently used resources file and builds the items list.
+ * we keep the items list inside the parser object, and build the
+ * RecentInfo object only on user's demand to avoid useless replication.
+ */
+static void
+build_recent_items_list (GtkRecentManager *manager)
+{
+ GtkRecentManagerPrivate *priv;
+ struct stat stat_buf;
+ int stat_res;
+ gboolean res;
+ GError *read_error;
+ gint size;
+
+ priv = manager->priv;
+ g_assert (priv->filename != NULL);
+
+ if (!priv->recent_items)
+ {
+ priv->recent_items = g_bookmark_file_new ();
+ 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
+ {
+ g_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);
+ if (read_error)
+ {
+ g_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)
+ {
+ priv->size = size;
+
+ g_object_notify (G_OBJECT (manager), "size");
+ }
+
+ priv->read_in_progress = FALSE;
+}
+
+
+/********************
+ * GtkRecentManager *
+ ********************/
+
+
+/**
+ * gtk_recent_manager_new:
+ *
+ * Creates a new recent manager object. Recent manager objects are used to
+ * handle the list of recently used resources. A #GtkRecentManager object
+ * 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.
+ *
+ * Return value: A newly created #GtkRecentManager object.
+ *
+ * Since: 2.10
+ */
+GtkRecentManager *
+gtk_recent_manager_new (void)
+{
+ GtkRecentManager *retval;
+ gchar *filename;
+
+ filename = g_build_filename (g_get_home_dir (),
+ GTK_RECENTLY_USED_FILE,
+ NULL);
+
+ retval = g_object_new (GTK_TYPE_RECENT_MANAGER,
+ "filename", filename,
+ NULL);
+
+ g_free (filename);
+
+ return retval;
+}
+
+/**
+ * gtk_recent_manager_get_default:
+ *
+ * Gets the recent manager for the default screen. See
+ * gtk_recent_manager_get_for_screen().
+ *
+ * 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.
+ *
+ * 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);
+ }
+
+ return manager;
+}
+
+static void
+display_closed (GdkDisplay *display,
+ gboolean is_error,
+ GtkRecentManager *manager)
+{
+ GtkRecentManagerPrivate *priv = manager->priv;
+ GdkScreen *screen = priv->screen;
+ gboolean was_screen_singleton = priv->is_screen_singleton;
+
+ if (was_screen_singleton)
+ {
+ g_object_set_data (G_OBJECT (screen), I_("gtk-recent-manager-error-quark"), NULL);
+ priv->is_screen_singleton = FALSE;
+ }
+
+ gtk_recent_manager_set_screen (manager, NULL);
+
+ if (was_screen_singleton)
+ g_object_unref (manager);
+}
+
+static void
+unset_screen (GtkRecentManager *manager)
+{
+ GtkRecentManagerPrivate *priv = manager->priv;
+ GdkDisplay *display;
+
+ if (priv->screen)
+ {
+ display = gdk_screen_get_display (priv->screen);
+
+ g_signal_handlers_disconnect_by_func (display,
+ (gpointer) display_closed,
+ manager);
+
+ 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;
+
+ g_return_if_fail (GTK_IS_RECENT_MANAGER (manager));
+ g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
+
+ priv = manager->priv;
+
+ unset_screen (manager);
+
+ if (screen)
+ {
+ display = gdk_screen_get_display (screen);
+
+ priv->screen = screen;
+
+ g_signal_connect (display, "closed",
+ G_CALLBACK (display_closed), manager);
+ }
+}
+
+/**
+ * gtk_recent_manager_set_limit:
+ * @recent_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 *recent_manager,
+ gint limit)
+{
+ GtkRecentManagerPrivate *priv;
+
+ g_return_if_fail (GTK_IS_RECENT_MANAGER (recent_manager));
+
+ priv = recent_manager->priv;
+ priv->limit = limit;
+}
+
+/**
+ * gtk_recent_manager_get_limit:
+ * @recent_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 *recent_manager)
+{
+ GtkRecentManagerPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), DEFAULT_LIMIT);
+
+ priv = recent_manager->priv;
+ return priv->limit;
+}
+
+/**
+ * gtk_recent_manager_add_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a valid URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Adds a new resource, pointed by @uri, into the recently used
+ * resources list.
+ *
+ * This function automatically retrieving 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
+ * define the metadata for the resource pointed by @uri.
+ *
+ * Return value: %TRUE if the new item was successfully added
+ * to the recently used resources list
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_add_item (GtkRecentManager *recent_manager,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentData *recent_data;
+ GError *add_error;
+ gboolean retval;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_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 (g_str_has_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 (recent_manager, uri, recent_data, &add_error);
+
+ g_free (recent_data->mime_type);
+ g_free (recent_data->app_name);
+ g_free (recent_data->app_exec);
+
+ g_slice_free (GtkRecentData, recent_data);
+
+ if (!retval)
+ {
+ g_propagate_error (error, add_error);
+
+ return FALSE;
+ }
+
+ return retval;
+}
+
+/**
+ * gtk_recent_manager_add_full:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a valid URI
+ * @recent_data: metadata of the resource
+ * @error: return location for a #GError, or %NULL
+ *
+ * Adds a new resource, pointed by @uri, into the recently used
+ * resources list, using the metadata specified inside the #GtkRecentData
+ * structure passed in @recent_data.
+ *
+ * The passed URI will be used to identify this resource inside the
+ * list.
+ *
+ * In order to register the new recently used resource, metadata about
+ * the resource must be passed as well as the URI; the metadata is
+ * stored in a #GtkRecentData structure, which must contain the MIME
+ * type of the resource pointed by the URI; the name of the application
+ * that is registering the item, and a command line to be used when
+ * launching the item.
+ *
+ * Optionally, a #GtkRecentData structure might contain a UTF-8 string
+ * to be used when viewing the item instead of the last component of the
+ * URI; a short description of the item; whether the item should be
+ * considered private - that is, should be displayed only by the
+ * applications that have registered it.
+ *
+ * Return value: %TRUE if the new item was successfully added to the
+ * recently used resources list, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_add_full (GtkRecentManager *recent_manager,
+ const gchar *uri,
+ const GtkRecentData *data,
+ GError **error)
+{
+ GtkRecentManagerPrivate *priv;
+
+ 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 (data != NULL, FALSE);
+
+ /* sanity checks */
+ if ((data->display_name) &&
+ (!g_utf8_validate (data->display_name, -1, NULL)))
+ {
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+ _("The display name of the recently used resource "
+ "must be a valid UTF-8 encoded string."));
+ return FALSE;
+ }
+
+ if ((data->description) &&
+ (!g_utf8_validate (data->description, -1, NULL)))
+ {
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+ _("The description of the recently used resource "
+ "must by a valid UTF-8 encoded string."));
+ return FALSE;
+ }
+
+
+ if (!data->mime_type)
+ {
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_INVALID_MIME,
+ _("You must specify the MIME type of the "
+ "resource pointed by `%s'"),
+ uri);
+ return FALSE;
+ }
+
+ if (!data->app_name)
+ {
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
+ _("You must specify the name of the application "
+ "that is registering the recently used resource "
+ "pointed by `%s'"),
+ uri);
+ return FALSE;
+ }
+
+ if (!data->app_exec)
+ {
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_BAD_EXEC_STRING,
+ _("You must specify a command line to "
+ "be used when launching the resource "
+ "pointed by `%s'"),
+ uri);
+ return FALSE;
+ }
+
+ priv = recent_manager->priv;
+
+ if (!priv->recent_items)
+ {
+ priv->recent_items = g_bookmark_file_new ();
+ priv->size = 0;
+ }
+
+ if (data->display_name)
+ g_bookmark_file_set_title (priv->recent_items, uri, data->display_name);
+
+ if (data->description)
+ g_bookmark_file_set_description (priv->recent_items, uri, data->description);
+
+ g_bookmark_file_set_mime_type (priv->recent_items, uri, data->mime_type);
+
+ if (data->groups && data->groups[0] != '\0')
+ {
+ gint j;
+
+ for (j = 0; (data->groups)[j] != NULL; j++)
+ g_bookmark_file_add_group (priv->recent_items, uri, (data->groups)[j]);
+ }
+
+ /* register the application; this will take care of updating the
+ * registration count and time in case the application has
+ * already registered the same document inside the list
+ */
+ g_bookmark_file_add_application (priv->recent_items, uri,
+ data->app_name,
+ data->app_exec);
+
+ g_bookmark_file_set_is_private (priv->recent_items, uri,
+ data->is_private);
+
+ /* mark us as dirty, so that when emitting the "changed" signal we
+ * will dump our changes
+ */
+ priv->is_dirty = TRUE;
+
+ gtk_recent_manager_changed (recent_manager);
+
+ return TRUE;
+}
+
+/**
+ * gtk_recent_manager_remove_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: the URI of the item you wish to remove
+ * @error: return location for a #GError, or %NULL
+ *
+ * Removes a resource pointed by @uri from the recently used resources
+ * list handled by a recent manager.
+ *
+ * Return value: %TRUE if the item pointed by @uri has been successfully
+ * removed by the recently used resources list, and %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_remove_item (GtkRecentManager *recent_manager,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentManagerPrivate *priv;
+ GError *remove_error = NULL;
+
+ 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)
+ {
+ priv->recent_items = g_bookmark_file_new ();
+ priv->size = 0;
+
+ 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;
+ }
+
+ g_bookmark_file_remove_item (priv->recent_items, uri, &remove_error);
+ if (remove_error)
+ {
+ g_propagate_error (error, remove_error);
+
+ return FALSE;
+ }
+
+ priv->is_dirty = TRUE;
+
+ gtk_recent_manager_changed (recent_manager);
+
+ return TRUE;
+}
+
+/**
+ * gtk_recent_manager_has_item:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a URI
+ *
+ * Checks whether there is a recently used resource registered
+ * with @uri inside the recent manager.
+ *
+ * Return value: %TRUE if the resource was found, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_manager_has_item (GtkRecentManager *recent_manager,
+ const gchar *uri)
+{
+ GtkRecentManagerPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), FALSE);
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ priv = recent_manager->priv;
+ g_return_val_if_fail (priv->recent_items != NULL, FALSE);
+
+ return g_bookmark_file_has_item (priv->recent_items, uri);
+}
+
+static gboolean
+build_recent_info (GBookmarkFile *bookmarks,
+ GtkRecentInfo *info)
+{
+ gchar **apps, **groups;
+ gsize apps_len, groups_len, i;
+
+ g_assert (bookmarks != NULL);
+ g_assert (info != NULL);
+
+ info->display_name = g_bookmark_file_get_title (bookmarks, info->uri, NULL);
+ info->description = g_bookmark_file_get_description (bookmarks, info->uri, NULL);
+ info->mime_type = g_bookmark_file_get_mime_type (bookmarks, info->uri, NULL);
+
+ info->is_private = g_bookmark_file_get_is_private (bookmarks, info->uri, NULL);
+
+ info->added = g_bookmark_file_get_added (bookmarks, info->uri, NULL);
+ info->modified = g_bookmark_file_get_modified (bookmarks, info->uri, NULL);
+ info->visited = g_bookmark_file_get_visited (bookmarks, info->uri, NULL);
+
+ groups = g_bookmark_file_get_groups (bookmarks, info->uri, &groups_len, NULL);
+ for (i = 0; i < groups_len; i++)
+ {
+ gchar *group_name = g_strdup (groups[i]);
+
+ info->groups = g_slist_append (info->groups, group_name);
+ }
+
+ g_strfreev (groups);
+
+ apps = g_bookmark_file_get_applications (bookmarks, info->uri, &apps_len, NULL);
+ for (i = 0; i < apps_len; i++)
+ {
+ gchar *app_name, *app_exec;
+ guint count;
+ time_t stamp;
+ RecentAppInfo *app_info;
+ gboolean res;
+
+ app_name = apps[i];
+
+ res = g_bookmark_file_get_app_info (bookmarks, info->uri, app_name,
+ &app_exec,
+ &count,
+ &stamp,
+ NULL);
+ if (!res)
+ continue;
+
+ app_info = recent_app_info_new (app_name);
+ app_info->exec = app_exec;
+ app_info->count = count;
+ app_info->stamp = stamp;
+
+ info->applications = g_slist_append (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:
+ * @recent_manager: a #GtkRecentManager
+ * @uri: a URI
+ * @error: 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
+ * like its MIME type, or its display name.
+ *
+ * Return value: a #GtkRecentInfo structure containing information
+ * about the resource pointed by @uri, or %NULL if the URI was
+ * not registered in the recently used resources list. Free with
+ * gtk_recent_info_unref().
+ **/
+GtkRecentInfo *
+gtk_recent_manager_lookup_item (GtkRecentManager *recent_manager,
+ const gchar *uri,
+ GError **error)
+{
+ GtkRecentManagerPrivate *priv;
+ GtkRecentInfo *info = NULL;
+ gboolean res;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ priv = recent_manager->priv;
+ if (!priv->recent_items)
+ {
+ priv->recent_items = g_bookmark_file_new ();
+ priv->size = 0;
+
+ g_set_error (error, GTK_RECENT_MANAGER_ERROR,
+ GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+ _("Unable to find an item with URI '%s'"),
+ uri);
+
+ return NULL;
+ }
+
+ 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);
+ return NULL;
+ }
+
+ info = gtk_recent_info_new (uri);
+ g_return_val_if_fail (info != 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);
+}
+
+/**
+ * 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
+ * remove the item pointed by @uri in the list
+ * @error: a return location for a #GError, or %NULL
+ *
+ * Changes the location of a recently used resource from @uri to @new_uri.
+ *
+ * Please note that this function will not affect the resource pointed
+ * by the URIs, but only the URI used in the recently used resources list.
+ *
+ * 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)
+{
+ 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 (!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);
+ return FALSE;
+ }
+
+ move_error = NULL;
+ res = g_bookmark_file_move_item (priv->recent_items,
+ uri, new_uri,
+ &move_error);
+ if (move_error)
+ {
+ g_propagate_error (error, move_error);
+ return FALSE;
+ }
+
+ priv->is_dirty = TRUE;
+
+ gtk_recent_manager_changed (recent_manager);
+
+ return TRUE;
+}
+
+/**
+ * gtk_recent_manager_get_items:
+ * @recent_manager: a #GtkRecentManager
+ *
+ * Gets the list of recently used resources.
+ *
+ * Return value: 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().
+ *
+ * Since: 2.10
+ */
+GList *
+gtk_recent_manager_get_items (GtkRecentManager *recent_manager)
+{
+ GtkRecentManagerPrivate *priv;
+ GList *retval = NULL;
+ gchar **uris;
+ gsize uris_len, i;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), NULL);
+
+ priv = recent_manager->priv;
+ if (!priv->recent_items)
+ 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;
+ }
+
+ 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;
+}
+
+static void
+purge_recent_items_list (GtkRecentManager *manager,
+ GError **error)
+{
+ GtkRecentManagerPrivate *priv = manager->priv;
+
+ if (!priv->recent_items)
+ 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 */
+ gtk_recent_manager_changed (manager);
+}
+
+/**
+ * gtk_recent_manager_purge_items:
+ * @recent_manager: a #GtkRecentManager
+ * @error: a return location for a #GError, or %NULL
+ *
+ * Purges every item from the recently used resources list.
+ *
+ * Return value: the number of items that have been removed from the
+ * recently used resources list.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_manager_purge_items (GtkRecentManager *recent_manager,
+ GError **error)
+{
+ GtkRecentManagerPrivate *priv;
+ gint count, purged;
+
+ g_return_val_if_fail (GTK_IS_RECENT_MANAGER (recent_manager), -1);
+
+ priv = recent_manager->priv;
+ if (!priv->recent_items)
+ return 0;
+
+ count = g_bookmark_file_get_size (priv->recent_items);
+ if (!count)
+ return 0;
+
+ purge_recent_items_list (recent_manager, error);
+
+ purged = count - g_bookmark_file_get_size (priv->recent_items);
+
+ return purged;
+}
+
+static void
+gtk_recent_manager_changed (GtkRecentManager *recent_manager)
+{
+ g_signal_emit (recent_manager, signal_changed, 0);
+}
+
+/*****************
+ * 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;
+}
+
+static GtkRecentInfo *
+gtk_recent_info_new (const gchar *uri)
+{
+ GtkRecentInfo *info;
+
+ g_assert (uri != NULL);
+
+ info = g_new0 (GtkRecentInfo, 1);
+ info->uri = g_strdup (uri);
+
+ info->applications = NULL;
+ info->apps_lookup = g_hash_table_new (g_str_hash, g_str_equal);
+
+ info->groups = NULL;
+
+ info->ref_count = 1;
+
+ return info;
+}
+
+static void
+gtk_recent_info_free (GtkRecentInfo *recent_info)
+{
+ if (!recent_info)
+ return;
+
+ g_free (recent_info->uri);
+ g_free (recent_info->display_name);
+ g_free (recent_info->description);
+ g_free (recent_info->mime_type);
+
+ if (recent_info->applications)
+ {
+ g_slist_foreach (recent_info->applications,
+ (GFunc) recent_app_info_free,
+ NULL);
+ g_slist_free (recent_info->applications);
+
+ recent_info->applications = NULL;
+ }
+
+ if (recent_info->apps_lookup)
+ g_hash_table_destroy (recent_info->apps_lookup);
+
+ if (recent_info->groups)
+ {
+ g_slist_foreach (recent_info->groups,
+ (GFunc) g_free,
+ NULL);
+ g_slist_free (recent_info->groups);
+
+ recent_info->groups = NULL;
+ }
+
+ if (recent_info->icon)
+ g_object_unref (recent_info->icon);
+
+ g_free (recent_info);
+}
+
+/**
+ * gtk_recent_info_ref:
+ * @info: a #GtkRecentInfo
+ *
+ * Increases the reference count of @recent_info by one.
+ *
+ * Return value: the recent info object with its reference count increased
+ * by one.
+ *
+ * Since: 2.10
+ */
+GtkRecentInfo *
+gtk_recent_info_ref (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (info->ref_count > 0, NULL);
+
+ info->ref_count += 1;
+
+ return info;
+}
+
+/**
+ * gtk_recent_info_unref:
+ * @info: a #GtkRecentInfo
+ *
+ * Decreases the reference count of @info by one. If the reference
+ * count reaches zero, @info is deallocated, and the memory freed.
+ *
+ * Since: 2.10
+ */
+void
+gtk_recent_info_unref (GtkRecentInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->ref_count > 0);
+
+ info->ref_count -= 1;
+
+ if (info->ref_count == 0)
+ gtk_recent_info_free (info);
+}
+
+/**
+ * gtk_recent_info_get_uri:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the URI of the resource.
+ *
+ * Return value: the URI of the resource. The returned string is
+ * owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_uri (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->uri;
+}
+
+/**
+ * gtk_recent_info_get_display_name:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the name of the resource. If none has been defined, the basename
+ * of the resource is obtained.
+ *
+ * Return value: the display name of the resource. The returned string
+ * is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_display_name (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (!info->display_name)
+ info->display_name = gtk_recent_info_get_short_name (info);
+
+ return info->display_name;
+}
+
+/**
+ * gtk_recent_info_get_description:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the (short) description of the resource.
+ *
+ * Return value: the description of the resource. The returned string
+ * is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ **/
+G_CONST_RETURN gchar *
+gtk_recent_info_get_description (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->description;
+}
+
+/**
+ * gtk_recent_info_get_mime_type:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the MIME type of the resource.
+ *
+ * Return value: the MIME type of the resource. The returned string
+ * is owned by the recent manager, and should not be freed.
+ *
+ * Since: 2.10
+ */
+G_CONST_RETURN gchar *
+gtk_recent_info_get_mime_type (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (!info->mime_type)
+ info->mime_type = g_strdup (GTK_RECENT_DEFAULT_MIME);
+
+ return info->mime_type;
+}
+
+/**
+ * gtk_recent_info_get_added:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was added to the recently used resources list.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ * the resource was added to the list, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_added (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, (time_t) -1);
+
+ return info->added;
+}
+
+/**
+ * gtk_recent_info_get_modified:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was last modified.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ * the resource was last modified, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_modified (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, (time_t) -1);
+
+ return info->modified;
+}
+
+/**
+ * gtk_recent_info_get_visited:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the timestamp (seconds from system's Epoch) when the resource
+ * was last visited.
+ *
+ * Return value: the number of seconds elapsed from system's Epoch when
+ * the resource was last visited, or -1 on failure.
+ *
+ * Since: 2.10
+ */
+time_t
+gtk_recent_info_get_visited (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, (time_t) -1);
+
+ return info->visited;
+}
+
+/**
+ * gtk_recent_info_get_private_hint:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the value of the "private" flag. Resources in the recently used
+ * list that have this flag set to %TRUE should only be displayed by the
+ * applications that have registered them.
+ *
+ * Return value: %TRUE if the private flag was found, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_get_private_hint (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return info->is_private;
+}
+
+
+static RecentAppInfo *
+recent_app_info_new (const gchar *app_name)
+{
+ RecentAppInfo *app_info;
+
+ g_assert (app_name != NULL);
+
+ app_info = g_new0 (RecentAppInfo, 1);
+ app_info->name = g_strdup (app_name);
+ app_info->exec = NULL;
+ app_info->count = 1;
+ app_info->stamp = time (NULL);
+
+ return app_info;
+}
+
+static void
+recent_app_info_free (RecentAppInfo *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);
+}
+
+/**
+ * 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
+ * for this application
+ *
+ * Gets the data regarding the application that has registered the resource
+ * pointed by @info.
+ *
+ * If the command line contains any escape characters defined inside the
+ * 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().
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_get_application_info (GtkRecentInfo *info,
+ const gchar *app_name,
+ gchar **app_exec,
+ guint *count,
+ time_t *time)
+{
+ RecentAppInfo *ai;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (app_name != NULL, FALSE);
+
+ ai = (RecentAppInfo *) g_hash_table_lookup (info->apps_lookup,
+ app_name);
+ if (!ai)
+ {
+ g_warning ("No registered application with name '%s' "
+ "for item with URI '%s' found",
+ app_name,
+ info->uri);
+ return FALSE;
+ }
+
+ if (app_exec)
+ *app_exec = ai->exec;
+
+ if (count)
+ *count = ai->count;
+
+ if (time)
+ *time = ai->stamp;
+
+ return TRUE;
+}
+
+/**
+ * gtk_recent_info_get_applications:
+ * @info: a #GtkRecentInfo
+ * @length: return location for the length of the returned list, or %NULL
+ *
+ * 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.
+ *
+ * Since: 2.10
+ */
+gchar **
+gtk_recent_info_get_applications (GtkRecentInfo *info,
+ gsize *length)
+{
+ GSList *l;
+ gchar **retval;
+ gsize n_apps, i;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (!info->applications)
+ {
+ if (length)
+ *length = 0;
+
+ return NULL;
+ }
+
+ n_apps = g_slist_length (info->applications);
+
+ retval = g_new0 (gchar *, n_apps + 1);
+
+ for (l = info->applications, i = 0;
+ l != NULL;
+ l = l->next)
+ {
+ RecentAppInfo *ai = (RecentAppInfo *) l->data;
+
+ g_assert (ai != NULL);
+
+ retval[i++] = g_strdup (ai->name);
+ }
+ retval[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_has_application:
+ * @info: a #GtkRecentInfo
+ * @app_name: a string containing an application name
+ *
+ * Checks whether an application registered this resource using @app_name.
+ *
+ * Return value: %TRUE if an application with name @app_name was found,
+ * %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_has_application (GtkRecentInfo *info,
+ const gchar *app_name)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (app_name != NULL, FALSE);
+
+ return (NULL != g_hash_table_lookup (info->apps_lookup, app_name));
+}
+
+/**
+ * gtk_recent_info_last_application:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the name of the last application that have registered the
+ * recently used resource represented by @info.
+ *
+ * Return value: an application name. Use g_free() to free it.
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_last_application (GtkRecentInfo *info)
+{
+ GSList *l;
+ time_t last_stamp = (time_t) -1;
+ gchar *name = NULL;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ for (l = info->applications; l != NULL; l = l->next)
+ {
+ RecentAppInfo *ai = (RecentAppInfo *) l->data;
+
+ if (ai->stamp > last_stamp)
+ name = ai->name;
+ }
+
+ 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)
+{
+ GtkIconTheme *icon_theme;
+ GHashTable *cache;
+ IconCacheElement *element;
+
+ 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);
+ }
+
+ 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;
+
+ separator = strchr (mime_type, '/');
+ if (!separator)
+ return NULL; /* maybe we should return a GError with "invalid MIME-type" */
+
+ 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;
+
+ 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);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+get_icon_fallback (const gchar *icon_name,
+ gint size)
+{
+ GtkIconTheme *icon_theme;
+ GdkPixbuf *retval;
+
+ icon_theme = gtk_icon_theme_get_default ();
+
+ retval = gtk_icon_theme_load_icon (icon_theme, icon_name,
+ size,
+ GTK_ICON_LOOKUP_USE_BUILTIN,
+ NULL);
+ g_assert (retval != NULL);
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_get_icon:
+ * @info: a #GtkRecentInfo
+ * @size: the size of the icon in pixels
+ *
+ * Retrieves the icon of size @size associated to the resource MIME type.
+ *
+ * Return value: a #GdkPixbuf containing the icon, or %NULL.
+ *
+ * Since: 2.10
+ */
+GdkPixbuf *
+gtk_recent_info_get_icon (GtkRecentInfo *info,
+ gint size)
+{
+ GdkPixbuf *retval = NULL;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (info->mime_type)
+ retval = get_icon_for_mime_type (info->mime_type, size);
+
+ /* this should never fail */
+ if (!retval)
+ retval = get_icon_fallback (GTK_STOCK_FILE, size);
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_is_local:
+ * @info: a #GtkRecentInfo
+ *
+ * Checks whether the resource is local or not by looking at the
+ * scheme of its URI.
+ *
+ * Return value: %TRUE if the resource is local.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_is_local (GtkRecentInfo *info)
+{
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ return g_str_has_prefix (info->uri, "file://");
+}
+
+/**
+ * gtk_recent_info_exists:
+ * @info: a #GtkRecentInfo
+ *
+ * Checks whether the resource pointed by @info still exists. At
+ * the moment this check is done only on resources pointing to local files.
+ *
+ * Return value: %TRUE if the resource exists
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_exists (GtkRecentInfo *info)
+{
+ gchar *filename;
+ struct stat stat_buf;
+ gboolean retval = FALSE;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+
+ /* we guarantee only local resources */
+ if (!gtk_recent_info_is_local (info))
+ return FALSE;
+
+ filename = g_filename_from_uri (info->uri, NULL, NULL);
+ if (filename)
+ {
+ if (stat (filename, &stat_buf) == 0)
+ retval = TRUE;
+
+ g_free (filename);
+ }
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_match:
+ * @a: a #GtkRecentInfo
+ * @b: a #GtkRecentInfo
+ *
+ * Checks whether two #GtkRecentInfo structures point to the same
+ * resource.
+ *
+ * Return value: %TRUE if both #GtkRecentInfo structures point to se same
+ * resource, %FALSE otherwise.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_match (GtkRecentInfo *a,
+ GtkRecentInfo *b)
+{
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+
+ return (0 == strcmp (a->uri, b->uri));
+}
+
+/* taken from gnome-vfs-uri.c */
+static const gchar *
+get_method_string (const gchar *substring, gchar **method_string)
+{
+ const gchar *p;
+ char *method;
+
+ for (p = substring;
+ g_ascii_isalnum (*p) || *p == '+' || *p == '-' || *p == '.';
+ p++)
+ ;
+
+ if (*p == ':'
+#ifdef G_OS_WIN32
+ &&
+ !(p == substring + 1 && g_ascii_isalpha (*substring))
+#endif
+ )
+ {
+ /* Found toplevel method specification. */
+ method = g_strndup (substring, p - substring);
+ *method_string = g_ascii_strdown (method, -1);
+ g_free (method);
+ p++;
+ }
+ else
+ {
+ *method_string = g_strdup ("file");
+ p = substring;
+ }
+
+ return p;
+}
+
+/* Stolen from gnome_vfs_make_valid_utf8() */
+static char *
+make_valid_utf8 (const char *name)
+{
+ GString *string;
+ const char *remainder, *invalid;
+ int remaining_bytes, valid_bytes;
+
+ string = NULL;
+ remainder = name;
+ remaining_bytes = name ? strlen (name) : 0;
+
+ while (remaining_bytes != 0)
+ {
+ if (g_utf8_validate (remainder, remaining_bytes, &invalid))
+ break;
+
+ valid_bytes = invalid - remainder;
+
+ if (string == NULL)
+ string = g_string_sized_new (remaining_bytes);
+
+ g_string_append_len (string, remainder, valid_bytes);
+ g_string_append_c (string, '?');
+
+ remaining_bytes -= valid_bytes + 1;
+ remainder = invalid + 1;
+ }
+
+ if (string == NULL)
+ return g_strdup (name);
+
+ g_string_append (string, remainder);
+ g_assert (g_utf8_validate (string->str, -1, NULL));
+
+ return g_string_free (string, FALSE);
+}
+
+static gchar *
+get_uri_shortname_for_display (const gchar *uri)
+{
+ gchar *name = NULL;
+ gboolean validated = FALSE;
+
+ if (g_str_has_prefix (uri, "file://"))
+ {
+ gchar *local_file;
+
+ local_file = g_filename_from_uri (uri, NULL, NULL);
+
+ if (local_file != NULL)
+ {
+ name = g_filename_display_basename (local_file);
+ validated = TRUE;
+ }
+
+ g_free (local_file);
+ }
+ else
+ {
+ gchar *method;
+ gchar *local_file;
+ const gchar *rest;
+
+ rest = get_method_string (uri, &method);
+ local_file = g_filename_display_basename (rest);
+
+ name = g_strdup_printf ("%s: %s", method, local_file);
+
+ g_free (local_file);
+ g_free (method);
+ }
+
+ g_assert (name != NULL);
+
+ if (!validated && !g_utf8_validate (name, -1, NULL))
+ {
+ gchar *utf8_name;
+
+ utf8_name = make_valid_utf8 (name);
+ g_free (name);
+
+ name = utf8_name;
+ }
+
+ return name;
+}
+
+/**
+ * gtk_recent_info_get_short_name:
+ * @info: an #GtkRecentInfo
+ *
+ * Computes a valid UTF-8 string that can be used as the name of the item in a
+ * menu or list. For example, calling this function on an item that refers to
+ * "file:///foo/bar.txt" will yield "bar.txt".
+ *
+ * Return value: A newly-allocated string in UTF-8 encoding; free it with
+ * g_free().
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_get_short_name (GtkRecentInfo *info)
+{
+ gchar *short_name;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (info->uri == NULL)
+ return NULL;
+
+ short_name = get_uri_shortname_for_display (info->uri);
+
+ return short_name;
+}
+
+/**
+ * gtk_recent_info_get_uri_display:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets a displayable version of the resource's URI.
+ *
+ * Return value: a UTF-8 string containing the resource's URI or %NULL
+ *
+ * Since: 2.10
+ */
+gchar *
+gtk_recent_info_get_uri_display (GtkRecentInfo *info)
+{
+ gchar *filename, *filename_utf8;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ filename = g_filename_from_uri (info->uri, NULL, NULL);
+ if (!filename)
+ return NULL;
+
+ filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+ g_free (filename);
+
+ return filename_utf8;
+}
+
+/**
+ * gtk_recent_info_get_age:
+ * @info: a #GtkRecentInfo
+ *
+ * Gets the number of days elapsed since the last update of the resource
+ * pointed by @info.
+ *
+ * Return value: a positive integer containing the number of days elapsed
+ * since the time this resource was last modified. On failure, -1 is
+ * returned.
+ *
+ * Since: 2.10
+ */
+gint
+gtk_recent_info_get_age (GtkRecentInfo *info)
+{
+ time_t now, delta;
+ gint retval;
+
+ g_return_val_if_fail (info != NULL, -1);
+
+ now = time (NULL);
+
+ delta = now - info->modified;
+ g_assert (delta >= 0);
+
+ retval = (gint) (delta / (60 * 60 * 24));
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_get_groups:
+ * @info: a #GtkRecentInfo
+ * @length: return location for the number of groups returned, or %NULL
+ *
+ * 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.
+ *
+ * Since: 2.10
+ */
+gchar **
+gtk_recent_info_get_groups (GtkRecentInfo *info,
+ gsize *length)
+{
+ GSList *l;
+ gchar **retval;
+ gsize n_groups, i;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (!info->groups)
+ {
+ if (length)
+ *length = 0;
+
+ return NULL;
+ }
+
+ n_groups = g_slist_length (info->groups);
+
+ retval = g_new0 (gchar *, n_groups + 1);
+
+ for (l = info->groups, i = 0;
+ l != NULL;
+ l = l->next)
+ {
+ gchar *group_name = (gchar *) l->data;
+
+ g_assert (group_name != NULL);
+
+ retval[i++] = g_strdup (group_name);
+ }
+ retval[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ return retval;
+}
+
+/**
+ * gtk_recent_info_has_group:
+ * @info: a #GtkRecentInfo
+ * @group_name: name of a group
+ *
+ * Checks whether @group_name appears inside the groups registered for the
+ * recently used item @info.
+ *
+ * Return value: %TRUE if the group was found.
+ *
+ * Since: 2.10
+ */
+gboolean
+gtk_recent_info_has_group (GtkRecentInfo *info,
+ const gchar *group_name)
+{
+ GSList *l;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (group_name != NULL, FALSE);
+
+ if (!info->groups)
+ return FALSE;
+
+ for (l = info->groups; l != NULL; l = l->next)
+ {
+ gchar *g = (gchar *) l->data;
+
+ if (strcmp (g, group_name) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#define __GTK_RECENT_MANAGER_C__
+#include "gtkaliasdef.c"
diff --git a/gtk/gtkrecentmanager.h b/gtk/gtkrecentmanager.h
new file mode 100644
index 0000000000..f41e2dab2b
--- /dev/null
+++ b/gtk/gtkrecentmanager.h
@@ -0,0 +1,214 @@
+/* GTK - The GIMP Toolkit
+ * gtkrecentmanager.h: a manager for the recently used resources
+ *
+ * Copyright (C) 2006 Emmanuele Bassi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * 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,
+ */
+
+#ifndef __GTK_RECENT_MANAGER_H__
+#define __GTK_RECENT_MANAGER_H__
+
+#include <glib-object.h>
+#include <gdk/gdkscreen.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <time.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_RECENT_INFO (gtk_recent_info_get_type ())
+
+#define GTK_TYPE_RECENT_MANAGER (gtk_recent_manager_get_type ())
+#define GTK_RECENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManager))
+#define GTK_IS_RECENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_RECENT_MANAGER))
+#define GTK_RECENT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerClass))
+#define GTK_IS_RECENT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_RECENT_MANAGER))
+#define GTK_RECENT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerClass))
+
+typedef struct _GtkRecentInfo GtkRecentInfo;
+typedef struct _GtkRecentData GtkRecentData;
+typedef struct _GtkRecentManager GtkRecentManager;
+typedef struct _GtkRecentManagerClass GtkRecentManagerClass;
+typedef struct _GtkRecentManagerPrivate GtkRecentManagerPrivate;
+
+/**
+ * GtkRecentData:
+ *
+ * @display_name: a UTF-8 encoded string, containing the name of the recently
+ * used resource to be displayed, or %NULL;
+ * @description: a UTF-8 encoded string, containing a short description of
+ * the resource, or %NULL;
+ * @mime_type: the MIME type of the resource;
+ * @app_name: the name of the application that is registering this recently
+ * used resource;
+ * @app_exec: command line used to launch this resource; may contain the
+ * "%f" and "%u" escape characters which will be expanded to the resource
+ * file path and URI respectively when the command line is retrieved;
+ * @groups: a vector of strings containing groups names;
+ * @is_private: whether this resource should be displayed only by the
+ * applications that have registered it or not.
+ *
+ * Meta-data to be passed to gtk_recent_manager_add_full() when
+ * registering a recently used resource.
+ **/
+struct _GtkRecentData
+{
+ gchar *display_name;
+ gchar *description;
+
+ gchar *mime_type;
+
+ gchar *app_name;
+ gchar *app_exec;
+
+ gchar **groups;
+
+ gboolean is_private;
+};
+
+struct _GtkRecentManager
+{
+ /*< private >*/
+ GObject parent_instance;
+
+ GtkRecentManagerPrivate *priv;
+};
+
+struct _GtkRecentManagerClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ void (*changed) (GtkRecentManager *manager);
+
+ /* padding for future expansion */
+ void (*_gtk_recent1) (void);
+ void (*_gtk_recent2) (void);
+ void (*_gtk_recent3) (void);
+ void (*_gtk_recent4) (void);
+};
+
+/**
+ * GtkRecentManagerError:
+ * @GTK_RECENT_MANAGER_ERROR_NOT_FOUND: the URI specified does not exists in
+ * the recently used resources list.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_URI: the URI specified is not valid.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_MIME: the MIME type specified is not
+ * valid.
+ * @GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING: the supplied string is not
+ * UTF-8 encoded.
+ * @GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED: no application has registered
+ * the specified item.
+ * @GTK_RECENT_MANAGER_ERROR_READ: failure while reading the recently used
+ * resources file.
+ * @GTK_RECENT_MANAGER_ERROR_WRITE: failure while writing the recently used
+ * resources file.
+ * @GTK_RECENT_MANAGER_ERROR_UNKNOWN: unspecified error.
+ *
+ * Error codes for GtkRecentManager operations
+ **/
+typedef enum
+{
+ GTK_RECENT_MANAGER_ERROR_NOT_FOUND,
+ GTK_RECENT_MANAGER_ERROR_INVALID_URI,
+ GTK_RECENT_MANAGER_ERROR_INVALID_MIME,
+ GTK_RECENT_MANAGER_ERROR_INVALID_ENCODING,
+ GTK_RECENT_MANAGER_ERROR_NOT_REGISTERED,
+ GTK_RECENT_MANAGER_ERROR_BAD_EXEC_STRING,
+ GTK_RECENT_MANAGER_ERROR_READ,
+ GTK_RECENT_MANAGER_ERROR_WRITE,
+ GTK_RECENT_MANAGER_ERROR_UNKNOWN
+} GtkRecentManagerError;
+
+#define GTK_RECENT_MANAGER_ERROR (gtk_recent_manager_error_quark ())
+GQuark gtk_recent_manager_error_quark (void);
+
+
+GType gtk_recent_manager_get_type (void) G_GNUC_CONST;
+
+GtkRecentManager *gtk_recent_manager_new (void);
+GtkRecentManager *gtk_recent_manager_get_default (void);
+GtkRecentManager *gtk_recent_manager_get_for_screen (GdkScreen *screen);
+
+void gtk_recent_manager_set_screen (GtkRecentManager *manager,
+ GdkScreen *screen);
+
+gboolean gtk_recent_manager_add_item (GtkRecentManager *manager,
+ const gchar *uri,
+ GError **error);
+gboolean gtk_recent_manager_add_full (GtkRecentManager *manager,
+ const gchar *uri,
+ const GtkRecentData *recent_data,
+ GError **error);
+gboolean gtk_recent_manager_remove_item (GtkRecentManager *manager,
+ const gchar *uri,
+ GError **error);
+GtkRecentInfo * gtk_recent_manager_lookup_item (GtkRecentManager *manager,
+ const gchar *uri,
+ GError **error);
+gboolean gtk_recent_manager_has_item (GtkRecentManager *manager,
+ const gchar *uri);
+gboolean gtk_recent_manager_move_item (GtkRecentManager *manager,
+ const gchar *uri,
+ const gchar *new_uri,
+ GError **error);
+void gtk_recent_manager_set_limit (GtkRecentManager *manager,
+ gint limit);
+gint gtk_recent_manager_get_limit (GtkRecentManager *manager);
+GList * gtk_recent_manager_get_items (GtkRecentManager *manager);
+gint gtk_recent_manager_purge_items (GtkRecentManager *manager,
+ GError **error);
+
+
+GType gtk_recent_info_get_type (void) G_GNUC_CONST;
+
+GtkRecentInfo * gtk_recent_info_ref (GtkRecentInfo *info);
+void gtk_recent_info_unref (GtkRecentInfo *info);
+
+G_CONST_RETURN gchar *gtk_recent_info_get_uri (GtkRecentInfo *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_display_name (GtkRecentInfo *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_description (GtkRecentInfo *info);
+G_CONST_RETURN gchar *gtk_recent_info_get_mime_type (GtkRecentInfo *info);
+time_t gtk_recent_info_get_added (GtkRecentInfo *info);
+time_t gtk_recent_info_get_modified (GtkRecentInfo *info);
+time_t gtk_recent_info_get_visited (GtkRecentInfo *info);
+gboolean gtk_recent_info_get_private_hint (GtkRecentInfo *info);
+gboolean gtk_recent_info_get_application_info (GtkRecentInfo *info,
+ const gchar *app_name,
+ gchar **app_exec,
+ guint *count,
+ time_t *time);
+gchar ** gtk_recent_info_get_applications (GtkRecentInfo *info,
+ gsize *length) G_GNUC_MALLOC;
+gchar * gtk_recent_info_last_application (GtkRecentInfo *info) G_GNUC_MALLOC;
+gboolean gtk_recent_info_has_application (GtkRecentInfo *info,
+ const gchar *app_name);
+gchar ** gtk_recent_info_get_groups (GtkRecentInfo *info,
+ gsize *length) G_GNUC_MALLOC;
+gboolean gtk_recent_info_has_group (GtkRecentInfo *info,
+ const gchar *group_name);
+GdkPixbuf * gtk_recent_info_get_icon (GtkRecentInfo *info,
+ gint size);
+gchar * gtk_recent_info_get_short_name (GtkRecentInfo *info) G_GNUC_MALLOC;
+gchar * gtk_recent_info_get_uri_display (GtkRecentInfo *info) G_GNUC_MALLOC;
+gint gtk_recent_info_get_age (GtkRecentInfo *info);
+gboolean gtk_recent_info_is_local (GtkRecentInfo *info);
+gboolean gtk_recent_info_exists (GtkRecentInfo *info);
+gboolean gtk_recent_info_match (GtkRecentInfo *info_a,
+ GtkRecentInfo *info_b);
+
+G_END_DECLS
+
+#endif /* __GTK_RECENT_MANAGER_H__ */