[3632] Add Album information to now-playing window.
[gmpc.git] / src / tray-icon2.c
blobc90767fb05b42d24254244a632ade96c81b1a98b
1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * AppIndicator Support added 2011 by Moritz Molch <mail@moritzmolch.de>
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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <gtk/gtk.h>
23 #include <libmpd/libmpd.h>
24 #include <string.h>
25 #include <config.h>
26 #include "main.h"
27 #include "playlist3.h"
28 #include "preferences.h"
29 #include "gmpc-extras.h"
30 #include "gmpc-metaimage.h"
31 #include "misc.h"
32 #include "tray-icon2.h"
34 #define LOG_DOMAIN "TrayIcon"
35 /* name of config field */
36 #define TRAY_ICON2_ID "tray-icon2"
38 #ifdef HAVE_APP_INDICATOR
39 #include <libappindicator/app-indicator.h>
40 AppIndicator *indicator;
41 #endif
43 GtkStatusIcon *tray_icon2_gsi = NULL;
45 static gchar *current_song_checksum = NULL;
47 static void tray_icon2_status_changed(MpdObj * mi, ChangedStatusType what, void *userdata);
48 static void tray_icon2_connection_changed(MpdObj * mi, int connect, void *user_data);
49 static void tray_icon2_create_tooltip_real(int position);
50 static gboolean tray_icon2_tooltip_destroy(void);
51 static void tray_icon2_init(void);
52 static void tray_icon2_update_menu_widget(GtkWidget *menu);
54 /**
55 * Tooltip
57 GtkWidget *tray_icon2_tooltip = NULL;
58 GtkWidget *tray_icon2_tooltip_pb = NULL;
59 guint tray_icon2_tooltip_timeout = 0;
60 enum
62 TI2_AT_TOOLTIP,
63 TI2_AT_UPPER_LEFT,
64 TI2_AT_UPPER_RIGHT,
65 TI2_AT_LOWER_LEFT,
66 TI2_AT_LOWER_RIGHT,
67 TI2_AT_NUM_OPTIONS
69 /**
70 * Preferences
72 static GtkBuilder *tray_icon2_preferences_xml = NULL;
73 void popup_timeout_changed(void);
74 void popup_position_changed(GtkComboBox * om);
75 void popup_enable_toggled(GtkToggleButton * but);
76 void tray_enable_toggled(GtkToggleButton * but);
77 void tray_icon2_preferences_pm_combo_changed(GtkComboBox * cm, gpointer data);
78 /**
79 * Tray icon
82 gboolean tray_icon2_get_available(void)
84 #ifdef HAVE_APP_INDICATOR
85 if ((indicator) && (app_indicator_get_status(indicator) != APP_INDICATOR_STATUS_PASSIVE))
86 return TRUE;
88 #endif
89 if (tray_icon2_gsi)
91 if (gtk_status_icon_is_embedded(tray_icon2_gsi) && gtk_status_icon_get_visible(tray_icon2_gsi))
93 return TRUE;
97 return FALSE;
102 * click on the tray icon
104 static void tray_icon2_activate(GtkStatusIcon * gsi, gpointer user_data)
106 pl3_toggle_hidden();
108 #ifdef HAVE_APP_INDICATOR
109 tray_icon2_update_menu();
110 #endif
115 * Right mouse press on tray icon
118 static void tray_icon2_seek_event(GtkWidget * pb, guint seek_time, gpointer user_data)
120 int state = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-timeout", 5);
121 mpd_player_seek(connection, (int)seek_time);
122 if (tray_icon2_tooltip_timeout)
124 g_source_remove(tray_icon2_tooltip_timeout);
126 tray_icon2_tooltip_timeout = g_timeout_add_seconds(state * 2, (GSourceFunc) tray_icon2_tooltip_destroy, NULL);
130 static void tray_icon2_update_menu_widget(GtkWidget *menu)
132 GtkWidget *item;
134 item = gtk_check_menu_item_new_with_mnemonic(_("Sho_w GMPC"));
135 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), !playlist3_window_is_hidden());
136 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
137 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(pl3_toggle_hidden), NULL);
139 item = gtk_separator_menu_item_new();
140 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
142 if (mpd_check_connected(connection))
144 if (mpd_server_check_command_allowed(connection, "play"))
146 if (mpd_player_get_state(connection) == MPD_STATUS_STATE_PLAY)
148 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_MEDIA_PAUSE, NULL);
149 } else
151 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_MEDIA_PLAY, NULL);
153 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
154 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(play_song), NULL);
156 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_MEDIA_STOP, NULL);
157 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
158 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(stop_song), NULL);
160 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_MEDIA_NEXT, NULL);
161 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
162 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(next_song), NULL);
164 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, NULL);
165 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
166 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(prev_song), NULL);
168 } else
170 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, NULL);
171 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
172 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(connect_to_mpd), NULL);
175 item = gtk_separator_menu_item_new();
176 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
178 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
179 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
180 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(main_quit), NULL);
181 gtk_widget_show_all(menu);
185 void tray_icon2_update_menu(void)
187 #ifdef HAVE_APP_INDICATOR
188 if (indicator != NULL) {
189 GtkWidget *menu;
190 if (app_indicator_get_menu(indicator) != NULL)
192 menu = (GtkWidget*)app_indicator_get_menu(indicator);
193 gtk_widget_destroy(menu);
196 menu = gtk_menu_new();
197 app_indicator_set_menu(indicator, GTK_MENU(menu));
198 tray_icon2_update_menu_widget(menu);
200 #endif
203 static void tray_icon2_populate_menu(GtkStatusIcon * gsi, guint button, guint activate_time, gpointer user_data)
205 GtkWidget *menu = gtk_menu_new();
206 tray_icon2_update_menu_widget(menu);
207 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gtk_status_icon_position_menu, gsi, button, activate_time);
211 static void tray_icon2_embedded_changed(GtkStatusIcon * icon, GParamSpec * arg1, gpointer data)
213 if (!gtk_status_icon_is_embedded(icon))
215 /* the widget isn't embedded anymore */
216 create_playlist3();
222 * Initialize the tray icon
224 static int tray_icon2_button_press_event(gpointer tray, GdkEventButton * event, gpointer data)
226 if (event->button == 2)
228 play_song();
229 } else
231 return FALSE;
234 return TRUE;
238 static int tray_icon2_button_scroll_event(gpointer tray, GdkEventScroll * event, gpointer data)
240 if (event->direction == GDK_SCROLL_UP)
242 if (mpd_server_check_command_allowed(connection, "volume") && mpd_status_get_volume(connection) >= 0)
243 mpd_status_set_volume(connection, mpd_status_get_volume(connection) + cfg_get_single_value_as_int_with_default(config, "Volume", "scroll-sensitivity", 5));
244 } else if (event->direction == GDK_SCROLL_DOWN)
246 if (mpd_server_check_command_allowed(connection, "volume") && mpd_status_get_volume(connection) >= 0)
247 mpd_status_set_volume(connection, mpd_status_get_volume(connection) - cfg_get_single_value_as_int_with_default(config, "Volume", "scroll-sensitivity", 5));
248 } else if (event->direction == GDK_SCROLL_LEFT)
250 prev_song();
251 } else if (event->direction == GDK_SCROLL_RIGHT)
253 next_song();
255 return TRUE;
258 #ifdef HAVE_APP_INDICATOR
259 static int tray_icon2_button_scroll_event_appindicator(AppIndicator *this_indicator, gint steps, guint direction, gpointer userdata)
261 if (direction == GDK_SCROLL_UP)
263 if (mpd_server_check_command_allowed(connection, "volume") && mpd_status_get_volume(connection) >= 0)
264 mpd_status_set_volume(connection, mpd_status_get_volume(connection) + cfg_get_single_value_as_int_with_default(config, "Volume", "scroll-sensitivity", 5));
265 } else if (direction == GDK_SCROLL_DOWN)
267 if (mpd_server_check_command_allowed(connection, "volume") && mpd_status_get_volume(connection) >= 0)
268 mpd_status_set_volume(connection, mpd_status_get_volume(connection) - cfg_get_single_value_as_int_with_default(config, "Volume", "scroll-sensitivity", 5));
269 } else if (direction == GDK_SCROLL_LEFT)
271 prev_song();
272 } else if (direction == GDK_SCROLL_RIGHT)
274 next_song();
276 return TRUE;
278 #endif
282 /* hack to delay tooltip showup on tray-icon*/
284 GTimeVal current = {0,0};
285 static gboolean tray_icon2_tooltip_query(GtkStatusIcon *icon,
286 gint x, gint y,
287 gboolean keyboard_mode,
288 GtkTooltip *tooltip,
289 gpointer user_data)
291 if(!keyboard_mode)
293 if(tray_icon2_tooltip == NULL){
294 GTimeVal now;
295 if(current.tv_sec == 0) {
296 g_get_current_time(&current);
297 return FALSE;
300 g_get_current_time(&now);
301 if((now.tv_sec - current.tv_sec ) > 5) {
302 current.tv_sec = 0;
303 return FALSE;
305 if(current.tv_sec < now.tv_sec)
307 current.tv_sec = 0;
308 tray_icon2_create_tooltip_real(TI2_AT_TOOLTIP);
309 return TRUE;
313 return FALSE;
317 static void tray_icon2_init(void)
320 if (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "enable", TRUE))
322 #ifndef HAVE_APP_INDICATOR
323 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "use_appindicator", FALSE);
324 #endif
326 #ifdef HAVE_APP_INDICATOR
327 if (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "use_appindicator", DEFAULT_TRAY_ICON_USE_APPINDICATOR))
329 GtkMenu *indicator_menu = GTK_MENU(gtk_menu_new());
330 indicator = app_indicator_new ("gmpc", "gmpc-tray-disconnected", APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
331 app_indicator_set_icon_theme_path(indicator, PIXMAP_PATH);
332 app_indicator_set_menu (indicator, GTK_MENU (indicator_menu));
333 tray_icon2_update_menu();
335 app_indicator_set_status (indicator, APP_INDICATOR_STATUS_ACTIVE);
336 g_signal_connect(G_OBJECT(indicator), "scroll-event", G_CALLBACK(tray_icon2_button_scroll_event_appindicator), NULL);
338 } else {
339 #endif
340 tray_icon2_gsi = gtk_status_icon_new_from_icon_name("gmpc-tray-disconnected");
341 #if GTK_CHECK_VERSION(2,15,0)
342 gtk_status_icon_set_has_tooltip(GTK_STATUS_ICON(tray_icon2_gsi), TRUE);
343 #endif
345 /* connect the (sparse) signals */
346 g_signal_connect(G_OBJECT(tray_icon2_gsi), "popup-menu", G_CALLBACK(tray_icon2_populate_menu), NULL);
347 g_signal_connect(G_OBJECT(tray_icon2_gsi), "activate", G_CALLBACK(tray_icon2_activate), NULL);
348 g_signal_connect(G_OBJECT(tray_icon2_gsi), "notify::embedded", G_CALLBACK(tray_icon2_embedded_changed), NULL);
349 if (gtk_check_version(2, 15, 0) == NULL)
351 g_signal_connect(G_OBJECT(tray_icon2_gsi), "button-press-event", G_CALLBACK(tray_icon2_button_press_event), NULL);
352 g_signal_connect(G_OBJECT(tray_icon2_gsi), "scroll-event", G_CALLBACK(tray_icon2_button_scroll_event), NULL);
353 /*g_signal_connect(G_OBJECT(tray_icon2_gsi), "query-tooltip", G_CALLBACK(tray_icon2_tooltip_query), NULL);*/
355 #ifdef HAVE_APP_INDICATOR
357 #endif // HAVE_APP_INDICATOR
359 /* Make sure the icons are up2date */
360 tray_icon2_connection_changed(connection, mpd_check_connected(connection), NULL);
366 * Destroy and cleanup
368 static void tray_icon2_destroy(void)
370 #ifdef HAVE_APP_INDICATOR
371 if (indicator) {
372 gtk_widget_destroy(GTK_WIDGET(app_indicator_get_menu(indicator)));
373 g_object_unref(indicator);
374 indicator = NULL;
376 #endif
377 if (tray_icon2_gsi)
379 g_object_unref(tray_icon2_gsi);
380 tray_icon2_gsi = NULL;
383 /* free the currnet song checksum */
384 if (current_song_checksum)
386 g_free(current_song_checksum);
387 current_song_checksum = NULL;
393 * Get enabled
395 static gboolean tray_icon2_get_enabled(void)
397 return cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "enable", TRUE);
402 * Set Disabled
404 static void tray_icon2_set_enabled(int enabled)
406 #ifndef HAVE_APP_INDICATOR
407 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "use_appindicator", FALSE);
408 #endif
410 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "enable", enabled);
411 #ifdef HAVE_APP_INDICATOR
412 if ((enabled) && (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "use_appindicator", DEFAULT_TRAY_ICON_USE_APPINDICATOR)))
414 if (!indicator)
416 tray_icon2_init();
417 tray_icon2_status_changed(connection, MPD_CST_SONGID, NULL);
418 } else
420 app_indicator_set_status(indicator, APP_INDICATOR_STATUS_ACTIVE);
422 } else
424 if (indicator)
426 app_indicator_set_status(indicator, APP_INDICATOR_STATUS_PASSIVE);
429 #endif
431 if ((enabled) && (!cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "use_appindicator", DEFAULT_TRAY_ICON_USE_APPINDICATOR)))
433 if (!tray_icon2_gsi)
435 tray_icon2_init();
436 tray_icon2_status_changed(connection, MPD_CST_SONGID, NULL);
437 } else
439 gtk_status_icon_set_visible(GTK_STATUS_ICON(tray_icon2_gsi), TRUE);
441 } else
443 if (tray_icon2_gsi)
445 gtk_status_icon_set_visible(GTK_STATUS_ICON(tray_icon2_gsi), FALSE);
452 * TOOLTIP
454 static gboolean has_buttons = FALSE;
455 static GtkWidget *play_button = NULL;
457 static gboolean tray_icon2_tooltip_destroy(void)
459 tray_icon2_tooltip_pb = NULL;
460 gtk_widget_destroy(tray_icon2_tooltip);
461 tray_icon2_tooltip = NULL;
462 /* remove timeout, this is for when it doesn't get called from inside the timeout */
463 if (tray_icon2_tooltip_timeout)
465 g_source_remove(tray_icon2_tooltip_timeout);
467 tray_icon2_tooltip_timeout = 0;
468 has_buttons = FALSE;
469 /* remove the timeout */
470 return FALSE;
474 static gboolean tray_icon2_tooltip_button_press_event(GtkWidget * box, GdkEventButton * event, GtkWidget * vbox)
476 if ((event == NULL || event->button == 3) && !has_buttons)
478 GtkWidget *hbox, *button;
479 int state = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-timeout", 5);
480 if (tray_icon2_tooltip_timeout)
482 g_source_remove(tray_icon2_tooltip_timeout);
484 tray_icon2_tooltip_timeout = g_timeout_add_seconds(state * 2, (GSourceFunc) tray_icon2_tooltip_destroy, NULL);
486 has_buttons = TRUE;
488 hbox = gtk_hbox_new(TRUE, 6);
489 /* prev */
490 button = gtk_button_new();
491 gtk_button_set_image(GTK_BUTTON(button),
492 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_BUTTON));
493 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
494 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(prev_song), NULL);
495 /* stop */
496 button = gtk_button_new();
497 gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_BUTTON));
498 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
499 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(stop_song), NULL);
500 /* pause/play */
501 state = mpd_player_get_state(connection);
502 if (state != MPD_PLAYER_PLAY)
504 play_button = button = gtk_button_new();
505 gtk_button_set_image(GTK_BUTTON(button),
506 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
507 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
508 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(play_song), NULL);
509 } else
511 play_button = button = gtk_button_new();
512 gtk_button_set_image(GTK_BUTTON(button),
513 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_BUTTON));
515 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
516 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(play_song), NULL);
518 /* next */
519 button = gtk_button_new();
520 gtk_button_set_image(GTK_BUTTON(button), gtk_image_new_from_stock(GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_BUTTON));
522 gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
523 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(next_song), NULL);
525 gtk_widget_show_all(hbox);
526 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
528 return TRUE;
530 tray_icon2_tooltip_destroy();
531 return TRUE;
535 static gboolean popup_enter_notify_event(GtkWidget * event, GdkEventCrossing * eventc, gpointer data)
537 if (tray_icon2_tooltip_timeout)
539 g_source_remove(tray_icon2_tooltip_timeout);
541 tray_icon2_tooltip_timeout = 0;
542 return FALSE;
546 static gboolean popup_leave_notify_event(GtkWidget * event, GdkEventCrossing * eventc, gpointer data)
548 int tooltip_timeout = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-timeout", 5);
549 tray_icon2_tooltip_timeout = g_timeout_add_seconds(tooltip_timeout, (GSourceFunc) tray_icon2_tooltip_destroy, NULL);
550 return FALSE;
554 static void tray_icon2_create_tooltip_real(int position)
556 int x = 0, y = 0, monitor;
557 GdkScreen *screen;
558 GtkWidget *pl3_win = playlist3_get_window();
559 GtkWidget *hbox = NULL;
560 GtkWidget *vbox = NULL;
561 GtkWidget *label = NULL;
562 GtkWidget *event = NULL;
563 GtkWidget *coverimg = NULL;
564 GdkColormap *colormap;
565 mpd_Song *song = NULL;
566 int tooltip_timeout = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-timeout", 5);
567 int state = 0;
568 int x_offset = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "x-offset", 0);
569 int y_offset = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "y-offset", 0);
571 song = mpd_playlist_get_current_song(connection);
573 * if the tooltip still exists destroy it...
575 if (tray_icon2_tooltip)
577 /* Do a check to avoid ugly redraws */
578 mpd_Song *song2 = g_object_get_data(G_OBJECT(tray_icon2_tooltip), "song");
579 if (song2 && song)
581 if (song->file && song2->file && strcmp(song2->file, song->file) == 0)
583 if (tray_icon2_tooltip_timeout)
585 g_source_remove(tray_icon2_tooltip_timeout);
587 tray_icon2_tooltip_timeout =
588 g_timeout_add_seconds(tooltip_timeout, (GSourceFunc) tray_icon2_tooltip_destroy, NULL);
589 return;
592 if (tray_icon2_tooltip_timeout)
594 g_source_remove(tray_icon2_tooltip_timeout);
595 tray_icon2_tooltip_timeout = 0;
597 tray_icon2_tooltip_pb = NULL;
598 gtk_widget_destroy(tray_icon2_tooltip);
599 tray_icon2_tooltip = NULL;
600 //tray_icon2_tooltip_destroy();
602 /* If gmpc is fullscreen, don't show the tooltip */
603 if (pl3_window_is_fullscreen())
604 return;
606 * Creat the tootlip window
608 tray_icon2_tooltip = gtk_window_new(GTK_WINDOW_POPUP);
609 screen = gtk_window_get_screen(GTK_WINDOW(tray_icon2_tooltip));
611 if (gdk_screen_is_composited(screen))
613 colormap = gdk_screen_get_rgba_colormap(screen);
614 if (colormap)
615 gtk_widget_set_colormap(tray_icon2_tooltip, colormap);
616 gtk_window_set_opacity(GTK_WINDOW(tray_icon2_tooltip), 0.9);
619 /* causes the border */
620 gtk_widget_modify_bg(GTK_WIDGET(tray_icon2_tooltip), GTK_STATE_NORMAL, &(pl3_win->style->black));
621 gtk_container_set_border_width(GTK_CONTAINER(tray_icon2_tooltip), 1);
622 gtk_window_set_default_size(GTK_WINDOW(tray_icon2_tooltip), 300, -1);
623 gtk_window_set_transient_for(GTK_WINDOW(tray_icon2_tooltip), GTK_WINDOW(pl3_win));
626 * Tooltip exists from 2 parts..
627 * ------------------
628 * | 1 | |
629 * | | 2 |
630 * ------------------
632 hbox = gtk_hbox_new(FALSE, 0);
633 gtk_container_add(GTK_CONTAINER(tray_icon2_tooltip), hbox);
637 * In first box we have image/coverart
638 * Re-use the gmpc-metaimage widget
640 if(cfg_get_single_value_as_int_with_default(config, "Interface", "hide-album-art", 0) == 0)
642 coverimg = (GtkWidget *)gmpc_metaimage_new_size(META_ALBUM_ART, 80);
643 gmpc_metaimage_set_squared(GMPC_METAIMAGE(coverimg), TRUE);
644 gmpc_metaimage_set_connection(GMPC_METAIMAGE(coverimg), connection);
645 gmpc_metaimage_set_no_cover_icon(GMPC_METAIMAGE(coverimg), (char *)"gmpc");
647 * Force an update if mpd is playing
649 state = mpd_player_get_state(connection);
650 if (state == MPD_PLAYER_PLAY || state == MPD_PLAYER_PAUSE)
652 gmpc_metaimage_update_cover(GMPC_METAIMAGE(coverimg), connection, MPD_CST_SONGID, NULL);
653 } else
655 gmpc_metaimage_set_cover_na(GMPC_METAIMAGE(coverimg));
659 * Pack the widget in a eventbox so we can set background color
661 event = gtk_event_box_new();
662 gtk_widget_set_size_request(event, 86, 86);
663 gtk_widget_modify_bg(GTK_WIDGET(event), GTK_STATE_NORMAL, &(pl3_win->style->bg[GTK_STATE_SELECTED]));
664 gtk_container_add(GTK_CONTAINER(event), coverimg);
665 gtk_box_pack_start(GTK_BOX(hbox), event, FALSE, TRUE, 0);
667 g_signal_connect(G_OBJECT(tray_icon2_tooltip), "enter-notify-event", G_CALLBACK(popup_enter_notify_event), NULL);
668 g_signal_connect(G_OBJECT(tray_icon2_tooltip), "leave-notify-event", G_CALLBACK(popup_leave_notify_event), NULL);
672 * Right (2) view
675 * Create white background label box
677 event = gtk_event_box_new();
678 vbox = gtk_vbox_new(FALSE, 0);
679 gtk_widget_modify_bg(GTK_WIDGET(event), GTK_STATE_NORMAL, &(pl3_win->style->light[GTK_STATE_NORMAL]));
680 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
681 gtk_container_add(GTK_CONTAINER(event), vbox);
682 gtk_box_pack_start(GTK_BOX(hbox), event, TRUE, TRUE, 0);
684 if (!has_buttons)
686 g_signal_connect(G_OBJECT(hbox), "button-press-event", G_CALLBACK(tray_icon2_tooltip_button_press_event), vbox);
690 * If there is a song, show show song info
692 if (song)
694 int i;
695 char buffer[256];
696 int size_offset = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "size-offset", 1024);
697 size_offset = (size_offset < 100) ? 1024 : size_offset;
699 /** Artist label */
700 if (song->title || song->file || song->name)
702 gchar *test =
703 g_strdup_printf("<span size='%i' weight='bold'>[%%title%%|%%shortfile%%][ (%%name%%)]</span>",
704 14 * size_offset);
705 mpd_song_markup_escaped(buffer, 256, test, song);
706 q_free(test);
707 label = gtk_label_new("");
708 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
709 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
710 gtk_label_set_markup(GTK_LABEL(label), buffer);
711 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
713 if (song->artist)
715 gchar *test = g_strdup_printf("<span size='%i'>%%artist%%</span>", 10 * size_offset);
716 label = gtk_label_new("");
717 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
718 mpd_song_markup_escaped(buffer, 256, test, song);
719 q_free(test);
720 gtk_label_set_markup(GTK_LABEL(label), buffer);
721 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
723 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
725 if (song->album)
727 gchar *test = g_strdup_printf("<span size='%i'>%%album%%[ (%%year%%)]</span>", 8 * size_offset);
728 label = gtk_label_new("");
729 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
730 mpd_song_markup_escaped(buffer, 256, test, song);
731 q_free(test);
732 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
733 gtk_label_set_markup(GTK_LABEL(label), buffer);
734 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
736 tray_icon2_tooltip_pb = (GtkWidget *) gmpc_progress_new();
737 gmpc_progress_set_hide_text(GMPC_PROGRESS(tray_icon2_tooltip_pb), TRUE);
738 /* Update the progressbar */
739 gmpc_progress_set_time(GMPC_PROGRESS(tray_icon2_tooltip_pb),
740 mpd_status_get_total_song_time(connection),
741 mpd_status_get_elapsed_song_time(connection));
743 g_signal_connect(G_OBJECT(tray_icon2_tooltip_pb), "seek-event", G_CALLBACK(tray_icon2_seek_event), NULL);
745 gtk_widget_modify_bg(GTK_WIDGET(tray_icon2_tooltip_pb), GTK_STATE_NORMAL,
746 &(pl3_win->style->light[GTK_STATE_NORMAL]));
747 g_object_set_data_full(G_OBJECT(tray_icon2_tooltip), "song", mpd_songDup(song), (GDestroyNotify) mpd_freeSong);
748 gtk_box_pack_start(GTK_BOX(vbox), tray_icon2_tooltip_pb, TRUE, FALSE, 0);
750 i = mpd_player_get_next_song_id(connection);
751 if (i > 0)
753 mpd_Song *next_psong = mpd_playlist_get_song(connection, i);
754 if (next_psong)
756 gchar *test =
757 g_strdup_printf("<span size='%i'>%s: <i>[[%%title%% - &[%%artist%%]]|%%shortfile%%]</i></span>",
758 7 * size_offset, _("Next"));
759 label = gtk_label_new("");
760 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
761 mpd_song_markup_escaped(buffer, 256, test, next_psong);
762 q_free(test);
763 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
764 gtk_label_set_markup(GTK_LABEL(label), buffer);
765 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
766 mpd_freeSong(next_psong);
769 } else
771 gchar *value = g_markup_printf_escaped("<span size='large'>%s</span>", _("Gnome Music Player Client"));
772 label = gtk_label_new("");
773 gtk_label_set_markup(GTK_LABEL(label), value);
774 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
775 gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
776 g_free(value);
779 * Position the popup
781 if (position == TI2_AT_TOOLTIP && tray_icon2_get_available())
784 GdkRectangle rect, rect2;
785 GtkOrientation orientation;
787 #ifdef HAVE_APP_INDICATOR
788 if (indicator != NULL) {
789 // there's no way to get the AppIndicator's position, so showing at tooltip is not possible
790 // choosing 50px above right corner
791 screen = gtk_widget_get_screen(pl3_win);
793 monitor = gdk_screen_get_monitor_at_window(screen, pl3_win->window);
794 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
795 /** Set Y = window height - size; */
796 y = rect2.y + rect2.height - 50 - 95;
797 /** X =window width - width */
798 x = rect2.x + rect2.width - 5 - 300;
799 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), x + x_offset, y + y_offset);
801 } else
802 #endif
804 if (gtk_status_icon_get_geometry(tray_icon2_gsi, &screen, &rect, &orientation))
806 monitor = gdk_screen_get_monitor_at_point(screen, rect.x, rect.y);
807 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
808 /* Get Y */
809 y = rect.y + rect.height + 5 - rect2.y + y_offset;
810 /* if the lower part falls off the screen, move it up */
811 if ((y + 95) > rect2.height)
813 y = rect.y - 95 - 5;
815 if (y < 0)
816 y = 0;
818 /* Get X */
819 x = rect.x - 300 / 2 - rect2.x + x_offset;
820 if ((x + 300) > rect2.width)
822 if (orientation == GTK_ORIENTATION_VERTICAL)
824 x = rect2.width + -300 - rect.width - 5;
825 } else
827 x = rect2.width - 300;
830 if (x < 0)
832 if (orientation == GTK_ORIENTATION_VERTICAL)
834 x = rect.width + 5;
835 } else
837 x = 0;
840 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), rect2.x + x, rect2.y + y);
843 } else if (position == TI2_AT_UPPER_LEFT)
845 GdkRectangle rect2;
846 screen = gtk_widget_get_screen(pl3_win);
848 monitor = gdk_screen_get_monitor_at_window(screen, pl3_win->window);
849 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
850 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), rect2.x + 5 + x_offset, rect2.y + 5 + y_offset);
851 } else if (position == TI2_AT_UPPER_RIGHT)
853 GdkRectangle rect2;
854 screen = gtk_widget_get_screen(pl3_win);
856 monitor = gdk_screen_get_monitor_at_window(screen, pl3_win->window);
857 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
858 /** Set Y = 0; */
859 y = rect2.y + 5;
860 /** X is upper right - width */
861 x = rect2.x + rect2.width - 5 - 300;
862 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), x + x_offset, y + y_offset);
863 } else if (position == TI2_AT_LOWER_LEFT)
865 GdkRectangle rect2;
866 screen = gtk_widget_get_screen(pl3_win);
868 monitor = gdk_screen_get_monitor_at_window(screen, pl3_win->window);
869 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
870 /** Set Y = window height - size; */
871 y = rect2.y + rect2.height - 5 - 95;
872 /** X =5 */
873 x = rect2.x + 5;
874 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), x + x_offset, y + y_offset);
875 } else
877 GdkRectangle rect2;
878 screen = gtk_widget_get_screen(pl3_win);
880 monitor = gdk_screen_get_monitor_at_window(screen, pl3_win->window);
881 gdk_screen_get_monitor_geometry(screen, monitor, &rect2);
882 /** Set Y = window height - size; */
883 y = rect2.y + rect2.height - 5 - 95;
884 /** X =window width - width */
885 x = rect2.x + rect2.width - 5 - 300;
886 gtk_window_move(GTK_WINDOW(tray_icon2_tooltip), x + x_offset, y + y_offset);
889 gtk_widget_show_all(tray_icon2_tooltip);
891 if (has_buttons)
893 has_buttons = FALSE;
894 tray_icon2_tooltip_button_press_event(hbox, NULL, vbox);
897 * Destroy it after 5 seconds
899 tray_icon2_tooltip_timeout = g_timeout_add_seconds(tooltip_timeout, (GSourceFunc) tray_icon2_tooltip_destroy, NULL);
903 void tray_icon2_create_tooltip(void)
905 int state = cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-position", TI2_AT_TOOLTIP);
906 tray_icon2_create_tooltip_real(state);
910 static void tray_icon2_status_changed(MpdObj * mi, ChangedStatusType what, void *userdata)
912 char buffer[256];
913 mpd_Song *song = mpd_playlist_get_current_song(connection);
914 if (what & (MPD_CST_SONGID) || what & MPD_CST_SONGPOS || what & MPD_CST_PLAYLIST)
917 * If enabled by user, show the tooltip.
918 * But only if playing or paused.
921 if (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "show-tooltip", TRUE))
923 int state = mpd_player_get_state(connection);
924 gchar *new_checksum;
925 new_checksum = mpd_song_checksum(song);
926 if (state == MPD_PLAYER_PLAY || state == MPD_PLAYER_PAUSE)
928 if (new_checksum == NULL || current_song_checksum == NULL
929 || strcmp(current_song_checksum, new_checksum))
930 tray_icon2_create_tooltip();
932 if (current_song_checksum)
934 g_free(current_song_checksum);
935 current_song_checksum = NULL;
937 current_song_checksum = new_checksum;
940 if (tray_icon2_gsi)
942 mpd_song_markup(buffer, 256, "[%name%: ][%title%|%shortfile%][ - %artist%]", song);
943 //gtk_status_icon_set_tooltip(tray_icon2_gsi,buffer);
947 /* update the progress bar if available */
948 if (what & MPD_CST_ELAPSED_TIME)
950 if (tray_icon2_tooltip && tray_icon2_tooltip_pb)
952 gmpc_progress_set_time(GMPC_PROGRESS(tray_icon2_tooltip_pb),
953 mpd_status_get_total_song_time(connection),
954 mpd_status_get_elapsed_song_time(connection));
958 #ifdef HAVE_APP_INDICATOR
959 if ((indicator == NULL) && (tray_icon2_gsi == NULL))
960 return;
962 if ((indicator != NULL) && (what & MPD_CST_STATE))
963 tray_icon2_update_menu();
965 #endif
967 if (what & MPD_CST_STATE)
969 int state = mpd_player_get_state(connection);
970 if (state == MPD_PLAYER_PLAY)
972 mpd_song_markup(buffer, 256, "[%name%: ][%title%|%shortfile%][ - %artist%]", song);
974 #ifdef HAVE_APP_INDICATOR
975 if (indicator != NULL)
976 app_indicator_set_icon_full(indicator, "gmpc-tray-play", "gmpc is playing");
977 #endif
978 if (tray_icon2_gsi != NULL)
979 gtk_status_icon_set_from_icon_name(tray_icon2_gsi, "gmpc-tray-play");
981 //gtk_status_icon_set_tooltip(tray_icon2_gsi,buffer);
982 if (has_buttons)
984 gtk_button_set_image(GTK_BUTTON(play_button),
985 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PAUSE, GTK_ICON_SIZE_BUTTON));
987 } else if (state == MPD_PLAYER_PAUSE)
989 #ifdef HAVE_APP_INDICATOR
990 if (indicator != NULL)
991 app_indicator_set_icon_full(indicator, "gmpc-tray-pause", "gmpc is pausing");
992 #endif
993 if (tray_icon2_gsi != NULL)
994 gtk_status_icon_set_from_icon_name(tray_icon2_gsi, "gmpc-tray-pause");
996 //gtk_status_icon_set_tooltip(tray_icon2_gsi,_("Gnome Music Player Client"));
997 if (has_buttons)
999 gtk_button_set_image(GTK_BUTTON(play_button),
1000 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
1002 } else
1004 #ifdef HAVE_APP_INDICATOR
1005 if (indicator != NULL)
1006 app_indicator_set_icon_full(indicator, "gmpc-tray", "gmpc is idling");
1007 #endif
1008 if (tray_icon2_gsi != NULL)
1009 gtk_status_icon_set_from_icon_name(tray_icon2_gsi, "gmpc-tray");
1011 //gtk_status_icon_set_tooltip(tray_icon2_gsi,_("Gnome Music Player Client"));
1012 if (has_buttons)
1014 gtk_button_set_image(GTK_BUTTON(play_button),
1015 gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_BUTTON));
1023 * Show right icon when (dis)connected
1025 static void tray_icon2_connection_changed(MpdObj * mi, int connect, void *user_data)
1027 #ifdef HAVE_APP_INDICATOR
1028 if ((indicator == NULL) && (tray_icon2_gsi == NULL))
1029 return;
1030 #endif
1032 if (connect)
1034 tray_icon2_status_changed(mi, MPD_CST_STATE, NULL);
1035 } else
1037 /* Set the disconnect image, and reset the GtkTooltip */
1038 #ifdef HAVE_APP_INDICATOR
1039 if (indicator != NULL) {
1040 app_indicator_set_icon_full(indicator, "gmpc-tray-disconnected", "gmpc is disconnected");
1041 tray_icon2_update_menu();
1043 #endif
1044 if (tray_icon2_gsi != NULL) {
1045 gtk_status_icon_set_from_icon_name(tray_icon2_gsi, "gmpc-tray-disconnected");
1048 /* Destroy notification */
1049 if (tray_icon2_tooltip)
1050 tray_icon2_tooltip_destroy();
1054 gboolean trayicon2_have_appindicator_support( void )
1056 #ifdef HAVE_APP_INDICATOR
1057 return TRUE;
1058 #else
1059 return FALSE;
1060 #endif
1065 * PREFERENCES
1068 void tray_enable_toggled(GtkToggleButton * but)
1070 g_log(LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "tray-icon.c: changing tray icon %i\n", gtk_toggle_button_get_active(but));
1071 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "enable", (int)gtk_toggle_button_get_active(but));
1073 if (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "enable", 1))
1075 tray_icon2_set_enabled(TRUE);
1076 } else
1078 tray_icon2_set_enabled(FALSE);
1082 void trayicon2_toggle_use_appindicator(void)
1084 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "use_appindicator", !cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "use_appindicator", TRUE));
1086 if (cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "enable", DEFAULT_TRAY_ICON_ENABLE))
1088 tray_icon2_set_enabled(FALSE);
1089 tray_icon2_set_enabled(TRUE);
1094 /* this sets all the settings in the notification area preferences correct */
1095 static void tray_update_settings(void)
1097 gtk_toggle_button_set_active((GtkToggleButton *)
1098 gtk_builder_get_object(tray_icon2_preferences_xml, "ck_tray_enable"),
1099 cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "enable",
1100 DEFAULT_TRAY_ICON_ENABLE));
1104 void popup_enable_toggled(GtkToggleButton * but)
1106 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "show-tooltip", gtk_toggle_button_get_active(but));
1110 void popup_position_changed(GtkComboBox * om)
1112 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "tooltip-position", gtk_combo_box_get_active(om));
1116 void popup_timeout_changed(void)
1118 cfg_set_single_value_as_int(config, TRAY_ICON2_ID, "tooltip-timeout",
1119 gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON
1120 (gtk_builder_get_object
1121 (tray_icon2_preferences_xml, "popup_timeout"))));
1125 static void update_popup_settings(void)
1127 gtk_toggle_button_set_active((GtkToggleButton *)
1128 gtk_builder_get_object(tray_icon2_preferences_xml, "ck_popup_enable"),
1129 cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "show-tooltip", 1));
1130 gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_builder_get_object(tray_icon2_preferences_xml, "om_popup_position")),
1131 cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-position", 0));
1132 gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtk_builder_get_object(tray_icon2_preferences_xml, "popup_timeout")),
1133 cfg_get_single_value_as_int_with_default(config, TRAY_ICON2_ID, "tooltip-timeout", 5));
1137 static void tray_icon2_preferences_destroy(GtkWidget * container)
1139 if (tray_icon2_preferences_xml)
1141 GtkWidget *vbox = (GtkWidget *) gtk_builder_get_object(tray_icon2_preferences_xml, "tray-pref-vbox");
1142 gtk_container_remove(GTK_CONTAINER(container), vbox);
1143 g_object_unref(tray_icon2_preferences_xml);
1144 tray_icon2_preferences_xml = NULL;
1149 void tray_icon2_preferences_pm_combo_changed(GtkComboBox * cm, gpointer data)
1151 int level = gtk_combo_box_get_active(cm);
1152 cfg_set_single_value_as_int(config, "Default", "min-error-level", level);
1156 static void tray_icon2_preferences_construct(GtkWidget * container)
1158 gchar *path = gmpc_get_full_glade_path("preferences-trayicon.ui");
1159 tray_icon2_preferences_xml = gtk_builder_new();
1160 gtk_builder_add_from_file(tray_icon2_preferences_xml, path, NULL);
1161 q_free(path);
1163 if (tray_icon2_preferences_xml)
1165 GtkWidget *vbox = (GtkWidget *) gtk_builder_get_object(tray_icon2_preferences_xml, "tray-pref-vbox");
1166 gtk_container_add(GTK_CONTAINER(container), vbox);
1167 tray_update_settings();
1168 update_popup_settings();
1169 gtk_builder_connect_signals(tray_icon2_preferences_xml, NULL);
1171 gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_builder_get_object(tray_icon2_preferences_xml, "pm-combo")),
1172 cfg_get_single_value_as_int_with_default(config, "Default", "min-error-level",
1173 ERROR_INFO));
1177 gmpcPrefPlugin tray_icon2_preferences =
1179 tray_icon2_preferences_construct,
1180 tray_icon2_preferences_destroy
1184 gmpcPlugin tray_icon2_plug =
1186 .name = N_("Notification"),
1187 .version = {0, 0, 0}
1189 .plugin_type = GMPC_INTERNALL,
1190 .init = tray_icon2_init,
1191 .destroy = tray_icon2_destroy,
1192 .mpd_status_changed = tray_icon2_status_changed,
1193 .mpd_connection_changed = tray_icon2_connection_changed,
1194 .set_enabled = NULL,
1195 .get_enabled = NULL,
1196 .pref = &tray_icon2_preferences