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 #define FALLBACK_ITEM_LIMIT 10
37 #define GTK_RECENT_ACTION_GET_PRIVATE(obj) \
38 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
39 GTK_TYPE_RECENT_ACTION, \
40 GtkRecentActionPrivate))
42 struct _GtkRecentActionPrivate
44 GtkRecentManager *manager;
45 guint manager_changed_id;
47 guint show_numbers : 1;
49 /* RecentChooser properties */
50 guint show_private : 1;
51 guint show_not_found : 1;
58 GtkRecentSortType sort_type;
59 GtkRecentSortFunc sort_func;
61 GDestroyNotify data_destroy;
63 GtkRecentFilter *current_filter;
66 GtkRecentChooser *current_chooser;
76 static void gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface);
78 G_DEFINE_TYPE_WITH_CODE (GtkRecentAction,
81 G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER,
82 gtk_recent_chooser_iface_init));
85 gtk_recent_action_set_current_uri (GtkRecentChooser *chooser,
89 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
90 GtkRecentActionPrivate *priv = action->priv;
93 for (l = priv->choosers; l; l = l->next)
95 GtkRecentChooser *recent_chooser = l->data;
97 if (!gtk_recent_chooser_set_current_uri (recent_chooser, uri, error))
105 gtk_recent_action_get_current_uri (GtkRecentChooser *chooser)
107 GtkRecentAction *recent_action = GTK_RECENT_ACTION (chooser);
108 GtkRecentActionPrivate *priv = recent_action->priv;
110 if (priv->current_chooser)
111 return gtk_recent_chooser_get_current_uri (priv->current_chooser);
117 gtk_recent_action_select_uri (GtkRecentChooser *chooser,
121 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
122 GtkRecentActionPrivate *priv = action->priv;
125 for (l = priv->choosers; l; l = l->next)
127 GtkRecentChooser *recent_chooser = l->data;
129 if (!gtk_recent_chooser_select_uri (recent_chooser, uri, error))
137 gtk_recent_action_unselect_uri (GtkRecentChooser *chooser,
140 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
141 GtkRecentActionPrivate *priv = action->priv;
144 for (l = priv->choosers; l; l = l->next)
146 GtkRecentChooser *chooser = l->data;
148 gtk_recent_chooser_unselect_uri (chooser, uri);
153 gtk_recent_action_select_all (GtkRecentChooser *chooser)
155 g_warning (_("This function is not implemented for "
156 "widgets of class '%s'"),
157 g_type_name (G_OBJECT_TYPE (chooser)));
161 gtk_recent_action_unselect_all (GtkRecentChooser *chooser)
163 g_warning (_("This function is not implemented for "
164 "widgets of class '%s'"),
165 g_type_name (G_OBJECT_TYPE (chooser)));
170 gtk_recent_action_get_items (GtkRecentChooser *chooser)
172 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
173 GtkRecentActionPrivate *priv = action->priv;
175 return _gtk_recent_chooser_get_items (chooser,
176 priv->current_filter,
181 static GtkRecentManager *
182 gtk_recent_action_get_recent_manager (GtkRecentChooser *chooser)
184 return GTK_RECENT_ACTION_GET_PRIVATE (chooser)->manager;
188 gtk_recent_action_set_sort_func (GtkRecentChooser *chooser,
189 GtkRecentSortFunc sort_func,
191 GDestroyNotify data_destroy)
193 GtkRecentAction *action = GTK_RECENT_ACTION (chooser);
194 GtkRecentActionPrivate *priv = action->priv;
196 if (priv->data_destroy)
198 priv->data_destroy (priv->sort_data);
199 priv->data_destroy = NULL;
202 priv->sort_func = NULL;
203 priv->sort_data = NULL;
207 priv->sort_func = sort_func;
208 priv->sort_data = sort_data;
209 priv->data_destroy = data_destroy;
214 set_current_filter (GtkRecentAction *action,
215 GtkRecentFilter *filter)
217 GtkRecentActionPrivate *priv = action->priv;
219 g_object_ref (action);
221 if (priv->current_filter)
222 g_object_unref (priv->current_filter);
224 priv->current_filter = filter;
226 if (priv->current_filter)
227 g_object_ref_sink (priv->current_filter);
229 g_object_notify (G_OBJECT (action), "filter");
231 g_object_unref (action);
235 gtk_recent_action_add_filter (GtkRecentChooser *chooser,
236 GtkRecentFilter *filter)
238 GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (chooser);
240 if (priv->current_filter != filter)
241 set_current_filter (GTK_RECENT_ACTION (chooser), filter);
245 gtk_recent_action_remove_filter (GtkRecentChooser *chooser,
246 GtkRecentFilter *filter)
248 GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (chooser);
250 if (priv->current_filter == filter)
251 set_current_filter (GTK_RECENT_ACTION (chooser), NULL);
255 gtk_recent_action_list_filters (GtkRecentChooser *chooser)
257 GSList *retval = NULL;
258 GtkRecentFilter *current_filter;
260 current_filter = GTK_RECENT_ACTION_GET_PRIVATE (chooser)->current_filter;
261 retval = g_slist_prepend (retval, current_filter);
268 gtk_recent_chooser_iface_init (GtkRecentChooserIface *iface)
270 iface->set_current_uri = gtk_recent_action_set_current_uri;
271 iface->get_current_uri = gtk_recent_action_get_current_uri;
272 iface->select_uri = gtk_recent_action_select_uri;
273 iface->unselect_uri = gtk_recent_action_unselect_uri;
274 iface->select_all = gtk_recent_action_select_all;
275 iface->unselect_all = gtk_recent_action_unselect_all;
276 iface->get_items = gtk_recent_action_get_items;
277 iface->get_recent_manager = gtk_recent_action_get_recent_manager;
278 iface->set_sort_func = gtk_recent_action_set_sort_func;
279 iface->add_filter = gtk_recent_action_add_filter;
280 iface->remove_filter = gtk_recent_action_remove_filter;
281 iface->list_filters = gtk_recent_action_list_filters;
285 gtk_recent_action_activate (GtkAction *action)
287 /* we have probably been invoked by a menu tool button or by a
288 * direct call of gtk_action_activate(); since no item has been
289 * selected, we must unset the current recent chooser pointer
291 GTK_RECENT_ACTION_GET_PRIVATE (action)->current_chooser = NULL;
295 delegate_selection_changed (GtkRecentAction *action,
296 GtkRecentChooser *chooser)
298 GtkRecentActionPrivate *priv = action->priv;
300 priv->current_chooser = chooser;
302 g_signal_emit_by_name (action, "selection-changed");
306 delegate_item_activated (GtkRecentAction *action,
307 GtkRecentChooser *chooser)
309 GtkRecentActionPrivate *priv = action->priv;
311 priv->current_chooser = chooser;
313 g_signal_emit_by_name (action, "item-activated");
317 gtk_recent_action_connect_proxy (GtkAction *action,
320 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
321 GtkRecentActionPrivate *priv = recent_action->priv;
323 if (GTK_IS_RECENT_CHOOSER (widget) &&
324 !g_slist_find (priv->choosers, widget))
326 g_object_set (G_OBJECT (widget),
327 "show-private", priv->show_private,
328 "show-not-found", priv->show_not_found,
329 "show-tips", priv->show_tips,
330 "show-icons", priv->show_icons,
331 "show-numbers", priv->show_numbers,
332 "limit", priv->limit,
333 "sort-type", priv->sort_type,
338 gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
344 g_signal_connect_swapped (widget, "selection_changed",
345 G_CALLBACK (delegate_selection_changed),
347 g_signal_connect_swapped (widget, "item-activated",
348 G_CALLBACK (delegate_item_activated),
352 GTK_ACTION_CLASS (gtk_recent_action_parent_class)->connect_proxy (action, widget);
356 gtk_recent_action_disconnect_proxy (GtkAction *action,
359 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
360 GtkRecentActionPrivate *priv = recent_action->priv;
362 /* if it was one of the recent choosers we created, remove it
365 if (g_slist_find (priv->choosers, widget))
366 priv->choosers = g_slist_remove (priv->choosers, widget);
368 GTK_ACTION_CLASS (gtk_recent_action_parent_class)->disconnect_proxy (action, widget);
372 gtk_recent_action_create_menu (GtkAction *action)
374 GtkRecentAction *recent_action = GTK_RECENT_ACTION (action);
375 GtkRecentActionPrivate *priv = recent_action->priv;
378 widget = g_object_new (GTK_TYPE_RECENT_CHOOSER_MENU,
379 "show-private", priv->show_private,
380 "show-not-found", priv->show_not_found,
381 "show-tips", priv->show_tips,
382 "show-icons", priv->show_icons,
383 "show-numbers", priv->show_numbers,
384 "limit", priv->limit,
385 "sort-type", priv->sort_type,
386 "recent-manager", priv->manager,
391 gtk_recent_chooser_set_sort_func (GTK_RECENT_CHOOSER (widget),
397 g_signal_connect_swapped (widget, "selection_changed",
398 G_CALLBACK (delegate_selection_changed),
400 g_signal_connect_swapped (widget, "item-activated",
401 G_CALLBACK (delegate_item_activated),
404 /* keep track of the choosers we create */
405 priv->choosers = g_slist_prepend (priv->choosers, widget);
411 gtk_recent_action_create_menu_item (GtkAction *action)
416 menu = gtk_recent_action_create_menu (action);
417 menuitem = g_object_new (GTK_TYPE_IMAGE_MENU_ITEM, NULL);
418 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
419 gtk_widget_show (menu);
425 gtk_recent_action_create_tool_item (GtkAction *action)
430 menu = gtk_recent_action_create_menu (action);
431 toolitem = g_object_new (GTK_TYPE_MENU_TOOL_BUTTON, NULL);
432 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (toolitem), menu);
433 gtk_widget_show (menu);
439 manager_changed_cb (GtkRecentManager *manager,
442 /* do we need to propagate the signal to all the proxies? guess not */
446 set_recent_manager (GtkRecentAction *action,
447 GtkRecentManager *manager)
449 GtkRecentActionPrivate *priv = action->priv;
453 if (priv->manager_changed_id)
455 g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
456 priv->manager_changed_id = 0;
459 priv->manager = NULL;
463 priv->manager = NULL;
465 priv->manager = gtk_recent_manager_get_default ();
468 priv->manager_changed_id = g_signal_connect (priv->manager, "changed",
469 G_CALLBACK (manager_changed_cb),
474 gtk_recent_action_finalize (GObject *gobject)
476 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
477 GtkRecentActionPrivate *priv = action->priv;
479 priv->manager = NULL;
481 if (priv->data_destroy)
483 priv->data_destroy (priv->sort_data);
484 priv->data_destroy = NULL;
487 priv->sort_data = NULL;
488 priv->sort_func = NULL;
490 g_slist_free (priv->choosers);
492 G_OBJECT_CLASS (gtk_recent_action_parent_class)->finalize (gobject);
496 gtk_recent_action_dispose (GObject *gobject)
498 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
499 GtkRecentActionPrivate *priv = action->priv;
501 if (priv->manager_changed_id)
504 g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
506 priv->manager_changed_id = 0;
509 if (priv->current_filter)
511 g_object_unref (priv->current_filter);
512 priv->current_filter = NULL;
515 G_OBJECT_CLASS (gtk_recent_action_parent_class)->dispose (gobject);
519 gtk_recent_action_set_property (GObject *gobject,
524 GtkRecentAction *action = GTK_RECENT_ACTION (gobject);
525 GtkRecentActionPrivate *priv = action->priv;
530 case PROP_SHOW_NUMBERS:
531 priv->show_numbers = g_value_get_boolean (value);
533 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
534 priv->show_private = g_value_get_boolean (value);
536 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
537 priv->show_not_found = g_value_get_boolean (value);
539 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
540 priv->show_tips = g_value_get_boolean (value);
542 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
543 priv->show_icons = g_value_get_boolean (value);
545 case GTK_RECENT_CHOOSER_PROP_LIMIT:
546 priv->limit = g_value_get_int (value);
548 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
549 priv->local_only = g_value_get_boolean (value);
551 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
552 priv->sort_type = g_value_get_enum (value);
554 case GTK_RECENT_CHOOSER_PROP_FILTER:
555 set_current_filter (action, g_value_get_object (value));
557 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
558 g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
560 G_OBJECT_TYPE_NAME (gobject));
562 case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER:
563 set_recent_manager (action, g_value_get_object (value));
566 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
570 /* propagate the properties to the proxies we have created */
571 for (l = priv->choosers; l != NULL; l = l->next)
573 GObject *proxy = l->data;
575 g_object_set_property (proxy, pspec->name, value);
580 gtk_recent_action_get_property (GObject *gobject,
585 GtkRecentActionPrivate *priv = GTK_RECENT_ACTION_GET_PRIVATE (gobject);
589 case PROP_SHOW_NUMBERS:
590 g_value_set_boolean (value, priv->show_numbers);
592 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE:
593 g_value_set_boolean (value, priv->show_private);
595 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND:
596 g_value_set_boolean (value, priv->show_not_found);
598 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS:
599 g_value_set_boolean (value, priv->show_tips);
601 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS:
602 g_value_set_boolean (value, priv->show_icons);
604 case GTK_RECENT_CHOOSER_PROP_LIMIT:
605 g_value_set_int (value, priv->limit);
607 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY:
608 g_value_set_boolean (value, priv->local_only);
610 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE:
611 g_value_set_enum (value, priv->sort_type);
613 case GTK_RECENT_CHOOSER_PROP_FILTER:
614 g_value_set_object (value, priv->current_filter);
616 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE:
617 g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
619 G_OBJECT_TYPE_NAME (gobject));
622 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
628 gtk_recent_action_class_init (GtkRecentActionClass *klass)
630 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
631 GtkActionClass *action_class = GTK_ACTION_CLASS (klass);
633 g_type_class_add_private (klass, sizeof (GtkRecentActionPrivate));
635 gobject_class->finalize = gtk_recent_action_finalize;
636 gobject_class->dispose = gtk_recent_action_dispose;
637 gobject_class->set_property = gtk_recent_action_set_property;
638 gobject_class->get_property = gtk_recent_action_get_property;
640 action_class->activate = gtk_recent_action_activate;
641 action_class->connect_proxy = gtk_recent_action_connect_proxy;
642 action_class->disconnect_proxy = gtk_recent_action_disconnect_proxy;
643 action_class->create_menu_item = gtk_recent_action_create_menu_item;
644 action_class->create_tool_item = gtk_recent_action_create_tool_item;
645 action_class->create_menu = gtk_recent_action_create_menu;
646 action_class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
647 action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
649 _gtk_recent_chooser_install_properties (gobject_class);
651 g_object_class_install_property (gobject_class,
653 g_param_spec_boolean ("show-numbers",
655 P_("Whether the items should be displayed with a number"),
662 gtk_recent_action_init (GtkRecentAction *action)
664 GtkRecentActionPrivate *priv;
666 action->priv = priv = GTK_RECENT_ACTION_GET_PRIVATE (action);
668 priv->show_numbers = FALSE;
669 priv->show_icons = TRUE;
670 priv->show_tips = FALSE;
671 priv->show_not_found = TRUE;
672 priv->show_private = FALSE;
673 priv->local_only = TRUE;
675 priv->limit = FALLBACK_ITEM_LIMIT;
677 priv->sort_type = GTK_RECENT_SORT_NONE;
678 priv->sort_func = NULL;
679 priv->sort_data = NULL;
680 priv->data_destroy = NULL;
682 priv->current_filter = NULL;
684 priv->manager = NULL;
685 priv->manager_changed_id = 0;
689 * gtk_recent_action_new:
690 * @name: a unique name for the action
691 * @label: the label displayed in menu items and on buttons
692 * @tooltip: a tooltip for the action
693 * @stock_id: the stock icon to display in widgets representing the action
695 * Creates a new #GtkRecentAction object. To add the action to
696 * a #GtkActionGroup and set the accelerator for the action,
697 * call gtk_action_group_add_action_with_accel().
699 * Return value: the newly created #GtkRecentAction.
704 gtk_recent_action_new (const gchar *name,
706 const gchar *tooltip,
707 const gchar *stock_id)
709 return g_object_new (GTK_TYPE_RECENT_ACTION,
713 "stock-id", stock_id,
718 * gtk_recent_action_new_for_manager:
719 * @name: a unique name for the action
720 * @label: the label displayed in menu items and on buttons
721 * @tooltip: a tooltip for the action
722 * @stock_id: the stock icon to display in widgets representing the action
723 * @manager: a #GtkRecentManager or %NULL
725 * Creates a new #GtkRecentAction object. To add the action to
726 * a #GtkActionGroup and set the accelerator for the action,
727 * call gtk_action_group_add_action_with_accel().
729 * Return value: the newly created #GtkRecentAction
734 gtk_recent_action_new_for_manager (const gchar *name,
736 const gchar *tooltip,
737 const gchar *stock_id,
738 GtkRecentManager *manager)
740 return g_object_new (GTK_TYPE_RECENT_ACTION,
744 "stock-id", stock_id,
745 "recent-manager", manager,
750 * gtk_recent_action_get_show_numbers:
751 * @action: a #GtkRecentAction
753 * Returns the value set by gtk_recent_chooser_menu_set_show_numbers().
755 * Return value: %TRUE if numbers should be shown.
760 gtk_recent_action_get_show_numbers (GtkRecentAction *action)
762 g_return_val_if_fail (GTK_IS_RECENT_ACTION (action), FALSE);
764 return action->priv->show_numbers;
768 * gtk_recent_action_set_show_numbers:
769 * @action: a #GtkRecentAction
770 * @show_numbers: %TRUE if the shown items should be numbered
772 * Sets whether a number should be added to the items shown by the
773 * widgets representing @action. The numbers are shown to provide
774 * a unique character for a mnemonic to be used inside the menu item's
775 * label. Only the first ten items get a number to avoid clashes.
780 gtk_recent_action_set_show_numbers (GtkRecentAction *action,
781 gboolean show_numbers)
783 GtkRecentActionPrivate *priv;
785 g_return_if_fail (GTK_IS_RECENT_ACTION (action));
789 if (priv->show_numbers != show_numbers)
791 g_object_ref (action);
793 priv->show_numbers = show_numbers;
795 g_object_notify (G_OBJECT (action), "show-numbers");
796 g_object_unref (action);
800 #define __GTK_RECENT_ACTION_C__
801 #include "gtkaliasdef.c"