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.
29 #include <glib/gi18n.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"
37 #include "eel-gconf-extensions.h"
38 #include "rb-preferences.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
,
50 static void rb_tray_icon_set_property (GObject
*object
,
54 static void rb_tray_icon_get_property (GObject
*object
,
58 static void rb_tray_icon_button_press_event_cb (GtkWidget
*ebox
, GdkEventButton
*event
,
60 static void rb_tray_icon_scroll_event_cb (GtkWidget
*ebox
, GdkEvent
*event
,
62 static void rb_tray_icon_show_window_changed_cb (GtkAction
*action
,
64 static void rb_tray_icon_show_notifications_changed_cb (GtkAction
*action
,
66 static void rb_tray_icon_drop_cb (GtkWidget
*widget
,
67 GdkDragContext
*context
,
70 GtkSelectionData
*data
,
75 struct RBTrayIconPrivate
77 GtkTooltips
*tooltips
;
78 GtkUIManager
*ui_manager
;
79 GtkActionGroup
*actiongroup
;
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))
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
)
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
,
132 g_param_spec_object ("shell",
136 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
137 g_object_class_install_property (object_class
,
139 g_param_spec_object ("ui-manager",
141 "GtkUIManager object",
143 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
144 g_object_class_install_property (object_class
,
146 g_param_spec_object ("action-group",
148 "GtkActionGroup object",
149 GTK_TYPE_ACTION_GROUP
,
152 g_type_class_add_private (klass
, sizeof (RBTrayIconPrivate
));
156 rb_tray_icon_init (RBTrayIcon
*icon
)
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
),
174 g_signal_connect_object (G_OBJECT (icon
->priv
->ebox
),
176 G_CALLBACK (rb_tray_icon_scroll_event_cb
),
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
));
191 rb_tray_icon_constructor (GType type
, guint n_construct_properties
,
192 GObjectConstructParam
*construct_properties
)
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
));
204 tray
->priv
->show_notifications
= eel_gconf_get_boolean (CONF_UI_SHOW_NOTIFICATIONS
);
207 tray
->priv
->actiongroup
= gtk_action_group_new ("TrayActions");
208 gtk_action_group_set_translation_domain (tray
->priv
->actiongroup
,
210 gtk_action_group_add_toggle_actions (tray
->priv
->actiongroup
,
211 rb_tray_icon_toggle_entries
,
212 rb_tray_icon_n_toggle_entries
,
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
);
223 rb_tray_icon_finalize (GObject
*object
)
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
);
248 rb_tray_icon_sync_action (RBShell
*shell
, gboolean visible
, RBTrayIcon
*tray
)
252 if ((tray
->priv
->actiongroup
!= NULL
) && (tray
->priv
->shell
!= NULL
)) {
255 action
= gtk_action_group_get_action (tray
->priv
->actiongroup
,
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");
263 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
),
264 tray
->priv
->show_notifications
);
266 gtk_action_set_visible (action
, FALSE
);
272 rb_tray_icon_set_property (GObject
*object
,
277 RBTrayIcon
*tray
= RB_TRAY_ICON (object
);
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
),
287 g_object_get (tray
->priv
->shell
,
288 "shell-player", &tray
->priv
->shell_player
,
290 rb_tray_icon_sync_action (NULL
, FALSE
, tray
);
292 case PROP_UI_MANAGER
:
293 tray
->priv
->ui_manager
= g_value_get_object (value
);
296 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
302 rb_tray_icon_get_property (GObject
*object
,
307 RBTrayIcon
*tray
= RB_TRAY_ICON (object
);
312 g_value_set_object (value
, tray
->priv
->shell
);
314 case PROP_UI_MANAGER
:
315 g_value_set_object (value
, tray
->priv
->ui_manager
);
317 case PROP_ACTION_GROUP
:
318 g_value_set_object (value
, tray
->priv
->actiongroup
);
321 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
327 rb_tray_icon_new (GtkUIManager
*mgr
,
330 return g_object_new (RB_TYPE_TRAY_ICON
,
331 "title", "Rhythmbox tray icon",
338 tray_popup_position_menu (GtkMenu
*menu
,
345 GtkRequisition requisition
;
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
;
361 menu_ypos
+= widget
->allocation
.height
+ widget
->style
->ythickness
;
369 rb_tray_icon_button_press_event_cb (GtkWidget
*ebox
, GdkEventButton
*event
,
374 /* filter out double, triple clicks */
375 if (event
->type
!= GDK_BUTTON_PRESS
)
378 rb_debug ("tray button press");
380 switch (event
->button
) {
382 rb_shell_toggle_visibility (icon
->priv
->shell
);
385 rb_shell_player_playpause (icon
->priv
->shell_player
, FALSE
, NULL
);
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 ());
399 rb_tray_icon_scroll_event_cb (GtkWidget
*ebox
, GdkEvent
*event
,
404 switch (event
->scroll
.direction
) {
408 case GDK_SCROLL_DOWN
:
415 rb_shell_player_set_volume_relative (icon
->priv
->shell_player
, adjust
, NULL
);
419 rb_tray_icon_drop_cb (GtkWidget
*widget
,
420 GdkDragContext
*context
,
423 GtkSelectionData
*data
,
428 GList
*list
, *uri_list
, *i
;
429 GtkTargetList
*tlist
;
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
);
439 list
= gnome_vfs_uri_list_parse ((char *) data
->data
);
442 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
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
);
458 for (i
= uri_list
; i
!= NULL
; i
= i
->next
) {
461 rb_shell_load_uri (icon
->priv
->shell
, uri
, FALSE
, NULL
);
466 g_list_free (uri_list
);
468 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
472 rb_tray_icon_show_window_changed_cb (GtkAction
*action
,
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
)),
482 rb_tray_icon_show_notifications_changed_cb (GtkAction
*action
,
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
);
491 rb_tray_icon_get_geom (RBTrayIcon
*icon
, int *x
, int *y
, int *width
, int *height
)
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
;
507 rb_tray_icon_set_tooltip (RBTrayIcon
*icon
, const char *tooltip
)
509 gtk_tooltips_set_tip (icon
->priv
->tooltips
,
515 rb_tray_icon_notify (RBTrayIcon
*icon
,
519 const char *secondary
,
522 if (!egg_tray_icon_have_manager (EGG_TRAY_ICON (icon
))) {
523 rb_debug ("not showing notification: %s", primary
);
526 if ((requested
|| icon
->priv
->show_notifications
) == FALSE
) {
527 rb_debug ("ignoring notification: %s", primary
);
531 rb_debug ("doing notify: %s", primary
);
532 egg_tray_icon_notify (EGG_TRAY_ICON (icon
), timeout
, primary
, msgicon
, secondary
);
536 rb_tray_icon_cancel_notify (RBTrayIcon
*icon
)
538 egg_tray_icon_cancel_message (EGG_TRAY_ICON (icon
), 1);