2006-10-27 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / shell / rb-tray-icon.c
blob1fcf0fcac36516c16fabfc6ac031c1219d4aacee
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * arch-tag: Implementation of Rhythmbox tray icon object
5 * Copyright (C) 2003,2004 Colin Walters <walters@redhat.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include <config.h>
25 #include <unistd.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31 #include <libgnomevfs/gnome-vfs.h>
32 #include <libgnomevfs/gnome-vfs-mime-utils.h>
34 #include "rb-tray-icon.h"
35 #include "rb-stock-icons.h"
36 #include "rb-debug.h"
37 #include "eel-gconf-extensions.h"
38 #include "rb-preferences.h"
39 #include "rb-shell.h"
40 #include "rb-shell-player.h"
42 static void rb_tray_icon_class_init (RBTrayIconClass *klass);
43 static void rb_tray_icon_init (RBTrayIcon *shell_player);
44 static GObject *rb_tray_icon_constructor (GType type, guint n_construct_properties,
45 GObjectConstructParam *construct_properties);
46 static void rb_tray_icon_finalize (GObject *object);
47 static void rb_tray_icon_sync_action (RBShell *shell,
48 gboolean visible,
49 RBTrayIcon *tray);
50 static void rb_tray_icon_set_property (GObject *object,
51 guint prop_id,
52 const GValue *value,
53 GParamSpec *pspec);
54 static void rb_tray_icon_get_property (GObject *object,
55 guint prop_id,
56 GValue *value,
57 GParamSpec *pspec);
58 static void rb_tray_icon_button_press_event_cb (GtkWidget *ebox, GdkEventButton *event,
59 RBTrayIcon *icon);
60 static void rb_tray_icon_scroll_event_cb (GtkWidget *ebox, GdkEvent *event,
61 RBTrayIcon *icon);
62 static void rb_tray_icon_show_window_changed_cb (GtkAction *action,
63 RBTrayIcon *icon);
64 static void rb_tray_icon_show_notifications_changed_cb (GtkAction *action,
65 RBTrayIcon *icon);
66 static void rb_tray_icon_drop_cb (GtkWidget *widget,
67 GdkDragContext *context,
68 gint x,
69 gint y,
70 GtkSelectionData *data,
71 guint info,
72 guint time,
73 RBTrayIcon *icon);
75 struct RBTrayIconPrivate
77 GtkTooltips *tooltips;
78 GtkUIManager *ui_manager;
79 GtkActionGroup *actiongroup;
81 GtkWidget *ebox;
83 RBShell *shell;
84 RBShellPlayer *shell_player;
86 gboolean show_notifications;
89 #define RB_TRAY_ICON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_TRAY_ICON, RBTrayIconPrivate))
91 enum
93 PROP_0,
94 PROP_UI_MANAGER,
95 PROP_ACTION_GROUP,
96 PROP_SHELL
99 enum
101 LAST_SIGNAL,
104 static GtkToggleActionEntry rb_tray_icon_toggle_entries [] =
106 { "TrayShowWindow", NULL, N_("_Show Music Player"), NULL,
107 N_("Choose music to play"),
108 G_CALLBACK (rb_tray_icon_show_window_changed_cb) },
109 { "TrayShowNotifications", NULL, N_("Show N_otifications"), NULL,
110 N_("Show notifications of song changes and other events"),
111 G_CALLBACK (rb_tray_icon_show_notifications_changed_cb) }
113 static guint rb_tray_icon_n_toggle_entries = G_N_ELEMENTS (rb_tray_icon_toggle_entries);
115 static const GtkTargetEntry target_uri [] = {{ "text/uri-list", 0, 0 }};
117 G_DEFINE_TYPE (RBTrayIcon, rb_tray_icon, EGG_TYPE_TRAY_ICON)
119 static void
120 rb_tray_icon_class_init (RBTrayIconClass *klass)
122 GObjectClass *object_class = G_OBJECT_CLASS (klass);
124 object_class->finalize = rb_tray_icon_finalize;
125 object_class->constructor = rb_tray_icon_constructor;
127 object_class->set_property = rb_tray_icon_set_property;
128 object_class->get_property = rb_tray_icon_get_property;
130 g_object_class_install_property (object_class,
131 PROP_SHELL,
132 g_param_spec_object ("shell",
133 "RBShell",
134 "RBShell object",
135 RB_TYPE_SHELL,
136 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
137 g_object_class_install_property (object_class,
138 PROP_UI_MANAGER,
139 g_param_spec_object ("ui-manager",
140 "GtkUIManager",
141 "GtkUIManager object",
142 GTK_TYPE_UI_MANAGER,
143 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
144 g_object_class_install_property (object_class,
145 PROP_ACTION_GROUP,
146 g_param_spec_object ("action-group",
147 "GtkActionGroup",
148 "GtkActionGroup object",
149 GTK_TYPE_ACTION_GROUP,
150 G_PARAM_READABLE));
152 g_type_class_add_private (klass, sizeof (RBTrayIconPrivate));
155 static void
156 rb_tray_icon_init (RBTrayIcon *icon)
158 GtkWidget *image;
160 rb_debug ("setting up tray icon");
162 icon->priv = RB_TRAY_ICON_GET_PRIVATE (icon);
164 icon->priv->tooltips = gtk_tooltips_new ();
166 gtk_tooltips_set_tip (icon->priv->tooltips, GTK_WIDGET (icon),
167 _("Not playing"), NULL);
169 icon->priv->ebox = gtk_event_box_new ();
170 g_signal_connect_object (G_OBJECT (icon->priv->ebox),
171 "button_press_event",
172 G_CALLBACK (rb_tray_icon_button_press_event_cb),
173 icon, 0);
174 g_signal_connect_object (G_OBJECT (icon->priv->ebox),
175 "scroll_event",
176 G_CALLBACK (rb_tray_icon_scroll_event_cb),
177 icon, 0);
178 gtk_drag_dest_set (icon->priv->ebox, GTK_DEST_DEFAULT_ALL, target_uri, 1, GDK_ACTION_COPY);
179 g_signal_connect_object (G_OBJECT (icon->priv->ebox), "drag_data_received",
180 G_CALLBACK (rb_tray_icon_drop_cb), icon, 0);
182 image = gtk_image_new_from_stock (RB_STOCK_TRAY_ICON,
183 GTK_ICON_SIZE_SMALL_TOOLBAR);
184 gtk_container_add (GTK_CONTAINER (icon->priv->ebox), image);
186 gtk_container_add (GTK_CONTAINER (icon), icon->priv->ebox);
187 gtk_widget_show_all (GTK_WIDGET (icon->priv->ebox));
190 static GObject *
191 rb_tray_icon_constructor (GType type, guint n_construct_properties,
192 GObjectConstructParam *construct_properties)
194 RBTrayIcon *tray;
195 RBTrayIconClass *klass;
197 klass = RB_TRAY_ICON_CLASS (g_type_class_peek (RB_TYPE_TRAY_ICON));
199 tray = RB_TRAY_ICON (G_OBJECT_CLASS (rb_tray_icon_parent_class)->constructor
200 (type, n_construct_properties,
201 construct_properties));
203 #ifdef HAVE_NOTIFY
204 tray->priv->show_notifications = eel_gconf_get_boolean (CONF_UI_SHOW_NOTIFICATIONS);
205 #endif
207 tray->priv->actiongroup = gtk_action_group_new ("TrayActions");
208 gtk_action_group_set_translation_domain (tray->priv->actiongroup,
209 GETTEXT_PACKAGE);
210 gtk_action_group_add_toggle_actions (tray->priv->actiongroup,
211 rb_tray_icon_toggle_entries,
212 rb_tray_icon_n_toggle_entries,
213 tray);
214 rb_tray_icon_sync_action (NULL, FALSE, tray);
216 gtk_ui_manager_insert_action_group (tray->priv->ui_manager, tray->priv->actiongroup, 0);
217 g_object_unref (tray->priv->actiongroup);
219 return G_OBJECT (tray);
222 static void
223 rb_tray_icon_finalize (GObject *object)
225 RBTrayIcon *tray;
227 g_return_if_fail (object != NULL);
228 g_return_if_fail (RB_IS_TRAY_ICON (object));
230 tray = RB_TRAY_ICON (object);
232 rb_debug ("tray icon %p finalizing", object);
234 gtk_ui_manager_remove_action_group (tray->priv->ui_manager, tray->priv->actiongroup);
236 g_return_if_fail (tray->priv != NULL);
238 if (tray->priv->shell_player != NULL) {
239 g_object_unref (tray->priv->shell_player);
242 gtk_object_destroy (GTK_OBJECT (tray->priv->tooltips));
244 G_OBJECT_CLASS (rb_tray_icon_parent_class)->finalize (object);
247 static void
248 rb_tray_icon_sync_action (RBShell *shell, gboolean visible, RBTrayIcon *tray)
250 GtkAction *action;
252 if ((tray->priv->actiongroup != NULL) && (tray->priv->shell != NULL)) {
253 gboolean visible;
255 action = gtk_action_group_get_action (tray->priv->actiongroup,
256 "TrayShowWindow");
257 g_object_get (tray->priv->shell, "visibility", &visible, NULL);
258 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
260 action = gtk_action_group_get_action (tray->priv->actiongroup,
261 "TrayShowNotifications");
262 #ifdef HAVE_NOTIFY
263 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
264 tray->priv->show_notifications);
265 #else
266 gtk_action_set_visible (action, FALSE);
267 #endif
271 static void
272 rb_tray_icon_set_property (GObject *object,
273 guint prop_id,
274 const GValue *value,
275 GParamSpec *pspec)
277 RBTrayIcon *tray = RB_TRAY_ICON (object);
279 switch (prop_id)
281 case PROP_SHELL:
282 tray->priv->shell = g_value_get_object (value);
283 g_signal_connect_object (G_OBJECT (tray->priv->shell),
284 "visibility_changed",
285 G_CALLBACK (rb_tray_icon_sync_action),
286 tray, 0);
287 g_object_get (tray->priv->shell,
288 "shell-player", &tray->priv->shell_player,
289 NULL);
290 rb_tray_icon_sync_action (NULL, FALSE, tray);
291 break;
292 case PROP_UI_MANAGER:
293 tray->priv->ui_manager = g_value_get_object (value);
294 break;
295 default:
296 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297 break;
301 static void
302 rb_tray_icon_get_property (GObject *object,
303 guint prop_id,
304 GValue *value,
305 GParamSpec *pspec)
307 RBTrayIcon *tray = RB_TRAY_ICON (object);
309 switch (prop_id)
311 case PROP_SHELL:
312 g_value_set_object (value, tray->priv->shell);
313 break;
314 case PROP_UI_MANAGER:
315 g_value_set_object (value, tray->priv->ui_manager);
316 break;
317 case PROP_ACTION_GROUP:
318 g_value_set_object (value, tray->priv->actiongroup);
319 break;
320 default:
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
322 break;
326 RBTrayIcon *
327 rb_tray_icon_new (GtkUIManager *mgr,
328 RBShell *shell)
330 return g_object_new (RB_TYPE_TRAY_ICON,
331 "title", "Rhythmbox tray icon",
332 "ui-manager", mgr,
333 "shell", shell,
334 NULL);
337 static void
338 tray_popup_position_menu (GtkMenu *menu,
339 int *x,
340 int *y,
341 gboolean *push_in,
342 gpointer user_data)
344 GtkWidget *widget;
345 GtkRequisition requisition;
346 gint menu_xpos;
347 gint menu_ypos;
349 widget = GTK_WIDGET (user_data);
351 gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
353 gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
355 menu_xpos += widget->allocation.x;
356 menu_ypos += widget->allocation.y;
358 if (menu_ypos > gdk_screen_get_height (gtk_widget_get_screen (widget)) / 2)
359 menu_ypos -= requisition.height + widget->style->ythickness;
360 else
361 menu_ypos += widget->allocation.height + widget->style->ythickness;
363 *x = menu_xpos;
364 *y = menu_ypos;
365 *push_in = TRUE;
368 static void
369 rb_tray_icon_button_press_event_cb (GtkWidget *ebox, GdkEventButton *event,
370 RBTrayIcon *icon)
372 GtkWidget *popup;
374 /* filter out double, triple clicks */
375 if (event->type != GDK_BUTTON_PRESS)
376 return;
378 rb_debug ("tray button press");
380 switch (event->button) {
381 case 1:
382 rb_shell_toggle_visibility (icon->priv->shell);
383 break;
384 case 2:
385 rb_shell_player_playpause (icon->priv->shell_player, FALSE, NULL);
386 break;
387 case 3:
388 popup = gtk_ui_manager_get_widget (GTK_UI_MANAGER (icon->priv->ui_manager),
389 "/RhythmboxTrayPopup");
390 gtk_menu_set_screen (GTK_MENU (popup), gtk_widget_get_screen (GTK_WIDGET (icon)));
391 gtk_menu_popup (GTK_MENU (popup), NULL, NULL,
392 tray_popup_position_menu, ebox, 2,
393 gtk_get_current_event_time ());
394 break;
398 static void
399 rb_tray_icon_scroll_event_cb (GtkWidget *ebox, GdkEvent *event,
400 RBTrayIcon *icon)
402 gdouble adjust;
404 switch (event->scroll.direction) {
405 case GDK_SCROLL_UP:
406 adjust = 0.1;
407 break;
408 case GDK_SCROLL_DOWN:
409 adjust = -0.1;
410 break;
411 default:
412 return;
415 rb_shell_player_set_volume_relative (icon->priv->shell_player, adjust, NULL);
418 static void
419 rb_tray_icon_drop_cb (GtkWidget *widget,
420 GdkDragContext *context,
421 gint x,
422 gint y,
423 GtkSelectionData *data,
424 guint info,
425 guint time,
426 RBTrayIcon *icon)
428 GList *list, *uri_list, *i;
429 GtkTargetList *tlist;
430 gboolean ret;
432 tlist = gtk_target_list_new (target_uri, 1);
433 ret = (gtk_drag_dest_find_target (widget, context, tlist) != GDK_NONE);
434 gtk_target_list_unref (tlist);
436 if (ret == FALSE)
437 return;
439 list = gnome_vfs_uri_list_parse ((char *) data->data);
441 if (list == NULL) {
442 gtk_drag_finish (context, FALSE, FALSE, time);
443 return;
446 uri_list = NULL;
448 for (i = list; i != NULL; i = g_list_next (i))
449 uri_list = g_list_prepend (uri_list, gnome_vfs_uri_to_string ((const GnomeVFSURI *) i->data, 0));
451 gnome_vfs_uri_list_free (list);
453 if (uri_list == NULL) {
454 gtk_drag_finish (context, FALSE, FALSE, time);
455 return;
458 for (i = uri_list; i != NULL; i = i->next) {
459 char *uri = i->data;
460 if (uri != NULL)
461 rb_shell_load_uri (icon->priv->shell, uri, FALSE, NULL);
463 g_free (uri);
466 g_list_free (uri_list);
468 gtk_drag_finish (context, TRUE, FALSE, time);
471 static void
472 rb_tray_icon_show_window_changed_cb (GtkAction *action,
473 RBTrayIcon *icon)
475 rb_debug ("show window clicked for %p", icon);
476 g_object_set (G_OBJECT (icon->priv->shell),
477 "visibility", gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
478 NULL);
481 static void
482 rb_tray_icon_show_notifications_changed_cb (GtkAction *action,
483 RBTrayIcon *icon)
485 rb_debug ("show notifications clicked for %p", icon);
486 icon->priv->show_notifications = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
487 eel_gconf_set_boolean (CONF_UI_SHOW_NOTIFICATIONS, icon->priv->show_notifications);
490 void
491 rb_tray_icon_get_geom (RBTrayIcon *icon, int *x, int *y, int *width, int *height)
493 GtkWidget *widget;
494 GtkRequisition requisition;
496 widget = GTK_WIDGET (icon->priv->ebox);
498 gtk_widget_size_request (widget, &requisition);
500 gdk_window_get_origin (widget->window, x, y);
502 *width = widget->allocation.x;
503 *height = widget->allocation.y;
506 void
507 rb_tray_icon_set_tooltip (RBTrayIcon *icon, const char *tooltip)
509 gtk_tooltips_set_tip (icon->priv->tooltips,
510 GTK_WIDGET (icon),
511 tooltip, NULL);
514 void
515 rb_tray_icon_notify (RBTrayIcon *icon,
516 guint timeout,
517 const char *primary,
518 GtkWidget *msgicon,
519 const char *secondary)
521 if (!egg_tray_icon_have_manager (EGG_TRAY_ICON (icon))) {
522 rb_debug ("not showing notification: %s", primary);
523 return;
525 if (!icon->priv->show_notifications) {
526 rb_debug ("ignoring notification: %s", primary);
527 return;
530 rb_debug ("doing notify: %s", primary);
531 egg_tray_icon_notify (EGG_TRAY_ICON (icon), timeout, primary, msgicon, secondary);
534 void
535 rb_tray_icon_cancel_notify (RBTrayIcon *icon)
537 egg_tray_icon_cancel_message (EGG_TRAY_ICON (icon), 1);