1 /* GTK - The GIMP Toolkit
2 * gtkrecentchoosermenu.c - Recently used items menu widget
3 * Copyright (C) 2005, Emmanuele Bassi
4 * 2008, Ignacio Casal Quinteiro
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., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
28 #include "anjuta-recent-chooser-menu.h"
32 #include <glib/gi18n.h>
34 struct _AnjutaRecentChooserMenuPrivate
36 /* the recent manager object */
37 GtkRecentManager
*manager
;
39 /* size of the icons of the menu items */
42 /* max size of the menu item label */
45 gint first_recent_item_pos
;
46 GtkWidget
*placeholder
;
48 /* RecentChooser properties */
50 guint show_private
: 1;
51 guint show_not_found
: 1;
56 guint show_numbers
: 1;
58 GtkRecentSortType sort_type
;
59 GtkRecentSortFunc sort_func
;
61 GDestroyNotify sort_data_destroy
;
64 GtkRecentFilter
*current_filter
;
66 guint local_manager
: 1;
67 gulong manager_changed_id
;
76 GTK_RECENT_CHOOSER_PROP_FIRST
= 0x3000,
77 GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER
,
78 GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
,
79 GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
,
80 GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
,
81 GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
,
82 GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
,
83 GTK_RECENT_CHOOSER_PROP_LIMIT
,
84 GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
,
85 GTK_RECENT_CHOOSER_PROP_SORT_TYPE
,
86 GTK_RECENT_CHOOSER_PROP_FILTER
,
87 GTK_RECENT_CHOOSER_PROP_LAST
88 } GtkRecentChooserProp
;
91 #define FALLBACK_ICON_SIZE 32
92 #define FALLBACK_ITEM_LIMIT 10
93 #define DEFAULT_LABEL_WIDTH 30
95 #define ANJUTA_RECENT_CHOOSER_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ANJUTA_TYPE_RECENT_CHOOSER_MENU, AnjutaRecentChooserMenuPrivate))
97 static void anjuta_recent_chooser_menu_finalize (GObject
*object
);
98 static void anjuta_recent_chooser_menu_dispose (GObject
*object
);
99 static GObject
*anjuta_recent_chooser_menu_constructor (GType type
,
100 guint n_construct_properties
,
101 GObjectConstructParam
*construct_params
);
104 anjuta_recent_chooser_menu_set_property (GObject
*object
,
110 anjuta_recent_chooser_menu_get_property (GObject
*object
,
115 static void gtk_recent_chooser_iface_init (GtkRecentChooserIface
*iface
);
117 static gboolean
anjuta_recent_chooser_menu_set_current_uri (GtkRecentChooser
*chooser
,
120 static gchar
* anjuta_recent_chooser_menu_get_current_uri (GtkRecentChooser
*chooser
);
121 static gboolean
anjuta_recent_chooser_menu_select_uri (GtkRecentChooser
*chooser
,
124 static void anjuta_recent_chooser_menu_unselect_uri (GtkRecentChooser
*chooser
,
126 static void anjuta_recent_chooser_menu_select_all (GtkRecentChooser
*chooser
);
127 static void anjuta_recent_chooser_menu_unselect_all (GtkRecentChooser
*chooser
);
128 static GList
* anjuta_recent_chooser_menu_get_items (GtkRecentChooser
*chooser
);
129 static GtkRecentManager
*anjuta_recent_chooser_menu_get_recent_manager (GtkRecentChooser
*chooser
);
130 static void anjuta_recent_chooser_menu_set_sort_func (GtkRecentChooser
*chooser
,
131 GtkRecentSortFunc sort_func
,
133 GDestroyNotify data_destroy
);
134 static void anjuta_recent_chooser_menu_add_filter (GtkRecentChooser
*chooser
,
135 GtkRecentFilter
*filter
);
136 static void anjuta_recent_chooser_menu_remove_filter (GtkRecentChooser
*chooser
,
137 GtkRecentFilter
*filter
);
138 static GSList
* anjuta_recent_chooser_menu_list_filters (GtkRecentChooser
*chooser
);
139 static void anjuta_recent_chooser_menu_set_current_filter (AnjutaRecentChooserMenu
*menu
,
140 GtkRecentFilter
*filter
);
142 static void anjuta_recent_chooser_menu_populate (AnjutaRecentChooserMenu
*menu
);
143 static void anjuta_recent_chooser_menu_set_show_tips (AnjutaRecentChooserMenu
*menu
,
146 static void set_recent_manager (AnjutaRecentChooserMenu
*menu
,
147 GtkRecentManager
*manager
);
149 static void chooser_set_sort_type (AnjutaRecentChooserMenu
*menu
,
150 GtkRecentSortType sort_type
);
152 static gint
get_icon_size_for_widget (GtkWidget
*widget
);
154 static void item_activate_cb (GtkWidget
*widget
,
156 static void manager_changed_cb (GtkRecentManager
*manager
,
159 G_DEFINE_TYPE_WITH_CODE (AnjutaRecentChooserMenu
,
160 anjuta_recent_chooser_menu
,
162 G_IMPLEMENT_INTERFACE (GTK_TYPE_RECENT_CHOOSER
,
163 gtk_recent_chooser_iface_init
))
167 gtk_recent_chooser_iface_init (GtkRecentChooserIface
*iface
)
169 iface
->set_current_uri
= anjuta_recent_chooser_menu_set_current_uri
;
170 iface
->get_current_uri
= anjuta_recent_chooser_menu_get_current_uri
;
171 iface
->select_uri
= anjuta_recent_chooser_menu_select_uri
;
172 iface
->unselect_uri
= anjuta_recent_chooser_menu_unselect_uri
;
173 iface
->select_all
= anjuta_recent_chooser_menu_select_all
;
174 iface
->unselect_all
= anjuta_recent_chooser_menu_unselect_all
;
175 iface
->get_items
= anjuta_recent_chooser_menu_get_items
;
176 iface
->get_recent_manager
= anjuta_recent_chooser_menu_get_recent_manager
;
177 iface
->set_sort_func
= anjuta_recent_chooser_menu_set_sort_func
;
178 iface
->add_filter
= anjuta_recent_chooser_menu_add_filter
;
179 iface
->remove_filter
= anjuta_recent_chooser_menu_remove_filter
;
180 iface
->list_filters
= anjuta_recent_chooser_menu_list_filters
;
184 _anjuta_recent_chooser_install_properties (GObjectClass
*klass
)
186 g_object_class_override_property (klass
,
187 GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER
,
189 g_object_class_override_property (klass
,
190 GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
,
192 g_object_class_override_property (klass
,
193 GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
,
195 g_object_class_override_property (klass
,
196 GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
,
198 g_object_class_override_property (klass
,
199 GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
,
201 g_object_class_override_property (klass
,
202 GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
,
204 g_object_class_override_property (klass
,
205 GTK_RECENT_CHOOSER_PROP_LIMIT
,
207 g_object_class_override_property (klass
,
208 GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
,
210 g_object_class_override_property (klass
,
211 GTK_RECENT_CHOOSER_PROP_SORT_TYPE
,
213 g_object_class_override_property (klass
,
214 GTK_RECENT_CHOOSER_PROP_FILTER
,
219 sort_recent_items_mru (GtkRecentInfo
*a
,
223 g_assert (a
!= NULL
&& b
!= NULL
);
225 return gtk_recent_info_get_modified (b
) - gtk_recent_info_get_modified (a
);
229 get_is_recent_filtered (GtkRecentFilter
*filter
,
232 GtkRecentFilterInfo filter_info
;
233 GtkRecentFilterFlags needed
;
236 g_assert (info
!= NULL
);
238 needed
= gtk_recent_filter_get_needed (filter
);
240 filter_info
.contains
= GTK_RECENT_FILTER_URI
| GTK_RECENT_FILTER_MIME_TYPE
;
242 filter_info
.uri
= gtk_recent_info_get_uri (info
);
243 filter_info
.mime_type
= gtk_recent_info_get_mime_type (info
);
245 if (needed
& GTK_RECENT_FILTER_DISPLAY_NAME
)
247 filter_info
.display_name
= gtk_recent_info_get_display_name (info
);
248 filter_info
.contains
|= GTK_RECENT_FILTER_DISPLAY_NAME
;
251 filter_info
.uri
= NULL
;
253 if (needed
& GTK_RECENT_FILTER_APPLICATION
)
255 filter_info
.applications
= (const gchar
**) gtk_recent_info_get_applications (info
, NULL
);
256 filter_info
.contains
|= GTK_RECENT_FILTER_APPLICATION
;
259 filter_info
.applications
= NULL
;
261 if (needed
& GTK_RECENT_FILTER_GROUP
)
263 filter_info
.groups
= (const gchar
**) gtk_recent_info_get_groups (info
, NULL
);
264 filter_info
.contains
|= GTK_RECENT_FILTER_GROUP
;
267 filter_info
.groups
= NULL
;
269 if (needed
& GTK_RECENT_FILTER_AGE
)
271 filter_info
.age
= gtk_recent_info_get_age (info
);
272 filter_info
.contains
|= GTK_RECENT_FILTER_AGE
;
275 filter_info
.age
= -1;
277 retval
= gtk_recent_filter_filter (filter
, &filter_info
);
280 if (filter_info
.applications
)
281 g_strfreev ((gchar
**) filter_info
.applications
);
282 if (filter_info
.groups
)
283 g_strfreev ((gchar
**) filter_info
.groups
);
289 _gtk_recent_chooser_get_items (GtkRecentChooser
*chooser
,
290 GtkRecentFilter
*filter
,
291 GtkRecentManager
*manager
,
295 GtkRecentSortType sort_type
;
297 GCompareFunc compare_func
;
300 g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser
), NULL
);
305 items
= gtk_recent_manager_get_items (manager
);
315 GList
*filter_items
, *l
;
316 gboolean local_only
= FALSE
;
317 gboolean show_private
= FALSE
;
318 gboolean show_not_found
= FALSE
;
320 g_object_get (G_OBJECT (chooser
),
321 "local-only", &local_only
,
322 "show-private", &show_private
,
323 "show-not-found", &show_not_found
,
327 for (l
= items
; l
!= NULL
; l
= l
->next
)
329 GtkRecentInfo
*info
= l
->data
;
330 gboolean remove_item
= FALSE
;
332 if (get_is_recent_filtered (filter
, info
))
335 if (local_only
&& !gtk_recent_info_is_local (info
))
338 if (!show_private
&& gtk_recent_info_get_private_hint (info
))
341 if (!show_not_found
&& !gtk_recent_info_exists (info
))
345 filter_items
= g_list_prepend (filter_items
, info
);
347 gtk_recent_info_unref (info
);
351 items
= filter_items
;
357 sort_type
= gtk_recent_chooser_get_sort_type (chooser
);
360 case GTK_RECENT_SORT_NONE
:
363 case GTK_RECENT_SORT_MRU
:
364 compare_func
= (GCompareFunc
) sort_recent_items_mru
;
366 case GTK_RECENT_SORT_LRU
:
369 case GTK_RECENT_SORT_CUSTOM
:
373 g_assert_not_reached ();
380 items
= g_list_sort (items
, compare_func
);
383 length
= g_list_length (items
);
384 if ((limit
!= -1) && (length
> limit
))
388 clamp
= g_list_nth (items
, limit
- 1);
395 g_list_free_full (l
, (GDestroyNotify
)gtk_recent_info_unref
);
403 anjuta_recent_chooser_menu_class_init (AnjutaRecentChooserMenuClass
*klass
)
405 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
407 gobject_class
->constructor
= anjuta_recent_chooser_menu_constructor
;
408 gobject_class
->dispose
= anjuta_recent_chooser_menu_dispose
;
409 gobject_class
->finalize
= anjuta_recent_chooser_menu_finalize
;
410 gobject_class
->set_property
= anjuta_recent_chooser_menu_set_property
;
411 gobject_class
->get_property
= anjuta_recent_chooser_menu_get_property
;
413 _anjuta_recent_chooser_install_properties (gobject_class
);
415 g_type_class_add_private (klass
, sizeof (AnjutaRecentChooserMenuPrivate
));
419 anjuta_recent_chooser_menu_init (AnjutaRecentChooserMenu
*menu
)
421 AnjutaRecentChooserMenuPrivate
*priv
;
423 priv
= ANJUTA_RECENT_CHOOSER_MENU_GET_PRIVATE (menu
);
427 priv
->show_icons
= TRUE
;
428 priv
->show_numbers
= FALSE
;
429 priv
->show_tips
= FALSE
;
430 priv
->show_not_found
= TRUE
;
431 priv
->show_private
= FALSE
;
432 priv
->local_only
= TRUE
;
434 priv
->limit
= FALLBACK_ITEM_LIMIT
;
435 priv
->sort_type
= GTK_RECENT_SORT_NONE
;
436 priv
->icon_size
= FALLBACK_ICON_SIZE
;
437 priv
->label_width
= DEFAULT_LABEL_WIDTH
;
439 priv
->first_recent_item_pos
= -1;
440 priv
->placeholder
= NULL
;
442 priv
->current_filter
= NULL
;
446 anjuta_recent_chooser_menu_finalize (GObject
*object
)
448 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
449 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
451 priv
->manager
= NULL
;
453 if (priv
->sort_data_destroy
)
455 priv
->sort_data_destroy (priv
->sort_data
);
456 priv
->sort_data_destroy
= NULL
;
459 priv
->sort_data
= NULL
;
460 priv
->sort_func
= NULL
;
462 G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
)->finalize (object
);
466 anjuta_recent_chooser_menu_dispose (GObject
*object
)
468 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
469 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
471 if (priv
->manager_changed_id
)
474 g_signal_handler_disconnect (priv
->manager
, priv
->manager_changed_id
);
476 priv
->manager_changed_id
= 0;
479 if (priv
->populate_id
)
481 g_source_remove (priv
->populate_id
);
482 priv
->populate_id
= 0;
485 if (priv
->current_filter
)
487 g_object_unref (priv
->current_filter
);
488 priv
->current_filter
= NULL
;
491 G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
)->dispose (object
);
495 anjuta_recent_chooser_menu_constructor (GType type
,
497 GObjectConstructParam
*params
)
499 AnjutaRecentChooserMenu
*menu
;
500 AnjutaRecentChooserMenuPrivate
*priv
;
501 GObjectClass
*parent_class
;
504 parent_class
= G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
);
505 object
= parent_class
->constructor (type
, n_params
, params
);
506 menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
509 g_assert (priv
->manager
);
511 /* we create a placeholder menuitem, to be used in case
512 * the menu is empty. this placeholder will stay around
513 * for the entire lifetime of the menu, and we just hide it
514 * when it's not used. we have to do this, and do it here,
515 * because we need a marker for the beginning of the recent
516 * items list, so that we can insert the new items at the
517 * right place when idly populating the menu in case the
518 * user appended or prepended custom menu items to the
519 * recent chooser menu widget.
521 priv
->placeholder
= gtk_menu_item_new_with_label (_("No items found"));
522 gtk_widget_set_sensitive (priv
->placeholder
, FALSE
);
523 g_object_set_data (G_OBJECT (priv
->placeholder
),
524 "gtk-recent-menu-placeholder",
525 GINT_TO_POINTER (TRUE
));
527 gtk_menu_shell_insert (GTK_MENU_SHELL (menu
), priv
->placeholder
, 0);
528 gtk_widget_set_no_show_all (priv
->placeholder
, TRUE
);
529 gtk_widget_show (priv
->placeholder
);
531 /* (re)populate the menu */
532 anjuta_recent_chooser_menu_populate (menu
);
538 anjuta_recent_chooser_menu_set_property (GObject
*object
,
543 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
544 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
548 case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER
:
549 set_recent_manager (menu
, g_value_get_object (value
));
551 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
:
552 priv
->show_private
= g_value_get_boolean (value
);
554 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
:
555 priv
->show_not_found
= g_value_get_boolean (value
);
557 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
:
558 anjuta_recent_chooser_menu_set_show_tips (menu
, g_value_get_boolean (value
));
560 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
:
561 priv
->show_icons
= g_value_get_boolean (value
);
563 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
:
564 g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
566 G_OBJECT_TYPE_NAME (object
));
568 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
:
569 priv
->local_only
= g_value_get_boolean (value
);
571 case GTK_RECENT_CHOOSER_PROP_LIMIT
:
572 priv
->limit
= g_value_get_int (value
);
574 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE
:
575 chooser_set_sort_type (menu
, g_value_get_enum (value
));
577 case GTK_RECENT_CHOOSER_PROP_FILTER
:
578 anjuta_recent_chooser_menu_set_current_filter (menu
, g_value_get_object (value
));
581 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
587 anjuta_recent_chooser_menu_get_property (GObject
*object
,
592 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
593 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
597 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
:
598 g_value_set_boolean (value
, priv
->show_tips
);
600 case GTK_RECENT_CHOOSER_PROP_LIMIT
:
601 g_value_set_int (value
, priv
->limit
);
603 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
:
604 g_value_set_boolean (value
, priv
->local_only
);
606 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE
:
607 g_value_set_enum (value
, priv
->sort_type
);
609 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
:
610 g_value_set_boolean (value
, priv
->show_private
);
612 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
:
613 g_value_set_boolean (value
, priv
->show_not_found
);
615 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
:
616 g_value_set_boolean (value
, priv
->show_icons
);
618 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
:
619 g_value_set_boolean (value
, FALSE
);
621 case GTK_RECENT_CHOOSER_PROP_FILTER
:
622 g_value_set_object (value
, priv
->current_filter
);
625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
631 anjuta_recent_chooser_menu_set_current_uri (GtkRecentChooser
*chooser
,
635 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
637 GtkWidget
*menu_item
= NULL
;
638 gboolean found
= FALSE
;
640 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
642 for (l
= children
; l
!= NULL
; l
= l
->next
)
646 menu_item
= GTK_WIDGET (l
->data
);
648 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
652 if (strcmp (uri
, gtk_recent_info_get_uri (info
)) == 0)
654 gtk_menu_shell_activate_item (GTK_MENU_SHELL (menu
),
663 g_list_free (children
);
667 g_set_error (error
, GTK_RECENT_CHOOSER_ERROR
,
668 GTK_RECENT_CHOOSER_ERROR_NOT_FOUND
,
669 _("No recently used resource found with URI \"%s\""),
677 anjuta_recent_chooser_menu_get_current_uri (GtkRecentChooser
*chooser
)
679 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
680 GtkWidget
*menu_item
;
683 menu_item
= gtk_menu_get_active (GTK_MENU (menu
));
687 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
691 return g_strdup (gtk_recent_info_get_uri (info
));
695 anjuta_recent_chooser_menu_select_uri (GtkRecentChooser
*chooser
,
699 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
701 GtkWidget
*menu_item
= NULL
;
702 gboolean found
= FALSE
;
704 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
705 for (l
= children
; l
!= NULL
; l
= l
->next
)
709 menu_item
= GTK_WIDGET (l
->data
);
711 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
715 if (0 == strcmp (uri
, gtk_recent_info_get_uri (info
)))
719 g_list_free (children
);
723 g_set_error (error
, GTK_RECENT_CHOOSER_ERROR
,
724 GTK_RECENT_CHOOSER_ERROR_NOT_FOUND
,
725 _("No recently used resource found with URI \"%s\""),
731 gtk_menu_shell_select_item (GTK_MENU_SHELL (menu
), menu_item
);
738 anjuta_recent_chooser_menu_unselect_uri (GtkRecentChooser
*chooser
,
741 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
743 gtk_menu_shell_deselect (GTK_MENU_SHELL (menu
));
747 anjuta_recent_chooser_menu_select_all (GtkRecentChooser
*chooser
)
749 g_warning (_("This function is not implemented for "
750 "widgets of class '%s'"),
751 g_type_name (G_OBJECT_TYPE (chooser
)));
755 anjuta_recent_chooser_menu_unselect_all (GtkRecentChooser
*chooser
)
757 g_warning (_("This function is not implemented for "
758 "widgets of class '%s'"),
759 g_type_name (G_OBJECT_TYPE (chooser
)));
763 anjuta_recent_chooser_menu_set_sort_func (GtkRecentChooser
*chooser
,
764 GtkRecentSortFunc sort_func
,
766 GDestroyNotify data_destroy
)
768 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
769 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
771 if (priv
->sort_data_destroy
)
773 priv
->sort_data_destroy (priv
->sort_data
);
774 priv
->sort_data_destroy
= NULL
;
777 priv
->sort_func
= NULL
;
778 priv
->sort_data
= NULL
;
779 priv
->sort_data_destroy
= NULL
;
783 priv
->sort_func
= sort_func
;
784 priv
->sort_data
= sort_data
;
785 priv
->sort_data_destroy
= data_destroy
;
790 chooser_set_sort_type (AnjutaRecentChooserMenu
*menu
,
791 GtkRecentSortType sort_type
)
793 if (menu
->priv
->sort_type
== sort_type
)
796 menu
->priv
->sort_type
= sort_type
;
801 anjuta_recent_chooser_menu_get_items (GtkRecentChooser
*chooser
)
803 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
804 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
806 return _gtk_recent_chooser_get_items (chooser
,
807 priv
->current_filter
,
812 static GtkRecentManager
*
813 anjuta_recent_chooser_menu_get_recent_manager (GtkRecentChooser
*chooser
)
815 AnjutaRecentChooserMenuPrivate
*priv
;
817 priv
= ANJUTA_RECENT_CHOOSER_MENU (chooser
)->priv
;
819 return priv
->manager
;
823 anjuta_recent_chooser_menu_add_filter (GtkRecentChooser
*chooser
,
824 GtkRecentFilter
*filter
)
826 AnjutaRecentChooserMenu
*menu
;
828 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
830 anjuta_recent_chooser_menu_set_current_filter (menu
, filter
);
834 anjuta_recent_chooser_menu_remove_filter (GtkRecentChooser
*chooser
,
835 GtkRecentFilter
*filter
)
837 AnjutaRecentChooserMenu
*menu
;
839 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
841 if (filter
== menu
->priv
->current_filter
)
843 g_object_unref (menu
->priv
->current_filter
);
844 menu
->priv
->current_filter
= NULL
;
846 g_object_notify (G_OBJECT (menu
), "filter");
851 anjuta_recent_chooser_menu_list_filters (GtkRecentChooser
*chooser
)
853 AnjutaRecentChooserMenu
*menu
;
854 GSList
*retval
= NULL
;
856 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
858 if (menu
->priv
->current_filter
)
859 retval
= g_slist_prepend (retval
, menu
->priv
->current_filter
);
865 anjuta_recent_chooser_menu_set_current_filter (AnjutaRecentChooserMenu
*menu
,
866 GtkRecentFilter
*filter
)
868 AnjutaRecentChooserMenuPrivate
*priv
;
872 if (priv
->current_filter
)
873 g_object_unref (G_OBJECT (priv
->current_filter
));
877 priv
->current_filter
= filter
;
878 g_object_ref_sink (priv
->current_filter
);
881 anjuta_recent_chooser_menu_populate (menu
);
883 g_object_notify (G_OBJECT (menu
), "filter");
886 /* taken from libeel/eel-strings.c */
888 escape_underscores (const gchar
*string
)
899 for (p
= string
; *p
!= '\0'; p
++)
900 underscores
+= (*p
== '_');
902 if (underscores
== 0)
903 return g_strdup (string
);
905 escaped
= g_new (char, strlen (string
) + underscores
+ 1);
906 for (p
= string
, q
= escaped
; *p
!= '\0'; p
++, q
++)
908 /* Add an extra underscore. */
921 anjuta_recent_chooser_menu_add_tip (AnjutaRecentChooserMenu
*menu
,
925 AnjutaRecentChooserMenuPrivate
*priv
;
928 g_assert (info
!= NULL
);
929 g_assert (item
!= NULL
);
933 path
= gtk_recent_info_get_uri_display (info
);
936 gchar
*tip_text
= g_strdup_printf (_("Open '%s'"), path
);
938 gtk_widget_set_tooltip_text (item
, tip_text
);
939 gtk_widget_set_has_tooltip (item
, priv
->show_tips
);
947 anjuta_recent_chooser_menu_create_item (AnjutaRecentChooserMenu
*menu
,
951 AnjutaRecentChooserMenuPrivate
*priv
;
953 GtkWidget
*item
, *image
, *label
;
956 g_assert (info
!= NULL
);
960 if (priv
->show_numbers
)
962 gchar
*name
, *escaped
;
964 name
= g_strdup (gtk_recent_info_get_display_name (info
));
966 name
= g_strdup (_("Unknown item"));
968 escaped
= escape_underscores (name
);
970 /* avoid clashing mnemonics */
972 /* This is the label format that is used for the first 10 items
973 * in a recent files menu. The %d is the number of the item,
974 * the %s is the name of the item. Please keep the _ in front
975 * of the number to give these menu items a mnemonic.
977 text
= g_strdup_printf (C_("recent menu label", "_%d. %s"), count
, escaped
);
979 /* This is the format that is used for items in a recent files menu.
980 * The %d is the number of the item, the %s is the name of the item.
982 text
= g_strdup_printf (C_("recent menu label", "%d. %s"), count
, escaped
);
984 item
= gtk_image_menu_item_new_with_mnemonic (text
);
991 text
= g_strdup (gtk_recent_info_get_display_name (info
));
992 item
= gtk_image_menu_item_new_with_label (text
);
997 /* ellipsize the menu item label, in case the recent document
998 * display name is huge.
1000 label
= gtk_bin_get_child (GTK_BIN (item
));
1001 if (GTK_IS_LABEL (label
))
1003 gtk_label_set_ellipsize (GTK_LABEL (label
), PANGO_ELLIPSIZE_END
);
1004 gtk_label_set_max_width_chars (GTK_LABEL (label
), priv
->label_width
);
1007 if (priv
->show_icons
)
1009 icon
= gtk_recent_info_get_icon (info
, priv
->icon_size
);
1011 image
= gtk_image_new_from_pixbuf (icon
);
1012 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item
), image
);
1013 g_object_unref (icon
);
1016 g_signal_connect (item
, "activate",
1017 G_CALLBACK (item_activate_cb
),
1024 anjuta_recent_chooser_menu_insert_item (AnjutaRecentChooserMenu
*menu
,
1025 GtkWidget
*menuitem
,
1027 gboolean anjuta_project
)
1029 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1032 if (priv
->first_recent_item_pos
== -1)
1034 GList
*children
, *l
;
1036 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
1038 for (real_position
= 0, l
= children
;
1040 real_position
+= 1, l
= l
->next
)
1042 GObject
*child
= l
->data
;
1043 gboolean is_placeholder
= FALSE
;
1046 GPOINTER_TO_INT (g_object_get_data (child
, "gtk-recent-menu-placeholder"));
1052 g_list_free (children
);
1053 priv
->first_recent_item_pos
= real_position
;
1057 real_position
= priv
->first_recent_item_pos
;
1061 if (priv
->prj_pos
!= 5)
1063 gtk_menu_shell_insert (GTK_MENU_SHELL (menu
), menuitem
,
1068 else gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1070 gtk_widget_show (menuitem
);
1073 /* removes the items we own from the menu */
1075 anjuta_recent_chooser_menu_dispose_items (AnjutaRecentChooserMenu
*menu
)
1077 GList
*children
, *l
;
1079 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
1080 for (l
= children
; l
!= NULL
; l
= l
->next
)
1082 GtkWidget
*menu_item
= GTK_WIDGET (l
->data
);
1083 gboolean has_mark
= FALSE
;
1085 /* check for our mark, in order to remove just the items we own */
1087 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-menu-mark"));
1091 GtkRecentInfo
*info
;
1093 /* destroy the attached RecentInfo struct, if found */
1094 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
1096 g_object_set_data_full (G_OBJECT (menu_item
), "gtk-recent-info",
1099 /* and finally remove the item from the menu */
1100 gtk_container_remove (GTK_CONTAINER (menu
), menu_item
);
1104 /* recalculate the position of the first recent item */
1105 menu
->priv
->first_recent_item_pos
= -1;
1107 g_list_free (children
);
1115 gint displayed_items
;
1116 AnjutaRecentChooserMenu
*menu
;
1117 GtkWidget
*placeholder
;
1121 idle_populate_func (gpointer data
)
1123 MenuPopulateData
*pdata
;
1124 AnjutaRecentChooserMenuPrivate
*priv
;
1125 GtkRecentInfo
*info
;
1129 pdata
= (MenuPopulateData
*) data
;
1130 priv
= pdata
->menu
->priv
;
1134 pdata
->items
= gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata
->menu
));
1137 /* show the placeholder here */
1138 gtk_widget_show (pdata
->placeholder
);
1139 pdata
->displayed_items
= 1;
1140 priv
->populate_id
= 0;
1145 /* We add the separator */
1146 GtkWidget
*menuitem
;
1147 menuitem
= gtk_separator_menu_item_new ();
1148 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, menuitem
,
1149 pdata
->displayed_items
, FALSE
);
1150 g_object_set_data (G_OBJECT (menuitem
),
1151 "gtk-recent-menu-mark",
1152 GINT_TO_POINTER (TRUE
));
1154 pdata
->n_items
= g_list_length (pdata
->items
);
1155 pdata
->loaded_items
= 0;
1158 info
= g_list_nth_data (pdata
->items
, pdata
->loaded_items
);
1159 item
= anjuta_recent_chooser_menu_create_item (pdata
->menu
,
1161 pdata
->displayed_items
);
1163 goto check_and_return
;
1165 anjuta_recent_chooser_menu_add_tip (pdata
->menu
, info
, item
);
1166 if (strcmp (gtk_recent_info_get_mime_type (info
), "application/x-anjuta") == 0)
1167 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, item
,
1168 pdata
->displayed_items
, TRUE
);
1171 if (priv
->max_files
!= 14)
1173 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, item
,
1174 pdata
->displayed_items
, FALSE
);
1179 pdata
->displayed_items
+= 1;
1181 /* mark the menu item as one of our own */
1182 g_object_set_data (G_OBJECT (item
),
1183 "gtk-recent-menu-mark",
1184 GINT_TO_POINTER (TRUE
));
1186 /* attach the RecentInfo object to the menu item, and own a reference
1187 * to it, so that it will be destroyed with the menu item when it's
1188 * not needed anymore.
1190 g_object_set_data_full (G_OBJECT (item
), "gtk-recent-info",
1191 gtk_recent_info_ref (info
),
1192 (GDestroyNotify
) gtk_recent_info_unref
);
1195 pdata
->loaded_items
+= 1;
1197 if (pdata
->loaded_items
== pdata
->n_items
)
1199 priv
->populate_id
= 0;
1210 idle_populate_clean_up (gpointer data
)
1212 MenuPopulateData
*pdata
= data
;
1214 if (!pdata
->displayed_items
)
1215 /* show the placeholder in case no item survived
1216 * the filtering process in the idle loop
1218 gtk_widget_show (pdata
->placeholder
);
1220 g_object_unref (pdata
->placeholder
);
1223 g_list_free_full(pdata
->items
, (GDestroyNotify
)gtk_recent_info_unref
);
1225 g_slice_free (MenuPopulateData
, data
);
1229 anjuta_recent_chooser_menu_populate (AnjutaRecentChooserMenu
*menu
)
1231 MenuPopulateData
*pdata
;
1232 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1234 if (menu
->priv
->populate_id
)
1237 pdata
= g_slice_new (MenuPopulateData
);
1238 pdata
->items
= NULL
;
1240 pdata
->loaded_items
= 0;
1241 pdata
->displayed_items
= 0;
1243 pdata
->placeholder
= g_object_ref (priv
->placeholder
);
1245 priv
->icon_size
= get_icon_size_for_widget (GTK_WIDGET (menu
));
1247 priv
->max_files
= 0;
1249 /* remove our menu items first and hide the placeholder */
1250 anjuta_recent_chooser_menu_dispose_items (menu
);
1251 gtk_widget_hide (priv
->placeholder
);
1253 priv
->populate_id
= gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE
+ 30,
1256 idle_populate_clean_up
);
1259 /* bounce activate signal from the recent menu item widget
1260 * to the recent menu widget
1263 item_activate_cb (GtkWidget
*widget
,
1266 GtkRecentChooser
*chooser
= GTK_RECENT_CHOOSER (user_data
);
1268 g_signal_emit_by_name (chooser
, "item_activated");
1271 /* we force a redraw if the manager changes when we are showing */
1273 manager_changed_cb (GtkRecentManager
*manager
,
1276 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (user_data
);
1278 anjuta_recent_chooser_menu_populate (menu
);
1282 set_recent_manager (AnjutaRecentChooserMenu
*menu
,
1283 GtkRecentManager
*manager
)
1285 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1289 if (priv
->manager_changed_id
)
1291 g_signal_handler_disconnect (priv
->manager
, priv
->manager_changed_id
);
1292 priv
->manager_changed_id
= 0;
1295 if (priv
->populate_id
)
1297 g_source_remove (priv
->populate_id
);
1298 priv
->populate_id
= 0;
1301 priv
->manager
= NULL
;
1305 priv
->manager
= manager
;
1307 priv
->manager
= gtk_recent_manager_get_default ();
1310 priv
->manager_changed_id
= g_signal_connect (priv
->manager
, "changed",
1311 G_CALLBACK (manager_changed_cb
),
1316 get_icon_size_for_widget (GtkWidget
*widget
)
1318 GtkSettings
*settings
;
1321 if (gtk_widget_has_screen (widget
))
1322 settings
= gtk_settings_get_for_screen (gtk_widget_get_screen (widget
));
1324 settings
= gtk_settings_get_default ();
1326 if (gtk_icon_size_lookup_for_settings (settings
, GTK_ICON_SIZE_MENU
,
1328 return MAX (width
, height
);
1330 return FALLBACK_ICON_SIZE
;
1334 foreach_set_shot_tips (GtkWidget
*widget
,
1337 AnjutaRecentChooserMenu
*menu
= user_data
;
1338 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1341 /* toggle the tooltip only on the items we create */
1343 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget
), "gtk-recent-menu-mark"));
1346 gtk_widget_set_has_tooltip (widget
, priv
->show_tips
);
1350 anjuta_recent_chooser_menu_set_show_tips (AnjutaRecentChooserMenu
*menu
,
1353 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1355 if (priv
->show_tips
== show_tips
)
1358 priv
->show_tips
= show_tips
;
1359 gtk_container_foreach (GTK_CONTAINER (menu
), foreach_set_shot_tips
, menu
);
1367 * anjuta_recent_chooser_menu_new:
1369 * Creates a new #AnjutaRecentChooserMenu widget.
1371 * This kind of widget shows the list of recently used resources as
1372 * a menu, each item as a menu item. Each item inside the menu might
1373 * have an icon, representing its MIME type, and a number, for mnemonic
1376 * This widget implements the #GtkRecentChooser interface.
1378 * This widget creates its own #GtkRecentManager object. See the
1379 * anjuta_recent_chooser_menu_new_for_manager() function to know how to create
1380 * a #AnjutaRecentChooserMenu widget bound to another #GtkRecentManager object.
1382 * Return value: a new #AnjutaRecentChooserMenu
1387 anjuta_recent_chooser_menu_new (void)
1389 return g_object_new (ANJUTA_TYPE_RECENT_CHOOSER_MENU
,
1390 "recent-manager", NULL
,
1395 * anjuta_recent_chooser_menu_new_for_manager:
1396 * @manager: a #GtkRecentManager
1398 * Creates a new #AnjutaRecentChooserMenu widget using @manager as
1399 * the underlying recently used resources manager.
1401 * This is useful if you have implemented your own recent manager,
1402 * or if you have a customized instance of a #GtkRecentManager
1403 * object or if you wish to share a common #GtkRecentManager object
1404 * among multiple #GtkRecentChooser widgets.
1406 * Return value: a new #AnjutaRecentChooserMenu, bound to @manager.
1411 anjuta_recent_chooser_menu_new_for_manager (GtkRecentManager
*manager
)
1413 g_return_val_if_fail (manager
== NULL
|| GTK_IS_RECENT_MANAGER (manager
), NULL
);
1415 return g_object_new (ANJUTA_TYPE_RECENT_CHOOSER_MENU
,
1416 "recent-manager", manager
,