*** empty log message ***
[anjuta-git-plugin.git] / libegg / egg-recent-view-gtk.c
blob34805b295140bfce7f1f046317ff88d6e466a1f7
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /**
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.
17 * Authors:
18 * James Willcox <jwillcox@cs.indiana.edu>
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <gtk/gtk.h>
29 #include <libgnomevfs/gnome-vfs.h>
30 #ifndef USE_STABLE_LIBGNOMEUI
31 #include <libgnomeui/gnome-icon-theme.h>
32 #endif
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 struct _EggRecentViewGtk {
41 GObject parent_instance; /* We emit signals */
43 GtkWidget *menu;
44 GtkWidget *start_menu_item;
46 gboolean leading_sep;
47 gboolean trailing_sep;
49 gulong changed_cb_id;
51 gchar *uid;
53 gboolean show_icons;
54 gboolean show_numbers;
55 #ifndef USE_STABLE_LIBGNOMEUI
56 GnomeIconTheme *theme;
57 #endif
59 GtkTooltips *tooltips;
60 EggRecentViewGtkTooltipFunc tooltip_func;
61 gpointer tooltip_func_data;
63 EggRecentModel *model;
64 GConfClient *client;
65 GtkIconSize icon_size;
70 struct _EggRecentViewGtkMenuData {
71 EggRecentViewGtk *view;
72 EggRecentItem *item;
75 typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;
77 enum {
78 ACTIVATE,
79 LAST_SIGNAL
82 /* GObject properties */
83 enum {
84 PROP_BOGUS,
85 PROP_MENU,
86 PROP_START_MENU_ITEM,
87 PROP_SHOW_ICONS,
88 PROP_SHOW_NUMBERS
91 static guint view_signals[LAST_SIGNAL] = { 0 };
94 static void
95 egg_recent_view_gtk_clear (EggRecentViewGtk *view)
97 GList *menu_children;
98 GList *p;
99 GObject *menu_item;
100 gint *menu_data=NULL;
102 g_return_if_fail (view->menu != NULL);
104 menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));
106 p = menu_children;
107 while (p != NULL) {
108 menu_item = (GObject *)p->data;
110 menu_data = (gint *)g_object_get_data (menu_item,
111 view->uid);
113 if (menu_data) {
114 gtk_container_remove (GTK_CONTAINER (view->menu),
115 GTK_WIDGET (menu_item));
119 p = p->next;
124 static gint
125 egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
127 gint i;
128 GList *menu_children;
129 GList *p;
130 GtkWidget *menu_item;
131 gint menu_loc=-1;
133 g_return_val_if_fail (view, 0);
135 menu_children = GTK_MENU_SHELL (view->menu)->children;
137 i = 0;
138 p = menu_children;
139 while (p != NULL) {
140 menu_item = (GtkWidget *)p->data;
142 if (menu_item == view->start_menu_item) {
143 menu_loc = i;
144 break;
147 p = p->next;
148 i++;
151 return menu_loc;
154 static void
155 egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
157 EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
158 EggRecentItem *item;
160 g_return_if_fail (md);
161 g_return_if_fail (md->item);
162 g_return_if_fail (md->view);
163 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));
165 item = md->item;
167 egg_recent_item_ref (item);
169 g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
170 item);
172 egg_recent_item_unref (item);
175 static void
176 egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
178 EggRecentViewGtkMenuData *md = data;
180 egg_recent_item_unref (md->item);
181 g_free (md);
184 static GtkWidget *
185 egg_recent_view_gtk_new_separator (EggRecentViewGtk *view)
187 GtkWidget *retval;
189 g_return_val_if_fail (view, NULL);
191 retval = gtk_separator_menu_item_new ();
194 * this is a tag so we can distinguish our menu items
195 * from others that may be in the menu.
197 g_object_set_data (G_OBJECT (retval),
198 view->uid,
199 GINT_TO_POINTER (1));
202 gtk_widget_show (retval);
204 return retval;
207 static GtkWidget *
208 egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
209 EggRecentItem *item,
210 gint index)
212 GtkWidget *menu_item;
213 EggRecentViewGtkMenuData *md;
214 gchar *mime_type;
215 GtkWidget *image;
216 GdkPixbuf *pixbuf;
217 gchar *text;
218 gchar *short_name;
219 gchar *escaped;
221 g_return_val_if_fail (view, NULL);
222 g_return_val_if_fail (item, NULL);
224 short_name = egg_recent_item_get_short_name (item);
225 if (!short_name)
226 return NULL;
228 escaped = egg_recent_util_escape_underlines (short_name);
229 g_free (short_name);
231 if (view->show_numbers) {
232 /* avoid having conflicting mnemonics */
233 if (index >= 10)
234 text = g_strdup_printf ("%d. %s", index,
235 escaped);
236 else
237 text = g_strdup_printf ("_%d. %s", index,
238 escaped);
239 g_free (escaped);
240 } else {
241 text = escaped;
244 mime_type = egg_recent_item_get_mime_type (item);
245 #ifndef USE_STABLE_LIBGNOMEUI
247 int width, height;
248 gchar *uri;
250 gtk_icon_size_lookup_for_settings
251 (gtk_widget_get_settings (view->menu),
252 view->icon_size,
253 &width, &height);
255 uri = egg_recent_item_get_uri (item);
256 pixbuf = egg_recent_util_get_icon (view->theme, uri,
257 mime_type,
258 height);
259 g_free (uri);
261 #else
262 pixbuf = NULL;
263 #endif
264 image = gtk_image_new_from_pixbuf (pixbuf);
265 if (pixbuf)
266 g_object_unref (pixbuf);
268 if (view->show_icons)
269 gtk_widget_show (image);
271 menu_item = gtk_image_menu_item_new_with_mnemonic (text);
272 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
273 image);
275 md = g_new0 (EggRecentViewGtkMenuData, 1);
276 md->view = view;
277 md->item = egg_recent_item_ref (item);
279 g_signal_connect_data (G_OBJECT (menu_item), "activate",
280 G_CALLBACK (egg_recent_view_gtk_menu_cb),
282 (GClosureNotify)egg_recent_view_gtk_destroy_cb,
285 g_free (mime_type);
286 g_free (text);
289 * this is a tag so we can distinguish our menu items
290 * from others that may be in the menu.
292 g_object_set_data (G_OBJECT (menu_item),
293 view->uid,
294 GINT_TO_POINTER (1));
297 gtk_widget_show (menu_item);
299 return menu_item;
302 static void
303 egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
304 EggRecentItem *item,
305 gint display,
306 gint index)
308 GtkWidget *menu_item;
309 gint menu_offset;
311 g_return_if_fail (view);
312 g_return_if_fail (view->menu);
314 menu_offset = egg_recent_view_gtk_find_menu_offset (view);
316 if (item != NULL)
317 menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);
318 else
319 menu_item = egg_recent_view_gtk_new_separator (view);
321 if (view->tooltip_func != NULL && menu_item != NULL) {
322 view->tooltip_func (view->tooltips, menu_item,
323 item, view->tooltip_func_data);
326 if (menu_item)
327 gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
328 menu_offset+index);
331 static void
332 egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
334 EggRecentItem *item;
335 GList *p;
336 gint display=1;
337 gint index=1;
339 g_return_if_fail (view);
341 egg_recent_view_gtk_clear (view);
343 if (view->leading_sep) {
344 egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
345 index++;
348 p = list;
349 while (p != NULL) {
350 item = (EggRecentItem *)p->data;
352 egg_recent_view_gtk_add_to_menu (view, item, display, index);
354 p = p->next;
355 display++;
356 index++;
359 if (view->trailing_sep)
360 egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
363 static void
364 model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
366 if (list != NULL)
367 egg_recent_view_gtk_set_list (view, list);
368 else
369 egg_recent_view_gtk_clear (view);
372 static EggRecentModel *
373 egg_recent_view_gtk_get_model (EggRecentView *view_parent)
375 EggRecentViewGtk *view;
377 g_return_val_if_fail (view_parent != NULL, NULL);
378 view = EGG_RECENT_VIEW_GTK (view_parent);
379 return view->model;
382 static void
383 egg_recent_view_gtk_set_model (EggRecentView *view_parent,
384 EggRecentModel *model)
386 EggRecentViewGtk *view;
388 g_return_if_fail (view_parent != NULL);
389 view = EGG_RECENT_VIEW_GTK (view_parent);
391 if (view->model != NULL) {
392 g_object_unref (view->model);
393 g_signal_handler_disconnect (G_OBJECT (model),
394 view->changed_cb_id);
397 view->model = model;
398 g_object_ref (view->model);
400 view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
401 "changed",
402 G_CALLBACK (model_changed_cb),
403 view, 0);
405 egg_recent_model_changed (view->model);
408 void
409 egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
411 view->leading_sep = val;
413 egg_recent_view_gtk_clear (view);
415 if (view->model)
416 egg_recent_model_changed (view->model);
419 void
420 egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
422 view->trailing_sep = val;
424 egg_recent_view_gtk_clear (view);
426 if (view->model)
427 egg_recent_model_changed (view->model);
430 static void
431 egg_recent_view_gtk_set_property (GObject *object,
432 guint prop_id,
433 const GValue *value,
434 GParamSpec *pspec)
436 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
438 switch (prop_id)
440 case PROP_MENU:
441 egg_recent_view_gtk_set_menu (view,
442 GTK_WIDGET (g_value_get_object (value)));
443 break;
444 case PROP_START_MENU_ITEM:
445 egg_recent_view_gtk_set_start_menu_item (view,
446 g_value_get_object (value));
447 break;
448 case PROP_SHOW_ICONS:
449 egg_recent_view_gtk_show_icons (view,
450 g_value_get_boolean (value));
451 break;
452 case PROP_SHOW_NUMBERS:
453 egg_recent_view_gtk_show_numbers (view,
454 g_value_get_boolean (value));
455 break;
456 default:
457 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
458 break;
462 static void
463 egg_recent_view_gtk_get_property (GObject *object,
464 guint prop_id,
465 GValue *value,
466 GParamSpec *pspec)
468 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
470 switch (prop_id)
472 case PROP_MENU:
473 g_value_set_object (value, view->menu);
474 break;
475 case PROP_START_MENU_ITEM:
476 g_value_set_object (value, view->start_menu_item);
477 break;
478 case PROP_SHOW_ICONS:
479 g_value_set_boolean (value, view->show_icons);
480 break;
481 case PROP_SHOW_NUMBERS:
482 g_value_set_boolean (value, view->show_numbers);
483 break;
484 default:
485 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
486 break;
490 static void
491 egg_recent_view_gtk_finalize (GObject *object)
493 EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);
495 g_signal_handler_disconnect (G_OBJECT (view->model),
496 view->changed_cb_id);
498 g_free (view->uid);
500 g_object_unref (view->menu);
501 g_object_unref (view->model);
502 #ifndef USE_STABLE_LIBGNOMEUI
503 g_object_unref (view->theme);
504 #endif
505 g_object_unref (view->client);
507 g_object_unref (view->tooltips);
510 static void
511 egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
513 GObjectClass *object_class;
515 object_class = G_OBJECT_CLASS (klass);
517 object_class->set_property = egg_recent_view_gtk_set_property;
518 object_class->get_property = egg_recent_view_gtk_get_property;
519 object_class->finalize = egg_recent_view_gtk_finalize;
521 view_signals[ACTIVATE] = g_signal_new ("activate",
522 G_OBJECT_CLASS_TYPE (object_class),
523 G_SIGNAL_RUN_LAST,
524 G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
525 NULL, NULL,
526 g_cclosure_marshal_VOID__BOXED,
527 G_TYPE_NONE, 1,
528 EGG_TYPE_RECENT_ITEM);
530 g_object_class_install_property (object_class,
531 PROP_MENU,
532 g_param_spec_object ("menu",
533 "Menu",
534 "The GtkMenuShell this object will update.",
535 gtk_menu_get_type(),
536 G_PARAM_READWRITE));
537 g_object_class_install_property (object_class,
538 PROP_START_MENU_ITEM,
539 g_param_spec_object ("start-menu-item",
540 "Start Menu Item",
541 "The menu item that precedes where are menu items will go",
542 gtk_menu_item_get_type (),
543 G_PARAM_READWRITE));
545 g_object_class_install_property (object_class,
546 PROP_SHOW_ICONS,
547 g_param_spec_boolean ("show-icons",
548 "Show Icons",
549 "Whether or not to show icons",
550 FALSE,
551 G_PARAM_READWRITE));
553 g_object_class_install_property (object_class,
554 PROP_SHOW_NUMBERS,
555 g_param_spec_boolean ("show-numbers",
556 "Show Numbers",
557 "Whether or not to show numbers",
558 TRUE,
559 G_PARAM_READWRITE));
561 klass->activate = NULL;
564 static void
565 egg_recent_view_init (EggRecentViewClass *iface)
567 iface->do_get_model = egg_recent_view_gtk_get_model;
568 iface->do_set_model = egg_recent_view_gtk_set_model;
571 static void
572 show_menus_changed_cb (GConfClient *client,
573 guint cnxn_id,
574 GConfEntry *entry,
575 EggRecentViewGtk *view)
577 GConfValue *value;
579 value = gconf_entry_get_value (entry);
581 g_return_if_fail (value->type == GCONF_VALUE_BOOL);
583 egg_recent_view_gtk_show_icons (view,
584 gconf_value_get_bool (value));
588 #ifndef USE_STABLE_LIBGNOMEUI
589 static void
590 theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
592 if (view->model != NULL)
593 egg_recent_model_changed (view->model);
595 #endif
597 static void
598 egg_recent_view_gtk_init (EggRecentViewGtk * view)
600 view->client = gconf_client_get_default ();
602 view->show_icons =
603 gconf_client_get_bool (view->client,
604 "/desktop/gnome/interface/menus_have_icons",
605 NULL);
607 gconf_client_add_dir (view->client, "/desktop/gnome/interface",
608 GCONF_CLIENT_PRELOAD_NONE,
609 NULL);
610 gconf_client_notify_add (view->client,
611 "/desktop/gnome/interface/menus_have_icons",
612 (GConfClientNotifyFunc)show_menus_changed_cb,
613 view, NULL, NULL);
616 view->leading_sep = FALSE;
617 view->trailing_sep = FALSE;
619 view->uid = egg_recent_util_get_unique_id ();
620 #ifndef USE_STABLE_LIBGNOMEUI
621 view->theme = gnome_icon_theme_new ();
622 gnome_icon_theme_set_allow_svg (view->theme, TRUE);
623 g_signal_connect_object (view->theme, "changed",
624 G_CALLBACK (theme_changed_cb), view, 0);
625 #endif
626 view->tooltips = gtk_tooltips_new ();
627 g_object_ref (view->tooltips);
628 gtk_object_sink (GTK_OBJECT (view->tooltips));
629 view->tooltip_func = NULL;
630 view->tooltip_func_data = NULL;
632 view->icon_size = GTK_ICON_SIZE_MENU;
635 void
636 egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
637 GtkIconSize icon_size)
639 if (view->icon_size != icon_size) {
640 view->icon_size = icon_size;
641 egg_recent_model_changed (view->model);
642 } else {
643 view->icon_size = icon_size;
647 GtkIconSize
648 egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
650 return view->icon_size;
653 void
654 egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
656 view->show_icons = show;
658 if (view->model)
659 egg_recent_model_changed (view->model);
662 void
663 egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
665 view->show_numbers = show;
667 if (view->model)
668 egg_recent_model_changed (view->model);
671 void
672 egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
673 EggRecentViewGtkTooltipFunc func,
674 gpointer user_data)
676 view->tooltip_func = func;
677 view->tooltip_func_data = user_data;
679 if (view->model)
680 egg_recent_model_changed (view->model);
684 * egg_recent_view_gtk_set_menu:
685 * @view: A EggRecentViewGtk object.
686 * @menu: The GtkMenuShell to put the menu items in.
688 * Use this function to change the GtkMenuShell that the recent
689 * documents appear in.
692 void
693 egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
694 GtkWidget *menu)
696 g_return_if_fail (view);
697 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
698 g_return_if_fail (menu);
700 if (view->menu != NULL)
701 g_object_unref (view->menu);
703 view->menu = menu;
704 g_object_ref (view->menu);
708 * egg_recent_view_gtk_set_start_menu_item:
709 * @view: A EggRecentViewGtk object.
710 * @start_menu_item: The menu item that appears just before where our menu
711 * items should appear
714 void
715 egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
716 GtkWidget *menu_item)
718 g_return_if_fail (view);
719 g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
721 view->start_menu_item = menu_item;
725 * egg_recent_view_gtk_get_menu:
726 * @view: A EggRecentViewGtk object.
729 GtkWidget *
730 egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
732 return view->menu;
736 * egg_recent_view_gtk_get_start_menu_item
737 * @view: A EggRecentViewGtk object.
740 GtkWidget *
741 egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
743 return view->start_menu_item;
748 * egg_recent_view_gtk_new:
749 * @appname: The name of your application.
750 * @limit: The maximum number of items allowed.
752 * This creates a new EggRecentViewGtk object.
754 * Returns: a EggRecentViewGtk object
756 EggRecentViewGtk *
757 egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
759 EggRecentViewGtk *view;
761 g_return_val_if_fail (menu, NULL);
763 view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
764 "start-menu-item",
765 start_menu_item,
766 "menu", menu,
767 "show-numbers", TRUE, NULL));
769 g_return_val_if_fail (view, NULL);
771 return view;
775 * egg_recent_view_gtk_get_type:
776 * @:
778 * This returns a GType representing a EggRecentViewGtk object.
780 * Returns: a GType
782 GType
783 egg_recent_view_gtk_get_type (void)
785 static GType egg_recent_view_gtk_type = 0;
787 if(!egg_recent_view_gtk_type) {
788 static const GTypeInfo egg_recent_view_gtk_info = {
789 sizeof (EggRecentViewGtkClass),
790 NULL, /* base init */
791 NULL, /* base finalize */
792 (GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
793 NULL, /* class finalize */
794 NULL, /* class data */
795 sizeof (EggRecentViewGtk),
797 (GInstanceInitFunc) egg_recent_view_gtk_init
800 static const GInterfaceInfo view_info =
802 (GInterfaceInitFunc) egg_recent_view_init,
803 NULL,
804 NULL
807 egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
808 "EggRecentViewGtk",
809 &egg_recent_view_gtk_info, 0);
810 g_type_add_interface_static (egg_recent_view_gtk_type,
811 EGG_TYPE_RECENT_VIEW,
812 &view_info);
815 return egg_recent_view_gtk_type;