diff src/crossfade/configure.c @ 3059:2e241e90494a

Import work in progress xmms-crossfade rewrite.
author William Pitcock <nenolod@atheme.org>
date Fri, 24 Apr 2009 05:57:35 -0500
parents
children 440cb96f005f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/crossfade/configure.c	Fri Apr 24 05:57:35 2009 -0500
@@ -0,0 +1,1779 @@
+/* 
+ *  XMMS Crossfade Plugin
+ *  Copyright (C) 2000-2007  Peter Eisenlohr <peter@eisenlohr.org>
+ *
+ *  based on the original OSS Output Plugin
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *  
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "crossfade.h"
+#include "configure.h"
+#include "cfgutil.h"
+#include "monitor.h"
+
+#include "interface-2.0.h"
+#include "support-2.0.h"
+
+#ifdef HAVE_OSS
+#  include "oss.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_OSS
+#  ifdef HAVE_SYS_SOUNDCARD_H
+#    include <sys/soundcard.h>
+#  elif defined(HAVE_MACHINE_SOUNDCARD_H)
+#    include <machine/soundcard.h>
+#  endif
+#endif
+
+#ifdef HAVE_LIBSAMPLERATE
+#  include <samplerate.h>
+#endif
+
+
+/* available rates for resampling */
+gint sample_rates[] =
+{
+#if MAX_RATE > 48000
+	192000,
+	96000,
+	88200,
+	64000,
+#endif
+	48000,
+	44100,
+	32000,
+	22050,
+	16000,
+	11025,
+	8000,
+	6000,
+	0
+};
+
+
+#define HIDE(name)					\
+{ if ((set_wgt = lookup_widget(config_win, name)))	\
+    gtk_widget_hide(set_wgt); }
+
+#define SHOW(name)					\
+{ if ((set_wgt = lookup_widget(config_win, name)))	\
+    gtk_widget_show(set_wgt); }
+
+
+#define SETW_SENSITIVE(wgt, sensitive)		\
+  gtk_widget_set_sensitive(wgt, sensitive)
+
+#define SETW_TOGGLE(wgt, active)				\
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wgt), active)
+
+#define SETW_SPIN(wgt, value)					\
+  gtk_spin_button_set_value(GTK_SPIN_BUTTON(wgt), value)
+
+
+#define SET_SENSITIVE(name, sensitive)			\
+{ if ((set_wgt = lookup_widget(config_win, name)))	\
+    gtk_widget_set_sensitive(set_wgt, sensitive); }
+
+#define SET_TOGGLE(name, active)					\
+{ if ((set_wgt = lookup_widget(config_win, name)))			\
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(set_wgt), active); }
+
+#define SET_SPIN(name, value)						\
+{ if ((set_wgt = lookup_widget(config_win, name)))			\
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(set_wgt), value); }
+
+#define SET_PAGE(name, index)					\
+{ if ((set_wgt = lookup_widget(config_win, name)))		\
+    gtk_notebook_set_page(GTK_NOTEBOOK(set_wgt), index); }
+
+#define SET_HISTORY(name, index)					\
+{ if((set_wgt = lookup_widget(config_win, name)))			\
+    gtk_option_menu_set_history(GTK_OPTION_MENU(set_wgt), index); }
+
+
+#define GET_SENSITIVE(name)			\
+((get_wgt = lookup_widget(config_win, name))	\
+  && GTK_WIDGET_SENSITIVE(get_wgt))		\
+
+#define GET_TOGGLE(name)					\
+((get_wgt = lookup_widget(config_win, name))			\
+  && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(get_wgt)))
+
+#define GET_SPIN(name)							\
+((get_wgt = lookup_widget(config_win, name))				\
+  ? gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(get_wgt)) : 0)
+
+
+static GtkWidget *config_win = NULL;
+static GtkWidget *about_win = NULL;
+static GtkWidget *set_wgt;
+static GtkWidget *get_wgt;
+
+/* defined in cfgutil.c */
+extern config_t _xfg;
+static config_t *xfg = &_xfg;
+
+/* some helpers to keep track of the GUI's state */
+static gboolean checking = FALSE;
+static gint op_index;
+static plugin_config_t op_config;
+static gint ep_index;
+
+/* from crossfade.c */
+extern MUTEX buffer_mutex;
+
+/*** internal helpers ********************************************************/
+
+typedef void (*activate_func_t)(GtkWidget *, gint index);
+
+static void
+add_menu_item(GtkWidget *menu, gchar *title, activate_func_t func, gint index, gint **imap)
+{
+	GtkWidget *item;
+	if (!menu || !title || !func)
+		return;
+		
+	item = gtk_menu_item_new_with_label(title);
+	gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)func, (gpointer) index);
+	gtk_widget_show(item);
+	gtk_menu_append(GTK_MENU(menu), item);
+
+	if (imap)
+		*((*imap)++) = index;
+}
+
+static void
+gtk2_spin_button_hack(GtkSpinButton *spin)
+{
+	static gboolean entered = FALSE;
+	const gchar *text;
+	
+	if (entered) return;
+	entered = TRUE;
+
+	text = gtk_entry_get_text(GTK_ENTRY(spin));		
+	if (text && *text)
+	{
+		gint value = atoi(text);
+		if (value != gtk_spin_button_get_value_as_int(spin))
+			gtk_spin_button_set_value(spin, value);
+	}
+	else
+	{
+		gtk_spin_button_set_value(spin, 0.0);
+		gtk_entry_set_text(GTK_ENTRY(spin), "");
+	}
+	
+	entered = FALSE;
+}
+
+/*** output method ***********************************************************/
+
+/*-- callbacks --------------------------------------------------------------*/
+
+void
+on_output_oss_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	SET_PAGE("output_notebook", 0);
+	xfg->output_method = OUTPUT_METHOD_BUILTIN_OSS;
+}
+
+void
+on_output_plugin_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	SET_PAGE("output_notebook", 1);
+	xfg->output_method = OUTPUT_METHOD_PLUGIN;
+}
+
+void
+on_output_none_radio_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	SET_PAGE("output_notebook", 2);
+	xfg->output_method = OUTPUT_METHOD_BUILTIN_NULL;
+}
+
+static void
+resampling_rate_cb(GtkWidget *widget, gint index)
+{
+	xfg->output_rate = index;
+}
+
+#ifdef HAVE_LIBSAMPLERATE
+static void
+resampling_quality_cb(GtkWidget *widget, gint index)
+{
+	xfg->output_quality = index;
+}
+#endif
+
+/*** oss output **************************************************************/
+
+static void
+scan_devices(gchar *type, GtkWidget *option_menu, activate_func_t signal_f)
+{
+#ifdef HAVE_OSS
+	gchar buffer[256];
+	FILE *file;
+
+	GtkWidget *item;
+	gboolean found = FALSE;
+	gint type_len = strlen(type);
+#endif
+
+	GtkWidget *menu;
+	gint index = 0;
+
+	menu = gtk_menu_new();
+
+#ifdef HAVE_OSS
+	/* look for devices in /dev/sndstat or /proc/asound/sndstat (OSS style) */
+	if ((file = fopen("/dev/sndstat",             "r")) ||
+	    (file = fopen("/proc/asound/sndstat",     "r")) ||
+	    (file = fopen("/proc/asound/oss/sndstat", "r")))
+	{
+		while (fgets(buffer, sizeof(buffer), file))
+		{
+			gint i = strlen(buffer) - 1;
+			while ((i >= 0) && isspace(buffer[i]))
+				buffer[i--] = 0;
+				
+			if (found)
+			{
+				if (!buffer[0] || !isdigit(buffer[0]))
+					break;
+					
+				if (index == 0)
+				{
+					gchar *label, *p = strchr(buffer, ':');
+					if (p)
+						while (*++p == ' ');
+					else
+						p = buffer;
+
+					label = g_strdup_printf("Default (%s)", p);
+					item = gtk_menu_item_new_with_label(label);
+					g_free(label);
+				}
+				else
+					item = gtk_menu_item_new_with_label(buffer);
+
+				gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)signal_f, (gpointer) index);
+				gtk_widget_show(item);
+				gtk_menu_append(GTK_MENU(menu), item);
+				index++;
+			}
+			else if (strcmp(buffer, type) == 0)
+				found = TRUE;
+			else if (strncmp(buffer, type, type_len) == 0)
+				DEBUG(("[crossfade] scan_devices: %s\n", buffer));
+		}
+		fclose(file);
+
+		if (!found)
+			DEBUG(("[crossfade] scan_devices: section \"%s\" not found!\n", type));
+	}
+	else
+	{
+		DEBUG(("[crossfade] scan_devices: no sndstat found!\n"));
+#ifdef SOUND_MIXER_INFO
+		/* from xmms-3dse7 by Frank Cornelis */
+		DEBUG(("[crossfade] scan_devices: using alternate method...\n"));
+		for (;;)
+		{
+			gchar dev_name[32];
+			int fd;
+			gint mixer = 0;
+			mixer_info info;
+			gchar *label;
+
+			if (mixer != 0)
+				sprintf(dev_name, "/dev/mixer%d", mixer);
+			else
+				strcpy(dev_name, "/dev/mixer");
+
+			if ((fd = open(dev_name, O_RDONLY)) != -1)
+			{
+				if (ioctl(fd, SOUND_MIXER_INFO, &info) != -1)
+				{
+					label = g_strdup_printf(index ? "%s" : "Default (%s)", info.name);
+					add_menu_item(menu, label, signal_f, index, NULL);
+					g_free(label);
+					index++;
+				}
+				close(fd);
+			}
+			else
+				break;
+			mixer++;
+		}
+#endif
+	}
+#endif /* HAVE_OSS */
+
+	/* create default entry if no device(s) could be found */
+	if (index == 0)
+		add_menu_item(menu, "Default", signal_f, 0, NULL);
+
+	/* attach menu */
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+}
+
+/*-- oss output callbacks ---------------------------------------------------*/
+
+void
+check_oss_dependencies()
+{
+	if (checking) return;
+	checking = TRUE;
+
+	SET_SENSITIVE("oss_adevice_optionmenu", !xfg->oss_use_alt_audio_device);
+	SET_SENSITIVE("oss_adevice_alt_entry",   xfg->oss_use_alt_audio_device);
+
+	SET_SENSITIVE("oss_mdevice_optionmenu", !xfg->oss_use_alt_mixer_device);
+	SET_SENSITIVE("oss_mdevice_alt_entry",   xfg->oss_use_alt_mixer_device);
+
+	SET_SENSITIVE("osshwb_fragments_label", !xfg->oss_maxbuf_enable);
+	SET_SENSITIVE("osshwb_fragments_spin",  !xfg->oss_maxbuf_enable);
+	SET_SENSITIVE("osshwb_fragsize_label",  !xfg->oss_maxbuf_enable);
+	SET_SENSITIVE("osshwb_fragsize_spin",   !xfg->oss_maxbuf_enable);
+
+	checking = FALSE;
+}
+
+void
+config_adevice_cb(GtkWidget *widget, gint device)
+{
+	xfg->oss_audio_device = device;
+}
+
+void
+config_mdevice_cb(GtkWidget *widget, gint device)
+{
+	xfg->oss_mixer_device = device;
+}
+
+void
+on_config_adevice_alt_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->oss_use_alt_audio_device = gtk_toggle_button_get_active(togglebutton);
+	check_oss_dependencies();
+}
+
+void
+on_config_mdevice_alt_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->oss_use_alt_mixer_device = gtk_toggle_button_get_active(togglebutton);
+	check_oss_dependencies();
+}
+
+void
+on_osshwb_maxbuf_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->oss_maxbuf_enable = gtk_toggle_button_get_active(togglebutton);
+	check_oss_dependencies();
+}
+
+/*** plugin output ***********************************************************/
+
+static void config_plugin_cb(GtkWidget *widget, gint index);
+
+static gint
+scan_plugins(GtkWidget *option_menu, gchar *selected)
+{
+	GtkWidget *menu = gtk_menu_new();
+	GList     *list = g_list_first(xfplayer_get_output_list());
+	gint      index =  0;
+	gint  sel_index = -1;
+	gint  def_index = -1;
+
+	/* sanity check */
+	if (selected == NULL)
+		selected = "";
+
+	/* parse module list */
+	while (list)
+	{
+		OutputPlugin *op = (OutputPlugin *) list->data;
+		GtkWidget  *item = gtk_menu_item_new_with_label(op->description);
+
+		if (op == get_crossfade_oplugin_info())  /* disable selecting ourselves */
+			gtk_widget_set_sensitive(item, FALSE);
+		else
+		{
+			if (def_index == -1)
+				def_index = index;
+				
+			if (op->filename && strcmp(g_basename(op->filename), selected) == 0)
+				sel_index = index;
+		}
+
+		/* create menu item */
+		gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)config_plugin_cb, (gpointer) index++);
+		gtk_widget_show(item);
+		gtk_menu_append(GTK_MENU(menu), item);
+
+		/* advance to next module */
+		list = g_list_next(list);
+	}
+
+	/* attach menu */
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+
+	if (sel_index == -1)
+	{
+		DEBUG(("[crossfade] scan_plugins: plugin not found (\"%s\")\n", selected));
+		return def_index;  /* use default (first entry) */
+	}
+	return sel_index;
+}
+
+/*-- plugin output callbacks ------------------------------------------------*/
+
+static void
+config_plugin_cb(GtkWidget *widget, gint index)
+{
+	OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), index);
+
+	/* get plugin options from gui */
+	op_config.throttle_enable  = GET_TOGGLE("op_throttle_check");
+	op_config.max_write_enable = GET_TOGGLE("op_maxblock_check");
+	op_config.max_write_len    = GET_SPIN  ("op_maxblock_spin");
+	op_config.force_reopen     = GET_TOGGLE("op_forcereopen_check");
+
+	/* config -> string */
+	xfade_save_plugin_config(&xfg->op_config_string, xfg->op_name, &op_config);
+
+	/* select new plugin */
+	op_index = index;
+
+	/* get new plugin's name */
+	if (xfg->op_name) g_free(xfg->op_name);
+	xfg->op_name = (op && op->filename) ? g_strdup(g_basename(op->filename)) : NULL;
+
+	/* string -> config */
+	xfade_load_plugin_config(xfg->op_config_string, xfg->op_name, &op_config);
+
+	/* update gui */
+	SET_SENSITIVE("op_configure_button",  op && (op->configure != NULL));
+	SET_SENSITIVE("op_about_button",      op && (op->about != NULL));
+	SET_TOGGLE   ("op_throttle_check",    op_config.throttle_enable);
+	SET_TOGGLE   ("op_maxblock_check",    op_config.max_write_enable);
+	SET_SPIN     ("op_maxblock_spin",     op_config.max_write_len);
+	SET_SENSITIVE("op_maxblock_spin",     op_config.max_write_enable);
+	SET_TOGGLE   ("op_forcereopen_check", op_config.force_reopen);
+}
+
+void
+on_output_plugin_configure_button_clicked(GtkButton *button, gpointer user_data)
+{
+	OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), op_index);
+	if ((op == NULL) || (op->configure == NULL))
+		return;
+		
+	op->configure();
+}
+
+void
+on_output_plugin_about_button_clicked(GtkButton *button, gpointer user_data)
+{
+	OutputPlugin *op = g_list_nth_data(xfplayer_get_output_list(), op_index);
+	if ((op == NULL) || (op->about == NULL))
+		return;
+		
+	op->about();
+}
+
+void
+on_op_throttle_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	op_config.throttle_enable = gtk_toggle_button_get_active(togglebutton);
+}
+
+void
+on_op_maxblock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	op_config.max_write_enable = gtk_toggle_button_get_active(togglebutton);
+	SET_SENSITIVE("op_maxblock_spin", op_config.max_write_enable);
+}
+
+void
+on_op_maxblock_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	op_config.max_write_len = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+}
+
+void
+on_op_forcereopen_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	op_config.max_write_enable = gtk_toggle_button_get_active(togglebutton);
+}
+
+/*** effects *****************************************************************/
+
+static void config_effect_plugin_cb(GtkWidget *widget, gint index);
+
+static gint
+scan_effect_plugins(GtkWidget *option_menu, gchar *selected)
+{
+	assert(xfplayer_get_effect_list());
+
+	GtkWidget *menu = gtk_menu_new();
+	GList *list = g_list_first(xfplayer_get_effect_list());
+	gint index = 0;
+	gint sel_index = -1;
+	gint def_index = -1;
+
+	/* sanity check */
+	if (selected == NULL)
+		selected = "";
+
+	/* parse module list */
+	while (list)
+	{
+		EffectPlugin *ep = (EffectPlugin *) list->data;
+		GtkWidget  *item = gtk_menu_item_new_with_label(ep->description);
+
+		if (def_index == -1)
+			def_index = index;
+			
+		if (ep->filename && !strcmp(g_basename(ep->filename), selected))
+			sel_index = index;
+
+		/* create menu item */
+		gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)config_effect_plugin_cb, (gpointer) index++);
+		gtk_widget_show(item);
+		gtk_menu_append(GTK_MENU(menu), item);
+
+		/* advance to next module */
+		list = g_list_next(list);
+	}
+
+	/* attach menu */
+	gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+
+	if (sel_index == -1)
+	{
+		DEBUG(("[crossfade] scan_effect_plugins: plugin not found (\"%s\")\n", selected));
+		return def_index;	/* use default (first entry) */
+	}
+	return sel_index;
+}
+
+/*-- plugin output callbacks ------------------------------------------------*/
+
+static void
+config_effect_plugin_cb(GtkWidget *widget, gint index)
+{
+	assert(xfplayer_get_effect_list());
+	EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), index);
+
+	/* select new plugin */
+	ep_index = index;
+
+	/* get new plugin's name */
+	if (xfg->ep_name) g_free(xfg->ep_name);
+	xfg->ep_name = (ep && ep->filename) ? g_strdup(g_basename(ep->filename)) : NULL;
+
+	/* update gui */
+	SET_SENSITIVE("ep_configure_button", ep && (ep->configure != NULL));
+	SET_SENSITIVE("ep_about_button",     ep && (ep->about     != NULL));
+
+	/* 0.3.5: apply effect config immediatelly */
+	if (config->ep_name) g_free(config->ep_name);
+	config->ep_name = g_strdup(xfg->ep_name);
+}
+
+void
+on_ep_configure_button_clicked(GtkButton *button, gpointer user_data)
+{
+	assert(xfplayer_get_effect_list());
+
+	EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index);
+	if ((ep == NULL) || (ep->configure == NULL))
+		return;
+		
+	ep->configure();
+}
+
+void
+on_ep_about_button_clicked(GtkButton *button, gpointer user_data)
+{
+	assert(xfplayer_get_effect_list());
+
+	EffectPlugin *ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index);
+	if ((ep == NULL) || (ep->about == NULL))
+		return;
+		
+	ep->about();
+}
+
+void
+on_ep_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	/* 0.3.5: apply effect config immediatelly */
+	config->ep_enable = xfg->ep_enable = GET_TOGGLE("ep_enable_check");
+}
+
+/*-- volume normalizer ------------------------------------------------------*/
+
+void
+check_effects_dependencies()
+{
+	if (checking) return;
+	checking = TRUE;
+
+	SET_SENSITIVE("volnorm_target_spin",      xfg->volnorm_enable);
+	SET_SENSITIVE("volnorm_target_label",     xfg->volnorm_enable);
+	SET_SENSITIVE("volnorm_quantaudio_check", xfg->volnorm_enable);
+	SET_SENSITIVE("volnorm_target_spin",      xfg->volnorm_enable);
+
+	checking = FALSE;
+}
+
+void
+on_volnorm_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->volnorm_enable = gtk_toggle_button_get_active(togglebutton);
+	check_effects_dependencies();
+}
+
+/*** crossfader **************************************************************/
+
+static void xf_config_cb(GtkWidget *widget, gint index);
+static void xf_type_cb  (GtkWidget *widget, gint index);
+
+/* crude hack to keep track of menu items */
+static gint xf_config_index_map[MAX_FADE_CONFIGS];
+static gint xf_type_index_map  [MAX_FADE_TYPES];
+
+static void
+create_crossfader_config_menu()
+{
+	GtkWidget *optionmenu, *menu;
+	gint i, *imap;
+
+	if ((optionmenu = lookup_widget(config_win, "xf_config_optionmenu")))
+	{
+		for (i = 0; i < MAX_FADE_CONFIGS; i++)
+			xf_config_index_map[i] = -1;
+			
+		imap = xf_config_index_map;
+		menu = gtk_menu_new();
+		
+		add_menu_item(menu, "Start of playback",    xf_config_cb, FADE_CONFIG_START, &imap);
+		add_menu_item(menu, "Automatic songchange", xf_config_cb, FADE_CONFIG_XFADE, &imap);
+#if 0
+		/* this should be FADE_TYPE_NONE all the time, anyway,
+		   so no need to make it configureable by the user */
+		add_menu_item(menu, "Automatic (gapless)", xf_config_cb, FADE_CONFIG_ALBUM, &imap);
+#endif
+		add_menu_item(menu, "Manual songchange", xf_config_cb, FADE_CONFIG_MANUAL, &imap);
+		add_menu_item(menu, "Manual stop",       xf_config_cb, FADE_CONFIG_STOP,   &imap);
+		add_menu_item(menu, "End of playlist",   xf_config_cb, FADE_CONFIG_EOP,    &imap);
+		add_menu_item(menu, "Seeking",           xf_config_cb, FADE_CONFIG_SEEK,   &imap);
+		add_menu_item(menu, "Pause",             xf_config_cb, FADE_CONFIG_PAUSE,  &imap);
+		gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
+	}
+
+}
+
+static void
+create_crossfader_type_menu()
+{
+	GtkWidget *optionmenu, *menu;
+	gint i, *imap;
+	guint32 mask;
+
+	if ((optionmenu = lookup_widget(config_win, "xf_type_optionmenu")))
+	{
+		for (i = 0; i < MAX_FADE_TYPES; i++)
+			xf_type_index_map[i] = -1;
+			
+		imap = xf_type_index_map;
+		menu = gtk_menu_new();
+		mask = xfg->fc[xfg->xf_index].type_mask;
+
+		if (mask & (1 << FADE_TYPE_REOPEN))      add_menu_item(menu, "Reopen output device", xf_type_cb, FADE_TYPE_REOPEN,      &imap);
+		if (mask & (1 << FADE_TYPE_FLUSH))       add_menu_item(menu, "Flush output device",  xf_type_cb, FADE_TYPE_FLUSH,       &imap);
+		if (mask & (1 << FADE_TYPE_NONE))        add_menu_item(menu, "None (gapless/off)",   xf_type_cb, FADE_TYPE_NONE,        &imap);
+		if (mask & (1 << FADE_TYPE_PAUSE))       add_menu_item(menu, "Pause",                xf_type_cb, FADE_TYPE_PAUSE,       &imap);
+		if (mask & (1 << FADE_TYPE_SIMPLE_XF))   add_menu_item(menu, "Simple crossfade",     xf_type_cb, FADE_TYPE_SIMPLE_XF,   &imap);
+		if (mask & (1 << FADE_TYPE_ADVANCED_XF)) add_menu_item(menu, "Advanced crossfade",   xf_type_cb, FADE_TYPE_ADVANCED_XF, &imap);
+		if (mask & (1 << FADE_TYPE_FADEIN))      add_menu_item(menu, "Fadein",               xf_type_cb, FADE_TYPE_FADEIN,      &imap);
+		if (mask & (1 << FADE_TYPE_FADEOUT))     add_menu_item(menu, "Fadeout",              xf_type_cb, FADE_TYPE_FADEOUT,     &imap);
+		if (mask & (1 << FADE_TYPE_PAUSE_NONE))  add_menu_item(menu, "None",                 xf_type_cb, FADE_TYPE_PAUSE_NONE,  &imap);
+		if (mask & (1 << FADE_TYPE_PAUSE_ADV))   add_menu_item(menu, "Fadeout/Fadein",       xf_type_cb, FADE_TYPE_PAUSE_ADV,   &imap);
+		gtk_option_menu_set_menu(GTK_OPTION_MENU(optionmenu), menu);
+	}
+}
+
+#define NONE             0x00000000L
+#define XF_CONFIG        0x00000001L
+#define XF_TYPE          0x00000002L
+#define XF_MIX_SIZE      0x00000004L
+#define XF_FADEOUT       0x00000008L
+#define XF_OFFSET        0x00000010L
+#define XF_FADEIN        0x00000020L
+#define XF_PAGE          0x00000040L
+#define XF_FLUSH         0x00000080L
+#define ANY              0xffffffffL
+
+static void
+check_crossfader_dependencies(guint32 mask)
+{
+	fade_config_t *fc = &xfg->fc[xfg->xf_index];
+	gint i;
+
+	/* HACK: avoid endless recursion */
+	if (checking) return;
+	checking = TRUE;
+
+	if (mask & XF_FLUSH)
+	{
+		SET_TOGGLE("xftfp_enable_check", fc->flush_pause_enable);
+		SET_SENSITIVE("xftfp_length_label", fc->flush_pause_enable);
+		SET_SENSITIVE("xftfp_length_spin", fc->flush_pause_enable);
+		SET_TOGGLE("xftffi_enable_check", fc->flush_in_enable);
+		SET_SENSITIVE("xftffi_length_label", fc->flush_in_enable);
+		SET_SENSITIVE("xftffi_length_spin", fc->flush_in_enable);
+		SET_SENSITIVE("xftffi_volume_label", fc->flush_in_enable);
+		SET_SENSITIVE("xftffi_volume_spin", fc->flush_in_enable);
+	}
+
+	if (mask & XF_MIX_SIZE)
+	{
+		SET_TOGGLE("xf_autobuf_check", xfg->mix_size_auto);
+		SET_SENSITIVE("xf_buffer_spin", !xfg->mix_size_auto);
+		SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg));
+	}
+
+	if (mask & XF_CONFIG)
+	{
+		for (i = 0; i < MAX_FADE_CONFIGS && (xf_config_index_map[i] != xfg->xf_index); i++);
+		if (i == MAX_FADE_CONFIGS)
+			i = 0;
+		SET_HISTORY("xf_config_optionmenu", i);
+	}
+
+	if (mask & XF_TYPE)
+	{
+		create_crossfader_type_menu();
+		for (i = 0; i < MAX_FADE_TYPES && (xf_type_index_map[i] != fc->type); i++);
+		if (i == MAX_FADE_TYPES)
+		{
+			fc->type = FADE_TYPE_NONE;
+			for (i = 0; i < MAX_FADE_TYPES && (xf_type_index_map[i] != fc->type); i++);
+			if (i == MAX_FADE_CONFIGS)
+				i = 0;
+		}
+		SET_HISTORY("xf_type_optionmenu", i);
+	}
+
+	if (mask & XF_PAGE)
+	{
+		SET_PAGE("xf_type_notebook", fc->type);
+		SET_SPIN("pause_length_spin", fc->pause_len_ms);
+		SET_SPIN("simple_length_spin", fc->simple_len_ms);
+		if (fc->config == FADE_CONFIG_SEEK)
+		{
+			HIDE("xftf_pause_frame");
+			HIDE("xftf_fadein_frame");
+		}
+		else
+		{
+			SHOW("xftf_pause_frame");
+			SHOW("xftf_fadein_frame");
+		}
+	}
+
+	if (mask & XF_FADEOUT)
+	{
+		SET_TOGGLE("fadeout_enable_check", fc->out_enable);
+		SET_SENSITIVE("fadeout_length_label", fc->out_enable);
+		SET_SENSITIVE("fadeout_length_spin", fc->out_enable);
+		SET_SPIN("fadeout_length_spin", fc->out_len_ms);
+		SET_SENSITIVE("fadeout_volume_label", fc->out_enable);
+		SET_SENSITIVE("fadeout_volume_spin", fc->out_enable);
+		SET_SPIN("fadeout_volume_spin", fc->out_volume);
+		SET_SPIN("xftfo_length_spin", fc->out_len_ms);
+		SET_SPIN("xftfo_volume_spin", fc->out_volume);
+		SET_SPIN("xftfoi_fadeout_spin", fc->out_len_ms);
+	}
+
+	if (mask & XF_FADEIN)
+	{
+		SET_TOGGLE("fadein_lock_check", fc->in_locked);
+		SET_SENSITIVE("fadein_enable_check", !fc->in_locked);
+		SET_TOGGLE("fadein_enable_check", fc->in_locked ? fc->out_enable : fc->in_enable);
+		SET_SENSITIVE("fadein_length_label", !fc->in_locked && fc->in_enable);
+		SET_SENSITIVE("fadein_length_spin", !fc->in_locked && fc->in_enable);
+		SET_SPIN("fadein_length_spin", fc->in_locked ? fc->out_len_ms : fc->in_len_ms);
+		SET_SENSITIVE("fadein_volume_label", !fc->in_locked && fc->in_enable);
+		SET_SENSITIVE("fadein_volume_spin", !fc->in_locked && fc->in_enable);
+		SET_SPIN("fadein_volume_spin", fc->in_locked ? fc->out_volume : fc->in_volume);
+		SET_SPIN("xftfi_length_spin", fc->in_len_ms);
+		SET_SPIN("xftfi_volume_spin", fc->in_volume);
+		SET_SPIN("xftfoi_fadein_spin", fc->in_len_ms);
+	}
+
+	if (mask & XF_OFFSET)
+	{
+		if (fc->out_enable)
+			SET_SENSITIVE("xfofs_lockout_radiobutton", TRUE);
+		if (!fc->in_locked && fc->in_enable)
+			SET_SENSITIVE("xfofs_lockin_radiobutton", TRUE);
+
+		switch (fc->ofs_type)
+		{
+			case FC_OFFSET_LOCK_OUT:
+				if (!fc->out_enable)
+				{
+					SET_TOGGLE("xfofs_none_radiobutton", TRUE);
+					fc->ofs_type = FC_OFFSET_NONE;
+				}
+				break;
+
+			case FC_OFFSET_LOCK_IN:
+				if (!(!fc->in_locked && fc->in_enable))
+				{
+					if ((fc->in_locked && fc->out_enable))
+					{
+						SET_TOGGLE("xfofs_lockout_radiobutton", TRUE);
+						fc->ofs_type = FC_OFFSET_LOCK_OUT;
+					}
+					else
+					{
+						SET_TOGGLE("xfofs_none_radiobutton", TRUE);
+						fc->ofs_type = FC_OFFSET_NONE;
+					}
+				}
+				break;
+		}
+
+		switch (fc->ofs_type_wanted)
+		{
+			case FC_OFFSET_NONE:
+				SET_TOGGLE("xfofs_none_radiobutton", TRUE);
+				fc->ofs_type = FC_OFFSET_NONE;
+				break;
+
+			case FC_OFFSET_LOCK_OUT:
+				if (fc->out_enable)
+				{
+					SET_TOGGLE("xfofs_lockout_radiobutton", TRUE);
+					fc->ofs_type = FC_OFFSET_LOCK_OUT;
+				}
+				break;
+
+			case FC_OFFSET_LOCK_IN:
+				if (!fc->in_locked && fc->in_enable)
+				{
+					SET_TOGGLE("xfofs_lockin_radiobutton", TRUE);
+					fc->ofs_type = FC_OFFSET_LOCK_IN;
+				}
+				else if (fc->out_enable)
+				{
+					SET_TOGGLE("xfofs_lockout_radiobutton", TRUE);
+					fc->ofs_type = FC_OFFSET_LOCK_OUT;
+				}
+				break;
+
+			case FC_OFFSET_CUSTOM:
+				SET_TOGGLE("xfofs_custom_radiobutton", TRUE);
+				fc->ofs_type = FC_OFFSET_CUSTOM;
+				break;
+		}
+
+		if (!fc->out_enable)
+			SET_SENSITIVE("xfofs_lockout_radiobutton", FALSE);
+		if (!(!fc->in_locked && fc->in_enable))
+			SET_SENSITIVE("xfofs_lockin_radiobutton", FALSE);
+
+		SET_SENSITIVE("xfofs_custom_spin", fc->ofs_type == FC_OFFSET_CUSTOM);
+		SET_SPIN("xfofs_custom_spin", xfade_cfg_offset(fc));
+		SET_SPIN("xftfo_silence_spin", xfade_cfg_offset(fc));
+		SET_SPIN("xftfoi_silence_spin", xfade_cfg_offset(fc));
+	}
+
+	checking = FALSE;
+}
+
+/*-- crossfader callbacks ---------------------------------------------------*/
+
+void
+on_xf_buffer_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->mix_size_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(NONE);
+}
+
+void
+on_xf_autobuf_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	xfg->mix_size_auto = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_MIX_SIZE);
+}
+
+/* - config/type  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+xf_config_cb(GtkWidget *widget, gint index)
+{
+	if (checking) return;
+	xfg->xf_index = index;
+	check_crossfader_dependencies(ANY & ~XF_CONFIG);
+}
+
+void
+xf_type_cb(GtkWidget *widget, gint index)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].type = index;
+	check_crossfader_dependencies(ANY & ~XF_CONFIG);
+}
+
+/* - flush  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_xftfp_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].flush_pause_enable = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_FLUSH | XF_MIX_SIZE);
+}
+
+void
+on_xftfp_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].flush_pause_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_FLUSH);
+}
+
+void
+on_xftffi_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].flush_in_enable = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_FLUSH | XF_OFFSET | XF_FADEOUT | XF_FADEIN);
+}
+
+void
+on_xftffi_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].flush_in_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_FLUSH);
+}
+
+void
+on_xftffi_volume_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].flush_in_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_FLUSH);
+}
+
+/* - pause  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_pause_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].pause_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_MIX_SIZE);
+}
+
+/* - simple - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_simple_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].simple_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_MIX_SIZE);
+}
+
+/* - crossfade-fadeout  - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_fadeout_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].out_enable = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET | XF_FADEOUT | XF_FADEIN);
+}
+
+void
+on_fadeout_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].out_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET | XF_FADEIN);
+}
+
+void
+on_fadeout_volume_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].out_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_OFFSET | XF_FADEIN);
+}
+
+/* - crossfade-offset - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_xfofs_none_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking || !gtk_toggle_button_get_active(togglebutton)) return;
+	xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_NONE;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_NONE;
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET);
+}
+
+void
+on_xfofs_none_radiobutton_clicked(GtkButton *button, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_NONE;
+}
+
+void
+on_xfofs_lockout_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking || !gtk_toggle_button_get_active(togglebutton)) return;
+	xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_LOCK_OUT;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_OUT;
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET);
+}
+
+void
+on_xfofs_lockout_radiobutton_clicked(GtkButton *button, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_OUT;
+}
+
+void
+on_xfofs_lockin_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking || !gtk_toggle_button_get_active(togglebutton)) return;
+	xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_LOCK_IN;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_IN;
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET);
+}
+
+void
+on_xfofs_lockin_radiobutton_clicked(GtkButton *button, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_LOCK_IN;
+}
+
+void
+on_xfofs_custom_radiobutton_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking || !gtk_toggle_button_get_active(togglebutton)) return;
+	xfg->fc[xfg->xf_index].ofs_type = FC_OFFSET_CUSTOM;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_CUSTOM;
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET);
+}
+
+void
+on_xfofs_custom_radiobutton_clicked(GtkButton *button, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].ofs_type_wanted = FC_OFFSET_CUSTOM;
+}
+
+void
+on_xfofs_custom_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].ofs_custom_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_MIX_SIZE);
+}
+
+/* - crossfade-fadein - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
+
+void
+on_fadein_lock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].in_locked = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_OFFSET | XF_FADEIN);
+}
+
+void
+on_fadein_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->fc[xfg->xf_index].in_enable = gtk_toggle_button_get_active(togglebutton);
+	check_crossfader_dependencies(XF_OFFSET | XF_FADEIN);
+}
+
+void
+on_fadein_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].in_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(XF_MIX_SIZE | XF_OFFSET);
+}
+
+void
+on_fadein_volume_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->fc[xfg->xf_index].in_volume = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_crossfader_dependencies(NONE);
+}
+
+/*-- fadein -----------------------------------------------------------------*/
+
+/* signal set to on_fadein_length_spin_changed */
+/* signal set to on_fadein_volume_spin_changed */
+
+/*-- fadeout ----------------------------------------------------------------*/
+
+/* signal set to on_fadeout_length_spin_changed */
+/* signal set to on_fadeout_volume_spin_changed */
+
+/*-- fadeout/fadein ---------------------------------------------------------*/
+
+/* signal set to on_fadeout_length_spin_changed */
+/* signal set to on_xfofs_custom_spin_changed */
+/* signal set to on_fadeout_volume_spin_changed */
+
+/*** gap killer **************************************************************/
+
+void
+check_gapkiller_dependencies()
+{
+	if (checking) return;
+	checking = TRUE;
+
+	SET_SENSITIVE("lgap_length_spin", xfg->gap_lead_enable);
+	SET_SENSITIVE("lgap_level_spin", xfg->gap_lead_enable);
+	SET_SENSITIVE("tgap_enable_check", !xfg->gap_trail_locked);
+	SET_SENSITIVE("tgap_length_spin", !xfg->gap_trail_locked && xfg->gap_trail_enable);
+	SET_SENSITIVE("tgap_level_spin", !xfg->gap_trail_locked && xfg->gap_trail_enable);
+
+	if (xfg->gap_trail_locked)
+	{
+		SET_TOGGLE("tgap_enable_check", xfg->gap_lead_enable);
+		SET_SPIN("tgap_length_spin", xfg->gap_lead_len_ms);
+		SET_SPIN("tgap_level_spin", xfg->gap_lead_level);
+	}
+	else
+	{
+		SET_TOGGLE("tgap_enable_check", xfg->gap_trail_enable);
+		SET_SPIN("tgap_length_spin", xfg->gap_trail_len_ms);
+		SET_SPIN("tgap_level_spin", xfg->gap_trail_level);
+	}
+
+	if (xfg->mix_size_auto)
+		SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg));
+
+	checking = FALSE;
+}
+
+/*-- gapkiller callbacks ----------------------------------------------------*/
+
+void
+on_lgap_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->gap_lead_enable = gtk_toggle_button_get_active(togglebutton);
+	check_gapkiller_dependencies();
+}
+
+void
+on_lgap_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->gap_lead_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_gapkiller_dependencies();
+}
+
+void
+on_lgap_level_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->gap_lead_level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_gapkiller_dependencies();
+}
+
+void
+on_tgap_lock_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->gap_trail_locked = gtk_toggle_button_get_active(togglebutton);
+	check_gapkiller_dependencies();
+}
+
+void
+on_tgap_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->gap_trail_enable = gtk_toggle_button_get_active(togglebutton);
+	check_gapkiller_dependencies();
+}
+
+void
+on_tgap_length_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->gap_trail_len_ms = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+}
+
+void
+on_tgap_level_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->gap_trail_level = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+}
+
+/*** misc ********************************************************************/
+
+void
+check_misc_dependencies()
+{
+	if (checking) return;
+	checking = TRUE;
+
+	if (xfg->mix_size_auto)
+		SET_SPIN("xf_buffer_spin", xfade_mix_size_ms(xfg));
+
+	SET_SENSITIVE("moth_opmaxused_spin", xfg->enable_op_max_used);
+
+	checking = FALSE;
+}
+
+/*-- misc callbacks ---------------------------------------------------------*/
+
+void
+on_config_mixopt_enable_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	SET_SENSITIVE("mixopt_reverse_check", gtk_toggle_button_get_active(togglebutton));
+	SET_SENSITIVE("mixopt_software_check", gtk_toggle_button_get_active(togglebutton));
+}
+
+void
+on_moth_songchange_spin_changed(GtkEditable *editable, gpointer user_data)
+{
+	if (checking) return;
+	gtk2_spin_button_hack(GTK_SPIN_BUTTON(editable));
+	xfg->songchange_timeout = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(editable));
+	check_misc_dependencies();
+}
+
+void
+on_moth_opmaxused_check_toggled(GtkToggleButton *togglebutton, gpointer user_data)
+{
+	if (checking) return;
+	xfg->enable_op_max_used = gtk_toggle_button_get_active(togglebutton);
+	check_misc_dependencies();
+}
+
+/*** main config *************************************************************/
+
+void
+on_config_apply_clicked(GtkButton *button, gpointer user_data)
+{
+	GtkWidget *widget;
+
+	/* get current notebook page */
+	if ((widget = lookup_widget(config_win, "config_notebook")))
+		xfg->page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
+
+	/* output method */
+
+	/* sample rate */
+
+	/* output method: builtin OSS */
+	if ((widget = lookup_widget(config_win, "output_oss_notebook")))
+		xfg->oss_page = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
+
+	if ((widget = lookup_widget(config_win, "oss_adevice_alt_entry")))
+	{
+		if (xfg->oss_alt_audio_device)
+			g_free(xfg->oss_alt_audio_device);
+		xfg->oss_alt_audio_device = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
+		g_strstrip(xfg->oss_alt_audio_device);
+	}
+
+	if ((widget = lookup_widget(config_win, "oss_mdevice_alt_entry")))
+	{
+		if (xfg->oss_alt_mixer_device)
+			g_free(xfg->oss_alt_mixer_device);
+		xfg->oss_alt_mixer_device = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
+		g_strstrip(xfg->oss_alt_mixer_device);
+	}
+
+	xfg->oss_buffer_size_ms = GET_SPIN("ossbuf_buffer_spin");
+	xfg->oss_preload_size_ms = GET_SPIN("ossbuf_preload_spin");
+
+	xfg->oss_fragments = GET_SPIN("osshwb_fragments_spin");
+	xfg->oss_fragment_size = GET_SPIN("osshwb_fragsize_spin");
+	xfg->oss_maxbuf_enable = GET_TOGGLE("osshwb_maxbuf_check");
+
+	xfg->oss_mixer_use_master = GET_TOGGLE("ossmixer_pcm_check");
+
+	/* output method: plugin */
+	op_config.throttle_enable = GET_TOGGLE("op_throttle_check");
+	op_config.max_write_enable = GET_TOGGLE("op_maxblock_check");
+	op_config.max_write_len = GET_SPIN("op_maxblock_spin");
+	op_config.force_reopen = GET_TOGGLE("op_forcereopen_check");
+
+	xfade_save_plugin_config(&xfg->op_config_string, xfg->op_name, &op_config);
+
+	/* output method: none: */
+
+	/* effects: pre-mixing effect plugin */
+
+	/* effects: volume normalizer */
+	xfg->volnorm_target = GET_SPIN("volnorm_target_spin");
+	xfg->volnorm_use_qa = GET_TOGGLE("volnorm_quantaudio_check");
+
+	/* crossfader */
+	xfg->mix_size_auto = GET_TOGGLE("xf_autobuf_check");
+
+	/* gap killer */
+	xfg->gap_lead_enable = GET_TOGGLE("lgap_enable_check");
+	xfg->gap_lead_len_ms = GET_SPIN("lgap_length_spin");
+	xfg->gap_lead_level = GET_SPIN("lgap_level_spin");
+
+	xfg->gap_trail_locked = GET_TOGGLE("tgap_lock_check");
+
+	xfg->gap_crossing = GET_TOGGLE("gadv_crossing_check");
+
+	/* misc */
+	xfg->enable_debug = GET_TOGGLE("debug_stderr_check");
+	xfg->enable_monitor = GET_TOGGLE("debug_monitor_check");
+	xfg->enable_mixer = GET_TOGGLE("mixopt_enable_check");
+	xfg->mixer_reverse = GET_TOGGLE("mixopt_reverse_check");
+	xfg->mixer_software = GET_TOGGLE("mixopt_software_check");
+	xfg->preload_size_ms = GET_SPIN("moth_preload_spin");
+	xfg->album_detection = GET_TOGGLE("noxf_album_check");
+	xfg->no_xfade_if_same_file = GET_TOGGLE("noxf_samefile_check");
+	xfg->enable_http_workaround = GET_TOGGLE("moth_httpworkaround_check");
+	xfg->op_max_used_ms = GET_SPIN("moth_opmaxused_spin");
+	xfg->output_keep_opened = GET_TOGGLE("moth_outputkeepopened_check");
+
+	/* presets */
+
+	/* lock buffer */
+	MUTEX_LOCK(&buffer_mutex);
+
+	/* free existing strings */
+	if (config->oss_alt_audio_device) g_free(config->oss_alt_audio_device);
+	if (config->oss_alt_mixer_device) g_free(config->oss_alt_mixer_device);
+	if (config->op_config_string)     g_free(config->op_config_string);
+	if (config->op_name)              g_free(config->op_name);
+	if (config->ep_name)              g_free(config->ep_name);
+
+	/* copy current settings (dupping the strings) */
+	*config = *xfg;
+	config->oss_alt_audio_device = g_strdup(xfg->oss_alt_audio_device);
+	config->oss_alt_mixer_device = g_strdup(xfg->oss_alt_mixer_device);
+	config->op_config_string = g_strdup(xfg->op_config_string);
+	config->op_name = g_strdup(xfg->op_name);
+	config->ep_name = g_strdup(xfg->ep_name);
+
+	/* tell the engine that the config has changed */
+	xfade_realize_config();
+
+	/* unlock buffer */
+	MUTEX_UNLOCK(&buffer_mutex);
+
+	/* save configuration */
+	xfade_save_config();
+
+	/* show/hide monitor win depending on config->enable_monitor */
+	xfade_check_monitor_win();
+}
+
+void
+on_config_ok_clicked(GtkButton *button, gpointer user_data)
+{
+	/* apply and save config */
+	on_config_apply_clicked(button, user_data);
+
+	/* close and destroy window */
+	gtk_widget_destroy(config_win);
+}
+
+void
+xfade_configure()
+{
+	GtkWidget *widget;
+
+	if (!config_win)
+	{
+		/* create */
+		if (!(config_win = create_config_win()))
+		{
+			DEBUG(("[crossfade] plugin_configure: error creating window!\n"));
+			return;
+		}
+
+		/* update config_win when window is destroyed */
+		gtk_signal_connect(GTK_OBJECT(config_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &config_win);
+
+		/* free any strings that might be left in our local copy of the config */
+		if (xfg->oss_alt_audio_device) g_free(xfg->oss_alt_audio_device);
+		if (xfg->oss_alt_mixer_device) g_free(xfg->oss_alt_mixer_device);
+		if (xfg->op_config_string)     g_free(xfg->op_config_string);
+		if (xfg->op_name)              g_free(xfg->op_name);
+		if (xfg->ep_name)              g_free(xfg->ep_name);
+
+		/* copy current settings (dupping the strings) */
+		*xfg = *config;
+		xfg->oss_alt_audio_device = g_strdup(config->oss_alt_audio_device);
+		xfg->oss_alt_mixer_device = g_strdup(config->oss_alt_mixer_device);
+		xfg->op_config_string     = g_strdup(config->op_config_string);
+		xfg->op_name              = g_strdup(config->op_name);
+		xfg->ep_name              = g_strdup(config->ep_name);
+
+		/* go to remembered notebook page */
+		if ((widget = lookup_widget(config_win, "config_notebook")))
+			gtk_notebook_set_page(GTK_NOTEBOOK(widget), config->page);
+
+		/* output: method */
+#ifdef HAVE_OSS
+		SET_SENSITIVE("output_oss_radio", TRUE);
+#else
+		SET_SENSITIVE("output_oss_radio", FALSE);
+#endif
+
+		switch (xfg->output_method)
+		{
+			case OUTPUT_METHOD_BUILTIN_OSS:
+				widget = lookup_widget(config_win, "output_oss_radio");
+				break;
+				
+			case OUTPUT_METHOD_PLUGIN:
+				widget = lookup_widget(config_win, "output_plugin_radio");
+				break;
+				
+			case OUTPUT_METHOD_BUILTIN_NULL:
+				widget = lookup_widget(config_win, "output_none_radio");
+				break;
+				
+			default:
+				widget = NULL;
+		}
+		if (widget)
+			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
+
+		if ((widget = lookup_widget(config_win, "output_notebook")))
+			gtk_notebook_set_page(GTK_NOTEBOOK(widget), xfg->output_method);
+
+		/* output: resampling rate */
+		if ((widget = lookup_widget(config_win, "resampling_rate_optionmenu")))
+		{
+			GtkWidget *menu = gtk_menu_new();
+			GtkWidget *item;
+			gint index, *rate;
+			char label[16];
+
+			for (rate = &sample_rates[0]; *rate; rate++)
+			{
+				g_snprintf(label, sizeof(label), "%d Hz", *rate);
+				item = gtk_menu_item_new_with_label(label);
+				gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)resampling_rate_cb, (gpointer)*rate);
+				gtk_widget_show(item);
+				gtk_menu_append(GTK_MENU(menu), item);
+			}
+			gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
+
+			/* find list index for xfg->output_rate */
+			for (rate = &sample_rates[0], index = 0; *rate && *rate != xfg->output_rate; rate++, index++);
+			
+			/* if the specified rate is not in the list of available rates, select default rate */
+			if (!*rate)
+			{
+				DEBUG(("[crossfade] plugin_configure: WARNING: invalid output sample rate (%d)!\n", xfg->output_rate));
+				DEBUG(("[crossfade] plugin_configure:          ... using default of 44100\n"));
+				for (rate = &sample_rates[0], index = 0; *rate && *rate != 44100; rate++, index++);
+			}
+			
+			/* finally, set the list selection */
+			gtk_option_menu_set_history(GTK_OPTION_MENU(widget), index);
+		}
+
+		/* output: resampling quality (libsamplerate setting) */
+#ifdef HAVE_LIBSAMPLERATE
+		if ((widget = lookup_widget(config_win, "resampling_quality_optionmenu")))
+		{
+			GtkWidget *menu = gtk_menu_new();
+			GtkWidget *item;
+
+			GtkTooltips *tooltips = (GtkTooltips *) gtk_object_get_data(GTK_OBJECT(config_win), "tooltips");
+
+			int converter_type;
+			const char *name, *description;
+			for (converter_type = 0; (name = src_get_name(converter_type)); converter_type++)
+			{
+				description = src_get_description(converter_type);
+
+				item = gtk_menu_item_new_with_label(name);
+				gtk_tooltips_set_tip(tooltips, item, description, NULL);
+
+				gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc)resampling_quality_cb, (gpointer) converter_type);
+				gtk_widget_show(item);
+				gtk_menu_append(GTK_MENU(menu), item);
+			}
+
+			gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu);
+			gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->output_quality);
+		}
+#else
+		HIDE("resampling_quality_hbox");
+		HIDE("resampling_quality_optionmenu");
+#endif
+
+		/* output method: builtin OSS */
+		if ((widget = lookup_widget(config_win, "output_oss_notebook")))
+			gtk_notebook_set_page(GTK_NOTEBOOK(widget), xfg->oss_page);
+
+		if ((widget = lookup_widget(config_win, "oss_adevice_optionmenu")))
+		{
+			scan_devices("Audio devices:", widget, config_adevice_cb);
+			gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->oss_audio_device);
+			gtk_widget_set_sensitive(widget, !xfg->oss_use_alt_audio_device);
+		}
+		SET_TOGGLE("oss_adevice_alt_check", xfg->oss_use_alt_audio_device);
+		if ((widget = lookup_widget(config_win, "oss_adevice_alt_entry")))
+		{
+			gtk_entry_set_text(GTK_ENTRY(widget), xfg->oss_alt_audio_device ? xfg->oss_alt_audio_device : DEFAULT_OSS_ALT_AUDIO_DEVICE);
+			gtk_widget_set_sensitive(widget, xfg->oss_use_alt_audio_device);
+		}
+
+		if ((widget = lookup_widget(config_win, "oss_mdevice_optionmenu")))
+		{
+			scan_devices("Mixers:", widget, config_mdevice_cb);
+			gtk_option_menu_set_history(GTK_OPTION_MENU(widget), xfg->oss_mixer_device);
+			gtk_widget_set_sensitive(widget, !xfg->oss_use_alt_mixer_device);
+		}
+		SET_TOGGLE("oss_mdevice_alt_check", xfg->oss_use_alt_mixer_device);
+		if ((widget = lookup_widget(config_win, "oss_mdevice_alt_entry")))
+		{
+			gtk_entry_set_text(GTK_ENTRY(widget), xfg->oss_alt_mixer_device ? xfg->oss_alt_mixer_device : DEFAULT_OSS_ALT_MIXER_DEVICE);
+			gtk_widget_set_sensitive(widget, xfg->oss_use_alt_mixer_device);
+		}
+
+		SET_SPIN("ossbuf_buffer_spin",    xfg->oss_buffer_size_ms);
+		SET_SPIN("ossbuf_preload_spin",   xfg->oss_preload_size_ms);
+
+		SET_SPIN("osshwb_fragments_spin", xfg->oss_fragments);
+		SET_SPIN("osshwb_fragsize_spin",  xfg->oss_fragment_size);
+		SET_TOGGLE("osshwb_maxbuf_check", xfg->oss_maxbuf_enable);
+
+		SET_TOGGLE("ossmixer_pcm_check",  xfg->oss_mixer_use_master);
+
+		check_oss_dependencies();
+
+		/* output method: plugin */
+		xfade_load_plugin_config(xfg->op_config_string, xfg->op_name, &op_config);
+		SET_TOGGLE   ("op_throttle_check",    op_config.throttle_enable);
+		SET_TOGGLE   ("op_maxblock_check",    op_config.max_write_enable);
+		SET_SPIN     ("op_maxblock_spin",     op_config.max_write_len);
+		SET_SENSITIVE("op_maxblock_spin",     op_config.max_write_enable);
+		SET_TOGGLE   ("op_forcereopen_check", op_config.force_reopen);
+
+		if ((widget = lookup_widget(config_win, "op_plugin_optionmenu")))
+		{
+			OutputPlugin *op = NULL;
+			if ((op_index = scan_plugins(widget, xfg->op_name)) >= 0)
+			{
+				gtk_option_menu_set_history(GTK_OPTION_MENU(widget), op_index);
+				op = g_list_nth_data(xfplayer_get_output_list(), op_index);
+			}
+			SET_SENSITIVE("op_configure_button", op && (op->configure != NULL));
+			SET_SENSITIVE("op_about_button",     op && (op->about     != NULL));
+		}
+
+		/* output method: none */
+
+		/* effects: pre-mixing effect plugin */
+		if (!xfplayer_get_effect_list())
+			HIDE("config_effects_page")
+		else if ((widget = lookup_widget(config_win, "ep_plugin_optionmenu")))
+		{
+			EffectPlugin *ep = NULL;
+			if ((ep_index = scan_effect_plugins(widget, xfg->ep_name)) >= 0)
+			{
+				gtk_option_menu_set_history(GTK_OPTION_MENU(widget), ep_index);
+				ep = g_list_nth_data(xfplayer_get_effect_list(), ep_index);
+			}
+			SET_SENSITIVE("ep_configure_button",  ep && (ep->configure != NULL));
+			SET_SENSITIVE("ep_about_button",      ep && (ep->about     != NULL));
+			SET_TOGGLE   ("ep_enable_check",      xfg->ep_enable);
+			SET_SENSITIVE("ep_enable_check",      ep_index != -1);
+			SET_SENSITIVE("ep_plugin_optionmenu", ep_index != -1);
+		}
+
+		/* effects: volume normalizer */
+		SET_TOGGLE("volnorm_enable_check",     xfg->volnorm_enable);
+		SET_TOGGLE("volnorm_quantaudio_check", xfg->volnorm_use_qa);
+		SET_SPIN  ("volnorm_target_spin",      xfg->volnorm_target);
+
+		check_effects_dependencies();
+
+		/* crossfader */
+		create_crossfader_config_menu();
+
+		if ((xfg->xf_index < 0) || (xfg->xf_index >= MAX_FADE_CONFIGS))
+		{
+			DEBUG(("[crossfade] plugin_configure: crossfade index out of range (%d)!\n", xfg->xf_index));
+			xfg->xf_index = CLAMP(xfg->xf_index, 0, MAX_FADE_CONFIGS);
+		}
+
+		check_crossfader_dependencies(ANY);
+
+		/* gap killer */
+		SET_TOGGLE("lgap_enable_check",   xfg->gap_lead_enable);
+		SET_SPIN  ("lgap_length_spin",    xfg->gap_lead_len_ms);
+		SET_SPIN  ("lgap_level_spin",     xfg->gap_lead_level);
+		SET_TOGGLE("tgap_lock_check",     xfg->gap_trail_locked);
+		SET_TOGGLE("tgap_enable_check",   xfg->gap_trail_enable);
+		SET_SPIN  ("tgap_length_spin",    xfg->gap_trail_len_ms);
+		SET_SPIN  ("tgap_level_spin",     xfg->gap_trail_level);
+		SET_TOGGLE("gadv_crossing_check", xfg->gap_crossing);
+
+		check_gapkiller_dependencies();
+
+		/* misc */
+		SET_TOGGLE("debug_stderr_check",          xfg->enable_debug);
+		SET_TOGGLE("debug_monitor_check",         xfg->enable_monitor);
+		SET_TOGGLE("mixopt_enable_check",         xfg->enable_mixer);
+		SET_TOGGLE("mixopt_reverse_check",        xfg->mixer_reverse);
+		SET_TOGGLE("mixopt_software_check",       xfg->mixer_software);
+		SET_SPIN  ("moth_songchange_spin",        xfg->songchange_timeout);
+		SET_SPIN  ("moth_preload_spin",           xfg->preload_size_ms);
+		SET_TOGGLE("noxf_album_check",            xfg->album_detection);
+		SET_TOGGLE("noxf_samefile_check",         xfg->album_detection);
+		SET_TOGGLE("moth_httpworkaround_check",   xfg->enable_http_workaround);
+		SET_TOGGLE("moth_opmaxused_check",        xfg->enable_op_max_used);
+		SET_SPIN  ("moth_opmaxused_spin",         xfg->op_max_used_ms);
+		SET_TOGGLE("moth_outputkeepopened_check", xfg->output_keep_opened);
+
+		check_misc_dependencies();
+
+		/* presets */
+		if ((set_wgt = lookup_widget(config_win, "presets_list_list")))
+		{
+			GList *item;
+
+			for (item = config->presets; item; item = g_list_next(item))
+			{
+				gchar *name = (gchar *) item->data;
+				gchar *text[] = { name, "Default", "No" };
+				gtk_clist_append(GTK_CLIST(set_wgt), text);
+			}
+		}
+
+		/* show window near mouse pointer */
+		gtk_window_set_position(GTK_WINDOW(config_win), GTK_WIN_POS_MOUSE);
+		gtk_widget_show(config_win);
+	}
+	else
+		/* bring window to front */
+		gdk_window_raise(config_win->window);
+}
+
+void
+xfade_about()
+{
+	if (!about_win)
+	{
+		gchar *about_text =
+			"Audacious Crossfade Plugin\n"
+			"Copyright (C) 2000-2007  Peter Eisenlohr <peter@eisenlohr.org>\n"
+			"\n"
+			"based on the original OSS Output Plugin  Copyright (C) 1998-2000\n"
+			"Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies\n"
+			"\n"
+			"This program is free software; you can redistribute it and/or modify\n"
+			"it under the terms of the GNU General Public License as published by\n"
+			"the Free Software Foundation; either version 2 of the License, or\n"
+			"(at your option) any later version.\n"
+			"\n"
+			"This program is distributed in the hope that it will be useful,\n"
+			"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+			"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+			"GNU General Public License for more details.\n"
+			"\n"
+			"You should have received a copy of the GNU General Public License\n"
+			"along with this program; if not, write to the Free Software\n"
+			"Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,\n" "USA.";
+
+		about_win = create_about_win();
+
+		/* update about_win when window is destroyed */
+		gtk_signal_connect(GTK_OBJECT(about_win), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &about_win);
+
+		/* set about box text (this is done here and not in interface.c because
+		   of the VERSION #define -- there is no way to do this with GLADE */
+		if ((set_wgt = lookup_widget(about_win, "about_label")))
+			gtk_label_set_text(GTK_LABEL(set_wgt), about_text);
+
+		/* show near mouse pointer */
+		gtk_window_set_position(GTK_WINDOW(about_win), GTK_WIN_POS_MOUSE);
+		gtk_widget_show(about_win);
+	}
+	else
+		gdk_window_raise(about_win->window);
+}