1 /* GTK - The GIMP Toolkit
2 * Recent chooser action for GtkUIManager
4 * Copyright (C) 2007, Emmanuele Bassi
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
25 #include "gtkrecentaction.h"
26 #include "gtkimagemenuitem.h"
27 #include "gtkmenutoolbutton.h"
28 #include "gtkrecentchooser.h"
29 #include "gtkrecentchoosermenu.h"
30 #include "gtkrecentchooserutils.h"
31 #include "gtkrecentchooserprivate.h"
32 #include "gtkprivate.h"
35 * SECTION:gtkrecentaction
36 * @Short_description: An action of which represents a list of recently used files
37 * @Title: GtkRecentAction
39 * A #GtkRecentAction represents a list of recently used files, which
40 * can be shown by widgets such as #GtkRecentChooserDialog or
41 * #GtkRecentChooserMenu.
43 * To construct a submenu showing recently used files, use a #GtkRecentAction
44 * as the action for a <menuitem>. To construct a menu toolbutton showing
45 * the recently used files in the popup menu, use a #GtkRecentAction as the
46 * action for a <toolitem> element.
50 #define FALLBACK_ITEM_LIMIT 10
53 struct _GtkRecentActionPrivate
55 GtkRecentManager *manager;
57 guint show_numbers : 1;
59 /* RecentChooser properties */
60 guint show_private : 1;
61 guint show_not_found : 1;
68 GtkRecentSortType sort_type;
69 GtkRecentSortFunc sort_func;
71 GDestroyNotify data_destroy;
73 GtkRecentFilter *current_filter;
76 GtkRecentChooser *current_chooser;
86 static void gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface);
88 G_DEFINE_TYPE_WITH_CODE (GtkRecentAction,
91 G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
92 gtk_recent_chooser_iface_init));
95 gtk_recent_action_set_current_uri (GtkRecentChooser *chooser,
99 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
100 GtkRecentActionPrivate *priv = action->priv;
103 for (l = priv->choosers; l; l = l->next)
105 GtkRecentChooser *recent_chooser = l->data;
107 if (!gtk_recent_chooser_set_current_uri (recent_chooser, uri, error))
115 gtk_recent_action_get_current_uri (GtkRecentChooser *chooser)
117 GtkRecentAction *recent_action = GTK_RECENT_ACTION (chooser);
118 GtkRecentActionPrivate *priv = recent_action->priv;
120 if (priv->current_chooser)
121 return gtk_recent_chooser_get_current_uri (priv->current_chooser);
127 gtk_recent_action_select_uri (GtkRecentChooser *chooser,
131 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
132 GtkRecentActionPrivate *priv = action->priv;
135 for (l = priv->choosers; l; l = l->next)
137 GtkRecentChooser *recent_chooser = l->data;
139 if (!gtk_recent_chooser_select_uri (recent_chooser, uri, error))
147 gtk_recent_action_unselect_uri (GtkRecentChooser *chooser,
150 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
151 GtkRecentActionPrivate *priv = action->priv;
154 for (l = priv->choosers; l; l = l->next)
156 GtkRecentChooser *chooser = l->data;
158 gtk_recent_chooser_unselect_uri (chooser, uri);
163 gtk_recent_action_select_all (GtkRecentChooser *chooser)
165 g_warning (_("This function is not implemented for "
166 "widgets of class '%s'"),
167 g_type_name (G_OBJECT_TYPE (chooser)));
171 gtk_recent_action_unselect_all (GtkRecentChooser *chooser)
173 g_warning (_("This function is not implemented for "
174 "widgets of class '%s'"),
175 g_type_name (G_OBJECT_TYPE (chooser)));
180 gtk_recent_action_get_items (GtkRecentChooser *chooser)
182 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
183 GtkRecentActionPrivate *priv = action->priv;
185 return _gtk_recent_chooser_get_items (chooser,
186 priv->current_filter,
191 static GtkRecentManager *
192 gtk_recent_action_get_recent_manager (GtkRecentChooser *chooser)
194 return GTK_RECENT_ACTION (chooser)->priv->manager;
198 gtk_recent_action_set_sort_func (GtkRecentChooser *chooser,
199 GtkRecentSortFunc sort_func,
201 GDestroyNotify data_destroy)
203 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
204 GtkRecentActionPrivate *priv = action->priv;
207 if (priv->data_destroy)
209 priv->data_destroy (priv->sort_data);
210 priv->data_destroy = NULL;
213 priv->sort_func = NULL;
214 priv->sort_data = NULL;
218 priv->sort_func = sort_func;
219 priv->sort_data = sort_data;
220 priv->data_destroy = data_destroy;
223 for (l = priv->choosers; l; l = l->next)
225 GtkRecentChooser *chooser_menu = l->data;
227 gtk_recent_chooser_set_sort_func (chooser_menu, priv->sort_func,
234 set_current_filter (GtkRecentAction *action,
235 GtkRecentFilter *filter)
237 GtkRecentActionPrivate *priv = action->priv;
239 g_object_ref (action);
241 if (priv->current_filter)
242 g_object_unref (priv->current_filter);
244 priv->current_filter = filter;
246 if (priv->current_filter)
247 g_object_ref_sink (priv->current_filter);
249 g_object_notify (G_OBJECT (action), "filter");
251 g_object_unref (action);
255 gtk_recent_action_add_filter (GtkRecentChooser *chooser,
256 GtkRecentFilter *filter)
258 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
259 GtkRecentActionPrivate *priv = action->priv;
261 if (priv->current_filter != filter)
262 set_current_filter (GTK_RECENT_ACTION (chooser), filter);
266 gtk_recent_action_remove_filter (GtkRecentChooser *chooser,
267 GtkRecentFilter *filter)
269 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
270 GtkRecentActionPrivate *priv = action->priv;
272 if (priv->current_filter == filter)
273 set_current_filter (GTK_RECENT_ACTION (chooser), NULL);
277 gtk_recent_action_list_filters (GtkRecentChooser *chooser)
279 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
280 GtkRecentActionPrivate *priv = action->priv;
281 GSList *retval = NULL;
282 GtkRecentFilter *current_filter;
284 current_filter = priv->current_filter;
285 retval = g_slist_prepend (retval, current_filter);
292 gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
294 iface->set_current_uri = gtk_recent_action_set_current_uri;
295 iface->get_current_uri = gtk_recent_action_get_current_uri;
296 iface->select_uri = gtk_recent_action_select_uri;
297 iface->unselect_uri = gtk_recent_action_unselect_uri;
298 iface->select_all = gtk_recent_action_select_all;
299 iface->unselect_all = gtk_recent_action_unselect_all;
300 iface->get_items = gtk_recent_action_get_items;
301 iface->get_recent_manager = gtk_recent_action_get_recent_manager;
302 iface->set_sort_func = gtk_recent_action_set_sort_func;
303 iface->add_filter = gtk_recent_action_add_filter;
304 iface->remove_filter = gtk_recent_action_remove_filter;
305 iface->list_filters = gtk_recent_action_list_filters;
309 gtk_recent_action_activate (GtkAction *action)
311 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
312 GtkRecentActionPrivate *priv = recent_action->priv;
314 /* we have probably been invoked by a menu tool button or by a
315 * direct call of gtk_action_activate(); since no item has been
316 * selected, we must unset the current recent chooser pointer
318 priv->current_chooser = NULL;
322 delegate_selection_changed (GtkRecentAction *action,
323 GtkRecentChooser *chooser)
325 GtkRecentActionPrivate *priv = action->priv;
327 priv->current_chooser = chooser;
329 g_signal_emit_by_name (action, "selection-changed");
333 delegate_item_activated (GtkRecentAction *action,
334 GtkRecentChooser *chooser)
336 GtkRecentActionPrivate *priv = action->priv;
338 priv->current_chooser = chooser;
340 g_signal_emit_by_name (action, "item-activated");
344 gtk_recent_action_connect_proxy (GtkAction *action,
347 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
348 GtkRecentActionPrivate *priv = recent_action->priv;
350 /* it can only be a recent chooser implementor anyway... */
351 if (GTK_IS_RECENT_CHOOSER (widget) &&
352 !g_slist_find (priv->choosers, widget))
356 gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
362 g_signal_connect_swapped (widget, "selection_changed",
363 G_CALLBACK (delegate_selection_changed),
365 g_signal_connect_swapped (widget, "item-activated",
366 G_CALLBACK (delegate_item_activated),
370 if (GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy)
371 GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy (action, widget);
375 gtk_recent_action_disconnect_proxy (GtkAction *action,
378 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
379 GtkRecentActionPrivate *priv = recent_action->priv;
381 /* if it was one of the recent choosers we created, remove it
384 if (g_slist_find (priv->choosers, widget))
385 priv->choosers = g_slist_remove (priv->choosers, widget);
387 if (GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy)
388 GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy (action, widget);
392 gtk_recent_action_create_menu (GtkAction *action)
394 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
395 GtkRecentActionPrivate *priv = recent_action->priv;
398 widget = g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
399 "show-private", priv->show_private,
400 "show-not-found", priv->show_not_found,
401 "show-tips", priv->show_tips,
402 "show-icons", priv->show_icons,
403 "show-numbers", priv->show_numbers,
404 "limit", priv->limit,
405 "sort-type", priv->sort_type,
406 "recent-manager", priv->manager,
407 "filter", priv->current_filter,
408 "local-only", priv->local_only,
413 gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
419 g_signal_connect_swapped (widget, "selection_changed",
420 G_CALLBACK (delegate_selection_changed),
422 g_signal_connect_swapped (widget, "item-activated",
423 G_CALLBACK (delegate_item_activated),
426 /* keep track of the choosers we create */
427 priv->choosers = g_slist_prepend (priv->choosers, widget);
433 gtk_recent_action_create_menu_item (GtkAction *action)
438 menu = gtk_recent_action_create_menu (action);
439 menuitem = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
440 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
441 gtk_widget_show (menu);
447 gtk_recent_action_create_tool_item (GtkAction *action)
452 menu = gtk_recent_action_create_menu (action);
453 toolitem = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
454 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (toolitem), menu);
455 gtk_widget_show (menu);
461 set_recent_manager (GtkRecentAction *action,
462 GtkRecentManager *manager)
464 GtkRecentActionPrivate *priv = action->priv;
467 priv->manager = NULL;
469 priv->manager = gtk_recent_manager_get_default ();
473 gtk_recent_action_finalize (GObject *gobject)
475 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
476 GtkRecentActionPrivate *priv = action->priv;
478 priv->manager = NULL;
480 if (priv->data_destroy)
482 priv->data_destroy (priv->sort_data);
483 priv->data_destroy = NULL;
486 priv->sort_data = NULL;
487 priv->sort_func = NULL;
489 g_slist_free (priv->choosers);
491 G_OBJECT_CLASS (gtk_recent_action_parent_class)->finalize (gobject);
495 gtk_recent_action_dispose (GObject *gobject)
497 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
498 GtkRecentActionPrivate *priv = action->priv;
500 if (priv->current_filter)
502 g_object_unref (priv->current_filter);
503 priv->current_filter = NULL;
506 G_OBJECT_CLASS (gtk_recent_action_parent_class)->dispose (gobject);
510 gtk_recent_action_set_property (GObject *gobject,
515 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
516 GtkRecentActionPrivate *priv = action->priv;
520 case PROP_SHOW_NUMBERS:
521 priv->show_numbers = g_value_get_boolean (value);
523 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
524 priv->show_private = g_value_get_boolean (value);
526 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
527 priv->show_not_found = g_value_get_boolean (value);
529 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
530 priv->show_tips = g_value_get_boolean (value);
532 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
533 priv->show_icons = g_value_get_boolean (value);
535 case GTK_RECENT_CHOOSER_PROP_LIMIT:
536 priv->limit = g_value_get_int (value);
538 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
539 priv->local_only = g_value_get_boolean (value);
541 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
542 priv->sort_type = g_value_get_enum (value);
544 case GTK_RECENT_CHOOSER_PROP_FILTER:
545 set_current_filter (action, g_value_get_object (value));
547 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
548 g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
550 G_OBJECT_TYPE_NAME (gobject));
552 case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
553 set_recent_manager (action, g_value_get_object (value));
556 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
562 gtk_recent_action_get_property (GObject *gobject,
567 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
568 GtkRecentActionPrivate *priv = action->priv;
572 case PROP_SHOW_NUMBERS:
573 g_value_set_boolean (value, priv->show_numbers);
575 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
576 g_value_set_boolean (value, priv->show_private);
578 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
579 g_value_set_boolean (value, priv->show_not_found);
581 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
582 g_value_set_boolean (value, priv->show_tips);
584 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
585 g_value_set_boolean (value, priv->show_icons);
587 case GTK_RECENT_CHOOSER_PROP_LIMIT:
588 g_value_set_int (value, priv->limit);
590 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
591 g_value_set_boolean (value, priv->local_only);
593 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
594 g_value_set_enum (value, priv->sort_type);
596 case GTK_RECENT_CHOOSER_PROP_FILTER:
597 g_value_set_object (value, priv->current_filter);
599 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
600 g_value_set_boolean (value, FALSE);
603 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
609 gtk_recent_action_class_init (GtkRecentActionClass *klass)
611 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
612 GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
614 g_type_class_add_private (klass, sizeof (GtkRecentActionPrivate));
616 gobject_class->finalize = gtk_recent_action_finalize;
617 gobject_class->dispose = gtk_recent_action_dispose;
618 gobject_class->set_property = gtk_recent_action_set_property;
619 gobject_class->get_property = gtk_recent_action_get_property;
621 action_class->activate = gtk_recent_action_activate;
622 action_class->connect_proxy = gtk_recent_action_connect_proxy;
623 action_class->disconnect_proxy = gtk_recent_action_disconnect_proxy;
624 action_class->create_menu_item = gtk_recent_action_create_menu_item;
625 action_class->create_tool_item = gtk_recent_action_create_tool_item;
626 action_class->create_menu = gtk_recent_action_create_menu;
627 action_class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
628 action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
630 _gtk_recent_chooser_install_properties (gobject_class);
632 g_object_class_install_property (gobject_class,
634 g_param_spec_boolean ("show-numbers",
636 P_("Whether the items should be displayed with a number"),
643 gtk_recent_action_init (GtkRecentAction *action)
645 GtkRecentActionPrivate *priv;
647 action->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (action,
648 GTK_TYPE_RECENT_ACTION,
649 GtkRecentActionPrivate);
651 priv->show_numbers = FALSE;
652 priv->show_icons = TRUE;
653 priv->show_tips = FALSE;
654 priv->show_not_found = TRUE;
655 priv->show_private = FALSE;
656 priv->local_only = TRUE;
658 priv->limit = FALLBACK_ITEM_LIMIT;
660 priv->sort_type = GTK_RECENT_SORT_NONE;
661 priv->sort_func = NULL;
662 priv->sort_data = NULL;
663 priv->data_destroy = NULL;
665 priv->current_filter = NULL;
667 priv->manager = NULL;
671 * gtk_recent_action_new:
672 * @name: a unique name for the action
673 * @label: (allow-none): the label displayed in menu items and on buttons, or %NULL
674 * @tooltip: (allow-none): a tooltip for the action, or %NULL
675 * @stock_id: the stock icon to display in widgets representing the
678 * Creates a new #GtkRecentAction object. To add the action to
679 * a #GtkActionGroup and set the accelerator for the action,
680 * call gtk_action_group_add_action_with_accel().
682 * Return value: the newly created #GtkRecentAction.
687 gtk_recent_action_new (const gchar *name,
689 const gchar *tooltip,
690 const gchar *stock_id)
692 g_return_val_if_fail (name != NULL, NULL);
694 return g_object_new (GTK_TYPE_RECENT_ACTION,
698 "stock-id", stock_id,
703 * gtk_recent_action_new_for_manager:
704 * @name: a unique name for the action
705 * @label: (allow-none): the label displayed in menu items and on buttons, or %NULL
706 * @tooltip: (allow-none): a tooltip for the action, or %NULL
707 * @stock_id: the stock icon to display in widgets representing the
709 * @manager: (allow-none): a #GtkRecentManager, or %NULL for using the default
712 * Creates a new #GtkRecentAction object. To add the action to
713 * a #GtkActionGroup and set the accelerator for the action,
714 * call gtk_action_group_add_action_with_accel().
716 * Return value: the newly created #GtkRecentAction
721 gtk_recent_action_new_for_manager (const gchar *name,
723 const gchar *tooltip,
724 const gchar *stock_id,
725 GtkRecentManager *manager)
727 g_return_val_if_fail (name != NULL, NULL);
728 g_return_val_if_fail (manager == NULL || GTK_IS_RECENT_MANAGER (manager), NULL);
730 return g_object_new (GTK_TYPE_RECENT_ACTION,
734 "stock-id", stock_id,
735 "recent-manager", manager,
740 * gtk_recent_action_get_show_numbers:
741 * @action: a #GtkRecentAction
743 * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
745 * Return value: %TRUE if numbers should be shown.
750 gtk_recent_action_get_show_numbers (GtkRecentAction *action)
752 g_return_val_if_fail (GTK_IS_RECENT_ACTION (action), FALSE);
754 return action->priv->show_numbers;
758 * gtk_recent_action_set_show_numbers:
759 * @action: a #GtkRecentAction
760 * @show_numbers: %TRUE if the shown items should be numbered
762 * Sets whether a number should be added to the items shown by the
763 * widgets representing @action. The numbers are shown to provide
764 * a unique character for a mnemonic to be used inside the menu item's
765 * label. Only the first ten items get a number to avoid clashes.
770 gtk_recent_action_set_show_numbers (GtkRecentAction *action,
771 gboolean show_numbers)
773 GtkRecentActionPrivate *priv;
775 g_return_if_fail (GTK_IS_RECENT_ACTION (action));
779 if (priv->show_numbers != show_numbers)
781 g_object_ref (action);
783 priv->show_numbers = show_numbers;
785 g_object_notify (G_OBJECT (action), "show-numbers");
786 g_object_unref (action);