Re: xmms plugin updated

Mario Streiber <[email protected]>
Newsgroups gmane.comp.desktop.xfce.goodies.devel
Message-ID <[email protected]>
On Tue, 26 Apr 2005 14:03:06 +0200 Mario Streiber <[email protected]> wrote:

> On Tue, 26 Apr 2005 13:58:29 +0200 Mario Streiber <[email protected]> wrote:
> 
> > > xmms_plugin.h does not exists.
> > 
> > Yes sorry I forgot to send it in my first mail. Here it is once more.
> 
> Oh and the .c file contains a bug which makes the panel crash as soon as you scroll
> to change the volume. Here is a fixed version.

Sorry again, this was not a fixed version, it doesn't even compile (I sent the one I was currently working on). Here is a working one.

Mario
xmms_plugin.c (text/plain, 21.2 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 = g_strdup_printf("%d: %s (%d:%02d)", pl_pos,
                                           xmms_remote_get_playlist_title(pd->xmms_session, pl_pos),
                                           time / 60, time % 60);
    else         tooltip = g_strdup_printf(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);
    g_free(tooltip);
  }

  /* 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->show_scrolledtitle     = TRUE;
  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), pd);
  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);

  /* get visibility  of scrolled song title */
/*
  value = xmlGetProp (node, (const xmlChar *) "title_visible");
  if (strcmp(value, "TRUE")) pd->show_scrolledtitle = 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");

  /* save visibility of scrolled sing title */
/*
  xmlSetProp (node, "title_visible", (pd->show_scrolledtitle) ? "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);
}

/* add check button to the right columns of the options table
static void add_check(GtkWidget *parent, gpointer cb, gpointer data) {
  GtkWidget *check = gtk_check_button_new_with_label("Show scrolled song title");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check
}
*/

/* 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.