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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
26 #include <gdk/gdkscreen.h>
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_foreach (l
, (GFunc
) gtk_recent_info_unref
, NULL
);
404 anjuta_recent_chooser_menu_class_init (AnjutaRecentChooserMenuClass
*klass
)
406 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
408 gobject_class
->constructor
= anjuta_recent_chooser_menu_constructor
;
409 gobject_class
->dispose
= anjuta_recent_chooser_menu_dispose
;
410 gobject_class
->finalize
= anjuta_recent_chooser_menu_finalize
;
411 gobject_class
->set_property
= anjuta_recent_chooser_menu_set_property
;
412 gobject_class
->get_property
= anjuta_recent_chooser_menu_get_property
;
414 _anjuta_recent_chooser_install_properties (gobject_class
);
416 g_type_class_add_private (klass
, sizeof (AnjutaRecentChooserMenuPrivate
));
420 anjuta_recent_chooser_menu_init (AnjutaRecentChooserMenu
*menu
)
422 AnjutaRecentChooserMenuPrivate
*priv
;
424 priv
= ANJUTA_RECENT_CHOOSER_MENU_GET_PRIVATE (menu
);
428 priv
->show_icons
= TRUE
;
429 priv
->show_numbers
= FALSE
;
430 priv
->show_tips
= FALSE
;
431 priv
->show_not_found
= TRUE
;
432 priv
->show_private
= FALSE
;
433 priv
->local_only
= TRUE
;
435 priv
->limit
= FALLBACK_ITEM_LIMIT
;
436 priv
->sort_type
= GTK_RECENT_SORT_NONE
;
437 priv
->icon_size
= FALLBACK_ICON_SIZE
;
438 priv
->label_width
= DEFAULT_LABEL_WIDTH
;
440 priv
->first_recent_item_pos
= -1;
441 priv
->placeholder
= NULL
;
443 priv
->current_filter
= NULL
;
447 anjuta_recent_chooser_menu_finalize (GObject
*object
)
449 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
450 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
452 priv
->manager
= NULL
;
454 if (priv
->sort_data_destroy
)
456 priv
->sort_data_destroy (priv
->sort_data
);
457 priv
->sort_data_destroy
= NULL
;
460 priv
->sort_data
= NULL
;
461 priv
->sort_func
= NULL
;
463 G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
)->finalize (object
);
467 anjuta_recent_chooser_menu_dispose (GObject
*object
)
469 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
470 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
472 if (priv
->manager_changed_id
)
475 g_signal_handler_disconnect (priv
->manager
, priv
->manager_changed_id
);
477 priv
->manager_changed_id
= 0;
480 if (priv
->populate_id
)
482 g_source_remove (priv
->populate_id
);
483 priv
->populate_id
= 0;
486 if (priv
->current_filter
)
488 g_object_unref (priv
->current_filter
);
489 priv
->current_filter
= NULL
;
492 G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
)->dispose (object
);
496 anjuta_recent_chooser_menu_constructor (GType type
,
498 GObjectConstructParam
*params
)
500 AnjutaRecentChooserMenu
*menu
;
501 AnjutaRecentChooserMenuPrivate
*priv
;
502 GObjectClass
*parent_class
;
505 parent_class
= G_OBJECT_CLASS (anjuta_recent_chooser_menu_parent_class
);
506 object
= parent_class
->constructor (type
, n_params
, params
);
507 menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
510 g_assert (priv
->manager
);
512 /* we create a placeholder menuitem, to be used in case
513 * the menu is empty. this placeholder will stay around
514 * for the entire lifetime of the menu, and we just hide it
515 * when it's not used. we have to do this, and do it here,
516 * because we need a marker for the beginning of the recent
517 * items list, so that we can insert the new items at the
518 * right place when idly populating the menu in case the
519 * user appended or prepended custom menu items to the
520 * recent chooser menu widget.
522 priv
->placeholder
= gtk_menu_item_new_with_label (_("No items found"));
523 gtk_widget_set_sensitive (priv
->placeholder
, FALSE
);
524 g_object_set_data (G_OBJECT (priv
->placeholder
),
525 "gtk-recent-menu-placeholder",
526 GINT_TO_POINTER (TRUE
));
528 gtk_menu_shell_insert (GTK_MENU_SHELL (menu
), priv
->placeholder
, 0);
529 gtk_widget_set_no_show_all (priv
->placeholder
, TRUE
);
530 gtk_widget_show (priv
->placeholder
);
532 /* (re)populate the menu */
533 anjuta_recent_chooser_menu_populate (menu
);
539 anjuta_recent_chooser_menu_set_property (GObject
*object
,
544 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
545 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
549 case GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER
:
550 set_recent_manager (menu
, g_value_get_object (value
));
552 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
:
553 priv
->show_private
= g_value_get_boolean (value
);
555 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
:
556 priv
->show_not_found
= g_value_get_boolean (value
);
558 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
:
559 anjuta_recent_chooser_menu_set_show_tips (menu
, g_value_get_boolean (value
));
561 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
:
562 priv
->show_icons
= g_value_get_boolean (value
);
564 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
:
565 g_warning ("%s: Choosers of type `%s' do not support selecting multiple items.",
567 G_OBJECT_TYPE_NAME (object
));
569 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
:
570 priv
->local_only
= g_value_get_boolean (value
);
572 case GTK_RECENT_CHOOSER_PROP_LIMIT
:
573 priv
->limit
= g_value_get_int (value
);
575 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE
:
576 chooser_set_sort_type (menu
, g_value_get_enum (value
));
578 case GTK_RECENT_CHOOSER_PROP_FILTER
:
579 anjuta_recent_chooser_menu_set_current_filter (menu
, g_value_get_object (value
));
582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
588 anjuta_recent_chooser_menu_get_property (GObject
*object
,
593 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (object
);
594 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
598 case GTK_RECENT_CHOOSER_PROP_SHOW_TIPS
:
599 g_value_set_boolean (value
, priv
->show_tips
);
601 case GTK_RECENT_CHOOSER_PROP_LIMIT
:
602 g_value_set_int (value
, priv
->limit
);
604 case GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY
:
605 g_value_set_boolean (value
, priv
->local_only
);
607 case GTK_RECENT_CHOOSER_PROP_SORT_TYPE
:
608 g_value_set_enum (value
, priv
->sort_type
);
610 case GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE
:
611 g_value_set_boolean (value
, priv
->show_private
);
613 case GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND
:
614 g_value_set_boolean (value
, priv
->show_not_found
);
616 case GTK_RECENT_CHOOSER_PROP_SHOW_ICONS
:
617 g_value_set_boolean (value
, priv
->show_icons
);
619 case GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE
:
620 g_value_set_boolean (value
, FALSE
);
622 case GTK_RECENT_CHOOSER_PROP_FILTER
:
623 g_value_set_object (value
, priv
->current_filter
);
626 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
632 anjuta_recent_chooser_menu_set_current_uri (GtkRecentChooser
*chooser
,
636 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
638 GtkWidget
*menu_item
= NULL
;
639 gboolean found
= FALSE
;
641 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
643 for (l
= children
; l
!= NULL
; l
= l
->next
)
647 menu_item
= GTK_WIDGET (l
->data
);
649 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
653 if (strcmp (uri
, gtk_recent_info_get_uri (info
)) == 0)
655 gtk_menu_shell_activate_item (GTK_MENU_SHELL (menu
),
664 g_list_free (children
);
668 g_set_error (error
, GTK_RECENT_CHOOSER_ERROR
,
669 GTK_RECENT_CHOOSER_ERROR_NOT_FOUND
,
670 _("No recently used resource found with URI `%s'"),
678 anjuta_recent_chooser_menu_get_current_uri (GtkRecentChooser
*chooser
)
680 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
681 GtkWidget
*menu_item
;
684 menu_item
= gtk_menu_get_active (GTK_MENU (menu
));
688 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
692 return g_strdup (gtk_recent_info_get_uri (info
));
696 anjuta_recent_chooser_menu_select_uri (GtkRecentChooser
*chooser
,
700 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
702 GtkWidget
*menu_item
= NULL
;
703 gboolean found
= FALSE
;
705 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
706 for (l
= children
; l
!= NULL
; l
= l
->next
)
710 menu_item
= GTK_WIDGET (l
->data
);
712 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
716 if (0 == strcmp (uri
, gtk_recent_info_get_uri (info
)))
720 g_list_free (children
);
724 g_set_error (error
, GTK_RECENT_CHOOSER_ERROR
,
725 GTK_RECENT_CHOOSER_ERROR_NOT_FOUND
,
726 _("No recently used resource found with URI `%s'"),
732 gtk_menu_shell_select_item (GTK_MENU_SHELL (menu
), menu_item
);
739 anjuta_recent_chooser_menu_unselect_uri (GtkRecentChooser
*chooser
,
742 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
744 gtk_menu_shell_deselect (GTK_MENU_SHELL (menu
));
748 anjuta_recent_chooser_menu_select_all (GtkRecentChooser
*chooser
)
750 g_warning (_("This function is not implemented for "
751 "widgets of class '%s'"),
752 g_type_name (G_OBJECT_TYPE (chooser
)));
756 anjuta_recent_chooser_menu_unselect_all (GtkRecentChooser
*chooser
)
758 g_warning (_("This function is not implemented for "
759 "widgets of class '%s'"),
760 g_type_name (G_OBJECT_TYPE (chooser
)));
764 anjuta_recent_chooser_menu_set_sort_func (GtkRecentChooser
*chooser
,
765 GtkRecentSortFunc sort_func
,
767 GDestroyNotify data_destroy
)
769 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
770 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
772 if (priv
->sort_data_destroy
)
774 priv
->sort_data_destroy (priv
->sort_data
);
775 priv
->sort_data_destroy
= NULL
;
778 priv
->sort_func
= NULL
;
779 priv
->sort_data
= NULL
;
780 priv
->sort_data_destroy
= NULL
;
784 priv
->sort_func
= sort_func
;
785 priv
->sort_data
= sort_data
;
786 priv
->sort_data_destroy
= data_destroy
;
791 chooser_set_sort_type (AnjutaRecentChooserMenu
*menu
,
792 GtkRecentSortType sort_type
)
794 if (menu
->priv
->sort_type
== sort_type
)
797 menu
->priv
->sort_type
= sort_type
;
802 anjuta_recent_chooser_menu_get_items (GtkRecentChooser
*chooser
)
804 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
805 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
807 return _gtk_recent_chooser_get_items (chooser
,
808 priv
->current_filter
,
813 static GtkRecentManager
*
814 anjuta_recent_chooser_menu_get_recent_manager (GtkRecentChooser
*chooser
)
816 AnjutaRecentChooserMenuPrivate
*priv
;
818 priv
= ANJUTA_RECENT_CHOOSER_MENU (chooser
)->priv
;
820 return priv
->manager
;
824 anjuta_recent_chooser_menu_add_filter (GtkRecentChooser
*chooser
,
825 GtkRecentFilter
*filter
)
827 AnjutaRecentChooserMenu
*menu
;
829 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
831 anjuta_recent_chooser_menu_set_current_filter (menu
, filter
);
835 anjuta_recent_chooser_menu_remove_filter (GtkRecentChooser
*chooser
,
836 GtkRecentFilter
*filter
)
838 AnjutaRecentChooserMenu
*menu
;
840 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
842 if (filter
== menu
->priv
->current_filter
)
844 g_object_unref (menu
->priv
->current_filter
);
845 menu
->priv
->current_filter
= NULL
;
847 g_object_notify (G_OBJECT (menu
), "filter");
852 anjuta_recent_chooser_menu_list_filters (GtkRecentChooser
*chooser
)
854 AnjutaRecentChooserMenu
*menu
;
855 GSList
*retval
= NULL
;
857 menu
= ANJUTA_RECENT_CHOOSER_MENU (chooser
);
859 if (menu
->priv
->current_filter
)
860 retval
= g_slist_prepend (retval
, menu
->priv
->current_filter
);
866 anjuta_recent_chooser_menu_set_current_filter (AnjutaRecentChooserMenu
*menu
,
867 GtkRecentFilter
*filter
)
869 AnjutaRecentChooserMenuPrivate
*priv
;
873 if (priv
->current_filter
)
874 g_object_unref (G_OBJECT (priv
->current_filter
));
878 priv
->current_filter
= filter
;
879 g_object_ref_sink (priv
->current_filter
);
882 anjuta_recent_chooser_menu_populate (menu
);
884 g_object_notify (G_OBJECT (menu
), "filter");
887 /* taken from libeel/eel-strings.c */
889 escape_underscores (const gchar
*string
)
900 for (p
= string
; *p
!= '\0'; p
++)
901 underscores
+= (*p
== '_');
903 if (underscores
== 0)
904 return g_strdup (string
);
906 escaped
= g_new (char, strlen (string
) + underscores
+ 1);
907 for (p
= string
, q
= escaped
; *p
!= '\0'; p
++, q
++)
909 /* Add an extra underscore. */
922 anjuta_recent_chooser_menu_add_tip (AnjutaRecentChooserMenu
*menu
,
926 AnjutaRecentChooserMenuPrivate
*priv
;
929 g_assert (info
!= NULL
);
930 g_assert (item
!= NULL
);
934 path
= gtk_recent_info_get_uri_display (info
);
937 gchar
*tip_text
= g_strdup_printf (_("Open '%s'"), path
);
939 gtk_widget_set_tooltip_text (item
, tip_text
);
940 gtk_widget_set_has_tooltip (item
, priv
->show_tips
);
948 anjuta_recent_chooser_menu_create_item (AnjutaRecentChooserMenu
*menu
,
952 AnjutaRecentChooserMenuPrivate
*priv
;
954 GtkWidget
*item
, *image
, *label
;
957 g_assert (info
!= NULL
);
961 if (priv
->show_numbers
)
963 gchar
*name
, *escaped
;
965 name
= g_strdup (gtk_recent_info_get_display_name (info
));
967 name
= g_strdup (_("Unknown item"));
969 escaped
= escape_underscores (name
);
971 /* avoid clashing mnemonics */
973 /* This is the label format that is used for the first 10 items
974 * in a recent files menu. The %d is the number of the item,
975 * the %s is the name of the item. Please keep the _ in front
976 * of the number to give these menu items a mnemonic.
978 * Don't include the prefix "recent menu label|" in the translation.
980 text
= g_strdup_printf (Q_("recent menu label|_%d. %s"), count
, escaped
);
982 /* This is the format that is used for items in a recent files menu.
983 * The %d is the number of the item, the %s is the name of the item.
985 * Don't include the prefix "recent menu label|" in the translation.
987 text
= g_strdup_printf (Q_("recent menu label|%d. %s"), count
, escaped
);
989 item
= gtk_image_menu_item_new_with_mnemonic (text
);
996 text
= g_strdup (gtk_recent_info_get_display_name (info
));
997 item
= gtk_image_menu_item_new_with_label (text
);
1002 /* ellipsize the menu item label, in case the recent document
1003 * display name is huge.
1005 label
= GTK_BIN (item
)->child
;
1006 if (GTK_IS_LABEL (label
))
1008 gtk_label_set_ellipsize (GTK_LABEL (label
), PANGO_ELLIPSIZE_END
);
1009 gtk_label_set_max_width_chars (GTK_LABEL (label
), priv
->label_width
);
1012 if (priv
->show_icons
)
1014 icon
= gtk_recent_info_get_icon (info
, priv
->icon_size
);
1016 image
= gtk_image_new_from_pixbuf (icon
);
1017 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item
), image
);
1018 g_object_unref (icon
);
1021 g_signal_connect (item
, "activate",
1022 G_CALLBACK (item_activate_cb
),
1029 anjuta_recent_chooser_menu_insert_item (AnjutaRecentChooserMenu
*menu
,
1030 GtkWidget
*menuitem
,
1032 gboolean anjuta_project
)
1034 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1037 if (priv
->first_recent_item_pos
== -1)
1039 GList
*children
, *l
;
1041 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
1043 for (real_position
= 0, l
= children
;
1045 real_position
+= 1, l
= l
->next
)
1047 GObject
*child
= l
->data
;
1048 gboolean is_placeholder
= FALSE
;
1051 GPOINTER_TO_INT (g_object_get_data (child
, "gtk-recent-menu-placeholder"));
1057 g_list_free (children
);
1058 priv
->first_recent_item_pos
= real_position
;
1062 real_position
= priv
->first_recent_item_pos
;
1066 if (priv
->prj_pos
!= 5)
1068 gtk_menu_shell_insert (GTK_MENU_SHELL (menu
), menuitem
,
1073 else gtk_menu_shell_append (GTK_MENU_SHELL (menu
), menuitem
);
1075 gtk_widget_show (menuitem
);
1078 /* removes the items we own from the menu */
1080 anjuta_recent_chooser_menu_dispose_items (AnjutaRecentChooserMenu
*menu
)
1082 GList
*children
, *l
;
1084 children
= gtk_container_get_children (GTK_CONTAINER (menu
));
1085 for (l
= children
; l
!= NULL
; l
= l
->next
)
1087 GtkWidget
*menu_item
= GTK_WIDGET (l
->data
);
1088 gboolean has_mark
= FALSE
;
1090 /* check for our mark, in order to remove just the items we own */
1092 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-menu-mark"));
1096 GtkRecentInfo
*info
;
1098 /* destroy the attached RecentInfo struct, if found */
1099 info
= g_object_get_data (G_OBJECT (menu_item
), "gtk-recent-info");
1101 g_object_set_data_full (G_OBJECT (menu_item
), "gtk-recent-info",
1104 /* and finally remove the item from the menu */
1105 gtk_container_remove (GTK_CONTAINER (menu
), menu_item
);
1109 /* recalculate the position of the first recent item */
1110 menu
->priv
->first_recent_item_pos
= -1;
1112 g_list_free (children
);
1120 gint displayed_items
;
1121 AnjutaRecentChooserMenu
*menu
;
1122 GtkWidget
*placeholder
;
1126 idle_populate_func (gpointer data
)
1128 MenuPopulateData
*pdata
;
1129 AnjutaRecentChooserMenuPrivate
*priv
;
1130 GtkRecentInfo
*info
;
1134 pdata
= (MenuPopulateData
*) data
;
1135 priv
= pdata
->menu
->priv
;
1139 pdata
->items
= gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata
->menu
));
1142 /* show the placeholder here */
1143 gtk_widget_show (pdata
->placeholder
);
1144 pdata
->displayed_items
= 1;
1145 priv
->populate_id
= 0;
1150 /* We add the separator */
1151 GtkWidget
*menuitem
;
1152 menuitem
= gtk_separator_menu_item_new ();
1153 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, menuitem
,
1154 pdata
->displayed_items
, FALSE
);
1155 g_object_set_data (G_OBJECT (menuitem
),
1156 "gtk-recent-menu-mark",
1157 GINT_TO_POINTER (TRUE
));
1159 pdata
->n_items
= g_list_length (pdata
->items
);
1160 pdata
->loaded_items
= 0;
1163 info
= g_list_nth_data (pdata
->items
, pdata
->loaded_items
);
1164 item
= anjuta_recent_chooser_menu_create_item (pdata
->menu
,
1166 pdata
->displayed_items
);
1168 goto check_and_return
;
1170 anjuta_recent_chooser_menu_add_tip (pdata
->menu
, info
, item
);
1171 if (strcmp (gtk_recent_info_get_mime_type (info
), "application/x-anjuta") == 0)
1172 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, item
,
1173 pdata
->displayed_items
, TRUE
);
1176 if (priv
->max_files
!= 14)
1178 anjuta_recent_chooser_menu_insert_item (pdata
->menu
, item
,
1179 pdata
->displayed_items
, FALSE
);
1184 pdata
->displayed_items
+= 1;
1186 /* mark the menu item as one of our own */
1187 g_object_set_data (G_OBJECT (item
),
1188 "gtk-recent-menu-mark",
1189 GINT_TO_POINTER (TRUE
));
1191 /* attach the RecentInfo object to the menu item, and own a reference
1192 * to it, so that it will be destroyed with the menu item when it's
1193 * not needed anymore.
1195 g_object_set_data_full (G_OBJECT (item
), "gtk-recent-info",
1196 gtk_recent_info_ref (info
),
1197 (GDestroyNotify
) gtk_recent_info_unref
);
1200 pdata
->loaded_items
+= 1;
1202 if (pdata
->loaded_items
== pdata
->n_items
)
1204 g_list_foreach (pdata
->items
, (GFunc
) gtk_recent_info_unref
, NULL
);
1205 g_list_free (pdata
->items
);
1207 priv
->populate_id
= 0;
1218 idle_populate_clean_up (gpointer data
)
1220 MenuPopulateData
*pdata
= data
;
1222 if (pdata
->menu
->priv
->populate_id
== 0)
1224 /* show the placeholder in case no item survived
1225 * the filtering process in the idle loop
1227 if (!pdata
->displayed_items
)
1228 gtk_widget_show (pdata
->placeholder
);
1229 g_object_unref (pdata
->placeholder
);
1231 g_slice_free (MenuPopulateData
, data
);
1236 anjuta_recent_chooser_menu_populate (AnjutaRecentChooserMenu
*menu
)
1238 MenuPopulateData
*pdata
;
1239 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1241 if (menu
->priv
->populate_id
)
1244 pdata
= g_slice_new (MenuPopulateData
);
1245 pdata
->items
= NULL
;
1247 pdata
->loaded_items
= 0;
1248 pdata
->displayed_items
= 0;
1250 pdata
->placeholder
= g_object_ref (priv
->placeholder
);
1252 priv
->icon_size
= get_icon_size_for_widget (GTK_WIDGET (menu
));
1254 priv
->max_files
= 0;
1256 /* remove our menu items first and hide the placeholder */
1257 anjuta_recent_chooser_menu_dispose_items (menu
);
1258 gtk_widget_hide (priv
->placeholder
);
1260 priv
->populate_id
= gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE
+ 30,
1263 idle_populate_clean_up
);
1266 /* bounce activate signal from the recent menu item widget
1267 * to the recent menu widget
1270 item_activate_cb (GtkWidget
*widget
,
1273 GtkRecentChooser
*chooser
= GTK_RECENT_CHOOSER (user_data
);
1275 g_signal_emit_by_name (chooser
, "item_activated");
1278 /* we force a redraw if the manager changes when we are showing */
1280 manager_changed_cb (GtkRecentManager
*manager
,
1283 AnjutaRecentChooserMenu
*menu
= ANJUTA_RECENT_CHOOSER_MENU (user_data
);
1285 anjuta_recent_chooser_menu_populate (menu
);
1289 set_recent_manager (AnjutaRecentChooserMenu
*menu
,
1290 GtkRecentManager
*manager
)
1292 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1296 if (priv
->manager_changed_id
)
1298 g_signal_handler_disconnect (priv
->manager
, priv
->manager_changed_id
);
1299 priv
->manager_changed_id
= 0;
1302 if (priv
->populate_id
)
1304 g_source_remove (priv
->populate_id
);
1305 priv
->populate_id
= 0;
1308 priv
->manager
= NULL
;
1312 priv
->manager
= manager
;
1314 priv
->manager
= gtk_recent_manager_get_default ();
1317 priv
->manager_changed_id
= g_signal_connect (priv
->manager
, "changed",
1318 G_CALLBACK (manager_changed_cb
),
1323 get_icon_size_for_widget (GtkWidget
*widget
)
1325 GtkSettings
*settings
;
1328 if (gtk_widget_has_screen (widget
))
1329 settings
= gtk_settings_get_for_screen (gtk_widget_get_screen (widget
));
1331 settings
= gtk_settings_get_default ();
1333 if (gtk_icon_size_lookup_for_settings (settings
, GTK_ICON_SIZE_MENU
,
1335 return MAX (width
, height
);
1337 return FALLBACK_ICON_SIZE
;
1341 foreach_set_shot_tips (GtkWidget
*widget
,
1344 AnjutaRecentChooserMenu
*menu
= user_data
;
1345 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1348 /* toggle the tooltip only on the items we create */
1350 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget
), "gtk-recent-menu-mark"));
1353 gtk_widget_set_has_tooltip (widget
, priv
->show_tips
);
1357 anjuta_recent_chooser_menu_set_show_tips (AnjutaRecentChooserMenu
*menu
,
1360 AnjutaRecentChooserMenuPrivate
*priv
= menu
->priv
;
1362 if (priv
->show_tips
== show_tips
)
1365 priv
->show_tips
= show_tips
;
1366 gtk_container_foreach (GTK_CONTAINER (menu
), foreach_set_shot_tips
, menu
);
1374 * anjuta_recent_chooser_menu_new:
1376 * Creates a new #AnjutaRecentChooserMenu widget.
1378 * This kind of widget shows the list of recently used resources as
1379 * a menu, each item as a menu item. Each item inside the menu might
1380 * have an icon, representing its MIME type, and a number, for mnemonic
1383 * This widget implements the #GtkRecentChooser interface.
1385 * This widget creates its own #GtkRecentManager object. See the
1386 * anjuta_recent_chooser_menu_new_for_manager() function to know how to create
1387 * a #AnjutaRecentChooserMenu widget bound to another #GtkRecentManager object.
1389 * Return value: a new #AnjutaRecentChooserMenu
1394 anjuta_recent_chooser_menu_new (void)
1396 return g_object_new (ANJUTA_TYPE_RECENT_CHOOSER_MENU
,
1397 "recent-manager", NULL
,
1402 * anjuta_recent_chooser_menu_new_for_manager:
1403 * @manager: a #GtkRecentManager
1405 * Creates a new #AnjutaRecentChooserMenu widget using @manager as
1406 * the underlying recently used resources manager.
1408 * This is useful if you have implemented your own recent manager,
1409 * or if you have a customized instance of a #GtkRecentManager
1410 * object or if you wish to share a common #GtkRecentManager object
1411 * among multiple #GtkRecentChooser widgets.
1413 * Return value: a new #AnjutaRecentChooserMenu, bound to @manager.
1418 anjuta_recent_chooser_menu_new_for_manager (GtkRecentManager
*manager
)
1420 g_return_val_if_fail (manager
== NULL
|| GTK_IS_RECENT_MANAGER (manager
), NULL
);
1422 return g_object_new (ANJUTA_TYPE_RECENT_CHOOSER_MENU
,
1423 "recent-manager", manager
,