xmms plugin updated
Mario Streiber <[email protected]>
| Newsgroups | gmane.comp.desktop.xfce.goodies.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi all, I picked Kernal's version of the xmms plugin and added some more changes/enhancements to it which I found useful: - klicking on the "play" button starts xmms now if not already running - removing or exiting the plugin (e.g. on logout) makes xmms quit - middle click on the progress bar shows/hides the xmms window(s) - the visibility state of the xmms windows is saved and reused on next login - smooth scrolling of the song title (pixel-, not character based) - properties menu to change song title font size and scrolling parameters - scrolled song title at the moment permanently visible (TBD: should probably be another option in the properties dialog) I attached the modified source file xmms_plugin.c to this mail. According to http://xfce-goodies.berlios.de/, Patrick van Staveren is the owner of this plugin, maybe he can make these changes "official". Until then, you could just download the current version and replace the source file before running make && make install. It would be nice to get some feedback if this is interesting for others as well. Regards Mario
xmms_plugin.c
(text/plain, 20.6 KB)
/* * Copyright (c) 2003 Patrick van Staveren <[email protected]> * Copyright (c) 2005 Kemal Ilgar Eroglu <[email protected]> * Copyright (c) 2005 Mario Streiber <[email protected]> * * 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 and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> #include <gtk/gtk.h> #include <libxfce4util/i18n.h> #include <libxfcegui4/dialogs.h> #include <panel/plugins.h> #include <panel/xfce.h> #include <xmmsctrl.h> #include "xmms_plugin.h" /* displays or hides the xmms windows */ static void display_xmms(plugin_data *pd, gboolean show) { /* do nothing if xmms is not running */ if (!xmms_remote_is_running(pd->xmms_session)) return; if (show) { /* display xmms windows based on their prior status */ if (pd->show_mw) xmms_remote_main_win_toggle(pd->xmms_session, TRUE); if (pd->show_pl) xmms_remote_pl_win_toggle (pd->xmms_session, TRUE); if (pd->show_eq) xmms_remote_eq_win_toggle (pd->xmms_session, TRUE); pd->xmmsvisible = TRUE; } else { /* save status of each xmms window */ pd->show_mw = xmms_remote_is_main_win(pd->xmms_session); pd->show_pl = xmms_remote_is_pl_win (pd->xmms_session); pd->show_eq = xmms_remote_is_eq_win (pd->xmms_session); /* make sure at least the main window is shown on redisplay */ if (!(pd->show_pl || pd->show_eq)) pd->show_mw = TRUE; /* hide all */ xmms_remote_main_win_toggle(pd->xmms_session, FALSE); xmms_remote_pl_win_toggle (pd->xmms_session, FALSE); xmms_remote_eq_win_toggle (pd->xmms_session, FALSE); pd->xmmsvisible = FALSE; } } /* callback to jump to the previous song in the playlist */ static void prev(GtkWidget *widget, GdkEventButton* event, gpointer data) { if (event->button != 1) return; xmms_remote_playlist_prev(((plugin_data*) data)->xmms_session); } /* callback to start playing; if xmms is not running it will be started */ static void play(GtkWidget *widget, GdkEventButton* event, gpointer data) { plugin_data *pd = (plugin_data*) data; int i=0; pid_t pid; int status; if (event->button != 1) return; /* start xmms and hide its windows if not already running */ if (!xmms_remote_is_running(pd->xmms_session)) { if (exec_command("xmms -p")) { while (!xmms_remote_is_running(pd->xmms_session) && (i <= 5)) { sleep(1); i++; } } if ((i < 5) && (!pd->xmmsvisible)) display_xmms(pd, FALSE); } else xmms_remote_play(pd->xmms_session); } /* callback for pausing xmms */ static void paus(GtkWidget *widget, GdkEventButton* event, gpointer data) { if (event->button != 1) return; xmms_remote_pause(((plugin_data*) data)->xmms_session); } /* callback for stopping xmms */ static void stop(GtkWidget *widget, GdkEventButton* event, gpointer data) { if (event->button != 1) return; xmms_remote_stop(((plugin_data*) data)->xmms_session); } /* callback to jump to the next song in the playlist */ static void next(GtkWidget *widget, GdkEventButton* event, gpointer data) { if (event->button != 1) return; xmms_remote_playlist_next(((plugin_data*) data)->xmms_session); } /* callback for clicking on the progress bar */ /* button 1: jump to time in the current song */ /* button 2: toggle xmms window visibility */ static void pbar_click(GtkWidget* widget, GdkEventButton* event, gpointer data) { gint width, time, total; plugin_data *pd = (plugin_data *) data; switch (event->button) { case 1: // jump to time within this song width = pd->pbar->allocation.width; total = xmms_remote_get_playlist_time(pd->xmms_session, xmms_remote_get_playlist_pos(pd->xmms_session)); time = (int) (event->x * total / width); xmms_remote_jump_to_time(pd->xmms_session, time); break; case 2: // toggle xmms window visibility display_xmms(pd, !pd->xmmsvisible); } } /* callback for changing the volume when using the scroll wheel */ static void box_scroll(GtkWidget* widget, GdkEventScroll* event, gpointer data) { gint vl, vr; plugin_data *pd = (plugin_data*) data; xmms_remote_get_volume(pd->xmms_session, &vl, &vr); if(event->direction == GDK_SCROLL_UP) xmms_remote_set_volume(pd->xmms_session, vl+8, vr+8); else xmms_remote_set_volume(pd->xmms_session, vl-8, vr-8); } /* timeout function to update the plugin widgets */ gboolean pbar_label_update(gpointer data) { plugin_data *pd = (plugin_data*) data; GtkAdjustment *adj; gchar *title, *tooltip; gint sp, len, time = 1, pl_pos, play_time = 0; PangoAttribute *attr; gboolean running, playing; /* check xmms status */ running = xmms_remote_is_running(pd->xmms_session); playing = (running && xmms_remote_is_playing(pd->xmms_session)); if (running) { /* get playlist position and song length */ pl_pos = xmms_remote_get_playlist_pos(pd->xmms_session); time = xmms_remote_get_playlist_time(pd->xmms_session, pl_pos) / 1000; } else pl_pos = -1; /* update tooltip and song title */ if (pd->playlist_position != pl_pos) { pd->playlist_position = pl_pos; pd->title_scroll_position = 0; if (running) { tooltip = xmms_remote_get_playlist_title(pd->xmms_session, pl_pos); title = g_strdup_printf("%d: %s (%d:%02d) +++ %d: %s (%d:%02d) +++ ", pl_pos, tooltip, time / 60, time % 60, pl_pos, tooltip, time / 60, time % 60); } else { tooltip = TITLE_STRING; title = g_strdup_printf("%s +++ %s +++", tooltip, tooltip); } gtk_tooltips_set_tip(pd->tooltip, GTK_WIDGET(pd->base), tooltip, NULL); pd->labelattr->start_index = 0; pd->labelattr->end_index = strlen(title); gtk_label_set_attributes(GTK_LABEL(pd->label), pd->labelattrlist); gtk_label_set_text(GTK_LABEL(pd->label), title); g_free(title); } /* update progress bar */ if (playing) play_time = xmms_remote_get_output_time(pd->xmms_session) / 1000; if (pd->play_time != play_time) { gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->pbar), (gdouble) play_time / time); pd->play_time = play_time; } /* scroll song title */ adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(pd->viewport)); len = pd->label->allocation.width / 2; sp = pd->title_scroll_position - pd->step_delay; sp = (sp < 0) ? 0 : sp; sp = (len > 0) ? sp % len : 0; gtk_adjustment_set_value(adj, sp); pd->title_scroll_position += pd->scroll_step; /* set up new timer and destroy old if new scroll speed has been set */ if (pd->timer_reset) { g_source_remove(pd->timeout); pd->timeout = g_timeout_add(1000 / pd->scroll_speed, pbar_label_update, pd); pd->timer_reset = FALSE; return FALSE; } return TRUE; } /* adds a new button with image and callback to the parent box */ static void new_button_with_img(GtkWidget *box, gchar *filename, gpointer cb, gpointer data) { GtkWidget *image, *eventbox; eventbox = gtk_event_box_new(); image = gtk_image_new_from_file(filename); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER(eventbox), image); gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK); g_signal_connect (G_OBJECT(eventbox), "button_press_event", G_CALLBACK(cb), data); gtk_box_pack_start (GTK_BOX(box), eventbox, DOEXPAND, DOFILL, PADDING); } /* creates the plugin widgets */ gboolean xmms_plugin_control_new(Control *ctrl) { GtkWidget *button, *box, *boxMain, *pbar, *viewport, *eventbox, *label; plugin_data *pd; gint i; gchar *title = TITLE_STRING" +++ "TITLE_STRING" +++ "; pd = g_new(plugin_data, 1); /* These defaults will be overwritten by read config */ pd->titletextsize = TITLE_SIZE; pd->title_scroll_position = 0; pd->scroll_speed = SCROLL_SPEED; pd->scroll_step = SCROLL_STEP; pd->step_delay = STEP_DELAY; pd->scroll_delay = SCROLL_DELAY; pd->playlist_position = -1; pd->play_time = -1; pd->xmmsvisible = FALSE; pd->xmms_session = 0; pd->timeout = 0; pd->timer_reset = FALSE; pd->tooltip = gtk_tooltips_new(); pd->labelattrlist = pango_attr_list_new(); pd->labelattr = pango_attr_size_new(pd->titletextsize * PANGO_SCALE); pd->labelattr->start_index = 0; pd->labelattr->end_index = strlen(title); pango_attr_list_insert (pd->labelattrlist, pd->labelattr); /* add scrolling callback for the plugin base widget */ pd->base = ctrl->base; gtk_widget_add_events(GTK_WIDGET(ctrl->base), GDK_SCROLL_MASK); g_signal_connect(G_OBJECT(ctrl->base),"scroll_event",G_CALLBACK(box_scroll),NULL); gtk_tooltips_set_tip (pd->tooltip, GTK_WIDGET(ctrl->base), TITLE_STRING, NULL); /* main container for the plugin widgets */ boxMain = gtk_vbox_new(FALSE, 0); /* label for the song title */ eventbox = gtk_event_box_new(); label = gtk_label_new(title); gtk_label_set_line_wrap (GTK_LABEL(label), FALSE); gtk_container_add (GTK_CONTAINER(eventbox), label); gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK); gtk_label_set_attributes (GTK_LABEL(label), pd->labelattrlist); /* viewport widget that manages the scrolling */ viewport = gtk_viewport_new(NULL, NULL); gtk_viewport_set_shadow_type (GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER(viewport), eventbox); gtk_widget_set_size_request (viewport, 0, -1); gtk_box_pack_start (GTK_BOX(boxMain), viewport, DOEXPAND, DOFILL, PADDING); /* the progress bar */ pbar = gtk_progress_bar_new(); gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR(pbar), GTK_PROGRESS_CONTINUOUS); gtk_widget_set_size_request (pbar, 0, -1); gtk_widget_set_events (pbar, GDK_BUTTON_PRESS_MASK); g_signal_connect (G_OBJECT(pbar), "button_press_event", G_CALLBACK(pbar_click), pd); gtk_box_pack_start (GTK_BOX(boxMain), pbar, DOEXPAND, DOFILL, PADDING); pd->boxMain = boxMain; pd->viewport = viewport; pd->label = label; pd->pbar = pbar; /* box that contains the xmms control buttons */ box = gtk_hbox_new(FALSE, 0); new_button_with_img(box, PREV, G_CALLBACK(prev), pd); new_button_with_img(box, PLAY, G_CALLBACK(play), pd); new_button_with_img(box, PAUS, G_CALLBACK(paus), pd); new_button_with_img(box, STOP, G_CALLBACK(stop), pd); new_button_with_img(box, NEXT, G_CALLBACK(next), pd); gtk_box_pack_start (GTK_BOX(boxMain), box, DOEXPAND, DOFILL, PADDING); gtk_container_set_border_width (GTK_CONTAINER(boxMain), 2); gtk_container_add (GTK_CONTAINER(ctrl->base), boxMain); gtk_widget_show_all(boxMain); pd->timeout = g_timeout_add(1000 / pd->scroll_speed, pbar_label_update, pd); ctrl->data = (gpointer) pd; ctrl->with_popup = FALSE; return(TRUE); } /* frees all resources used by the plugin (I hope :-) */ static void xmms_plugin_free(Control *ctrl) { plugin_data *pd; g_return_if_fail(ctrl != NULL); g_return_if_fail(ctrl-> data != NULL); pd = (plugin_data*) ctrl->data; /* remove timeout */ if (pd->timeout) g_source_remove(pd->timeout); /* destroy all widgets */ gtk_widget_destroy(pd->boxMain); /* destroy text attribute for the label widget */ pango_attribute_destroy(pd->labelattr); /* let xmms exit */ xmms_remote_quit(pd->xmms_session); /* free the plugin data structure */ g_free(pd); } /* read plugin parameters */ void xmms_plugin_read_config(Control *control, xmlNodePtr node) { xmlChar *value; gint n; gboolean b; plugin_data *pd = (plugin_data *) control->data; g_return_if_fail(node!=NULL && node->children!=NULL); /* Read xml properties. All values out of range will be ignored. */ /* The default will be used then. */ node=node->children; /* get song text size */ value = xmlGetProp (node, (const xmlChar *) "textsize"); if (value) { n = atoi((char*) value); if ((n >= MIN_TITLE_SIZE) && (n <= MAX_TITLE_SIZE)) pd->titletextsize = n; } xmlFree(value); /* get scroll speed */ value = xmlGetProp (node, (const xmlChar *) "scroll_speed"); if (value) { n = atoi((char*) value); if ((n >= MIN_SCROLL_SPEED) && (n <= MAX_SCROLL_SPEED)) { pd->scroll_speed = n; pd->timer_reset = TRUE; } } xmlFree(value); /* get scroll step width */ value = xmlGetProp (node, (const xmlChar *) "scroll_step"); if (value) { n = atoi((char*) value); if ((n >= MIN_SCROLL_STEP) && (n <= MAX_SCROLL_STEP)) pd->scroll_step = n; } xmlFree(value); /* get scroll delay */ value = xmlGetProp (node, (const xmlChar *) "scroll_delay"); if (value) { n = atoi((char*) value); if ((n >= MIN_SCROLL_DELAY) && (n <= MAX_SCROLL_DELAY)) pd->scroll_delay = n; } xmlFree(value); pd->step_delay = pd->scroll_delay * pd->scroll_speed * pd->scroll_step; /* get xmms window visibility */ value = xmlGetProp (node, (const xmlChar *) "xmms_visible"); if (strcmp(value, "TRUE")) pd->xmmsvisible = TRUE; xmlFree(value); } /* save plugin parameters */ void xmms_plugin_write_config(Control *ctrl, xmlNodePtr parent) { xmlChar value[10]; plugin_data *pd=(plugin_data *)ctrl->data; xmlNodePtr node; g_return_if_fail(parent!=NULL); node = xmlNewTextChild (parent, NULL, (const xmlChar *)"XmmsControl", NULL); g_return_if_fail(node!=NULL); /* save title font size */ g_snprintf (value, 5, "%d", pd->titletextsize); xmlSetProp (node, "textsize", (const xmlChar *)value); /* save scroll speed */ g_snprintf (value, 5, "%d", pd->scroll_speed); xmlSetProp (node, "scroll_speed", (const xmlChar *)value); /* save scroll step width */ g_snprintf (value, 5, "%d", pd->scroll_step); xmlSetProp (node, "scroll_step", (const xmlChar *)value); /* save scroll delay */ g_snprintf (value, 5, "%d", pd->scroll_delay); xmlSetProp (node, "scroll_delay", (const xmlChar *)value); /* save xmms window visibility */ xmlSetProp (node, "xmms_visible", (pd->xmmsvisible) ? "TRUE" : "FALSE"); } /* generic function to add panel callbacks to the base widget */ static void xmms_plugin_attach_callback(Control *ctrl, const gchar *signal, GCallback cb, gpointer data) { g_signal_connect(ctrl->base, signal, cb, data); } /* callback to change the plugin size */ static void xmms_plugin_set_size(Control *ctrl, int size){ /* do the resize */ /* yeah...do it....or something */ } /* callback to change the font size */ static void set_font_size(GtkSpinButton *spin, gpointer data) { plugin_data *pd = (plugin_data*) data; PangoAttrSize *attr = (PangoAttrSize*) pd->labelattr; pd->titletextsize = gtk_spin_button_get_value_as_int(spin); if (pd->titletextsize < MIN_TITLE_SIZE) pd->titletextsize = 1; if (pd->titletextsize > MAX_TITLE_SIZE) pd->titletextsize = MAX_TITLE_SIZE; attr->size = pd->titletextsize * PANGO_SCALE; gtk_label_set_attributes(GTK_LABEL(pd->label), pd->labelattrlist); } /* callback to change the scroll speed */ static void set_scroll_speed(GtkSpinButton *spin, gpointer data) { plugin_data *pd = (plugin_data*) data; pd->scroll_speed = gtk_spin_button_get_value_as_int(spin); if (pd->scroll_speed < MIN_SCROLL_SPEED) pd->scroll_speed = MIN_SCROLL_SPEED; if (pd->scroll_speed > MAX_SCROLL_SPEED) pd->scroll_speed = MAX_SCROLL_SPEED; pd->timer_reset = TRUE; } /* callback to change the scroll step width */ static void set_scroll_step(GtkSpinButton *spin, gpointer data) { plugin_data *pd = (plugin_data*) data; gint s = gtk_spin_button_get_value_as_int(spin); if (s < MIN_SCROLL_STEP) s = MIN_SCROLL_STEP; if (s > MAX_SCROLL_STEP) s = MAX_SCROLL_STEP; pd->scroll_step = s; if (s == 0) pd->title_scroll_position = 0; } /* callback to change the scroll delay */ static void set_scroll_delay(GtkSpinButton *spin, gpointer data) { plugin_data *pd = (plugin_data*) data; gint d = gtk_spin_button_get_value_as_int(spin); if (d < MIN_SCROLL_DELAY) d = MIN_SCROLL_DELAY; if (d > MAX_SCROLL_DELAY) d = MAX_SCROLL_DELAY; pd->scroll_delay = d; pd->step_delay = d * pd->scroll_speed * pd->scroll_step; pd->title_scroll_position = 0; } /* add label to the left column of the options table */ static void add_label(GtkWidget *table, gchar *text, gint pos) { gint att_opts = GTK_SHRINK | GTK_EXPAND | GTK_FILL; GtkWidget *label = gtk_label_new(text); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); gtk_table_attach (GTK_TABLE(table), label, 0, 1, pos, pos+1, att_opts, att_opts, 0, 0); } /* add spin button to the right columns of the options table */ static void add_spin(GtkWidget *table, gint min, gint max, gint value, gpointer cb, gpointer data, gint pos) { gint att_opts = GTK_SHRINK/* | GTK_EXPAND | GTK_FILL*/; GtkWidget *spin = gtk_spin_button_new_with_range(min, max, 1.0); gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), value); gtk_table_attach (GTK_TABLE(table), spin, 1, 2, pos, pos+1, att_opts, att_opts, 0, 0); g_signal_connect (G_OBJECT(spin), "value-changed", G_CALLBACK(cb), data); } /* options dialog */ static void xmms_plugin_create_options (Control *ctrl, GtkContainer *con, GtkWidget *done) { plugin_data *pd = (plugin_data*) ctrl->data; GtkWidget *vbox, *table, *label, *size, *speed, *step, *delay; gint att_opts = GTK_SHRINK | GTK_EXPAND | GTK_FILL; vbox = gtk_vbox_new (FALSE, 4); label = gtk_hseparator_new(); table = gtk_table_new (4, 2, FALSE); gtk_box_pack_start (GTK_BOX(vbox), label, DOEXPAND, DOFILL, PADDING); gtk_box_pack_start (GTK_BOX(vbox), table, DOEXPAND, DOFILL, PADDING); /* put labels into the left column of our table */ add_label(table, "Font Size", 0); add_label(table, "Scroll Speed", 1); add_label(table, "Scroll Stepwidth", 2); add_label(table, "Scroll Delay", 3); /* put spin buttons into the right column */ add_spin(table, MIN_TITLE_SIZE, MAX_TITLE_SIZE, pd->titletextsize, set_font_size, pd, 0); add_spin(table, MIN_SCROLL_SPEED, MAX_SCROLL_SPEED, pd->scroll_speed, set_scroll_speed, pd, 1); add_spin(table, MIN_SCROLL_STEP, MAX_SCROLL_STEP, pd->scroll_step, set_scroll_step, pd, 2); add_spin(table, MIN_SCROLL_DELAY, MAX_SCROLL_DELAY, pd->scroll_delay, set_scroll_delay, pd, 3); gtk_container_add (GTK_CONTAINER(con), vbox); gtk_widget_show_all(vbox); } /* initialization */ G_MODULE_EXPORT void xfce_control_class_init(ControlClass *cc){ /* these are required */ cc->name = "xmms_plugin"; cc->caption = _("XMMS Control"); cc->create_control = (CreateControlFunc) xmms_plugin_control_new; cc->free = xmms_plugin_free; cc->attach_callback = xmms_plugin_attach_callback; /* options */ cc->read_config = xmms_plugin_read_config; cc->write_config = xmms_plugin_write_config; cc->create_options = xmms_plugin_create_options; /* Don't use this function at all if you want xfce to * do the sizing. * Just define the set_size function to NULL, or rather, don't * set it to something else. */ cc->set_size = xmms_plugin_set_size; /* unused in the sample: * ->set_orientation * ->set_theme */ } /* required! defined in panel/plugins.h */ XFCE_PLUGIN_CHECK_INIT