1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of the
6 * License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
18 * James Willcox <jwillcox@cs.indiana.edu>
29 #include <libgnomevfs/gnome-vfs.h>
30 #ifndef USE_STABLE_LIBGNOMEUI
31 #include <libgnomeui/gnome-icon-theme.h>
33 #include <gconf/gconf-client.h>
34 #include "egg-recent-model.h"
35 #include "egg-recent-view.h"
36 #include "egg-recent-view-gtk.h"
37 #include "egg-recent-util.h"
38 #include "egg-recent-item.h"
40 #define DEFAULT_LABEL_WIDTH 30
42 struct _EggRecentViewGtk {
43 GObject parent_instance; /* We emit signals */
46 GtkWidget *start_menu_item;
49 gboolean trailing_sep;
56 gboolean show_numbers;
57 #ifndef USE_STABLE_LIBGNOMEUI
58 GnomeIconTheme *theme;
61 GtkTooltips *tooltips;
62 EggRecentViewGtkTooltipFunc tooltip_func;
63 gpointer tooltip_func_data;
65 EggRecentModel *model;
67 GtkIconSize icon_size;
74 struct _EggRecentViewGtkMenuData {
75 EggRecentViewGtk *view;
79 typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;
86 /* GObject properties */
96 static guint view_signals[LAST_SIGNAL] = { 0 };
100 egg_recent_view_gtk_clear (EggRecentViewGtk *view)
102 GList *menu_children;
105 gint *menu_data=NULL;
107 if (view->menu == NULL)
110 menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));
114 menu_item = (GObject *)p->data;
116 menu_data = (gint *)g_object_get_data (menu_item,
120 gtk_container_remove (GTK_CONTAINER (view->menu),
121 GTK_WIDGET (menu_item));
131 egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
134 GList *menu_children;
136 GtkWidget *menu_item;
139 g_return_val_if_fail (view, 0);
141 menu_children = GTK_MENU_SHELL (view->menu)->children;
146 menu_item = (GtkWidget *)p->data;
148 if (menu_item == view->start_menu_item) {
161 egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
163 EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
166 g_return_if_fail (md);
167 g_return_if_fail (md->item);
168 g_return_if_fail (md->view);
169 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));
173 egg_recent_item_ref (item);
175 g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
178 egg_recent_item_unref (item);
182 egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
184 EggRecentViewGtkMenuData *md = data;
186 egg_recent_item_unref (md->item);
191 egg_recent_view_gtk_new_separator (EggRecentViewGtk *view)
195 g_return_val_if_fail (view, NULL);
197 retval = gtk_separator_menu_item_new ();
200 * this is a tag so we can distinguish our menu items
201 * from others that may be in the menu.
203 g_object_set_data (G_OBJECT (retval),
205 GINT_TO_POINTER (1));
208 gtk_widget_show (retval);
214 egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
218 GtkWidget *menu_item;
219 EggRecentViewGtkMenuData *md;
228 g_return_val_if_fail (view, NULL);
229 g_return_val_if_fail (item, NULL);
231 short_name = egg_recent_item_get_short_name (item);
235 escaped = egg_recent_util_escape_underlines (short_name);
238 if (view->show_numbers) {
239 /* avoid having conflicting mnemonics */
241 text = g_strdup_printf ("%d. %s", index,
244 text = g_strdup_printf ("_%d. %s", index,
251 mime_type = egg_recent_item_get_mime_type (item);
252 #ifndef USE_STABLE_LIBGNOMEUI
257 gtk_icon_size_lookup_for_settings
258 (gtk_widget_get_settings (view->menu),
262 uri = egg_recent_item_get_uri (item);
263 pixbuf = egg_recent_util_get_icon (view->theme, uri,
271 image = gtk_image_new_from_pixbuf (pixbuf);
273 g_object_unref (pixbuf);
275 if (view->show_icons)
276 gtk_widget_show (image);
278 menu_item = gtk_image_menu_item_new_with_mnemonic (text);
279 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
282 label = GTK_BIN (menu_item)->child;
283 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
284 gtk_label_set_max_width_chars (GTK_LABEL (label), view->label_width);
286 md = g_new0 (EggRecentViewGtkMenuData, 1);
288 md->item = egg_recent_item_ref (item);
290 g_signal_connect_data (G_OBJECT (menu_item), "activate",
291 G_CALLBACK (egg_recent_view_gtk_menu_cb),
293 (GClosureNotify)egg_recent_view_gtk_destroy_cb,
300 * this is a tag so we can distinguish our menu items
301 * from others that may be in the menu.
303 g_object_set_data (G_OBJECT (menu_item),
305 GINT_TO_POINTER (1));
308 gtk_widget_show (menu_item);
314 egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
319 GtkWidget *menu_item;
322 g_return_if_fail (view);
323 g_return_if_fail (view->menu);
325 menu_offset = egg_recent_view_gtk_find_menu_offset (view);
328 menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);
330 menu_item = egg_recent_view_gtk_new_separator (view);
332 if (view->tooltip_func != NULL && menu_item != NULL) {
333 view->tooltip_func (view->tooltips, menu_item,
334 item, view->tooltip_func_data);
338 gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
343 egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
350 if (view->menu == NULL)
353 egg_recent_view_gtk_clear (view);
355 if (view->leading_sep) {
356 egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
362 item = (EggRecentItem *)p->data;
364 egg_recent_view_gtk_add_to_menu (view, item, display, index);
371 if (view->trailing_sep)
372 egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
376 model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
379 egg_recent_view_gtk_set_list (view, list);
381 egg_recent_view_gtk_clear (view);
384 static EggRecentModel *
385 egg_recent_view_gtk_get_model (EggRecentView *view_parent)
387 EggRecentViewGtk *view;
389 g_return_val_if_fail (view_parent != NULL, NULL);
390 view = EGG_RECENT_VIEW_GTK (view_parent);
395 egg_recent_view_gtk_set_model (EggRecentView *view_parent,
396 EggRecentModel *model)
398 EggRecentViewGtk *view;
400 g_return_if_fail (view_parent != NULL);
401 view = EGG_RECENT_VIEW_GTK (view_parent);
403 if (view->model != NULL) {
404 g_object_unref (view->model);
405 g_signal_handler_disconnect (G_OBJECT (model),
406 view->changed_cb_id);
410 g_object_ref (view->model);
412 view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
414 G_CALLBACK (model_changed_cb),
417 egg_recent_model_changed (view->model);
421 egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
423 view->leading_sep = val;
425 egg_recent_view_gtk_clear (view);
428 egg_recent_model_changed (view->model);
432 egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
434 view->trailing_sep = val;
436 egg_recent_view_gtk_clear (view);
439 egg_recent_model_changed (view->model);
443 egg_recent_view_gtk_set_property (GObject *object,
448 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
453 egg_recent_view_gtk_set_menu (view,
454 GTK_WIDGET (g_value_get_object (value)));
456 case PROP_START_MENU_ITEM:
457 egg_recent_view_gtk_set_start_menu_item (view,
458 g_value_get_object (value));
460 case PROP_SHOW_ICONS:
461 egg_recent_view_gtk_show_icons (view,
462 g_value_get_boolean (value));
464 case PROP_SHOW_NUMBERS:
465 egg_recent_view_gtk_show_numbers (view,
466 g_value_get_boolean (value));
468 case PROP_LABEL_WIDTH:
469 egg_recent_view_gtk_set_label_width (view,
470 g_value_get_int (value));
473 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479 egg_recent_view_gtk_get_property (GObject *object,
484 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
489 g_value_set_object (value, view->menu);
491 case PROP_START_MENU_ITEM:
492 g_value_set_object (value, view->start_menu_item);
494 case PROP_SHOW_ICONS:
495 g_value_set_boolean (value, view->show_icons);
497 case PROP_SHOW_NUMBERS:
498 g_value_set_boolean (value, view->show_numbers);
500 case PROP_LABEL_WIDTH:
501 g_value_set_int (value, view->label_width);
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
510 egg_recent_view_gtk_finalize (GObject *object)
512 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
514 g_signal_handler_disconnect (G_OBJECT (view->model),
515 view->changed_cb_id);
519 g_object_unref (view->model);
520 #ifndef USE_STABLE_LIBGNOMEUI
521 g_object_unref (view->theme);
523 g_object_unref (view->client);
525 g_object_unref (view->tooltips);
527 egg_recent_view_gtk_clear (view);
531 egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
533 GObjectClass *object_class;
535 object_class = G_OBJECT_CLASS (klass);
537 object_class->set_property = egg_recent_view_gtk_set_property;
538 object_class->get_property = egg_recent_view_gtk_get_property;
539 object_class->finalize = egg_recent_view_gtk_finalize;
541 view_signals[ACTIVATE] = g_signal_new ("activate",
542 G_OBJECT_CLASS_TYPE (object_class),
544 G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
546 g_cclosure_marshal_VOID__BOXED,
548 EGG_TYPE_RECENT_ITEM);
550 g_object_class_install_property (object_class,
552 g_param_spec_object ("menu",
554 "The GtkMenuShell this object will update.",
557 g_object_class_install_property (object_class,
558 PROP_START_MENU_ITEM,
559 g_param_spec_object ("start-menu-item",
561 "The menu item that precedes where are menu items will go",
562 gtk_menu_item_get_type (),
565 g_object_class_install_property (object_class,
567 g_param_spec_boolean ("show-icons",
569 "Whether or not to show icons",
573 g_object_class_install_property (object_class,
575 g_param_spec_boolean ("show-numbers",
577 "Whether or not to show numbers",
581 g_object_class_install_property (object_class,
583 g_param_spec_int ("label-width",
585 "The desired width of the menu label, in characters",
591 klass->activate = NULL;
595 egg_recent_view_init (EggRecentViewClass *iface)
597 iface->do_get_model = egg_recent_view_gtk_get_model;
598 iface->do_set_model = egg_recent_view_gtk_set_model;
602 show_menus_changed_cb (GConfClient *client,
605 EggRecentViewGtk *view)
609 value = gconf_entry_get_value (entry);
611 g_return_if_fail (value->type == GCONF_VALUE_BOOL);
613 egg_recent_view_gtk_show_icons (view,
614 gconf_value_get_bool (value));
618 #ifndef USE_STABLE_LIBGNOMEUI
620 theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
622 if (view->model != NULL)
623 egg_recent_model_changed (view->model);
628 egg_recent_view_gtk_init (EggRecentViewGtk * view)
630 view->client = gconf_client_get_default ();
633 gconf_client_get_bool (view->client,
634 "/desktop/gnome/interface/menus_have_icons",
637 gconf_client_add_dir (view->client, "/desktop/gnome/interface",
638 GCONF_CLIENT_PRELOAD_NONE,
640 gconf_client_notify_add (view->client,
641 "/desktop/gnome/interface/menus_have_icons",
642 (GConfClientNotifyFunc)show_menus_changed_cb,
646 view->leading_sep = FALSE;
647 view->trailing_sep = FALSE;
649 view->uid = egg_recent_util_get_unique_id ();
650 #ifndef USE_STABLE_LIBGNOMEUI
651 view->theme = gnome_icon_theme_new ();
652 gnome_icon_theme_set_allow_svg (view->theme, TRUE);
653 g_signal_connect_object (view->theme, "changed",
654 G_CALLBACK (theme_changed_cb), view, 0);
656 view->tooltips = gtk_tooltips_new ();
657 g_object_ref (view->tooltips);
658 gtk_object_sink (GTK_OBJECT (view->tooltips));
659 view->tooltip_func = NULL;
660 view->tooltip_func_data = NULL;
662 view->icon_size = GTK_ICON_SIZE_MENU;
664 view->label_width = DEFAULT_LABEL_WIDTH;
668 egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
669 GtkIconSize icon_size)
671 if (view->icon_size != icon_size) {
672 view->icon_size = icon_size;
673 egg_recent_model_changed (view->model);
675 view->icon_size = icon_size;
680 egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
682 return view->icon_size;
686 egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
688 view->show_icons = show;
691 egg_recent_model_changed (view->model);
695 egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
697 view->show_numbers = show;
700 egg_recent_model_changed (view->model);
704 egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
705 EggRecentViewGtkTooltipFunc func,
708 view->tooltip_func = func;
709 view->tooltip_func_data = user_data;
712 egg_recent_model_changed (view->model);
716 egg_recent_view_gtk_set_label_width (EggRecentViewGtk *view,
719 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
721 view->label_width = chars;
725 egg_recent_view_gtk_get_label_width (EggRecentViewGtk *view)
727 g_return_val_if_fail (EGG_IS_RECENT_VIEW_GTK (view), -1);
729 return view->label_width;
733 * egg_recent_view_gtk_set_menu:
734 * @view: A EggRecentViewGtk object.
735 * @menu: The GtkMenuShell to put the menu items in.
737 * Use this function to change the GtkMenuShell that the recent
738 * documents appear in.
742 egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
745 g_return_if_fail (view);
746 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
748 if (view->menu != NULL)
749 g_object_remove_weak_pointer (G_OBJECT (view->menu),
750 (gpointer *) &view->menu);
754 if (view->menu != NULL)
755 g_object_add_weak_pointer (G_OBJECT (view->menu),
756 (gpointer *) &view->menu);
760 * egg_recent_view_gtk_set_start_menu_item:
761 * @view: A EggRecentViewGtk object.
762 * @start_menu_item: The menu item that appears just before where our menu
763 * items should appear
767 egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
768 GtkWidget *menu_item)
770 g_return_if_fail (view);
771 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
773 view->start_menu_item = menu_item;
777 * egg_recent_view_gtk_get_menu:
778 * @view: A EggRecentViewGtk object.
782 egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
788 * egg_recent_view_gtk_get_start_menu_item
789 * @view: A EggRecentViewGtk object.
793 egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
795 return view->start_menu_item;
800 * egg_recent_view_gtk_new:
801 * @appname: The name of your application.
802 * @limit: The maximum number of items allowed.
804 * This creates a new EggRecentViewGtk object.
806 * Returns: a EggRecentViewGtk object
809 egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
811 EggRecentViewGtk *view;
813 g_return_val_if_fail (menu, NULL);
815 view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
819 "show-numbers", TRUE, NULL));
821 g_return_val_if_fail (view, NULL);
827 * egg_recent_view_gtk_get_type:
829 * This returns a GType representing a EggRecentViewGtk object.
834 egg_recent_view_gtk_get_type (void)
836 static GType egg_recent_view_gtk_type = 0;
838 if(!egg_recent_view_gtk_type) {
839 static const GTypeInfo egg_recent_view_gtk_info = {
840 sizeof (EggRecentViewGtkClass),
841 NULL, /* base init */
842 NULL, /* base finalize */
843 (GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
844 NULL, /* class finalize */
845 NULL, /* class data */
846 sizeof (EggRecentViewGtk),
848 (GInstanceInitFunc) egg_recent_view_gtk_init
851 static const GInterfaceInfo view_info =
853 (GInterfaceInitFunc) egg_recent_view_init,
858 egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
860 &egg_recent_view_gtk_info, 0);
861 g_type_add_interface_static (egg_recent_view_gtk_type,
862 EGG_TYPE_RECENT_VIEW,
866 return egg_recent_view_gtk_type;