/* * infopopup.c * Copyright 2006-2012 William Pitcock, Giacomo Lozito, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions, and the following disclaimer in the documentation * provided with the distribution. * * This software is provided "as is" and without any warranty, express or * implied. In no event shall the authors be liable for any damages arising from * the use of this software. */ #include #include #include #include #include #include #include #include "libaudgui.h" #include "libaudgui-gtk.h" #define IMAGE_SIZE 96 static GtkWidget * infopopup = NULL; static struct { GtkWidget * title_header, * title_label; GtkWidget * artist_header, * artist_label; GtkWidget * album_header, * album_label; GtkWidget * genre_header, * genre_label; GtkWidget * year_header, * year_label; GtkWidget * track_header, * track_label; GtkWidget * length_header, * length_label; GtkWidget * image; GtkWidget * progress; } widgets; static char * current_file = NULL; static int progress_source = 0; static void infopopup_display_image (const char * filename) { if (! current_file || strcmp (filename, current_file)) return; GdkPixbuf * pb = audgui_pixbuf_request (filename); if (! pb) pb = audgui_pixbuf_fallback (); audgui_pixbuf_scale_within (& pb, IMAGE_SIZE); gtk_image_set_from_pixbuf ((GtkImage *) widgets.image, pb); g_object_unref (pb); } static bool_t infopopup_progress_cb (void * unused) { char * filename = NULL; int length = 0, time = 0; if (aud_drct_get_playing ()) { filename = aud_drct_get_filename (); length = aud_drct_get_length (); time = aud_drct_get_time (); } if (aud_get_bool (NULL, "filepopup_showprogressbar") && filename && current_file && ! strcmp (filename, current_file) && length > 0) { gtk_progress_bar_set_fraction ((GtkProgressBar *) widgets.progress, time / (float) length); SPRINTF (time_str, "%d:%02d", time / 60000, (time / 1000) % 60); gtk_progress_bar_set_text ((GtkProgressBar *) widgets.progress, time_str); gtk_widget_show (widgets.progress); } else gtk_widget_hide (widgets.progress); str_unref (filename); return TRUE; } static void infopopup_add_category (GtkWidget * grid, int position, const char * text, GtkWidget * * header, GtkWidget * * label) { * header = gtk_label_new (NULL); * label = gtk_label_new (NULL); gtk_misc_set_alignment ((GtkMisc *) * header, 0, 0.5); gtk_misc_set_alignment ((GtkMisc *) * label, 0, 0.5); gtk_misc_set_padding ((GtkMisc *) * header, 0, 1); gtk_misc_set_padding ((GtkMisc *) * label, 0, 1); char * markup = g_markup_printf_escaped ("%s", text); gtk_label_set_markup ((GtkLabel *) * header, markup); g_free (markup); gtk_grid_attach ((GtkGrid *) grid, * header, 0, position, 1, 1); gtk_grid_attach ((GtkGrid *) grid, * label, 1, position, 1, 1); } static void infopopup_destroyed (void) { hook_dissociate ("art ready", (HookFunction) infopopup_display_image); if (progress_source) { g_source_remove (progress_source); progress_source = 0; } infopopup = NULL; memset (& widgets, 0, sizeof widgets); str_unref (current_file); current_file = NULL; } static void infopopup_create (void) { infopopup = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_type_hint ((GtkWindow *) infopopup, GDK_WINDOW_TYPE_HINT_TOOLTIP); gtk_window_set_decorated ((GtkWindow *) infopopup, FALSE); gtk_container_set_border_width ((GtkContainer *) infopopup, 4); GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_container_add ((GtkContainer *) infopopup, hbox); widgets.image = gtk_image_new (); gtk_widget_set_size_request (widgets.image, IMAGE_SIZE, IMAGE_SIZE); gtk_box_pack_start ((GtkBox *) hbox, widgets.image, FALSE, FALSE, 0); GtkWidget * grid = gtk_grid_new (); gtk_grid_set_column_spacing ((GtkGrid *) grid, 6); gtk_box_pack_start ((GtkBox *) hbox, grid, TRUE, TRUE, 0); infopopup_add_category (grid, 0, _("Title"), & widgets.title_header, & widgets.title_label); infopopup_add_category (grid, 1, _("Artist"), & widgets.artist_header, & widgets.artist_label); infopopup_add_category (grid, 2, _("Album"), & widgets.album_header, & widgets.album_label); infopopup_add_category (grid, 3, _("Genre"), & widgets.genre_header, & widgets.genre_label); infopopup_add_category (grid, 4, _("Year"), & widgets.year_header, & widgets.year_label); infopopup_add_category (grid, 5, _("Track"), & widgets.track_header, & widgets.track_label); infopopup_add_category (grid, 6, _("Length"), & widgets.length_header, & widgets.length_label); /* track progress */ widgets.progress = gtk_progress_bar_new (); gtk_widget_set_margin_top (widgets.progress, 6); gtk_progress_bar_set_show_text ((GtkProgressBar *) widgets.progress, TRUE); gtk_progress_bar_set_text ((GtkProgressBar *) widgets.progress, ""); gtk_grid_attach ((GtkGrid *) grid, widgets.progress, 0, 7, 2, 1); /* do not show the track progress */ gtk_widget_set_no_show_all (widgets.progress, TRUE); gtk_widget_show_all (hbox); } /* calls str_unref() on */ static void infopopup_set_field (GtkWidget * header, GtkWidget * label, char * text) { if (text) { gtk_label_set_text ((GtkLabel *) label, text); str_unref (text); gtk_widget_show (header); gtk_widget_show (label); } else { gtk_widget_hide (header); gtk_widget_hide (label); } } static void infopopup_set_fields (const Tuple * tuple, const char * title) { /* use title from tuple if possible */ char * title2 = tuple_get_str (tuple, FIELD_TITLE, NULL); if (! title2) title2 = str_get (title); char * artist = tuple_get_str (tuple, FIELD_ARTIST, NULL); char * album = tuple_get_str (tuple, FIELD_ALBUM, NULL); char * genre = tuple_get_str (tuple, FIELD_GENRE, NULL); infopopup_set_field (widgets.title_header, widgets.title_label, title2); infopopup_set_field (widgets.artist_header, widgets.artist_label, artist); infopopup_set_field (widgets.album_header, widgets.album_label, album); infopopup_set_field (widgets.genre_header, widgets.genre_label, genre); int value; char * tmp; value = tuple_get_int (tuple, FIELD_LENGTH, NULL); tmp = (value > 0) ? str_printf ("%d:%02d", value / 60000, value / 1000 % 60) : NULL; infopopup_set_field (widgets.length_header, widgets.length_label, tmp); value = tuple_get_int (tuple, FIELD_YEAR, NULL); tmp = (value > 0) ? str_printf ("%d", value) : NULL; infopopup_set_field (widgets.year_header, widgets.year_label, tmp); value = tuple_get_int (tuple, FIELD_TRACK_NUMBER, NULL); tmp = (value > 0) ? str_printf ("%d", value) : NULL; infopopup_set_field (widgets.track_header, widgets.track_label, tmp); } static void infopopup_move_to_mouse (void) { int x, y, h, w; audgui_get_mouse_coords (NULL, & x, & y); gtk_window_get_size ((GtkWindow *) infopopup, & w, & h); /* If we show the popup right under the cursor, the underlying window gets * a "leave-notify-event" and immediately hides the popup again. So, we * offset the popup slightly. */ if (x + w > gdk_screen_width ()) x -= w + 3; else x += 3; if (y + h > gdk_screen_height ()) y -= h + 3; else y += 3; gtk_window_move ((GtkWindow *) infopopup, x, y); } static void infopopup_show (const char * filename, const Tuple * tuple, const char * title) { if (infopopup) gtk_widget_destroy (infopopup); str_unref (current_file); current_file = str_get (filename); infopopup_create (); infopopup_set_fields (tuple, title); infopopup_display_image (filename); hook_associate ("art ready", (HookFunction) infopopup_display_image, NULL); g_signal_connect (infopopup, "destroy", (GCallback) infopopup_destroyed, NULL); /* start a timer that updates a progress bar if the tooltip is shown for the song that is being currently played */ if (! progress_source) progress_source = g_timeout_add (500, infopopup_progress_cb, NULL); /* immediately run the callback once to update progressbar status */ infopopup_progress_cb (NULL); infopopup_move_to_mouse (); gtk_widget_show (infopopup); } EXPORT void audgui_infopopup_show (int playlist, int entry) { char * filename = aud_playlist_entry_get_filename (playlist, entry); char * title = aud_playlist_entry_get_title (playlist, entry, FALSE); Tuple * tuple = aud_playlist_entry_get_tuple (playlist, entry, FALSE); if (filename && title && tuple) infopopup_show (filename, tuple, title); str_unref (filename); str_unref (title); if (tuple) tuple_unref (tuple); } EXPORT void audgui_infopopup_show_current (void) { int playlist = aud_playlist_get_playing (); if (playlist < 0) playlist = aud_playlist_get_active (); int position = aud_playlist_get_position (playlist); if (position < 0) return; audgui_infopopup_show (playlist, position); } EXPORT void audgui_infopopup_hide (void) { if (infopopup) gtk_widget_destroy (infopopup); }