changeset 2631:887b2c5a1fbb

Branch merge
author Ralf Ertzinger <ralf@skytale.net>
date Thu, 22 May 2008 19:32:39 +0200
parents 76924684ee4f (current diff) 2ce9e17525dc (diff)
children 55bc7318ff40
files src/alsa/init.c src/neon/neon.c src/scrobbler/md5.c src/scrobbler/md5.h src/sid/xs_md5.c src/sid/xs_md5.h
diffstat 184 files changed, 22474 insertions(+), 1180 deletions(-) [+]
line wrap: on
line diff
--- a/po/Makevars	Thu May 22 19:31:48 2008 +0200
+++ b/po/Makevars	Thu May 22 19:32:39 2008 +0200
@@ -16,7 +16,7 @@
 # message catalogs shall be used.  It is usually empty.
 EXTRA_LOCALE_CATEGORIES =
 
-MSGID_BUGS_ADDRESS = http://bugs.audacious-media-player.org
+MSGID_BUGS_ADDRESS = http://bugzilla.atheme.org/
 
 build: all
 
--- a/po/update-potfiles.sh	Thu May 22 19:31:48 2008 +0200
+++ b/po/update-potfiles.sh	Thu May 22 19:32:39 2008 +0200
@@ -2,4 +2,4 @@
 rm POTFILES*
 echo "# Please don't update this file manually - use ./update-potfiles.sh instead!" > POTFILES.in
 cd ..
-find src/ \( -name "*.c*" -o -name "*.glade" \) -exec grep -lE "translatable|_\(" \{\} \; | sort | uniq >> po/POTFILES.in
+find src/ \( -name "*.c" -o -name "*.cxx" -o -name "*.cc" -o -name "*.glade" \) -exec grep -lE "translatable|_\(" \{\} \; | sort | uniq >> po/POTFILES.in
--- a/src/CoreAudio/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/CoreAudio/about.c	Thu May 22 19:32:39 2008 +0200
@@ -16,7 +16,6 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 #include "coreaudio.h"
-#include <audacious/util.h>
 
 void osx_about(void)
 {
--- a/src/CoreAudio/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/CoreAudio/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -18,7 +18,6 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 #include "coreaudio.h"
-#include <audacious/util.h>
 #include <errno.h>
 #include <CoreAudio/CoreAudio.h>
 
--- a/src/OSS/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/OSS/about.c	Thu May 22 19:32:39 2008 +0200
@@ -25,7 +25,6 @@
 #include <audacious/i18n.h>
 #include <gtk/gtk.h>
 
-#include <audacious/util.h>
 
 
 void
--- a/src/OSS/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/OSS/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -22,7 +22,6 @@
 extern void close_mixer_device();
 
 #include <glib.h>
-#include <audacious/util.h>
 #include <string.h>
 
 #include <unistd.h>
--- a/src/OSS4/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/OSS4/about.c	Thu May 22 19:32:39 2008 +0200
@@ -28,7 +28,6 @@
 #include <audacious/i18n.h>
 #include <gtk/gtk.h>
 
-#include <audacious/util.h>
 
 
 void
--- a/src/OSS4/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/OSS4/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -22,7 +22,6 @@
 extern void close_mixer_device();
 
 #include <glib.h>
-#include <audacious/util.h>
 #include <string.h>
 
 #include <unistd.h>
--- a/src/aac/libmp4.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/aac/libmp4.c	Thu May 22 19:32:39 2008 +0200
@@ -8,9 +8,7 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
-#include <audacious/strings.h>
 
 #define MP4_VERSION VERSION
 #define SBR_DEC
--- a/src/aac/mp4_utils.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/aac/mp4_utils.c	Thu May 22 19:32:39 2008 +0200
@@ -10,7 +10,6 @@
 #include <string.h>
 #include <stdlib.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 
 const char *mp4AudioNames[]=
   {
--- a/src/adplug/adplug-xmms.cc	Thu May 22 19:31:48 2008 +0200
+++ b/src/adplug/adplug-xmms.cc	Thu May 22 19:32:39 2008 +0200
@@ -34,7 +34,6 @@
 {
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 }
 
 
--- a/src/alac/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/alac/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -47,7 +47,6 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 
 #include "demux.h"
 #include "decomp.h"
--- a/src/alsa/Makefile	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/Makefile	Thu May 22 19:32:39 2008 +0200
@@ -3,8 +3,7 @@
 SRCS = alsa.c		\
        about.c		\
        audio.c		\
-       configure.c	\
-       init.c
+       configure.c
 
 include ../../buildsys.mk
 include ../../extra.mk
--- a/src/alsa/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/about.c	Thu May 22 19:32:39 2008 +0200
@@ -17,7 +17,6 @@
  */
 
 #include "alsa.h"
-#include <audacious/util.h>
 
 void alsa_about(void)
 {
--- a/src/alsa/alsa.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/alsa.c	Thu May 22 19:32:39 2008 +0200
@@ -17,9 +17,64 @@
  */
 
 #include "alsa.h"
+#include <glib.h>
 #include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+
+struct alsa_config alsa_cfg;
+
+
+static void alsa_cleanup(void)
+{
+	if (alsa_cfg.pcm_device) {
+		free(alsa_cfg.pcm_device);
+		alsa_cfg.pcm_device = NULL;
+	}
+
+	if (alsa_cfg.mixer_device) {
+		free(alsa_cfg.mixer_device);
+		alsa_cfg.mixer_device = NULL;
+	}
+}
+
+
+static void alsa_init(void)
+{
+	mcs_handle_t *cfgfile;
 
-OutputPlugin alsa_op =
+	memset(&alsa_cfg, 0, sizeof (alsa_cfg));
+	
+	alsa_cfg.buffer_time = 500;
+	alsa_cfg.period_time = 100;
+	alsa_cfg.debug = 0;
+	alsa_cfg.vol.left = 100;
+	alsa_cfg.vol.right = 100;
+
+	cfgfile = aud_cfg_db_open();
+	if (!aud_cfg_db_get_string(cfgfile, ALSA_CFGID, "pcm_device",
+				  &alsa_cfg.pcm_device))
+		alsa_cfg.pcm_device = g_strdup("default");
+	g_message("device: %s", alsa_cfg.pcm_device);
+	if (!aud_cfg_db_get_string(cfgfile, ALSA_CFGID, "mixer_device",
+				  &alsa_cfg.mixer_device))
+		alsa_cfg.mixer_device = g_strdup("PCM");
+	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "mixer_card", &alsa_cfg.mixer_card);
+	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "buffer_time", &alsa_cfg.buffer_time);
+	aud_cfg_db_get_int(cfgfile, ALSA_CFGID, "period_time", &alsa_cfg.period_time);
+
+	aud_cfg_db_get_bool(cfgfile, ALSA_CFGID, "debug", &alsa_cfg.debug);
+	aud_cfg_db_close(cfgfile);
+
+	if (dlopen("libasound.so.2", RTLD_NOW | RTLD_GLOBAL) == NULL)
+	{
+		g_message("Cannot load alsa library: %s", dlerror());
+		/* FIXME, this plugin wont work... */
+	}
+}
+
+
+static OutputPlugin alsa_op =
 {
 	.description = "ALSA Output Plugin",
 	.init = alsa_init,
@@ -43,16 +98,3 @@
 OutputPlugin *alsa_oplist[] = { &alsa_op, NULL };
 
 DECLARE_PLUGIN(alsa, NULL, NULL, NULL, alsa_oplist, NULL, NULL, NULL, NULL)
-
-void alsa_cleanup(void)
-{
-	if (alsa_cfg.pcm_device) {
-		free(alsa_cfg.pcm_device);
-		alsa_cfg.pcm_device = NULL;
-	}
-
-	if (alsa_cfg.mixer_device) {
-		free(alsa_cfg.mixer_device);
-		alsa_cfg.mixer_device = NULL;
-	}
-}
--- a/src/alsa/alsa.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/alsa.h	Thu May 22 19:32:39 2008 +0200
@@ -25,7 +25,6 @@
 
 #include "config.h"
 
-#include <audacious/util.h>
 #include <audacious/plugin.h>
 #include <audacious/i18n.h>
 
@@ -36,11 +35,7 @@
 
 #include <gtk/gtk.h>
 
-#ifdef WORDS_BIGENDIAN
-# define IS_BIG_ENDIAN TRUE
-#else
-# define IS_BIG_ENDIAN FALSE
-#endif
+#define ALSA_CFGID  "ALSA"
 
 extern OutputPlugin op;
 
@@ -60,8 +55,6 @@
 
 extern struct alsa_config alsa_cfg;
 
-void alsa_init(void);
-void alsa_cleanup(void);
 void alsa_about(void);
 void alsa_configure(void);
 int alsa_get_mixer(snd_mixer_t **mixer, int card);
--- a/src/alsa/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -68,8 +68,6 @@
 static int prebuffer_size;
 GStaticMutex alsa_mutex = G_STATIC_MUTEX_INIT;
 
-static guint mixer_timeout;
-
 struct snd_format {
 	unsigned int rate;
 	unsigned int channels;
@@ -463,20 +461,6 @@
 	return 0;
 }
 
-static int alsa_mixer_timeout(void *data)
-{
-	if (mixer)
-	{
-		snd_mixer_close(mixer);
-		mixer = NULL;
-		pcm_element = NULL;
-	}
-	mixer_timeout = 0;
-	mixer_start = TRUE;
-
-	return FALSE;
-}
-
 static void alsa_cleanup_mixer(void)
 {
 	pcm_element = NULL;
@@ -510,10 +494,6 @@
 					    &lr);
 	*l = ll;
 	*r = lr;
-
-	if (mixer_timeout)
-		gtk_timeout_remove(mixer_timeout);
-	mixer_timeout = gtk_timeout_add(5000, alsa_mixer_timeout, NULL);
 }
 
 
--- a/src/alsa/configure.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/alsa/configure.c	Thu May 22 19:32:39 2008 +0200
@@ -48,13 +48,13 @@
 {
 	mcs_handle_t *cfgfile = aud_cfg_db_open();
 
-	aud_cfg_db_set_int(cfgfile, "ALSA", "buffer_time", alsa_cfg.buffer_time);
-	aud_cfg_db_set_int(cfgfile, "ALSA", "period_time", alsa_cfg.period_time);
-	aud_cfg_db_set_string(cfgfile,"ALSA","pcm_device", alsa_cfg.pcm_device);
-	aud_cfg_db_set_int(cfgfile, "ALSA", "mixer_card", alsa_cfg.mixer_card);
-	aud_cfg_db_set_string(cfgfile,"ALSA","mixer_device", alsa_cfg.mixer_device);
-	aud_cfg_db_set_int(cfgfile, "ALSA", "volume_left", alsa_cfg.vol.left);
-	aud_cfg_db_set_int(cfgfile, "ALSA", "volume_right", alsa_cfg.vol.right);
+	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "buffer_time", alsa_cfg.buffer_time);
+	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "period_time", alsa_cfg.period_time);
+	aud_cfg_db_set_string(cfgfile,ALSA_CFGID,"pcm_device", alsa_cfg.pcm_device);
+	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "mixer_card", alsa_cfg.mixer_card);
+	aud_cfg_db_set_string(cfgfile,ALSA_CFGID,"mixer_device", alsa_cfg.mixer_device);
+	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "volume_left", alsa_cfg.vol.left);
+	aud_cfg_db_set_int(cfgfile, ALSA_CFGID, "volume_right", alsa_cfg.vol.right);
 	aud_cfg_db_close(cfgfile);
 }
 
--- a/src/alsa/init.c	Thu May 22 19:31:48 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*  XMMS - ALSA output plugin
- *  Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
- *  Copyright (C) 2003-2005 Haavard Kvaalen
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-#include <glib.h>
-#include "alsa.h"
-#include <dlfcn.h>
-#include <ctype.h>
-
-struct alsa_config alsa_cfg;
-
-void alsa_init(void)
-{
-	mcs_handle_t *cfgfile;
-
-	memset(&alsa_cfg, 0, sizeof (alsa_cfg));
-	alsa_cfg.buffer_time = 500;
-	alsa_cfg.period_time = 100;
-	alsa_cfg.debug = 0;
-	alsa_cfg.vol.left = 100;
-	alsa_cfg.vol.right = 100;
-
-	cfgfile = aud_cfg_db_open();
-	if (!aud_cfg_db_get_string(cfgfile, "ALSA", "pcm_device",
-				  &alsa_cfg.pcm_device))
-		alsa_cfg.pcm_device = g_strdup("default");
-	g_message("device: %s", alsa_cfg.pcm_device);
-	if (!aud_cfg_db_get_string(cfgfile, "ALSA", "mixer_device",
-				  &alsa_cfg.mixer_device))
-		alsa_cfg.mixer_device = g_strdup("PCM");
-	aud_cfg_db_get_int(cfgfile, "ALSA", "mixer_card", &alsa_cfg.mixer_card);
-	aud_cfg_db_get_int(cfgfile, "ALSA", "buffer_time", &alsa_cfg.buffer_time);
-	aud_cfg_db_get_int(cfgfile, "ALSA", "period_time", &alsa_cfg.period_time);
-
-	aud_cfg_db_get_bool(cfgfile, "ALSA", "debug", &alsa_cfg.debug);
-	aud_cfg_db_close(cfgfile);
-
-	if (dlopen("libasound.so.2", RTLD_NOW | RTLD_GLOBAL) == NULL)
-	{
-		g_message("Cannot load alsa library: %s", dlerror());
-		/* FIXME, this plugin wont work... */
-	}
-}
--- a/src/amidi-plug/i_configure.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/amidi-plug/i_configure.c	Thu May 22 19:32:39 2008 +0200
@@ -29,7 +29,6 @@
 #include "i_configure-dummy.h"
 #include "i_utils.h"
 #include <audacious/auddrct.h>
-#include <audacious/util.h>
 
 
 amidiplug_cfg_backend_t * amidiplug_cfg_backend;
--- a/src/aosd/aosd_trigger.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/aosd/aosd_trigger.c	Thu May 22 19:32:39 2008 +0200
@@ -25,7 +25,6 @@
 #include <glib.h>
 #include <audacious/i18n.h>
 #include <audacious/playlist.h>
-#include <audacious/strings.h>
 #include <audacious/hook.h>
 #include <audacious/auddrct.h>
 
--- a/src/arts/arts.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/arts/arts.c	Thu May 22 19:32:39 2008 +0200
@@ -10,7 +10,6 @@
  */
 
 #include "arts.h"
-#include <audacious/util.h>
 
 static void about(void)
 {
--- a/src/audiocompress/audacious-glue.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/audiocompress/audacious-glue.c	Thu May 22 19:32:39 2008 +0200
@@ -10,7 +10,6 @@
 #include <gtk/gtk.h>
 #include <audacious/i18n.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 
 #include "audiocompress_config.h"
 #include "compress.h"
@@ -37,6 +36,7 @@
 static void myPrefs(void);
 static int myModify(gpointer * data, gint length, AFormat fmt,
 		    gint srate, gint nch);
+static void myQueryFormat(AFormat * fmt, gint * rate, gint * nch);
 
 static int inited = 0;
 
@@ -48,6 +48,7 @@
 	.about = myAbout,
 	.configure = myPrefs,
 	.mod_samples = myModify,
+	.query_format = myQueryFormat,
 };
 
 EffectPlugin *audiocompress_eplist[] = { &xmms_plugin, NULL };
@@ -76,6 +77,14 @@
         inited = 0;
 }
 
+void myQueryFormat(AFormat * fmt, gint * rate, gint * nch)
+{
+	if ((*fmt != FMT_S16_NE) ||
+	    (*fmt != FMT_S16_LE && G_BYTE_ORDER == G_LITTLE_ENDIAN) ||
+	    (*fmt != FMT_S16_BE && G_BYTE_ORDER == G_BIG_ENDIAN))
+		*fmt = FMT_S16_NE;
+}
+
 int myModify(gpointer * data, gint length, AFormat fmt, gint srate, gint nch)
 {
 	if (fmt == FMT_S16_NE ||
--- a/src/blur_scope/blur_scope.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/blur_scope/blur_scope.c	Thu May 22 19:32:39 2008 +0200
@@ -26,7 +26,6 @@
 #include <gtk/gtk.h>
 #include <string.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include "blur_scope.h"
 #include "bscope_logo.xpm"
 
--- a/src/cdaudio-ng/cdaudio-ng.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/cdaudio-ng/cdaudio-ng.c	Thu May 22 19:32:39 2008 +0200
@@ -49,7 +49,6 @@
 #include <audacious/i18n.h>
 #include <audacious/output.h>
 #include <audacious/ui_plugin_menu.h>
-#include <audacious/util.h>
 
 #include "cdaudio-ng.h"
 #include "configure.h"
--- a/src/cdaudio-ng/cdaudio-ng.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/cdaudio-ng/cdaudio-ng.h	Thu May 22 19:32:39 2008 +0200
@@ -21,10 +21,10 @@
 #define CDAUDIO_NG_H
 
 
-#define DEF_STRING_LEN				256
-#define CDDA_DUMMYPATH				"cdda://"
-#define CDDA_DAE_FRAMES				8
-#define CDDA_DEFAULT_LIMIT_SPEED		1
+#define DEF_STRING_LEN			256
+#define CDDA_DUMMYPATH			"cdda://"
+#define CDDA_DAE_FRAMES			8
+#define CDDA_DEFAULT_LIMIT_SPEED	1
 #define CDDA_DEFAULT_CDDB_SERVER	"freedb.org"
 #define CDDA_DEFAULT_CDDB_PORT		8880
 #define CDDA_DEFAULT_PROXY_PORT		8080
@@ -46,7 +46,7 @@
 	lsn_t				endlsn;
 	lsn_t				currlsn;
 	lsn_t				seektime;	/* in miliseconds */
-	InputPlayback		*pplayback;
+	InputPlayback                   *pplayback;
 	GThread				*thread;
 
 } dae_params_t;
--- a/src/console/Audacious_Driver.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/console/Audacious_Driver.cxx	Thu May 22 19:32:39 2008 +0200
@@ -13,7 +13,6 @@
 #include <gtk/gtk.h>
 extern "C" {
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/output.h>
 }
 #include <string.h>
--- a/src/crystalizer/crystalizer.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/crystalizer/crystalizer.c	Thu May 22 19:32:39 2008 +0200
@@ -21,19 +21,20 @@
 
 #include <gtk/gtk.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 #include <audacious/plugin.h>
 
 static void init(void);
 static void configure(void);
 static int mod_samples(gpointer *d, gint length, AFormat afmt, gint srate, gint nch);
+static void query_format(AFormat * fmt, gint * rate, gint * nch);
 
 EffectPlugin crystalizer_ep =
 {
 	.description = "Crystalizer", /* Description */
 	.init = init,
 	.configure = configure,
-	.mod_samples = mod_samples
+	.mod_samples = mod_samples,
+	.query_format = query_format
 };
 
 static GtkWidget *conf_dialog = NULL;
@@ -138,6 +139,14 @@
 	gtk_widget_show(conf_dialog);
 }
 
+static void query_format(AFormat * fmt, gint * rate, gint * nch)
+{
+	if (!(*fmt == FMT_S16_NE ||
+	      (*fmt == FMT_S16_LE && G_BYTE_ORDER == G_LITTLE_ENDIAN) ||
+	      (*fmt == FMT_S16_BE && G_BYTE_ORDER == G_BIG_ENDIAN)))
+		*fmt = FMT_S16_NE;
+}
+
 static int mod_samples(gpointer *d, gint length, AFormat afmt, gint srate, gint nch)
 {
 	gint i;
--- a/src/cue/cuesheet.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/cue/cuesheet.c	Thu May 22 19:32:39 2008 +0200
@@ -25,8 +25,6 @@
 #include <ctype.h>
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 
 #define MAX_CUE_LINE_LENGTH 1000
 #define MAX_CUE_TRACKS 1000
--- a/src/demac/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/demac/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -45,7 +45,6 @@
 
 #include <audacious/plugin.h> 
 #include <audacious/output.h> 
-#include <audacious/util.h> 
 
 #include "ape.h"
 #include "apev2.h"
--- a/src/dockalbumart/dockalbumart.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/dockalbumart/dockalbumart.c	Thu May 22 19:32:39 2008 +0200
@@ -28,7 +28,6 @@
 #include <Carbon/Carbon.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 
 #include "audacious_player.xpm"
 
--- a/src/echo_plugin/echo.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/echo_plugin/echo.c	Thu May 22 19:32:39 2008 +0200
@@ -11,6 +11,7 @@
 static void init(void);
 static void cleanup(void);
 static int mod_samples(gpointer * d, gint length, AFormat afmt, gint srate, gint nch);
+static void query_format(AFormat * fmt, gint * rate, gint * nch);
 
 #define MAX_SRATE 50000
 #define MAX_CHANNELS 2
@@ -27,6 +28,7 @@
 	.about = echo_about,
 	.configure = echo_configure,
 	.mod_samples = mod_samples,
+	.query_format = query_format,
 };
 
 static gint16 *buffer = NULL;
@@ -59,6 +61,14 @@
 	buffer = NULL;	
 }
 
+static void query_format(AFormat * fmt, gint * rate, gint * nch)
+{
+	if (!(*fmt == FMT_S16_NE ||
+	      (*fmt == FMT_S16_LE && G_BYTE_ORDER == G_LITTLE_ENDIAN) ||
+	      (*fmt == FMT_S16_BE && G_BYTE_ORDER == G_BIG_ENDIAN)))
+		*fmt = FMT_S16_NE;
+}
+
 static int mod_samples(gpointer * d, gint length, AFormat afmt, gint srate, gint nch)
 {
 	gint i, in, out, buf, r_ofs, fb_div;
--- a/src/echo_plugin/gui.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/echo_plugin/gui.c	Thu May 22 19:32:39 2008 +0200
@@ -3,7 +3,6 @@
 #include <gtk/gtk.h>
 #include <audacious/plugin.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 #include "echo.h"
 
 static const char *echo_about_text =
--- a/src/esd/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/esd/about.c	Thu May 22 19:32:39 2008 +0200
@@ -20,7 +20,6 @@
 #include <audacious/i18n.h>
 #include <gtk/gtk.h>
 
-#include <audacious/util.h>
 
 
 void
--- a/src/esd/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/esd/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -25,7 +25,6 @@
 #include <esd.h>
 
 #include <unistd.h>
-#include <audacious/util.h>
 
 #include "esdout.h"
 
--- a/src/esd/mixer.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/esd/mixer.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,6 @@
 #include <string.h>
 #include <esd.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
--- a/src/evdev-plug/ed_internals.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/evdev-plug/ed_internals.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,6 @@
 #include "ed_bindings_store.h"
 #include "ed_common.h"
 
-#include <audacious/util.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <linux/input.h>
@@ -36,7 +35,7 @@
 #include <regex.h>
 /* for variadic */
 #include <stdarg.h>
-
+#include <audacious/plugin.h>
 #include <audacious/i18n.h>
 #include <glib.h>
 #include <glib/gstdio.h>
--- a/src/filewriter/filewriter.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/filewriter/filewriter.h	Thu May 22 19:32:39 2008 +0200
@@ -31,7 +31,6 @@
 
 #include <audacious/plugin.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 
 struct format_info { 
     AFormat format;
--- a/src/flacng/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/flacng/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -19,7 +19,6 @@
 /* #define FLACNG_DEBUG */
 
 #include "flacng.h"
-#include <audacious/util.h>
 #include <audacious/output.h>
 #include <audacious/i18n.h>
 #include "tools.h"
--- a/src/gnomeshortcuts/gnomeshortcuts.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/gnomeshortcuts/gnomeshortcuts.c	Thu May 22 19:32:39 2008 +0200
@@ -32,7 +32,6 @@
 #include <audacious/auddrct.h>
 
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 
 
 static void init (void);
--- a/src/hotkey/gui.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/hotkey/gui.c	Thu May 22 19:32:39 2008 +0200
@@ -39,8 +39,8 @@
 #include <gdk/gdkx.h>
 #include <gdk/gdkkeysyms.h>
 
+#include <audacious/plugin.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 
 #include "plugin.h"
 #include "gui.h"
@@ -621,7 +621,7 @@
 		HotkeyConfiguration * old;
 		old = hotkey;
 		hotkey = hotkey->next;
-		free(old);
+		g_free(old);
 	}
 	plugin_cfg->first.next = NULL;
 	plugin_cfg->first.key = 0;
@@ -633,8 +633,7 @@
 	{
 		if (controls->hotkey.key) {
 			if (hotkey->key) {
-				hotkey->next = (HotkeyConfiguration*)
-					malloc(sizeof (HotkeyConfiguration));
+				hotkey->next = g_new(HotkeyConfiguration, 1);
 				hotkey = hotkey->next;
 				hotkey->next = NULL;
 			}
--- a/src/jack/jack.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/jack/jack.c	Thu May 22 19:32:39 2008 +0200
@@ -7,7 +7,6 @@
  */
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <dlfcn.h>
 #include <gtk/gtk.h>
 #include <audacious/i18n.h>
--- a/src/m3u/m3u.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/m3u/m3u.c	Thu May 22 19:32:39 2008 +0200
@@ -34,8 +34,6 @@
 #include <sys/errno.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 
 static void
 parse_extm3u_info(const gchar * info, gchar ** title, gint * length)
--- a/src/madplug/input.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/madplug/input.c	Thu May 22 19:32:39 2008 +0200
@@ -55,7 +55,6 @@
 
 #include <fcntl.h>
 #include <errno.h>
-#include <audacious/util.h>
 
 #include "input.h"
 #include "replaygain.h"
--- a/src/madplug/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/madplug/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -28,7 +28,6 @@
 #include <math.h>
 
 #include <gtk/gtk.h>
-#include <audacious/util.h>
 #include <stdarg.h>
 #include <fcntl.h>
 #include <sys/stat.h>
--- a/src/madplug/plugin.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/madplug/plugin.h	Thu May 22 19:32:39 2008 +0200
@@ -37,8 +37,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 #include <audacious/id3tag.h>
 #include <mad.h>
--- a/src/madplug/tuple.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/madplug/tuple.c	Thu May 22 19:32:39 2008 +0200
@@ -31,7 +31,6 @@
 #include <glib.h> 
 #include <glib/gprintf.h>
 
-#include <audacious/util.h>
 #include <audacious/plugin.h>
 #include <audacious/id3tag.h>
 
--- a/src/metronom/metronom.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/metronom/metronom.c	Thu May 22 19:32:39 2008 +0200
@@ -20,7 +20,6 @@
 #include "config.h"
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 #include <glib.h>
 #include <string.h>
--- a/src/modplug/archive/arch_bz2.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/modplug/archive/arch_bz2.cxx	Thu May 22 19:32:39 2008 +0200
@@ -40,7 +40,7 @@
 		return;
 	}
 	
-	if(fscanf(f, "%u", &mSize) != 1); // this is the size.
+	if(fscanf(f, "%u", &mSize) != 1) // this is the size.
 	{
 		mSize = 0;
 		return;
--- a/src/modplug/gui/main.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/modplug/gui/main.cxx	Thu May 22 19:32:39 2008 +0200
@@ -17,8 +17,6 @@
 #include <gtk/gtk.h>
 #include <libintl.h>
 extern "C" {
-#include <audacious/util.h>
-#include <audacious/strings.h>
 }
 
 #include "interface.h"
--- a/src/modplug/modplugbmp.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/modplug/modplugbmp.cxx	Thu May 22 19:32:39 2008 +0200
@@ -16,7 +16,6 @@
 #include "archive/open.h"
 extern "C" {
 #include <audacious/output.h>
-#include <audacious/strings.h>
 }
 
 static char* format_and_free_ti( Tuple* ti, int* length )
--- a/src/mtp_up/mtp.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/mtp_up/mtp.c	Thu May 22 19:32:39 2008 +0200
@@ -26,7 +26,6 @@
 #include <audacious/i18n.h>
 
 #include <gtk/gtk.h>
-#include <audacious/util.h>
 #include "filetype.h"
 
 #define DEBUG 1
--- a/src/musepack/libmpc.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/musepack/libmpc.h	Thu May 22 19:32:39 2008 +0200
@@ -8,7 +8,6 @@
 {
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 }
 
--- a/src/neon/neon.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/neon/neon.c	Thu May 22 19:32:39 2008 +0200
@@ -862,7 +862,6 @@
  */
 
 VFSFile* neon_aud_vfs_fopen_impl(const gchar* path, const gchar* mode) {
-
     VFSFile* file;
     struct neon_handle* handle;
 
--- a/src/null/null.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/null/null.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,6 @@
 #include <gtk/gtk.h>
 #include <audacious/plugin.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 
 static GTimer *timer;
 static gulong offset_time, written;
--- a/src/paranormal-ng/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/paranormal-ng/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -38,7 +38,6 @@
 
 #include <gtk/gtk.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <SDL/SDL.h>
 #include <SDL/SDL_thread.h>
 
--- a/src/paranormal/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/paranormal/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -38,7 +38,6 @@
 
 #include <gtk/gtk.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <SDL.h>
 #include <SDL_thread.h>
 
--- a/src/pls/pls.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/pls/pls.c	Thu May 22 19:32:39 2008 +0200
@@ -33,9 +33,7 @@
 #include <sys/stat.h>
 #include <sys/errno.h>
 
-#include <audacious/util.h>
 #include <audacious/plugin.h>
-#include <audacious/strings.h>
 
 static void
 playlist_load_pls(const gchar * filename, gint pos)
--- a/src/projectm/main.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/projectm/main.c	Thu May 22 19:32:39 2008 +0200
@@ -35,7 +35,6 @@
 #include <string.h>
 #include <stdlib.h>
 #include <gtk/gtk.h>
-#include <audacious/util.h>
 #include <SDL/SDL.h>
 #include <SDL/SDL_thread.h>
 #include <GL/gl.h>
--- a/src/pulse_audio/pulse_audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/pulse_audio/pulse_audio.c	Thu May 22 19:32:39 2008 +0200
@@ -28,7 +28,6 @@
 
 #include <gtk/gtk.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 #include <pulse/pulseaudio.h>
--- a/src/rootvis/rootvis.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/rootvis/rootvis.h	Thu May 22 19:32:39 2008 +0200
@@ -4,7 +4,6 @@
 #include <X11/Xatom.h>
 #include <X11/Xproto.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 
 
 /* following values are used if there is no user configuration */
--- a/src/scrobbler/Makefile	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/Makefile	Thu May 22 19:32:39 2008 +0200
@@ -3,7 +3,6 @@
 SRCS = fmt.c			\
        configure.c		\
        gtkstuff.c		\
-       md5.c			\
        scrobbler.c		\
        gerpok.c			\
        plugin.c
@@ -20,4 +19,4 @@
 
 CFLAGS += ${PLUGIN_CFLAGS}
 CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS}  ${GTK_CFLAGS} ${GLIB_CFLAGS}  ${BEEP_DEFINES} ${CURL_CFLAGS} -I../..
-LIBS += ${GTK_LIBS} ${GLIB_LIBS}  ${CURL_LIBS} ${MUSICBRAINZ_LIBS} ${MOWGLI_LIBS}
+LIBS += ${GTK_LIBS} ${GLIB_LIBS}  ${CURL_LIBS} ${MUSICBRAINZ_LIBS} ${MOWGLI_LIBS} -laudutil
--- a/src/scrobbler/configure.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/configure.c	Thu May 22 19:32:39 2008 +0200
@@ -1,9 +1,6 @@
 #include "settings.h"
 
-#include <config.h>
-
-#include <audacious/util.h>
-#include <audacious/plugin.h>
+#include "config.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -11,9 +8,9 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "md5.h"
-
 #include <glib.h>
+#include <audacious/plugin.h>
+#include <audacious/audutil.h>
 #include <audacious/i18n.h>
 
 #include <gdk/gdkkeysyms.h>
@@ -53,16 +50,16 @@
     const char *ge_pwd = gtk_entry_get_text(GTK_ENTRY(ge_entry2));
 
     if ((cfgfile = aud_cfg_db_open())) {
-        md5_state_t md5state;
+        aud_md5state_t md5state;
         unsigned char md5pword[16], ge_md5pword[16];
 
         if (uid != NULL && uid[0] != '\0' && strlen(uid) &&
             pwd != NULL && pwd[0] != '\0' && strlen(pwd))
         {
             aud_cfg_db_set_string(cfgfile, "audioscrobbler", "username", (char *)uid);
-            md5_init(&md5state);
-            md5_append(&md5state, (unsigned const char *)pwd, strlen(pwd));
-            md5_finish(&md5state, md5pword);
+            aud_md5_init(&md5state);
+            aud_md5_append(&md5state, (unsigned const char *)pwd, strlen(pwd));
+            aud_md5_finish(&md5state, md5pword);
             aud_cfg_db_set_string(cfgfile, "audioscrobbler", "password",
                                  hexify((char*)md5pword, sizeof(md5pword)));
         } else if (!uid || uid[0] == '\0') {
@@ -74,9 +71,9 @@
             ge_pwd != NULL && ge_pwd[0] != '\0' && strlen(ge_pwd))
         {
             aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_username", (char *)ge_uid);
-            md5_init(&md5state);
-            md5_append(&md5state, (unsigned const char *)ge_pwd, strlen(ge_pwd));
-            md5_finish(&md5state, ge_md5pword);
+            aud_md5_init(&md5state);
+            aud_md5_append(&md5state, (unsigned const char *)ge_pwd, strlen(ge_pwd));
+            aud_md5_finish(&md5state, ge_md5pword);
             aud_cfg_db_set_string(cfgfile, "audioscrobbler", "ge_password",
                                   hexify((char*)ge_md5pword, sizeof(ge_md5pword)));
         } else if (!ge_uid || ge_uid[0] == '\0') {
--- a/src/scrobbler/gerpok.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/gerpok.c	Thu May 22 19:32:39 2008 +0200
@@ -8,13 +8,11 @@
 #include <curl/curl.h>
 #include <stdio.h>
 #include "fmt.h"
-#include "md5.h"
 #include "scrobbler.h"
 #include "config.h"
 #include <glib.h>
-
 #include <audacious/plugin.h>
-#include <audacious/util.h>
+#include <audacious/audutil.h>
 
 #define SCROBBLER_HS_URL "http://post.gerpok.com"
 #define SCROBBLER_CLI_ID "aud"
@@ -446,17 +444,17 @@
 	}
 
 	if (gerpok_sc_challenge_hash != NULL) {
-		md5_state_t md5state;
+		aud_md5state_t md5state;
 		unsigned char md5pword[16];
 		
-		md5_init(&md5state);
+		aud_md5_init(&md5state);
 		/*pdebug(fmt_vastr("Pass Hash: %s", gerpok_sc_password), DEBUG);*/
-		md5_append(&md5state, (unsigned const char *)gerpok_sc_password,
+		aud_md5_append(&md5state, (unsigned const char *)gerpok_sc_password,
 				strlen(gerpok_sc_password));
 		/*pdebug(fmt_vastr("Challenge Hash: %s", gerpok_sc_challenge_hash), DEBUG);*/
-		md5_append(&md5state, (unsigned const char *)gerpok_sc_challenge_hash,
+		aud_md5_append(&md5state, (unsigned const char *)gerpok_sc_challenge_hash,
 				strlen(gerpok_sc_challenge_hash));
-		md5_finish(&md5state, md5pword);
+		aud_md5_finish(&md5state, md5pword);
 		hexify((char*)md5pword, sizeof(md5pword));
 		/*pdebug(fmt_vastr("Response Hash: %s", gerpok_sc_response_hash), DEBUG);*/
 	}
--- a/src/scrobbler/gtkstuff.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/gtkstuff.c	Thu May 22 19:32:39 2008 +0200
@@ -1,4 +1,3 @@
-#include <audacious/util.h>
 
 #include <glib.h>
 #include <audacious/i18n.h>
@@ -8,7 +7,6 @@
 #include <string.h>
 #include "settings.h"
 #include "config.h"
-#include "md5.h"
 
 void about_show(void)
 {
--- a/src/scrobbler/md5.c	Thu May 22 19:31:48 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,381 +0,0 @@
-/*
-  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  L. Peter Deutsch
-  ghost@aladdin.com
-
- */
-/* $Id: md5.c,v 1.1 2003/08/24 01:20:37 audhov Exp $ */
-/*
-  Independent implementation of MD5 (RFC 1321).
-
-  This code implements the MD5 Algorithm defined in RFC 1321, whose
-  text is available at
-	http://www.ietf.org/rfc/rfc1321.txt
-  The code is derived from the text of the RFC, including the test suite
-  (section A.5) but excluding the rest of Appendix A.  It does not include
-  any code or documentation that is identified in the RFC as being
-  copyrighted.
-
-  The original and principal author of md5.c is L. Peter Deutsch
-  <ghost@aladdin.com>.  Other authors are noted in the change history
-  that follows (in reverse chronological order):
-
-  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
-	either statically or dynamically; added missing #include <string.h>
-	in library.
-  2002-03-11 lpd Corrected argument list for main(), and added int return
-	type, in test program and T value program.
-  2002-02-21 lpd Added missing #include <stdio.h> in test program.
-  2000-07-03 lpd Patched to eliminate warnings about "constant is
-	unsigned in ANSI C, signed in traditional"; made test program
-	self-checking.
-  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
-  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
-  1999-05-03 lpd Original version.
- */
-
-#include "md5.h"
-#include <string.h>
-
-#undef BYTE_ORDER	/* 1 = big-endian, -1 = little-endian, 0 = unknown */
-#ifdef ARCH_IS_BIG_ENDIAN
-#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
-#else
-#  define BYTE_ORDER 0
-#endif
-
-#define T_MASK ((md5_word_t)~0)
-#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
-#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
-#define T3    0x242070db
-#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
-#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
-#define T6    0x4787c62a
-#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
-#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
-#define T9    0x698098d8
-#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
-#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
-#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
-#define T13    0x6b901122
-#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
-#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
-#define T16    0x49b40821
-#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
-#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
-#define T19    0x265e5a51
-#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
-#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
-#define T22    0x02441453
-#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
-#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
-#define T25    0x21e1cde6
-#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
-#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
-#define T28    0x455a14ed
-#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
-#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
-#define T31    0x676f02d9
-#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
-#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
-#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
-#define T35    0x6d9d6122
-#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
-#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
-#define T38    0x4bdecfa9
-#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
-#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
-#define T41    0x289b7ec6
-#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
-#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
-#define T44    0x04881d05
-#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
-#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
-#define T47    0x1fa27cf8
-#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
-#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
-#define T50    0x432aff97
-#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
-#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
-#define T53    0x655b59c3
-#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
-#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
-#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
-#define T57    0x6fa87e4f
-#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
-#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
-#define T60    0x4e0811a1
-#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
-#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
-#define T63    0x2ad7d2bb
-#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
-
-
-static void
-md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
-{
-    md5_word_t
-	a = pms->abcd[0], b = pms->abcd[1],
-	c = pms->abcd[2], d = pms->abcd[3];
-    md5_word_t t;
-#if BYTE_ORDER > 0
-    /* Define storage only for big-endian CPUs. */
-    md5_word_t X[16];
-#else
-    /* Define storage for little-endian or both types of CPUs. */
-    md5_word_t xbuf[16];
-    const md5_word_t *X;
-#endif
-
-    {
-#if BYTE_ORDER == 0
-	/*
-	 * Determine dynamically whether this is a big-endian or
-	 * little-endian machine, since we can use a more efficient
-	 * algorithm on the latter.
-	 */
-	static const int w = 1;
-
-	if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
-#endif
-#if BYTE_ORDER <= 0		/* little-endian */
-	{
-	    /*
-	     * On little-endian machines, we can process properly aligned
-	     * data without copying it.
-	     */
-	    if (!((data - (const md5_byte_t *)0) & 3)) {
-		/* data are properly aligned */
-		X = (const md5_word_t *)data;
-	    } else {
-		/* not aligned */
-		memcpy(xbuf, data, 64);
-		X = xbuf;
-	    }
-	}
-#endif
-#if BYTE_ORDER == 0
-	else			/* dynamic big-endian */
-#endif
-#if BYTE_ORDER >= 0		/* big-endian */
-	{
-	    /*
-	     * On big-endian machines, we must arrange the bytes in the
-	     * right order.
-	     */
-	    const md5_byte_t *xp = data;
-	    int i;
-
-#  if BYTE_ORDER == 0
-	    X = xbuf;		/* (dynamic only) */
-#  else
-#    define xbuf X		/* (static only) */
-#  endif
-	    for (i = 0; i < 16; ++i, xp += 4)
-		xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
-	}
-#endif
-    }
-
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
-
-    /* Round 1. */
-    /* Let [abcd k s i] denote the operation
-       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
-#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + F(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-    /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  7,  T1);
-    SET(d, a, b, c,  1, 12,  T2);
-    SET(c, d, a, b,  2, 17,  T3);
-    SET(b, c, d, a,  3, 22,  T4);
-    SET(a, b, c, d,  4,  7,  T5);
-    SET(d, a, b, c,  5, 12,  T6);
-    SET(c, d, a, b,  6, 17,  T7);
-    SET(b, c, d, a,  7, 22,  T8);
-    SET(a, b, c, d,  8,  7,  T9);
-    SET(d, a, b, c,  9, 12, T10);
-    SET(c, d, a, b, 10, 17, T11);
-    SET(b, c, d, a, 11, 22, T12);
-    SET(a, b, c, d, 12,  7, T13);
-    SET(d, a, b, c, 13, 12, T14);
-    SET(c, d, a, b, 14, 17, T15);
-    SET(b, c, d, a, 15, 22, T16);
-#undef SET
-
-     /* Round 2. */
-     /* Let [abcd k s i] denote the operation
-          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
-#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + G(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  1,  5, T17);
-    SET(d, a, b, c,  6,  9, T18);
-    SET(c, d, a, b, 11, 14, T19);
-    SET(b, c, d, a,  0, 20, T20);
-    SET(a, b, c, d,  5,  5, T21);
-    SET(d, a, b, c, 10,  9, T22);
-    SET(c, d, a, b, 15, 14, T23);
-    SET(b, c, d, a,  4, 20, T24);
-    SET(a, b, c, d,  9,  5, T25);
-    SET(d, a, b, c, 14,  9, T26);
-    SET(c, d, a, b,  3, 14, T27);
-    SET(b, c, d, a,  8, 20, T28);
-    SET(a, b, c, d, 13,  5, T29);
-    SET(d, a, b, c,  2,  9, T30);
-    SET(c, d, a, b,  7, 14, T31);
-    SET(b, c, d, a, 12, 20, T32);
-#undef SET
-
-     /* Round 3. */
-     /* Let [abcd k s t] denote the operation
-          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + H(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  5,  4, T33);
-    SET(d, a, b, c,  8, 11, T34);
-    SET(c, d, a, b, 11, 16, T35);
-    SET(b, c, d, a, 14, 23, T36);
-    SET(a, b, c, d,  1,  4, T37);
-    SET(d, a, b, c,  4, 11, T38);
-    SET(c, d, a, b,  7, 16, T39);
-    SET(b, c, d, a, 10, 23, T40);
-    SET(a, b, c, d, 13,  4, T41);
-    SET(d, a, b, c,  0, 11, T42);
-    SET(c, d, a, b,  3, 16, T43);
-    SET(b, c, d, a,  6, 23, T44);
-    SET(a, b, c, d,  9,  4, T45);
-    SET(d, a, b, c, 12, 11, T46);
-    SET(c, d, a, b, 15, 16, T47);
-    SET(b, c, d, a,  2, 23, T48);
-#undef SET
-
-     /* Round 4. */
-     /* Let [abcd k s t] denote the operation
-          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-#define SET(a, b, c, d, k, s, Ti)\
-  t = a + I(b,c,d) + X[k] + Ti;\
-  a = ROTATE_LEFT(t, s) + b
-     /* Do the following 16 operations. */
-    SET(a, b, c, d,  0,  6, T49);
-    SET(d, a, b, c,  7, 10, T50);
-    SET(c, d, a, b, 14, 15, T51);
-    SET(b, c, d, a,  5, 21, T52);
-    SET(a, b, c, d, 12,  6, T53);
-    SET(d, a, b, c,  3, 10, T54);
-    SET(c, d, a, b, 10, 15, T55);
-    SET(b, c, d, a,  1, 21, T56);
-    SET(a, b, c, d,  8,  6, T57);
-    SET(d, a, b, c, 15, 10, T58);
-    SET(c, d, a, b,  6, 15, T59);
-    SET(b, c, d, a, 13, 21, T60);
-    SET(a, b, c, d,  4,  6, T61);
-    SET(d, a, b, c, 11, 10, T62);
-    SET(c, d, a, b,  2, 15, T63);
-    SET(b, c, d, a,  9, 21, T64);
-#undef SET
-
-     /* Then perform the following additions. (That is increment each
-        of the four registers by the value it had before this block
-        was started.) */
-    pms->abcd[0] += a;
-    pms->abcd[1] += b;
-    pms->abcd[2] += c;
-    pms->abcd[3] += d;
-}
-
-void
-md5_init(md5_state_t *pms)
-{
-    pms->count[0] = pms->count[1] = 0;
-    pms->abcd[0] = 0x67452301;
-    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
-    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
-    pms->abcd[3] = 0x10325476;
-}
-
-void
-md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
-{
-    const md5_byte_t *p = data;
-    int left = nbytes;
-    int offset = (pms->count[0] >> 3) & 63;
-    md5_word_t nbits = (md5_word_t)(nbytes << 3);
-
-    if (nbytes <= 0)
-	return;
-
-    /* Update the message length. */
-    pms->count[1] += nbytes >> 29;
-    pms->count[0] += nbits;
-    if (pms->count[0] < nbits)
-	pms->count[1]++;
-
-    /* Process an initial partial block. */
-    if (offset) {
-	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
-
-	memcpy(pms->buf + offset, p, copy);
-	if (offset + copy < 64)
-	    return;
-	p += copy;
-	left -= copy;
-	md5_process(pms, pms->buf);
-    }
-
-    /* Process full blocks. */
-    for (; left >= 64; p += 64, left -= 64)
-	md5_process(pms, p);
-
-    /* Process a final partial block. */
-    if (left)
-	memcpy(pms->buf, p, left);
-}
-
-void
-md5_finish(md5_state_t *pms, md5_byte_t digest[16])
-{
-    static const md5_byte_t pad[64] = {
-	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
-    };
-    md5_byte_t data[8];
-    int i;
-
-    /* Save the length before padding. */
-    for (i = 0; i < 8; ++i)
-	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
-    /* Pad to 56 bytes mod 64. */
-    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
-    /* Append the length. */
-    md5_append(pms, data, 8);
-    for (i = 0; i < 16; ++i)
-	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
-}
--- a/src/scrobbler/md5.h	Thu May 22 19:31:48 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
-  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
-
-  This software is provided 'as-is', without any express or implied
-  warranty.  In no event will the authors be held liable for any damages
-  arising from the use of this software.
-
-  Permission is granted to anyone to use this software for any purpose,
-  including commercial applications, and to alter it and redistribute it
-  freely, subject to the following restrictions:
-
-  1. The origin of this software must not be misrepresented; you must not
-     claim that you wrote the original software. If you use this software
-     in a product, an acknowledgment in the product documentation would be
-     appreciated but is not required.
-  2. Altered source versions must be plainly marked as such, and must not be
-     misrepresented as being the original software.
-  3. This notice may not be removed or altered from any source distribution.
-
-  L. Peter Deutsch
-  ghost@aladdin.com
-
- */
-/* $Id: md5.h,v 1.1 2003/08/24 01:20:37 audhov Exp $ */
-/*
-  Independent implementation of MD5 (RFC 1321).
-
-  This code implements the MD5 Algorithm defined in RFC 1321, whose
-  text is available at
-	http://www.ietf.org/rfc/rfc1321.txt
-  The code is derived from the text of the RFC, including the test suite
-  (section A.5) but excluding the rest of Appendix A.  It does not include
-  any code or documentation that is identified in the RFC as being
-  copyrighted.
-
-  The original and principal author of md5.h is L. Peter Deutsch
-  <ghost@aladdin.com>.  Other authors are noted in the change history
-  that follows (in reverse chronological order):
-
-  2002-04-13 lpd Removed support for non-ANSI compilers; removed
-	references to Ghostscript; clarified derivation from RFC 1321;
-	now handles byte order either statically or dynamically.
-  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
-  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
-	added conditionalization for C++ compilation from Martin
-	Purschke <purschke@bnl.gov>.
-  1999-05-03 lpd Original version.
- */
-
-#ifndef md5_INCLUDED
-#  define md5_INCLUDED
-
-/*
- * This package supports both compile-time and run-time determination of CPU
- * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
- * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
- * defined as non-zero, the code will be compiled to run only on big-endian
- * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
- * run on either big- or little-endian CPUs, but will run slightly less
- * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
- */
-
-typedef unsigned char md5_byte_t; /* 8-bit byte */
-typedef unsigned int md5_word_t; /* 32-bit word */
-
-/* Define the state of the MD5 Algorithm. */
-typedef struct md5_state_s {
-    md5_word_t count[2];	/* message length in bits, lsw first */
-    md5_word_t abcd[4];		/* digest buffer */
-    md5_byte_t buf[64];		/* accumulate block */
-} md5_state_t;
-
-#ifdef __cplusplus
-extern "C" 
-{
-#endif
-
-/* Initialize the algorithm. */
-void md5_init(md5_state_t *pms);
-
-/* Append a string to the message. */
-void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
-
-/* Finish the message and return the digest. */
-void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
-
-#ifdef __cplusplus
-}  /* end extern "C" */
-#endif
-
-#endif /* md5_INCLUDED */
--- a/src/scrobbler/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -10,7 +10,6 @@
 #include <audacious/plugin.h>
 #include <audacious/ui_preferences.h>
 #include <audacious/hook.h>
-#include <audacious/strings.h>
 
 #include <unistd.h>
 #include <stdio.h>
--- a/src/scrobbler/scrobbler.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/scrobbler/scrobbler.c	Thu May 22 19:32:39 2008 +0200
@@ -6,14 +6,13 @@
 #include <curl/curl.h>
 #include <stdio.h>
 #include "fmt.h"
-#include "md5.h"
 #include "scrobbler.h"
 #include "config.h"
 #include "settings.h"
 #include <glib.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
+#include <audacious/audutil.h>
 
 #define SCROBBLER_HS_URL "http://post.audioscrobbler.com"
 #define SCROBBLER_CLI_ID "aud"
@@ -441,12 +440,12 @@
 
 static unsigned char *md5_string(char *pass, int len)
 {
-    md5_state_t md5state;
+    aud_md5state_t md5state;
     static unsigned char md5pword[16];
         
-    md5_init(&md5state);
-    md5_append(&md5state, (unsigned const char *)pass, len);
-    md5_finish(&md5state, md5pword);
+    aud_md5_init(&md5state);
+    aud_md5_append(&md5state, (unsigned const char *)pass, len);
+    aud_md5_finish(&md5state, md5pword);
 
     return md5pword;
 }
@@ -518,17 +517,17 @@
     }
 
     if (sc_challenge_hash != NULL) {
-        md5_state_t md5state;
+        aud_md5state_t md5state;
         unsigned char md5pword[16];
         
-        md5_init(&md5state);
+        aud_md5_init(&md5state);
         /*pdebug(fmt_vastr("Pass Hash: %s", sc_password), DEBUG);*/
-        md5_append(&md5state, (unsigned const char *)sc_password,
+        aud_md5_append(&md5state, (unsigned const char *)sc_password,
                 strlen(sc_password));
         /*pdebug(fmt_vastr("Challenge Hash: %s", sc_challenge_hash), DEBUG);*/
-        md5_append(&md5state, (unsigned const char *)sc_challenge_hash,
+        aud_md5_append(&md5state, (unsigned const char *)sc_challenge_hash,
                 strlen(sc_challenge_hash));
-        md5_finish(&md5state, md5pword);
+        aud_md5_finish(&md5state, md5pword);
         hexify((char*)md5pword, sizeof(md5pword));
         /*pdebug(fmt_vastr("Response Hash: %s", sc_response_hash), DEBUG);*/
     }
--- a/src/sexypsf/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sexypsf/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -18,7 +18,6 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
--- a/src/shnplug/gtk.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/shnplug/gtk.c	Thu May 22 19:32:39 2008 +0200
@@ -22,7 +22,6 @@
 
 #include <stdlib.h>
 #include <glib.h>
-#include <audacious/util.h>
 #include "shorten.h"
 
 #undef XMMS_SHN_LOAD_TEXTFILES
--- a/src/shnplug/seek.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/shnplug/seek.c	Thu May 22 19:32:39 2008 +0200
@@ -22,7 +22,6 @@
 
 #include <stdlib.h>
 #include <glib.h>
-#include <audacious/util.h>
 #include "shorten.h"
 
 #ifdef HAVE_CONFIG_H
--- a/src/shnplug/shn.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/shnplug/shn.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,6 @@
 #include <stdlib.h>
 #include <math.h>
 #include <glib.h>
-#include <audacious/util.h>
 #include "shorten.h"
 
 #ifdef HAVE_CONFIG_H
--- a/src/sid/Makefile	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/Makefile	Thu May 22 19:32:39 2008 +0200
@@ -4,7 +4,6 @@
        xs_about.c	\
        xs_support.c	\
        xs_config.c	\
-       xs_md5.c		\
        xs_length.c	\
        xs_genui.c	\
        xs_glade.c	\
@@ -26,4 +25,4 @@
 CFLAGS += ${PLUGIN_CFLAGS}
 CXXFLAGS += ${PLUGIN_CFLAGS}
 CPPFLAGS += ${PLUGIN_CPPFLAGS} -D_REENTRANT -I../.. -DAUDACIOUS_PLUGIN ${MOWGLI_CFLAGS} ${SIDPLAY1_CFLAGS} ${SIDPLAY2_CFLAGS} ${BUILDERS_CFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS}
-LIBS += ${BUILDERS_LDFLAGS} ${SIDPLAY1_LIBS} ${SIDPLAY2_LIBS} ${BUILDERS_LIBS} ${GTK_LIBS} ${GLIB_LIBS} -lstdc++
+LIBS += ${BUILDERS_LDFLAGS} ${SIDPLAY1_LIBS} ${SIDPLAY2_LIBS} ${BUILDERS_LIBS} ${GTK_LIBS} ${GLIB_LIBS} -lstdc++ -laudutil
--- a/src/sid/xs_config.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_config.c	Thu May 22 19:32:39 2008 +0200
@@ -24,32 +24,32 @@
 
 #ifdef AUDACIOUS_PLUGIN
 #include <audacious/plugin.h>
-#define XS_CONFIG_FILE        mcs_handle_t
-#define XS_CONFIG_OPEN        aud_cfg_db_open
-#define XS_CONFIG_FREE        aud_cfg_db_close
+#define XS_CONFIG_FILE      ConfigDb
+#define XS_CONFIG_OPEN      aud_cfg_db_open
+#define XS_CONFIG_FREE      aud_cfg_db_close
 
-#define XS_CFG_SET_STRING     aud_cfg_db_set_string
-#define XS_CFG_SET_FLOAT      aud_cfg_db_set_float
-#define XS_CFG_SET_INT        aud_cfg_db_set_int
-#define XS_CFG_SET_BOOL       aud_cfg_db_set_bool
-#define XS_CFG_GET_STRING     aud_cfg_db_get_string
-#define XS_CFG_GET_FLOAT      aud_cfg_db_get_float
-#define XS_CFG_GET_INT        aud_cfg_db_get_int
-#define XS_CFG_GET_BOOL       aud_cfg_db_get_bool
+#define XS_CFG_SET_STRING   aud_cfg_db_set_string
+#define XS_CFG_SET_FLOAT    aud_cfg_db_set_float
+#define XS_CFG_SET_INT      aud_cfg_db_set_int
+#define XS_CFG_SET_BOOL     aud_cfg_db_set_bool
+#define XS_CFG_GET_STRING   aud_cfg_db_get_string
+#define XS_CFG_GET_FLOAT    aud_cfg_db_get_float
+#define XS_CFG_GET_INT      aud_cfg_db_get_int
+#define XS_CFG_GET_BOOL     aud_cfg_db_get_bool
 #else
 #include <xmms/configfile.h>
-#define XS_CONFIG_FILE        ConfigFile
-#define XS_CONFIG_OPEN        xmms_cfg_open_default_file
-#define XS_CONFIG_FREE        xmms_cfg_free
+#define XS_CONFIG_FILE      ConfigFile
+#define XS_CONFIG_OPEN      xmms_cfg_open_default_file
+#define XS_CONFIG_FREE      xmms_cfg_free
 
-#define XS_CFG_SET_STRING     xmms_cfg_write_string
-#define XS_CFG_SET_FLOAT      xmms_cfg_write_float
-#define XS_CFG_SET_INT        xmms_cfg_write_int
-#define XS_CFG_SET_BOOL       xmms_cfg_write_boolean
-#define XS_CFG_GET_STRING     xmms_cfg_read_string
-#define XS_CFG_GET_FLOAT      xmms_cfg_read_float
-#define XS_CFG_GET_INT        xmms_cfg_read_int
-#define XS_CFG_GET_BOOL       xmms_cfg_read_boolean
+#define XS_CFG_SET_STRING   xmms_cfg_write_string
+#define XS_CFG_SET_FLOAT    xmms_cfg_write_float
+#define XS_CFG_SET_INT      xmms_cfg_write_int
+#define XS_CFG_SET_BOOL     xmms_cfg_write_boolean
+#define XS_CFG_GET_STRING   xmms_cfg_read_string
+#define XS_CFG_GET_FLOAT    xmms_cfg_read_float
+#define XS_CFG_GET_INT      xmms_cfg_read_int
+#define XS_CFG_GET_BOOL     xmms_cfg_read_boolean
 #endif
 #include <stdio.h>
 #include <ctype.h>
@@ -87,116 +87,116 @@
 struct t_xs_cfg xs_cfg;
 
 static t_xs_cfg_item xs_cfgtable[] = {
-{ CTYPE_INT,    &xs_cfg.audioBitsPerSample,    "audioBitsPerSample" },
-{ CTYPE_INT,    &xs_cfg.audioChannels,        "audioChannels" },
-{ CTYPE_INT,    &xs_cfg.audioFrequency,        "audioFrequency" },
+{ CTYPE_INT,    &xs_cfg.audioBitsPerSample,     "audioBitsPerSample" },
+{ CTYPE_INT,    &xs_cfg.audioChannels,          "audioChannels" },
+{ CTYPE_INT,    &xs_cfg.audioFrequency,         "audioFrequency" },
 
-{ CTYPE_BOOL,    &xs_cfg.mos8580,        "mos8580" },
-{ CTYPE_BOOL,    &xs_cfg.forceModel,        "forceModel" },
-{ CTYPE_BOOL,    &xs_cfg.emulateFilters,        "emulateFilters" },
-{ CTYPE_FLOAT,    &xs_cfg.sid1FilterFs,        "filterFs" },
-{ CTYPE_FLOAT,    &xs_cfg.sid1FilterFm,        "filterFm" },
-{ CTYPE_FLOAT,    &xs_cfg.sid1FilterFt,        "filterFt" },
-{ CTYPE_INT,    &xs_cfg.memoryMode,        "memoryMode" },
-{ CTYPE_INT,    &xs_cfg.clockSpeed,        "clockSpeed" },
-{ CTYPE_BOOL,    &xs_cfg.forceSpeed,        "forceSpeed" },
+{ CTYPE_BOOL,   &xs_cfg.mos8580,                "mos8580" },
+{ CTYPE_BOOL,   &xs_cfg.forceModel,             "forceModel" },
+{ CTYPE_BOOL,   &xs_cfg.emulateFilters,         "emulateFilters" },
+{ CTYPE_FLOAT,  &xs_cfg.sid1FilterFs,           "filterFs" },
+{ CTYPE_FLOAT,  &xs_cfg.sid1FilterFm,           "filterFm" },
+{ CTYPE_FLOAT,  &xs_cfg.sid1FilterFt,           "filterFt" },
+{ CTYPE_INT,    &xs_cfg.memoryMode,             "memoryMode" },
+{ CTYPE_INT,    &xs_cfg.clockSpeed,             "clockSpeed" },
+{ CTYPE_BOOL,   &xs_cfg.forceSpeed,             "forceSpeed" },
 
-{ CTYPE_INT,    &xs_cfg.playerEngine,        "playerEngine" },
+{ CTYPE_INT,    &xs_cfg.playerEngine,           "playerEngine" },
 
-{ CTYPE_INT,    &xs_cfg.sid2Builder,        "sid2Builder" },
-{ CTYPE_INT,    &xs_cfg.sid2OptLevel,        "sid2OptLevel" },
-{ CTYPE_INT,    &xs_cfg.sid2NFilterPresets,    "sid2NFilterPresets" },
+{ CTYPE_INT,    &xs_cfg.sid2Builder,            "sid2Builder" },
+{ CTYPE_INT,    &xs_cfg.sid2OptLevel,           "sid2OptLevel" },
+{ CTYPE_INT,    &xs_cfg.sid2NFilterPresets,     "sid2NFilterPresets" },
 
-{ CTYPE_BOOL,    &xs_cfg.oversampleEnable,    "oversampleEnable" },
-{ CTYPE_INT,    &xs_cfg.oversampleFactor,    "oversampleFactor" },
+{ CTYPE_BOOL,   &xs_cfg.oversampleEnable,       "oversampleEnable" },
+{ CTYPE_INT,    &xs_cfg.oversampleFactor,       "oversampleFactor" },
 
-{ CTYPE_BOOL,    &xs_cfg.playMaxTimeEnable,    "playMaxTimeEnable" },
-{ CTYPE_BOOL,    &xs_cfg.playMaxTimeUnknown,    "playMaxTimeUnknown" },
-{ CTYPE_INT,    &xs_cfg.playMaxTime,        "playMaxTime" },
-{ CTYPE_BOOL,    &xs_cfg.playMinTimeEnable,    "playMinTimeEnable" },
-{ CTYPE_INT,    &xs_cfg.playMinTime,        "playMinTime" },
-{ CTYPE_BOOL,    &xs_cfg.songlenDBEnable,    "songlenDBEnable" },
-{ CTYPE_STR,    &xs_cfg.songlenDBPath,        "songlenDBPath" },
+{ CTYPE_BOOL,   &xs_cfg.playMaxTimeEnable,      "playMaxTimeEnable" },
+{ CTYPE_BOOL,   &xs_cfg.playMaxTimeUnknown,     "playMaxTimeUnknown" },
+{ CTYPE_INT,    &xs_cfg.playMaxTime,            "playMaxTime" },
+{ CTYPE_BOOL,   &xs_cfg.playMinTimeEnable,      "playMinTimeEnable" },
+{ CTYPE_INT,    &xs_cfg.playMinTime,            "playMinTime" },
+{ CTYPE_BOOL,   &xs_cfg.songlenDBEnable,        "songlenDBEnable" },
+{ CTYPE_STR,    &xs_cfg.songlenDBPath,          "songlenDBPath" },
 
-{ CTYPE_BOOL,    &xs_cfg.stilDBEnable,        "stilDBEnable" },
-{ CTYPE_STR,    &xs_cfg.stilDBPath,        "stilDBPath" },
-{ CTYPE_STR,    &xs_cfg.hvscPath,        "hvscPath" },
+{ CTYPE_BOOL,   &xs_cfg.stilDBEnable,           "stilDBEnable" },
+{ CTYPE_STR,    &xs_cfg.stilDBPath,             "stilDBPath" },
+{ CTYPE_STR,    &xs_cfg.hvscPath,               "hvscPath" },
 
 #ifndef AUDACIOUS_PLUGIN
-{ CTYPE_INT,    &xs_cfg.subsongControl,        "subsongControl" },
-{ CTYPE_BOOL,    &xs_cfg.detectMagic,        "detectMagic" },
+{ CTYPE_INT,    &xs_cfg.subsongControl,         "subsongControl" },
+{ CTYPE_BOOL,   &xs_cfg.detectMagic,            "detectMagic" },
 #endif
 
-{ CTYPE_BOOL,    &xs_cfg.titleOverride,        "titleOverride" },
-{ CTYPE_STR,    &xs_cfg.titleFormat,        "titleFormat" },
+{ CTYPE_BOOL,   &xs_cfg.titleOverride,          "titleOverride" },
+{ CTYPE_STR,    &xs_cfg.titleFormat,            "titleFormat" },
 
-{ CTYPE_BOOL,    &xs_cfg.subAutoEnable,        "subAutoEnable" },
-{ CTYPE_BOOL,    &xs_cfg.subAutoMinOnly,        "subAutoMinOnly" },
-{ CTYPE_INT,    &xs_cfg.subAutoMinTime,        "subAutoMinTime" },
+{ CTYPE_BOOL,   &xs_cfg.subAutoEnable,          "subAutoEnable" },
+{ CTYPE_BOOL,   &xs_cfg.subAutoMinOnly,         "subAutoMinOnly" },
+{ CTYPE_INT,    &xs_cfg.subAutoMinTime,         "subAutoMinTime" },
 };
 
 static const gint xs_cfgtable_max = (sizeof(xs_cfgtable) / sizeof(t_xs_cfg_item));
 
 
 static t_xs_wid_item xs_widtable[] = {
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_res_16bit",    &xs_cfg.audioBitsPerSample,    XS_RES_16BIT },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_res_8bit",        &xs_cfg.audioBitsPerSample,    XS_RES_8BIT },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_chn_mono",        &xs_cfg.audioChannels,        XS_CHN_MONO },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_chn_stereo",    &xs_cfg.audioChannels,        XS_CHN_STEREO },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_chn_autopan",    &xs_cfg.audioChannels,        XS_CHN_AUTOPAN },
-{ WTYPE_COMBO,    CTYPE_INT,    "cfg_samplerate",    &xs_cfg.audioFrequency,        XS_AUDIO_FREQ },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_oversample",    &xs_cfg.oversampleEnable,    0 },
-{ WTYPE_SPIN,    CTYPE_INT,    "cfg_oversample_factor",&xs_cfg.oversampleFactor,    0 },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_res_16bit",        &xs_cfg.audioBitsPerSample,     XS_RES_16BIT },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_res_8bit",         &xs_cfg.audioBitsPerSample,     XS_RES_8BIT },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_chn_mono",         &xs_cfg.audioChannels,          XS_CHN_MONO },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_chn_stereo",       &xs_cfg.audioChannels,          XS_CHN_STEREO },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_chn_autopan",      &xs_cfg.audioChannels,          XS_CHN_AUTOPAN },
+{ WTYPE_COMBO,  CTYPE_INT,      "cfg_samplerate",       &xs_cfg.audioFrequency,         XS_AUDIO_FREQ },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_oversample",       &xs_cfg.oversampleEnable,       0 },
+{ WTYPE_SPIN,   CTYPE_INT,      "cfg_oversample_factor",&xs_cfg.oversampleFactor,       0 },
 
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_sidplay1",    &xs_cfg.playerEngine,        XS_ENG_SIDPLAY1 },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_sidplay2",    &xs_cfg.playerEngine,        XS_ENG_SIDPLAY2 },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_mem_real",    &xs_cfg.memoryMode,        XS_MPU_REAL },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_mem_banksw",    &xs_cfg.memoryMode,        XS_MPU_BANK_SWITCHING },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_mem_transrom",    &xs_cfg.memoryMode,        XS_MPU_TRANSPARENT_ROM },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_mem_playsid",    &xs_cfg.memoryMode,        XS_MPU_PLAYSID_ENVIRONMENT },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_sidplay1",     &xs_cfg.playerEngine,           XS_ENG_SIDPLAY1 },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_sidplay2",     &xs_cfg.playerEngine,           XS_ENG_SIDPLAY2 },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_mem_real",     &xs_cfg.memoryMode,             XS_MPU_REAL },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_mem_banksw",   &xs_cfg.memoryMode,             XS_MPU_BANK_SWITCHING },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_mem_transrom", &xs_cfg.memoryMode,             XS_MPU_TRANSPARENT_ROM },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_mem_playsid",  &xs_cfg.memoryMode,             XS_MPU_PLAYSID_ENVIRONMENT },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_emu_mos8580",    &xs_cfg.mos8580,        0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_emu_sid_force",    &xs_cfg.forceModel,        0 },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_clock_ntsc",    &xs_cfg.clockSpeed,        XS_CLOCK_NTSC },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_clock_pal",    &xs_cfg.clockSpeed,        XS_CLOCK_PAL },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_emu_clock_force",    &xs_cfg.forceSpeed,        0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_emu_sp2_opt",    &xs_cfg.sid2OptLevel,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_emu_mos8580",      &xs_cfg.mos8580,                0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_emu_sid_force",    &xs_cfg.forceModel,             0 },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_clock_ntsc",   &xs_cfg.clockSpeed,             XS_CLOCK_NTSC },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_clock_pal",    &xs_cfg.clockSpeed,             XS_CLOCK_PAL },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_emu_clock_force",  &xs_cfg.forceSpeed,             0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_emu_sp2_opt",      &xs_cfg.sid2OptLevel,           0 },
 
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_sp2_resid",    &xs_cfg.sid2Builder,        XS_BLD_RESID },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_emu_sp2_hardsid",    &xs_cfg.sid2Builder,        XS_BLD_HARDSID },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_sp2_resid",    &xs_cfg.sid2Builder,            XS_BLD_RESID },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_emu_sp2_hardsid",  &xs_cfg.sid2Builder,            XS_BLD_HARDSID },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_emu_filters",    &xs_cfg.emulateFilters,        0 },
-{ WTYPE_SCALE,    CTYPE_FLOAT,    "cfg_sp1_filter_fs",    &xs_cfg.sid1FilterFs,        0 },
-{ WTYPE_SCALE,    CTYPE_FLOAT,    "cfg_sp1_filter_fm",    &xs_cfg.sid1FilterFm,        0 },
-{ WTYPE_SCALE,    CTYPE_FLOAT,    "cfg_sp1_filter_ft",    &xs_cfg.sid1FilterFt,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_emu_filters",      &xs_cfg.emulateFilters,         0 },
+{ WTYPE_SCALE,  CTYPE_FLOAT,    "cfg_sp1_filter_fs",    &xs_cfg.sid1FilterFs,           0 },
+{ WTYPE_SCALE,  CTYPE_FLOAT,    "cfg_sp1_filter_fm",    &xs_cfg.sid1FilterFm,           0 },
+{ WTYPE_SCALE,  CTYPE_FLOAT,    "cfg_sp1_filter_ft",    &xs_cfg.sid1FilterFt,           0 },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_maxtime_enable",    &xs_cfg.playMaxTimeEnable,    0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_maxtime_unknown",    &xs_cfg.playMaxTimeUnknown,    0 },
-{ WTYPE_SPIN,    CTYPE_INT,    "cfg_maxtime",        &xs_cfg.playMaxTime,        0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_mintime_enable",    &xs_cfg.playMinTimeEnable,    0 },
-{ WTYPE_SPIN,    CTYPE_INT,    "cfg_mintime",        &xs_cfg.playMinTime,        0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_sld_enable",    &xs_cfg.songlenDBEnable,    0 },
-{ WTYPE_TEXT,    CTYPE_STR,    "cfg_sld_dbpath",    &xs_cfg.songlenDBPath,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_maxtime_enable",   &xs_cfg.playMaxTimeEnable,      0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_maxtime_unknown",  &xs_cfg.playMaxTimeUnknown,     0 },
+{ WTYPE_SPIN,   CTYPE_INT,      "cfg_maxtime",          &xs_cfg.playMaxTime,            0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_mintime_enable",   &xs_cfg.playMinTimeEnable,      0 },
+{ WTYPE_SPIN,   CTYPE_INT,      "cfg_mintime",          &xs_cfg.playMinTime,            0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_sld_enable",       &xs_cfg.songlenDBEnable,        0 },
+{ WTYPE_TEXT,   CTYPE_STR,      "cfg_sld_dbpath",       &xs_cfg.songlenDBPath,          0 },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_stil_enable",    &xs_cfg.stilDBEnable,        0 },
-{ WTYPE_TEXT,    CTYPE_STR,    "cfg_stil_dbpath",    &xs_cfg.stilDBPath,        0 },
-{ WTYPE_TEXT,    CTYPE_STR,    "cfg_hvsc_path",    &xs_cfg.hvscPath,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_stil_enable",      &xs_cfg.stilDBEnable,           0 },
+{ WTYPE_TEXT,   CTYPE_STR,      "cfg_stil_dbpath",      &xs_cfg.stilDBPath,             0 },
+{ WTYPE_TEXT,   CTYPE_STR,      "cfg_hvsc_path",        &xs_cfg.hvscPath,               0 },
 
 #ifndef AUDACIOUS_PLUGIN
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_subctrl_none",    &xs_cfg.subsongControl,        XS_SSC_NONE },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_subctrl_seek",    &xs_cfg.subsongControl,        XS_SSC_SEEK },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_subctrl_popup",    &xs_cfg.subsongControl,        XS_SSC_POPUP },
-{ WTYPE_BGROUP,    CTYPE_INT,    "cfg_subctrl_patch",    &xs_cfg.subsongControl,        XS_SSC_PATCH },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_subctrl_none",     &xs_cfg.subsongControl,         XS_SSC_NONE },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_subctrl_seek",     &xs_cfg.subsongControl,         XS_SSC_SEEK },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_subctrl_popup",    &xs_cfg.subsongControl,         XS_SSC_POPUP },
+{ WTYPE_BGROUP, CTYPE_INT,      "cfg_subctrl_patch",    &xs_cfg.subsongControl,         XS_SSC_PATCH },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_detectmagic",    &xs_cfg.detectMagic,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_detectmagic",      &xs_cfg.detectMagic,            0 },
 #endif
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_ftitle_override",    &xs_cfg.titleOverride,        0 },
-{ WTYPE_TEXT,    CTYPE_STR,    "cfg_ftitle_format",    &xs_cfg.titleFormat,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_ftitle_override",  &xs_cfg.titleOverride,          0 },
+{ WTYPE_TEXT,   CTYPE_STR,      "cfg_ftitle_format",    &xs_cfg.titleFormat,            0 },
 
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_subauto_enable",    &xs_cfg.subAutoEnable,        0 },
-{ WTYPE_BUTTON,    CTYPE_BOOL,    "cfg_subauto_min_only",    &xs_cfg.subAutoMinOnly,        0 },
-{ WTYPE_SPIN,    CTYPE_INT,    "cfg_subauto_mintime",    &xs_cfg.subAutoMinTime,        0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_subauto_enable",   &xs_cfg.subAutoEnable,          0 },
+{ WTYPE_BUTTON, CTYPE_BOOL,     "cfg_subauto_min_only", &xs_cfg.subAutoMinOnly,         0 },
+{ WTYPE_SPIN,   CTYPE_INT,      "cfg_subauto_mintime",  &xs_cfg.subAutoMinTime,         0 },
 };
 
 static const gint xs_widtable_max = (sizeof(xs_widtable) / sizeof(t_xs_wid_item));
--- a/src/sid/xs_config.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_config.h	Thu May 22 19:32:39 2008 +0200
@@ -81,54 +81,54 @@
     gint        audioFrequency;
 
     gboolean    oversampleEnable;
-    gint        oversampleFactor;    /* Factor of oversampling */
+    gint        oversampleFactor;   /* Factor of oversampling */
 
     /* Emulation settings */
-    gboolean    mos8580;        /* TRUE = 8580, FALSE = 6581 */
+    gboolean    mos8580;            /* TRUE = 8580, FALSE = 6581 */
     gboolean    forceModel;
-    gint        memoryMode;        /* See XS_MPU-constants */
-    gint        clockSpeed;        /* PAL (50Hz) or NTSC (60Hz) */
-    gboolean    forceSpeed;        /* TRUE = force to given clockspeed */
+    gint        memoryMode;         /* See XS_MPU-constants */
+    gint        clockSpeed;         /* PAL (50Hz) or NTSC (60Hz) */
+    gboolean    forceSpeed;         /* TRUE = force to given clockspeed */
 
-    gint        playerEngine;        /* Selected player engine */
+    gint        playerEngine;       /* Selected player engine */
 
     gboolean    emulateFilters;
-    gfloat        sid1FilterFs;
-    gfloat        sid1FilterFm;
-    gfloat        sid1FilterFt;
+    gfloat      sid1FilterFs;
+    gfloat      sid1FilterFm;
+    gfloat      sid1FilterFt;
 
-    gint        sid2OptLevel;        /* SIDPlay2 emulation optimization */
+    gint        sid2OptLevel;       /* SIDPlay2 emulation optimization */
     gint        sid2Builder;        /* SIDPlay2 "builder" aka SID-emu */
-    t_xs_sid2_filter    sid2Filter;    /* Current SIDPlay2 filter */
+    t_xs_sid2_filter    sid2Filter; /* Current SIDPlay2 filter */
     t_xs_sid2_filter    **sid2FilterPresets;
     gint        sid2NFilterPresets;
     
     
     /* Playing settings */
     gboolean    playMaxTimeEnable,
-            playMaxTimeUnknown;    /* Use max-time only when song-length is unknown */
+                playMaxTimeUnknown; /* Use max-time only when song-length is unknown */
     gint        playMaxTime;        /* MAX playtime in seconds */
 
     gboolean    playMinTimeEnable;
     gint        playMinTime;        /* MIN playtime in seconds */
 
     gboolean    songlenDBEnable;
-    gchar        *songlenDBPath;        /* Path to Songlengths.txt */
+    gchar       *songlenDBPath;     /* Path to Songlengths.txt */
 
 
     /* Miscellaneous settings */
     gboolean    stilDBEnable;
-    gchar        *stilDBPath;        /* Path to STIL.txt */
-    gchar        *hvscPath;        /* Path-prefix for HVSC */
+    gchar       *stilDBPath;        /* Path to STIL.txt */
+    gchar       *hvscPath;          /* Path-prefix for HVSC */
 
     gint        subsongControl;
     gboolean    detectMagic;
 
-    gboolean    titleOverride;        /* TRUE if XMMS titles are overriden */
-    gchar        *titleFormat;
+    gboolean    titleOverride;      /* TRUE if XMMS titles are overriden */
+    gchar       *titleFormat;
 
     gboolean    subAutoEnable,
-            subAutoMinOnly;
+                subAutoMinOnly;
     gint        subAutoMinTime;
 } xs_cfg;
 
@@ -155,16 +155,16 @@
 
 
 typedef struct {
-    gint    itemType;    /* Type of item (CTYPE_*) */
-    void    *itemData;    /* Pointer to variable */
-    gchar    *itemName;    /* Name of configuration item */
+    gint    itemType;   /* Type of item (CTYPE_*) */
+    void    *itemData;  /* Pointer to variable */
+    gchar   *itemName;  /* Name of configuration item */
 } t_xs_cfg_item;
 
 
 typedef struct {
     gint    widType;
     gint    itemType;
-    gchar    *widName;
+    gchar   *widName;
     void    *itemData;
     gint    itemSet;
 } t_xs_wid_item;
--- a/src/sid/xs_length.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_length.h	Thu May 22 19:32:39 2008 +0200
@@ -2,7 +2,18 @@
 #define XS_LENGTH_H
 
 #include "xmms-sid.h"
+#ifdef AUDACIOUS_PLUGIN
+#include <audacious/audutil.h>
+#define XS_MD5HASH_LENGTH   AUD_MD5HASH_LENGTH
+#define XS_MD5HASH_LENGTH_CH   AUD_MD5HASH_LENGTH_CH
+#define xs_md5hash_t        aud_md5hash_t
+#define xs_md5state_t       aud_md5state_t
+#define xs_md5_init         aud_md5_init
+#define xs_md5_append       aud_md5_append
+#define xs_md5_finish       aud_md5_finish
+#else
 #include "xs_md5.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -12,25 +23,25 @@
  */
 typedef struct _sldb_node_t {
     xs_md5hash_t    md5Hash;    /* 128-bit MD5 hash-digest */
-    gint        nlengths;    /* Number of lengths */
-    gint        *lengths;    /* Lengths in seconds */
+    gint            nlengths;   /* Number of lengths */
+    gint            *lengths;   /* Lengths in seconds */
     struct _sldb_node_t *prev, *next;
 } sldb_node_t;
 
 
 typedef struct {
-    sldb_node_t    *nodes,
-            **pindex;
-    size_t        n;
+    sldb_node_t     *nodes,
+                    **pindex;
+    size_t          n;
 } xs_sldb_t;
 
 
 /* Functions
  */
-gint        xs_sldb_read(xs_sldb_t *, const gchar *);
-gint        xs_sldb_index(xs_sldb_t *);
-void        xs_sldb_free(xs_sldb_t *);
-sldb_node_t *    xs_sldb_get(xs_sldb_t *, const gchar *);
+gint            xs_sldb_read(xs_sldb_t *, const gchar *);
+gint            xs_sldb_index(xs_sldb_t *);
+void            xs_sldb_free(xs_sldb_t *);
+sldb_node_t *   xs_sldb_get(xs_sldb_t *, const gchar *);
 
 #ifdef __cplusplus
 }
--- a/src/sid/xs_md5.c	Thu May 22 19:31:48 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,230 +0,0 @@
-/*
- * MD5 implementation, modified for XMMS-SID from
- * Colin Plumb's implementation by Matti 'ccr' Hämäläinen.
- *
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest.  This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- */
-#include "xs_support.h"
-#include "xs_md5.h"
-#include <glib.h>
-
-
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-#define xs_md5_bytereverse(buf, len)    /* Nothing */
-#else
-#if G_BYTE_ORDER == G_BIG_ENDIAN
-static void xs_md5_bytereverse(guint8 *buf, guint l)
-{
-    guint32 t;
-    do {
-        t = (guint32) ((guint) buf[3] << 8 | buf[2]) << 16 | ((guint) buf[1] << 8 | buf[0]);
-        *(guint32 *) buf = t;
-        buf += sizeof(guint32);
-    } while (--l);
-}
-#else
-#error Unsupported endianess!
-#endif
-#endif
-
-
-/* Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-void xs_md5_init(xs_md5state_t *ctx)
-{
-    ctx->buf[0] = 0x67452301;
-    ctx->buf[1] = 0xefcdab89;
-    ctx->buf[2] = 0x98badcfe;
-    ctx->buf[3] = 0x10325476;
-
-    ctx->bits[0] = 0;
-    ctx->bits[1] = 0;
-}
-
-
-/* The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  xs_md5_update blocks
- * the data and converts bytes into longwords for this routine.
- */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-#define MD5STEP(f, w, x, y, z, data, s) \
-    ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
-
-static void xs_md5_transform(guint32 buf[4], guint32 const in[16])
-{
-    register guint32 a, b, c, d;
-
-    a = buf[0];
-    b = buf[1];
-    c = buf[2];
-    d = buf[3];
-
-    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-
-    buf[0] += a;
-    buf[1] += b;
-    buf[2] += c;
-    buf[3] += d;
-}
-
-
-/* Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-void xs_md5_append(xs_md5state_t *ctx, const guint8 *buf, guint len)
-{
-    guint32 t;
-
-    /* Update bitcount */
-    t = ctx->bits[0];
-    if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
-        ctx->bits[1]++;    /* Carry from low to high */
-    ctx->bits[1] += len >> 29;
-
-    t = (t >> 3) & 0x3f;    /* Bytes already in shsInfo->data */
-
-    /* Handle any leading odd-sized chunks */
-    if (t) {
-        guint8 *p = (guint8 *) ctx->in + t;
-
-        t = 64 - t;
-        if (len < t) {
-            memcpy(p, buf, len);
-            return;
-        }
-        memcpy(p, buf, t);
-        xs_md5_bytereverse(ctx->in, 16);
-        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
-        buf += t;
-        len -= t;
-    }
-
-    /* Process data in 64-byte chunks */
-    while (len >= 64) {
-        memcpy(ctx->in, buf, 64);
-        xs_md5_bytereverse(ctx->in, 16);
-        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
-        buf += 64;
-        len -= 64;
-    }
-
-    /* Handle any remaining bytes of data. */
-    memcpy(ctx->in, buf, len);
-}
-
-/* Final wrapup - pad to 64-byte boundary with the bit pattern 
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-void xs_md5_finish(xs_md5state_t *ctx, xs_md5hash_t digest)
-{
-    guint count;
-    guint8 *p;
-
-    /* Compute number of bytes mod 64 */
-    count = (ctx->bits[0] >> 3) & 0x3F;
-
-    /* Set the first char of padding to 0x80.  This is safe since there is
-       always at least one byte free */
-    p = ctx->in + count;
-    *p++ = 0x80;
-
-    /* Bytes of padding needed to make 64 bytes */
-    count = 64 - 1 - count;
-
-    /* Pad out to 56 mod 64 */
-    if (count < 8) {
-        /* Two lots of padding:  Pad the first block to 64 bytes */
-        memset(p, 0, count);
-        xs_md5_bytereverse(ctx->in, 16);
-        xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
-
-        /* Now fill the next block with 56 bytes */
-        memset(ctx->in, 0, 56);
-    } else {
-        /* Pad block to 56 bytes */
-        memset(p, 0, count - 8);
-    }
-    xs_md5_bytereverse(ctx->in, 14);
-
-    /* Append length in bits and transform */
-    ((guint32 *) ctx->in)[14] = ctx->bits[0];
-    ((guint32 *) ctx->in)[15] = ctx->bits[1];
-
-    xs_md5_transform(ctx->buf, (guint32 *) ctx->in);
-    xs_md5_bytereverse((guint8 *) ctx->buf, 4);
-    memcpy(digest, ctx->buf, 16);
-    memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
-}
--- a/src/sid/xs_md5.h	Thu May 22 19:31:48 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-#ifndef XS_MD5_H
-#define XS_MD5_H
-
-#include <glib.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Typedefs
- */
-typedef struct md5_state_s {
-    guint32 bits[2];    /* message length in bits, lsw first */
-    guint32 buf[4];        /* digest buffer */
-    guint8 in[64];        /* accumulate block */
-} xs_md5state_t;
-
-#define XS_MD5HASH_LENGTH    (16)
-#define XS_MD5HASH_LENGTH_CH    (XS_MD5HASH_LENGTH * 2)
-
-typedef guint8 xs_md5hash_t[XS_MD5HASH_LENGTH];
-
-
-/* Functions
- */
-void xs_md5_init(xs_md5state_t *ctx);
-void xs_md5_append(xs_md5state_t *ctx, const guint8 *buf, guint len);
-void xs_md5_finish(xs_md5state_t *ctx, xs_md5hash_t digest);
-
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* XS_MD5_H */
--- a/src/sid/xs_player.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_player.h	Thu May 22 19:32:39 2008 +0200
@@ -16,7 +16,7 @@
     gboolean    (*plrInit)(struct xs_status_t *);
     void        (*plrClose)(struct xs_status_t *);
     gboolean    (*plrInitSong)(struct xs_status_t *);
-    guint        (*plrFillBuffer)(struct xs_status_t *, gchar *, guint);
+    guint       (*plrFillBuffer)(struct xs_status_t *, gchar *, guint);
     gboolean    (*plrLoadSID)(struct xs_status_t *, gchar *);
     void        (*plrDeleteSID)(struct xs_status_t *);
     xs_tuneinfo_t*    (*plrGetSIDInfo)(const gchar *);
@@ -26,22 +26,24 @@
 
 
 typedef struct xs_status_t {
-    gint        audioFrequency,        /* Audio settings */
-            audioChannels,
-            audioBitsPerSample,
-            oversampleFactor;    /* Factor of oversampling */
-    AFormat        audioFormat;
-    gboolean    oversampleEnable;    /* TRUE after sidEngine initialization,
-                        if xs_cfg.oversampleEnable == TRUE and
-                        emulation backend supports oversampling.
-                        */
-    void        *sidEngine;        /* SID-emulation internal engine data */
-    xs_player_t    *sidPlayer;        /* Selected player engine */
-    gboolean    isError, isPlaying, isInitialized;
-    gint        currSong,        /* Current sub-tune */
-            lastTime;
+    gint        audioFrequency,     /* Audio settings */
+                audioChannels,
+                audioBitsPerSample,
+                oversampleFactor;   /* Factor of oversampling */
+    AFormat     audioFormat;
+    gboolean    oversampleEnable;   /* TRUE after sidEngine initialization,
+                                    if xs_cfg.oversampleEnable == TRUE and
+                                    emulation backend supports oversampling.
+                                    */
+    void        *sidEngine;         /* SID-emulation internal engine data */
+    xs_player_t *sidPlayer;         /* Selected player engine */
+    gboolean    isError,
+                isPlaying,
+                isInitialized;
+    gint        currSong,           /* Current sub-tune */
+                lastTime;
 
-    xs_tuneinfo_t    *tuneInfo;
+    xs_tuneinfo_t *tuneInfo;
 } xs_status_t;
 
 
--- a/src/sid/xs_sidplay1.cc	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_sidplay1.cc	Thu May 22 19:32:39 2008 +0200
@@ -52,11 +52,11 @@
 
 /* Return song information
  */
-#define    TFUNCTION    xs_sidplay1_getinfo
-#define    TFUNCTION2    xs_sidplay1_updateinfo
-#define    TTUNEINFO    sidTuneInfo
-#define    TTUNE        sidTune
-#define TENGINE        xs_sidplay1_t
+#define TFUNCTION   xs_sidplay1_getinfo
+#define TFUNCTION2  xs_sidplay1_updateinfo
+#define TTUNEINFO   sidTuneInfo
+#define TTUNE       sidTune
+#define TENGINE     xs_sidplay1_t
 #include "xs_sidplay.h"
 
 
--- a/src/sid/xs_sidplay2.cc	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_sidplay2.cc	Thu May 22 19:32:39 2008 +0200
@@ -30,22 +30,50 @@
 
 
 #include <sidplay/sidplay2.h>
-#ifdef HAVE_RESID_BUILDER
-#include <sidplay/builders/resid.h>
-#endif
-#ifdef HAVE_HARDSID_BUILDER
-#include <sidplay/builders/hardsid.h>
+#ifdef HAVE_SIDPLAY2_COMI
+#  include <sidplay/sidlazyiptr.h>
 #endif
 
 
-typedef struct {
+class xs_sidplay2_t {
+public:
+#ifdef HAVE_SIDPLAY2_COMI
+    SidIPtr<ISidplay2> currEng;
+    SidLazyIPtr<ISidUnknown> currBuilder;
+#else
     sidplay2 *currEng;
     sidbuilder *currBuilder;
+#endif
     sid2_config_t currConfig;
     SidTune *currTune;
     guint8 *buf;
     size_t bufSize;
-} xs_sidplay2_t;
+    
+    xs_sidplay2_t(void);
+    virtual ~xs_sidplay2_t(void) { ; }
+};
+
+
+#ifdef HAVE_RESID_BUILDER
+#  include <sidplay/builders/resid.h>
+#endif
+#ifdef HAVE_HARDSID_BUILDER
+#  include <sidplay/builders/hardsid.h>
+#endif
+
+
+xs_sidplay2_t::xs_sidplay2_t(void)
+#ifdef HAVE_SIDPLAY2_COMI
+:currEng(sidplay2::create())
+#else
+:currEng(NULL)
+#endif
+{
+    buf = NULL;
+    bufSize = 0;
+    currTune = NULL;
+    currBuilder = NULL;    
+}
 
 
 /* We need to 'export' all this pseudo-C++ crap */
@@ -54,11 +82,11 @@
 
 /* Return song information
  */
-#define TFUNCTION    xs_sidplay2_getinfo
-#define TFUNCTION2    xs_sidplay2_updateinfo
-#define TTUNEINFO    SidTuneInfo
-#define TTUNE        SidTune
-#define TENGINE        xs_sidplay2_t
+#define TFUNCTION   xs_sidplay2_getinfo
+#define TFUNCTION2  xs_sidplay2_updateinfo
+#define TTUNEINFO   SidTuneInfo
+#define TTUNE       SidTune
+#define TENGINE     xs_sidplay2_t
 #include "xs_sidplay.h"
 
 
@@ -91,12 +119,14 @@
     assert(myStatus);
 
     /* Allocate internal structures */
-    myEngine = (xs_sidplay2_t *) g_malloc0(sizeof(xs_sidplay2_t));
+    myEngine = new xs_sidplay2_t();
     myStatus->sidEngine = myEngine;
     if (!myEngine) return FALSE;
 
     /* Initialize the engine */
+#ifndef HAVE_SIDPLAY2_COMI
     myEngine->currEng = new sidplay2;
+#endif
     if (!myEngine->currEng) {
         xs_error("[SIDPlay2] Could not initialize emulation engine.\n");
         return FALSE;
@@ -228,9 +258,16 @@
     XSDEBUG("init builder #%i, maxsids=%i\n", xs_cfg.sid2Builder, (myEngine->currEng->info()).maxsids);
 #ifdef HAVE_RESID_BUILDER
     if (xs_cfg.sid2Builder == XS_BLD_RESID) {
+#ifdef HAVE_SIDPLAY2_COMI
+        myEngine->currBuilder = ReSIDBuilderCreate("");
+        SidLazyIPtr<IReSIDBuilder> rs(myEngine->currBuilder);
+        if (rs) {
+            myEngine->currConfig.sidEmulation = rs->iaggregate();
+#else
         ReSIDBuilder *rs = new ReSIDBuilder("ReSID builder");
         myEngine->currBuilder = (sidbuilder *) rs;
         if (rs) {
+#endif
             /* Builder object created, initialize it */
             rs->create((myEngine->currEng->info()).maxsids);
             if (!*rs) {
@@ -266,9 +303,16 @@
 #endif
 #ifdef HAVE_HARDSID_BUILDER
     if (xs_cfg.sid2Builder == XS_BLD_HARDSID) {
+#ifdef HAVE_SIDPLAY2_COMI
+        myEngine->currBuilder = HardSIDBuilderCreate("");
+        SidLazyIPtr<IHardSIDBuilder> hs(myEngine->currBuilder);
+        if (hs) {
+            myEngine->currConfig.sidEmulation = hs->iaggregate();
+#else
         HardSIDBuilder *hs = new HardSIDBuilder("HardSID builder");
         myEngine->currBuilder = (sidbuilder *) hs;
         if (hs) {
+#endif
             /* Builder object created, initialize it */
             hs->create((myEngine->currEng->info()).maxsids);
             if (!*hs) {
@@ -290,8 +334,10 @@
         return FALSE;
     }
 
+#ifndef HAVE_SIDPLAY2_COMI
+    myEngine->currConfig.sidEmulation = myEngine->currBuilder;
     XSDEBUG("%s\n", myEngine->currBuilder->credits());
-
+#endif
 
     /* Clockspeed settings */
     switch (xs_cfg.clockSpeed) {
@@ -311,7 +357,6 @@
 
 
     /* Configure rest of the emulation */
-    myEngine->currConfig.sidEmulation = myEngine->currBuilder;
     
     if (xs_cfg.forceSpeed) { 
         myEngine->currConfig.clockForced = true;
@@ -374,14 +419,18 @@
 
     /* Free internals */
     if (myEngine->currBuilder) {
+#ifndef HAVE_SIDPLAY2_COMI
         delete myEngine->currBuilder;
+#endif
         myEngine->currBuilder = NULL;
     }
 
+#ifndef HAVE_SIDPLAY2_COMI
     if (myEngine->currEng) {
         delete myEngine->currEng;
         myEngine->currEng = NULL;
     }
+#endif
 
     if (myEngine->currTune) {
         delete myEngine->currTune;
@@ -390,7 +439,7 @@
 
     xs_sidplay2_delete(myStatus);
 
-    g_free(myEngine);
+    delete myEngine;
     myStatus->sidEngine = NULL;
 }
 
--- a/src/sid/xs_slsup.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_slsup.h	Thu May 22 19:32:39 2008 +0200
@@ -11,18 +11,18 @@
 
 gint        xs_stil_init(void);
 void        xs_stil_close(void);
-stil_node_t    *xs_stil_get(gchar *filename);
+stil_node_t *xs_stil_get(gchar *filename);
 
 gint        xs_songlen_init(void);
 void        xs_songlen_close(void);
-sldb_node_t    *xs_songlen_get(const gchar *);
+sldb_node_t *xs_songlen_get(const gchar *);
 
-xs_tuneinfo_t    *xs_tuneinfo_new(const gchar * pcFilename,
-        gint nsubTunes, gint startTune, const gchar * sidName,
-        const gchar * sidComposer, const gchar * sidCopyright,
-        gint loadAddr, gint initAddr, gint playAddr,
-        gint dataFileLen, const gchar *sidFormat, gint sidModel);
-void    xs_tuneinfo_free(xs_tuneinfo_t *);
+xs_tuneinfo_t *xs_tuneinfo_new(const gchar * pcFilename,
+            gint nsubTunes, gint startTune, const gchar * sidName,
+            const gchar * sidComposer, const gchar * sidCopyright,
+            gint loadAddr, gint initAddr, gint playAddr,
+            gint dataFileLen, const gchar *sidFormat, gint sidModel);
+void        xs_tuneinfo_free(xs_tuneinfo_t *);
 
 
 #ifdef __cplusplus
--- a/src/sid/xs_stil.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_stil.c	Thu May 22 19:32:39 2008 +0200
@@ -228,7 +228,7 @@
                     if (subEntry < 1) {
                         XS_STILDB_ERR(lineNum, tmpLine,
                             "Number of subEntry (%i) for '%s' is invalid\n",
-                            subEntry, tmnode->filename);
+                            subEntry, tmnode ? tmnode->filename : "");
                         subEntry = 0;
                     }
                 } else {
--- a/src/sid/xs_stil.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_stil.h	Thu May 22 19:32:39 2008 +0200
@@ -10,25 +10,25 @@
 /* Types
  */
 typedef struct {
-    gchar    *name,
-        *author,
-        *title,
-        *info;
+    gchar *name,
+          *author,
+          *title,
+          *info;
 } stil_subnode_t;
 
 
 typedef struct _stil_node_t {
-    gchar            *filename;
-    gint            nsubTunes;
-    stil_subnode_t        **subTunes;
-    struct _stil_node_t    *prev, *next;
+    gchar               *filename;
+    gint                nsubTunes;
+    stil_subnode_t      **subTunes;
+    struct _stil_node_t *prev, *next;
 } stil_node_t;
 
 
 typedef struct {
-    stil_node_t    *nodes,
-            **pindex;
-    size_t        n;
+    stil_node_t *nodes,
+                **pindex;
+    size_t      n;
 } xs_stildb_t;
 
 
@@ -37,7 +37,7 @@
 gint            xs_stildb_read(xs_stildb_t *, gchar *);
 gint            xs_stildb_index(xs_stildb_t *);
 void            xs_stildb_free(xs_stildb_t *);
-stil_node_t *    xs_stildb_get_node(xs_stildb_t *, gchar *);
+stil_node_t *   xs_stildb_get_node(xs_stildb_t *, gchar *);
 
 #ifdef __cplusplus
 }
--- a/src/sid/xs_support.h	Thu May 22 19:31:48 2008 +0200
+++ b/src/sid/xs_support.h	Thu May 22 19:32:39 2008 +0200
@@ -15,7 +15,6 @@
 #ifdef AUDACIOUS_PLUGIN
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #else
 #include <xmms/plugin.h>
 #include <xmms/util.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/Makefile	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,41 @@
+PLUGIN = skins${PLUGIN_SUFFIX}
+
+SRCS = plugin.c \
+       skins_cfg.c \
+       pixbuf_effects.c \
+       dnd.c \
+       ui_fileopener.c \
+       ui_skin.c \
+       ui_skinned_window.c \
+       ui_dock.c \
+       util.c \
+       ui_vis.c \
+       ui_svis.c \
+       ui_skinned_menurow.c \
+       ui_skinned_button.c \
+       ui_skinned_textbox.c \
+       ui_skinned_playstatus.c \
+       ui_skinned_monostereo.c \
+       ui_skinned_number.c \
+       ui_skinned_horizontal_slider.c \
+       ui_skinned_equalizer_graph.c \
+       ui_skinned_equalizer_slider.c \
+       ui_skinned_playlist.c \
+       ui_skinned_playlist_slider.c \
+       ui_main.c \
+       ui_equalizer.c \
+       ui_playlist.c \
+       ui_main_evlisteners.c \
+       ui_playlist_evlisteners.c \
+       ui_manager.c \
+       ui_hints.c \
+       icons-stock.c
+
+include ../../buildsys.mk
+include ../../extra.mk
+
+plugindir := ${plugindir}/${GENERAL_PLUGIN_DIR}
+
+CFLAGS += ${PLUGIN_CFLAGS} ${BEEP_DEFINES}
+CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS}  ${GTK_CFLAGS} ${GLIB_CFLAGS} ${PANGO_CFLAGS} ${CAIRO_CFLAGS} ${PANGOCAIRO_CFLAGS} ${XRENDER_CFLAGS} ${XCOMPOSITE_CFLAGS} -I../..
+LIBS += ${GTK_LIBS} ${GLIB_LIBS} ${PANGO_LIBS} ${CAIRO_LIBS} ${PANGOCAIRO_LIBS} ${XRENDER_LIBS} ${XCOMPOSITE_LIBS} ${MOWGLI_LIBS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/actions-equalizer.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,38 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_ACTIONS_EQUALIZER_H
+#define AUDACIOUS_ACTIONS_EQUALIZER_H
+
+void action_equ_load_preset(void);
+void action_equ_load_auto_preset(void);
+void action_equ_load_default_preset(void);
+void action_equ_zero_preset(void);
+void action_equ_load_preset_file(void);
+void action_equ_load_preset_eqf(void);
+void action_equ_import_winamp_presets(void);
+void action_equ_save_preset(void);
+void action_equ_save_auto_preset(void);
+void action_equ_save_default_preset(void);
+void action_equ_save_preset_file(void);
+void action_equ_save_preset_eqf(void);
+void action_equ_delete_preset(void);
+void action_equ_delete_auto_preset(void);
+
+#endif /* AUDACIOUS_ACTIONS_EQUALIZER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/actions-mainwin.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,76 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_ACTIONS_MAINWIN_H
+#define AUDACIOUS_ACTIONS_MAINWIN_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+/* actions below are handled in mainwin.c */
+
+
+/* toggle actions */
+void action_anamode_peaks(GtkToggleAction*);
+void action_autoscroll_songname(GtkToggleAction*);
+void action_playback_noplaylistadvance(GtkToggleAction*);
+void action_playback_repeat(GtkToggleAction*);
+void action_playback_shuffle(GtkToggleAction*);
+void action_stop_after_current_song(GtkToggleAction*);
+void action_view_always_on_top(GtkToggleAction*);
+void action_view_scaled(GtkToggleAction*);
+void action_view_easymove(GtkToggleAction*);
+void action_view_on_all_workspaces(GtkToggleAction*);
+void action_roll_up_equalizer(GtkToggleAction*);
+void action_roll_up_player(GtkToggleAction*);
+void action_roll_up_playlist_editor(GtkToggleAction*);
+void action_show_equalizer(GtkToggleAction*);
+void action_show_player(GtkToggleAction*);
+void action_show_playlist_editor(GtkToggleAction*);
+
+/* radio actions (one for each radio action group) */
+void action_anafoff(GtkAction*,GtkRadioAction*);
+void action_anamode(GtkAction*,GtkRadioAction*);
+void action_anatype(GtkAction*,GtkRadioAction*);
+void action_peafoff(GtkAction*,GtkRadioAction*);
+void action_refrate(GtkAction*,GtkRadioAction*);
+void action_scomode(GtkAction*,GtkRadioAction*);
+void action_vismode(GtkAction*,GtkRadioAction*);
+void action_vprmode(GtkAction*,GtkRadioAction*);
+void action_wshmode(GtkAction*,GtkRadioAction*);
+void action_viewtime(GtkAction*,GtkRadioAction*);
+
+/* normal actions */
+void action_about_audacious(void);
+void action_ab_clear(void);
+void action_ab_set(void);
+void action_jump_to_file(void);
+void action_jump_to_playlist_start(void);
+void action_jump_to_time(void);
+void action_play_file(void);
+void action_play_location(void);
+void action_playback_next(void);
+void action_playback_pause(void);
+void action_playback_play(void);
+void action_playback_previous(void);
+void action_playback_stop(void);
+void action_preferences(void);
+void action_quit(void);
+void action_current_track_info(void);
+#endif /* AUDACIOUS_ACTIONS_MAINWIN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/actions-playlist.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,74 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_ACTIONS_PLAYLIST_H
+#define AUDACIOUS_ACTIONS_PLAYLIST_H
+
+void action_playlist_load_list(void);
+void action_playlist_save_list(void);
+void action_playlist_save_default_list(void);
+void action_playlist_refresh_list(void);
+void action_open_list_manager(void);
+
+void action_playlist_prev(void);
+void action_playlist_new(void);
+void action_playlist_next(void);
+void action_playlist_delete(void);
+
+void action_playlist_search_and_select(void);
+void action_playlist_invert_selection(void);
+void action_playlist_select_all(void);
+void action_playlist_select_none(void);
+
+void action_playlist_clear_queue(void);
+void action_playlist_remove_unavailable(void);
+void action_playlist_remove_dupes_by_title(void);
+void action_playlist_remove_dupes_by_filename(void);
+void action_playlist_remove_dupes_by_full_path(void);
+void action_playlist_remove_all(void);
+void action_playlist_remove_selected(void);
+void action_playlist_remove_unselected(void);
+
+/* void action_playlist_add_cd(void); - this is no longer needed, as the respective menu entry is added from within the cdaudio plugin */
+void action_playlist_add_url(void);
+void action_playlist_add_files(void);
+
+void action_playlist_randomize_list(void);
+void action_playlist_reverse_list(void);
+
+void action_playlist_sort_by_title(void);
+void action_playlist_sort_by_artist(void);
+void action_playlist_sort_by_filename(void);
+void action_playlist_sort_by_full_path(void);
+void action_playlist_sort_by_date(void);
+void action_playlist_sort_by_track_number(void);
+void action_playlist_sort_by_playlist_entry(void);
+
+void action_playlist_sort_selected_by_title(void);
+void action_playlist_sort_selected_by_artist(void);
+void action_playlist_sort_selected_by_filename(void);
+void action_playlist_sort_selected_by_full_path(void);
+void action_playlist_sort_selected_by_date(void);
+void action_playlist_sort_selected_by_track_number(void);
+void action_playlist_sort_selected_by_playlist_entry(void);
+
+void action_playlist_track_info(void);
+void action_queue_toggle(void);
+
+#endif /* AUDACIOUS_ACTIONS_PLAYLIST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/debug.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,44 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <glib.h>
+
+#ifdef NDEBUG
+
+/* void REQUIRE_LOCK(GMutex *m); */
+#  define REQUIRE_LOCK(m)
+
+/* void REQUIRE_STR_UTF8(const gchar *str); */
+#  define REQUIRE_STR_UTF8(str)
+
+/* void REQUIRE_STATIC_LOCK(GStaticMutex *m); */
+#  define REQUIRE_STATIC_LOCK(m)
+
+#else                           /* !NDEBUG */
+
+/* void REQUIRE_LOCK(GMutex *m); */
+#  define REQUIRE_LOCK(m) G_STMT_START { \
+       if (g_mutex_trylock(m)) { \
+           g_critical(G_STRLOC ": Mutex not locked!"); \
+           g_mutex_unlock(m); \
+       } \
+   } G_STMT_END
+
+/* void REQUIRE_STATIC_LOCK(GStaticMutex *m); */
+#  define REQUIRE_STATIC_LOCK(m) G_STMT_START { \
+       if (G_TRYLOCK(m)) { \
+           g_critical(G_STRLOC ": Mutex not locked!"); \
+           G_UNLOCK(m); \
+       } \
+   } G_STMT_END
+
+/* void REQUIRE_STR_UTF8(const gchar *str); */
+#  define REQUIRE_STR_UTF8(str) G_STMT_START { \
+       if (!g_utf_validate(str, -1, NULL)) \
+            g_warning(G_STRLOC ": String is not UTF-8!"); \
+   } G_STMT_END
+
+#endif                          /* NDEBUG */
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/dnd.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,34 @@
+/*  Audacious
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#include "dnd.h"
+
+void
+aud_drag_dest_set(GtkWidget *widget)
+{
+    gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
+					  aud_drop_types, 5,
+                      GDK_ACTION_COPY | GDK_ACTION_MOVE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/dnd.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,51 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_DND_H
+#define AUDACIOUS_DND_H
+
+#include <gtk/gtk.h>
+
+/* Designate dropped data types that we know and care about */
+enum {
+    BMP_DROP_STRING,
+    BMP_DROP_PLAINTEXT,
+    BMP_DROP_URLENCODED,
+    BMP_DROP_SKIN,
+    BMP_DROP_FONT
+};
+
+/* Drag data format listing for gtk_drag_dest_set() */
+static const GtkTargetEntry aud_drop_types[] = {
+    {"text/plain", 0, BMP_DROP_PLAINTEXT},
+    {"text/uri-list", 0, BMP_DROP_URLENCODED},
+    {"STRING", 0, BMP_DROP_STRING},
+    {"interface/x-winamp-skin", 0, BMP_DROP_SKIN},
+    {"application/x-font-ttf", 0, BMP_DROP_FONT},
+};
+
+void aud_drag_dest_set(GtkWidget*);
+
+#endif /* AUDACIOUS_DND_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/icons-stock.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,61 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2008  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+
+#include "icons-stock.h"
+#include "plugin.h"
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+
+static void
+load_stock_icon(gchar *id, gchar *filename, GtkIconFactory *iconfactory)
+{
+    GtkIconSet *iconset;
+    GdkPixbuf *pixbuf;
+
+    pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
+    if (pixbuf == NULL)
+        return;
+
+    iconset = gtk_icon_set_new_from_pixbuf(pixbuf);
+    g_object_unref(pixbuf);
+
+    gtk_icon_factory_add(iconfactory, id, iconset);
+}
+
+void
+register_aud_stock_icons(void)
+{
+    GtkIconFactory *iconfactory = gtk_icon_factory_new();
+
+    load_stock_icon(AUD_STOCK_PLAYLIST,
+                    DATA_DIR "/images/menu_playlist.png", iconfactory);
+    load_stock_icon(AUD_STOCK_PLUGIN,
+                    DATA_DIR "/images/menu_plugin.png", iconfactory);
+    load_stock_icon(AUD_STOCK_QUEUETOGGLE,
+                    DATA_DIR "/images/menu_queue_toggle.png", iconfactory);
+    load_stock_icon(AUD_STOCK_RANDOMIZEPL,
+                    DATA_DIR "/images/menu_randomize_playlist.png", iconfactory);
+    
+
+    gtk_icon_factory_add_default( iconfactory );
+    g_object_unref( iconfactory );
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/icons-stock.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,32 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_ICONS_STOCK_H
+#define AUDACIOUS_ICONS_STOCK_H
+
+void register_aud_stock_icons(void);
+
+/* this header contains macro defines for Audacious stock icons */
+
+#define AUD_STOCK_PLAYLIST			"aud-playlist"
+#define AUD_STOCK_PLUGIN			"aud-plugin"
+#define AUD_STOCK_QUEUETOGGLE		"aud-queuetoggle"
+#define AUD_STOCK_RANDOMIZEPL		"aud-randomizepl"
+
+#endif /* AUDACIOUS_ICONS_STOCK_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/images/audacious_eq.xpm	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,93 @@
+/* XPM */
+static char *audacious_eq_icon[] = {
+/* columns rows colors chars-per-pixel */
+"48 48 39 1",
+"  c #e36d45",
+". c #e3734d",
+"X c #e47650",
+"o c #e57a54",
+"O c #e57d59",
+"+ c #e6805d",
+"@ c #e68462",
+"# c #e78867",
+"$ c #e88665",
+"% c #e88866",
+"& c #e88b6b",
+"* c #e98f70",
+"= c #e99274",
+"- c #ea9678",
+"; c #ea997c",
+": c #eb9d82",
+"> c #eca086",
+", c #eda48b",
+"< c #eda88f",
+"1 c #eeab93",
+"2 c #efae98",
+"3 c #efb09a",
+"4 c #f0b29c",
+"5 c #f0b5a0",
+"6 c #f2bba7",
+"7 c #f2bca9",
+"8 c #f3c5b4",
+"9 c #f4c7b8",
+"0 c #f4cabb",
+"q c #f5cec0",
+"w c #f6d6cb",
+"e c #f7d8ce",
+"r c #f7dad0",
+"t c #f8dcd3",
+"y c #f8e0d7",
+"u c #f9e3db",
+"i c #fae6e0",
+"p c #fae8e2",
+"a c white",
+/* pixels */
+"<2211111<<<<,,:,,:::::::::::-=---=====&=&&&=&&&&",
+"255544422211111<<<<<<::::::::::----======&&&&&##",
+"25554442222111<<,<,,:<:::::::------===&=*&&&%%$&",
+"1544442222111<<<<<<:<:<:::::-:----=====&*&&&&%$%",
+"2554442221111<<<:<:<::<::::::----=====&=&&%&$%%#",
+"154442222111<<,<,,<:<::::-:-----====&&=&&&&&%$@$",
+"14422222111<<<,,,:<<:::::::-:----====&&&&&&%%$@%",
+"1442222211<<<<,,:<:::::::--:=--===&==&&&&&%%$@@%",
+"1442221211<<<,,,,<,:::::-:----=-====&&&&&$$$$@@@",
+"124222111<<<,,,,::::::;::-:---===*=&=&&&&&%@@@@#",
+"1422211111<<<,,,,:::::;;-:----===&=&&&&&$$$#@@@@",
+"12222111<taaaaa4,:::::::---=======&=&&&&%$$@@@+@",
+"<222111<<taaaaa4::::::-----=-====&&&&&&&$$#@+@+@",
+"<2211111<taaaaa4,::::--::---==**=&&&&&$%$#@@@++@",
+"<21211<<<taaaaa3:::::::----====*=&&&&%%%$@@+@+O@",
+"<21211<<<taaaaa3::::-:---=====**&&&&&%%$@@@@+++@",
+"<111<<<<<taaaaa3::::;;----*-*=&&&&&&#%$$@@@+++O+",
+"<111<<<,,raaaaa3:::;;;---*-*==*&&&&&%%$@@@@+++++",
+",1111<<,,raaaaa2::;;-----111111&&&&%%$#@@@+++OO+",
+"<11<<<<,<raaaaa2;;;;;---:aaaaap&&&%%$$@@@+++OOO+",
+",8ppppi8>raaaaa3:;;;--*-;aaaaap&&%&$$$@@++@OOOOO",
+",qaaaaa0,raaaaa1;;;----=-aaaaap&&%#$@@@+@+O+OOO+",
+",qaaaaa0,raaaaa2;;;---==;aaaaap&&%%$#@@@OO+OOooO",
+",qaaaaa0>eaaaaa1;;---===-aaaaap&%%$$@++@++OOOOo+",
+",0aaaaa0,eaaaaa1;;----==-aaaaap#&$$#@@+++OOOOooO",
+",0aaaaa0>raaaaa1-;--====-aaaaap#:ttttt7++OOooooO",
+",qaaaaa0:taaaaa1--;==***-aaaaap$,aaaaaq+OOOooooO",
+":0aaaaa9:eaaaaa1-wuuuuu&-aaaaap$,aaaaaq+OOOooo.O",
+",0aaaaa0:eaaaaa1-paaaaa==aaaaap$,aaaaaqOOOooo.oO",
+":0aaaaa9:eaaaaa1=paaaaa&=aaaaap@,aaaaaqOOOoooo.O",
+":qaaaaa9:waaaaa1-paaaaa=-aaaaap@>aaaaaqOoOoooXXO",
+":0aaaaa9:waaaaa1=paaaaa&*aaaaap@,aaaaa0O;77777*o",
+">0aaaaa9:eaaaaa1=paaaaa&=aaaaap@>aaaaaqO4aaaaa3o",
+":0aaaaa8;eaaaaa,-paaaaa&=aaaaai@>aaaaaqo4aaaaa3o",
+":0aaaaa8;eaaaaa,=paaaaa&=aaaaap+>aaaaa0O5aaaaa3o",
+":9aaaaa9;eaaaaa<*paaaaa&&aaaaai@>aaaaaqo4aaaaa3o",
+":0aaaaa8;eaaaaa,&paaaaa&=aaaaap+>aaaaa0o4aaaaa2o",
+":9aaaaa8-eaaaaa,=paaaaa%&aaaaau+:aaaaa0o4aaaaa2X",
+":9aaaaa8;waaaaa,=paaaaa%=aaaaai+>aaaaa0o4aaaaa2X",
+":9aaaaa8-eaaaaa,&paaaaa#&aaaaai+:aaaaa0o4aaaaa3X",
+";8aaaaa8-waaaaa,*paaaaa#*aaaaapO:aaaaa0o3aaaaa2X",
+";8aaaaa8-waaaaa,*paaaaa#&aaaaaiO:aaaaa0X3aaaaa2.",
+";9aaaaa8-waaaaa>&paaaaa@&aaaaaiO:aaaaa0X3aaaaa1o",
+";9aaaaa8=eaaaaa,&paaaaa@&aaaaauO:aaaaa0.3aaaaa1.",
+";8aaaaa8*waaaaa:&paaaaa@&aaaaauO:aaaaa0.2aaaaa1.",
+";8aaaaa8=waaaaa>&paaaaa@&aaaaaiO;aaaaa0.3aaaaa2.",
+";;;;;=====***&*&#&###@@+++OOOOOooXoX..... .    .",
+"---=--====*****&&&&#####@@@@++++OOOOOooooXXX...o"
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/images/audacious_playlist.xpm	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,97 @@
+/* XPM */
+static char *audacious_playlist_icon[] = {
+/* columns rows colors chars-per-pixel */
+"48 48 43 1",
+"  c #e36e45",
+". c #e3734c",
+"X c #e47650",
+"o c #e57a54",
+"O c #e57d59",
+"+ c #e6815d",
+"@ c #e68462",
+"# c #e78867",
+"$ c #e88665",
+"% c #e88866",
+"& c #e88b6b",
+"* c #e98f70",
+"= c #e99273",
+"- c #ea9679",
+"; c #eb997c",
+": c #eb9d82",
+"> c #eca086",
+", c #eda48b",
+"< c #eda88f",
+"1 c #eeaa93",
+"2 c #efae98",
+"3 c #efb09a",
+"4 c #f0b29c",
+"5 c #f0b5a1",
+"6 c #f0b8a5",
+"7 c #f1bcaa",
+"8 c #f2c0ae",
+"9 c #f3c3b3",
+"0 c #f4c7b8",
+"q c #f4cabb",
+"w c #f5cec1",
+"e c #f5d1c4",
+"r c #f6d5c9",
+"t c #f7d8cd",
+"y c #f8ddd4",
+"u c #f8e0d7",
+"i c #f9e3db",
+"p c #fae6e0",
+"a c #fae8e2",
+"s c #fcf0eb",
+"d c #fcf4f1",
+"f c #fdf9f7",
+"g c white",
+/* pixels */
+"<22111111<<<<,,,::,:::::;;;;;;----====*=&=&&&&#&",
+"2555444423111<<<<<<,,>>>:>:;;;;;---====**&&&&&#&",
+"25444424312111<<<,,,,>,>>:::;;;----===&==&&&%%%#",
+"1554442222111<<<,,,,,>:::::;;;----=====&&&&&%%%#",
+"25544422211111<<,,,,::>::;;;;;---=====**&&&%%%$#",
+"19aat22uaaaapppppppppppipppiiipiiiiiu4*&4iuuue$$",
+"19ggi22sggggggggggggggggggggggggggggg7*&7ggggp%$",
+"10ggi24dggggggggggggggggggggggggggggg7*&7ggggp@#",
+"17trq21errreeeeeewwwwwwwqwqqq0qq00000,&&,99993@$",
+"1333221111<<<,,,,,>::::;;;;-;--===***&&%##$#$@@@",
+"1432211111<<<,,,>>>>:::;;;;---===***&&&&####$@@@",
+"<9aar11upaappppppipipiiiiiiiu5===***&&&%2uyyyw+@",
+"<0ggu11dggggggggggggggggggggg9=&=&&*&&%%7ggggp@@",
+"10ggu11dggggggggggggggggggggg9===&&&&&%%7ggggp+@",
+"<39961<6766666665554544442222:&=**&&&#%%=,,>>;O@",
+"<11111<<<<<,,>::::::;;----=====&*&&&&%%$@@@@++++",
+"<1111<<1<,,>,>::::;;;-;----=*=&&&&&%%%$$@@@+++O@",
+"<0ggy<<sgggggggggggggggggggggggggggs%%$$6ggggpO+",
+"<9ggy<<sgggggggggggggggggggggggggggs%%$@6ggggpO+",
+",9ggy<<sgggggggggggggggggggggggggggs%%$@6ggggiO+",
+",2774<,,,,>:>:::;;;----====&&&&&&%%%$$$+@+++OOO+",
+",111<<,,>>>>::::;;;---=-===&=&&&%#%%$@@+++++OOO+",
+",<<<,,,6665555545444422112111111<#$$@@@@&:>;;-OO",
+",9ggy,,fggggggggggggggggggggggggd$$$@@+@6ggggpoO",
+",9ggy,:fggggggggggggggggggggggggf$$@@@++6ggggpoO",
+",7ggy,,fggggggggggggggggggggggggd$$@@+@+5ggggioO",
+"><<,,,:,>::::;;;---=====&&&&&%%%$$@@+@++OOOooooO",
+":<,,>,>::::;;;;----====&&=&&&%%%$@@@@OOOOOOooo.O",
+":1664,:65535444442121211<11<1<,,,,>>,&+O&:;;;-oO",
+",7ggy><gggggggggggggggggggggggggggggg6OO5ggggp.O",
+":6ggu:,gggggggggggggggggggggggggggggg3OO5ggggi.o",
+":5ppe:,ippiiiiiiiiiuiiuiuuuuuuyyyyuyy1Oo<yyyy0Xo",
+">,,:::::::;;----====&&&&&%%%$$@@++++OOOOoooXXX.o",
+":,,::::::;;;;--=-==***&%&&&%%$@@@+++OOOooooo.X.o",
+":1ww9:<qqqqqq000000000099,%%@@@@+++OOoOo-76662.o",
+":6ggy:1gggggggggggggggggg7$$@@@+++OOOOoo4ggggi.o",
+":6ggy:2gggggggggggggggggg6%@@@++++OOOOoo4ggggi.X",
+":2ppw:1iiiiiiiiuuiuiuuuiu2@@@@+++OOOoooo,yyyy0.o",
+";::::;;;;--======&=&&&%%%$$@+@+++OOOoooo.X.... X",
+";,::;;;;----=====&&&&%#$$$@@@+++OOOoOoo.o... ..X",
+";1wq7;1qqq0q0000900999999979979777777;oo-66661 o",
+";5ggy;5gggggggggggggggggggggggggggggg3..3ggggi .",
+";5ggy;6gggggggggggggggggggggggggggggg2o.3ggggi X",
+"-1wq7;<q0q000000099999979779777777777-X.=66561 .",
+";;;:=;-====**=&&&&#%#$@@@@+++OOOOoooo..o....   .",
+";;;;;;=-====*&&&&&#%$$@@++++OOOOooooo.....     .",
+";;;-;-====***&*&&###$@@@++O+OOOooo.o.... ..    .",
+"----=-======&=&&&#&##$$$@@@@+++OOOOOOoooo.o....o"
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/pixbuf_effects.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,88 @@
+/*
+ * Audacious
+ * Copyright (c) 2006-2007 Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include <glib.h>
+
+#include "platform/smartinclude.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+static GdkPixbuf *
+create_new_pixbuf (GdkPixbuf *src)
+{
+    g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+    g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+                           && gdk_pixbuf_get_n_channels (src) == 3)
+                           || (gdk_pixbuf_get_has_alpha (src)
+                           && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+
+    return gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
+                           gdk_pixbuf_get_has_alpha (src),
+                           gdk_pixbuf_get_bits_per_sample (src),
+                           gdk_pixbuf_get_width (src),
+                           gdk_pixbuf_get_height (src));
+}
+
+GdkPixbuf *
+audacious_create_colorized_pixbuf (GdkPixbuf *src,
+                                   int red_value,
+                                   int green_value,
+                                   int blue_value)
+{
+    int i, j;
+    int width, height, has_alpha, src_row_stride, dst_row_stride;
+    guchar *target_pixels;
+    guchar *original_pixels;
+    guchar *pixsrc;
+    guchar *pixdest;
+    GdkPixbuf *dest;
+
+    g_return_val_if_fail (gdk_pixbuf_get_colorspace (src) == GDK_COLORSPACE_RGB, NULL);
+    g_return_val_if_fail ((!gdk_pixbuf_get_has_alpha (src)
+                           && gdk_pixbuf_get_n_channels (src) == 3)
+                           || (gdk_pixbuf_get_has_alpha (src)
+                           && gdk_pixbuf_get_n_channels (src) == 4), NULL);
+    g_return_val_if_fail (gdk_pixbuf_get_bits_per_sample (src) == 8, NULL);
+
+    dest = create_new_pixbuf (src);
+
+    has_alpha = gdk_pixbuf_get_has_alpha (src);
+    width = gdk_pixbuf_get_width (src);
+    height = gdk_pixbuf_get_height (src);
+    src_row_stride = gdk_pixbuf_get_rowstride (src);
+    dst_row_stride = gdk_pixbuf_get_rowstride (dest);
+    target_pixels = gdk_pixbuf_get_pixels (dest);
+    original_pixels = gdk_pixbuf_get_pixels (src);
+
+    for (i = 0; i < height; i++) {
+        pixdest = target_pixels + i*dst_row_stride;
+        pixsrc = original_pixels + i*src_row_stride;
+        for (j = 0; j < width; j++) {
+            *pixdest++ = (*pixsrc++ * red_value) >> 8;
+            *pixdest++ = (*pixsrc++ * green_value) >> 8;
+            *pixdest++ = (*pixsrc++ * blue_value) >> 8;
+            if (has_alpha) {
+               *pixdest++ = *pixsrc++;
+            }
+        }
+    }
+    return dest;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/platform/smartinclude.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,34 @@
+/*
+ *  Copyright (c) 2006-2007 William Pitcock <nenolod -at- nenolod.net>
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#include <gdk/gdk.h>
+
+#ifdef GDK_WINDOWING_X11
+# include <gdk/gdkx.h>
+#endif
+
+#ifdef GDK_WINDOWING_WIN32
+# include <gdk/gdkwin32.h>
+#endif
+
+#include <gdk/gdkkeysyms.h>
+
+#ifdef GDK_WINDOWING_QUARTZ
+# include <Carbon/Carbon.h>
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,98 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2008 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+
+#include "plugin.h"
+#include "skins_cfg.h"
+#include "ui_skin.h"
+#include "ui_skinned_window.h"
+#include "ui_manager.h"
+#include "icons-stock.h"
+#include <audacious/i18n.h>
+#include <libintl.h>
+
+#define PACKAGE "audacious-plugins"
+
+GeneralPlugin skins_gp =
+{
+    .description= "Audacious Skinned GUI",
+    .init = skins_init,
+    .about = skins_about,
+    .configure = skins_configure,
+    .cleanup = skins_cleanup
+};
+
+GeneralPlugin *skins_gplist[] = { &skins_gp, NULL };
+SIMPLE_GENERAL_PLUGIN(skins, skins_gplist);
+GtkWidget *mainwin;
+gboolean plugin_is_active = FALSE;
+
+void skins_init(void) {
+    plugin_is_active = TRUE;
+    g_log_set_handler(NULL, G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);
+
+    skins_cfg_load();
+
+    register_aud_stock_icons();
+    ui_manager_init();
+    ui_manager_create_menus();
+    mainwin_setup_menus();
+
+    init_skins(config.skin);
+
+    mainwin_real_show();
+
+    return;
+}
+
+void skins_cleanup(void) {
+    if (plugin_is_active == TRUE) {
+        skins_cfg_free();
+        gtk_widget_destroy(mainwin);
+        gtk_widget_destroy(equalizerwin);
+        skin_destroy(aud_active_skin);
+        aud_active_skin = NULL;
+        mainwin = NULL;
+        equalizerwin = NULL;
+        plugin_is_active = FALSE;
+    }
+
+    return;
+}
+
+
+void skins_configure(void) {
+    return;
+}
+
+void skins_about(void) {
+    static GtkWidget* about_window = NULL;
+
+    if (about_window) {
+        gtk_window_present(GTK_WINDOW(about_window));
+        return;
+    }
+
+    about_window = audacious_info_dialog(_("About Skinned GUI"),
+                   _("Copyright (c) 2008, by Tomasz Moń <desowin@gmail.com>\n\n"),
+                   _("OK"), FALSE, NULL, NULL);
+
+    g_signal_connect(G_OBJECT(about_window), "destroy",	G_CALLBACK(gtk_widget_destroyed), &about_window);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/plugin.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,37 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2008 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef PLUGIN_SKINS_H
+#define PLUGIN_SKINS_H
+
+#include <glib.h>
+#include <audacious/plugin.h>
+#include "skins_cfg.h"
+#include "ui_main.h"
+#include "ui_equalizer.h"
+
+#define PACKAGE_NAME "audacious-plugins"
+
+void skins_init(void);
+void skins_cleanup(void);
+void skins_configure(void);
+void skins_about(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/skins_cfg.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,228 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2008 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+
+#include "skins_cfg.h"
+#include "ui_skin.h"
+#include "ui_vis.h"
+#include <glib.h>
+#include <stdlib.h>
+#include <audacious/plugin.h>
+
+skins_cfg_t config;
+
+skins_cfg_t skins_default_config = {
+    .scaled = FALSE,
+    .autoscroll = TRUE,
+    .always_on_top = FALSE,
+    .sticky = FALSE,
+    .scale_factor = 2.0,
+    .always_show_cb = TRUE,
+    .close_dialog_open = TRUE,
+    .close_dialog_add = TRUE,
+    .skin = NULL,
+    .filesel_path = NULL,
+    .playlist_visible = FALSE,
+    .equalizer_visible = FALSE,
+    .player_visible = TRUE,
+    .player_shaded = FALSE,
+    .equalizer_shaded = FALSE,
+    .playlist_shaded = FALSE,
+    .dim_titlebar = TRUE,
+    .show_wm_decorations = FALSE,
+    .easy_move = TRUE,
+    .allow_broken_skins = FALSE,
+    .warn_about_broken_gtk_engines = TRUE,
+    .warn_about_win_visibility = TRUE,
+    .disable_inline_gtk = FALSE,
+    .timer_mode = 0,
+    .vis_type = VIS_ANALYZER,
+    .analyzer_mode = ANALYZER_NORMAL,
+    .analyzer_type = ANALYZER_BARS,
+    .scope_mode = SCOPE_DOT,
+    .voiceprint_mode = VOICEPRINT_NORMAL,
+    .vu_mode = VU_SMOOTH,
+    .vis_refresh = REFRESH_FULL,
+    .analyzer_falloff = FALLOFF_FAST,
+    .peaks_falloff = FALLOFF_SLOW,
+    .player_x = MAINWIN_DEFAULT_POS_X,
+    .player_y = MAINWIN_DEFAULT_POS_Y,
+    .equalizer_x = EQUALIZER_DEFAULT_POS_X,
+    .equalizer_y = EQUALIZER_DEFAULT_POS_Y,
+    .playlist_x = PLAYLISTWIN_DEFAULT_POS_X,
+    .playlist_y = PLAYLISTWIN_DEFAULT_POS_Y,
+    .playlist_width = PLAYLISTWIN_DEFAULT_WIDTH,
+    .playlist_height = PLAYLISTWIN_DEFAULT_HEIGHT,
+    .playlist_position = 0,
+    .mouse_change = 8,                 /* mouse wheel scroll step */
+    .scroll_pl_by = 3,
+    .colorize_r = 255, .colorize_g = 255, .colorize_b = 255,
+    .snap_distance = 10,
+    .snap_windows = TRUE,
+    .save_window_position = TRUE,
+    .analyzer_peaks = TRUE,
+    .twoway_scroll = TRUE,             /* use back and forth scroll */
+    .mainwin_use_bitmapfont = TRUE,
+    .eq_scaled_linked = TRUE,
+    .use_xmms_style_fileselector = FALSE,
+    .show_numbers_in_pl = TRUE,
+    .show_separator_in_pl = TRUE,
+};
+
+typedef struct skins_cfg_boolent_t {
+    char const *be_vname;
+    gboolean *be_vloc;
+    gboolean be_wrt;
+} skins_cfg_boolent;
+
+static skins_cfg_boolent skins_boolents[] = {
+    {"always_show_cb", &config.always_show_cb, TRUE},
+    {"always_on_top", &config.always_on_top, TRUE},
+    {"sticky", &config.sticky, TRUE},
+    {"always_show_cb", &config.always_show_cb, TRUE},
+    {"scaled", &config.scaled, TRUE},
+    {"autoscroll_songname", &config.autoscroll, TRUE},
+    {"equalizer_visible", &config.equalizer_visible, TRUE},
+    {"playlist_visible", &config.playlist_visible, TRUE},
+    {"player_visible", &config.player_visible, TRUE},
+    {"player_shaded", &config.player_shaded, TRUE},
+    {"equalizer_shaded", &config.equalizer_shaded, TRUE},
+    {"playlist_shaded", &config.playlist_shaded, TRUE},
+    {"dim_titlebar", &config.dim_titlebar, TRUE},
+    {"show_wm_decorations", &config.show_wm_decorations, TRUE},
+    {"easy_move", &config.easy_move, TRUE},
+    {"allow_broken_skins", &config.allow_broken_skins, TRUE},
+    {"disable_inline_gtk", &config.disable_inline_gtk, TRUE},
+    {"snap_windows", &config.snap_windows, TRUE},
+    {"save_window_positions", &config.save_window_position, TRUE},
+    {"analyzer_peaks", &config.analyzer_peaks, TRUE},
+    {"twoway_scroll", &config.twoway_scroll, TRUE},
+    {"warn_about_win_visibility", &config.warn_about_win_visibility, TRUE},
+    {"warn_about_broken_gtk_engines", &config.warn_about_broken_gtk_engines, TRUE},
+    {"mainwin_use_bitmapfont", &config.mainwin_use_bitmapfont, TRUE},
+    {"eq_scaled_linked", &config.eq_scaled_linked, TRUE},
+    {"show_numbers_in_pl", &config.show_numbers_in_pl, TRUE},
+    {"show_separator_in_pl", &config.show_separator_in_pl, TRUE},
+};
+
+static gint ncfgbent = G_N_ELEMENTS(skins_boolents);
+
+typedef struct skins_cfg_nument_t {
+    char const *ie_vname;
+    gint *ie_vloc;
+    gboolean ie_wrt;
+} skins_cfg_nument;
+
+static skins_cfg_nument skins_numents[] = {
+    {"player_x", &config.player_x, TRUE},
+    {"player_y", &config.player_y, TRUE},
+    {"timer_mode", &config.timer_mode, TRUE},
+    {"vis_type", &config.vis_type, TRUE},
+    {"analyzer_mode", &config.analyzer_mode, TRUE},
+    {"analyzer_type", &config.analyzer_type, TRUE},
+    {"scope_mode", &config.scope_mode, TRUE},
+    {"vu_mode", &config.vu_mode, TRUE},
+    {"voiceprint_mode", &config.voiceprint_mode, TRUE},
+    {"vis_refresh_rate", &config.vis_refresh, TRUE},
+    {"analyzer_falloff", &config.analyzer_falloff, TRUE},
+    {"peaks_falloff", &config.peaks_falloff, TRUE},
+    {"playlist_x", &config.playlist_x, TRUE},
+    {"playlist_y", &config.playlist_y, TRUE},
+    {"playlist_width", &config.playlist_width, TRUE},
+    {"playlist_height", &config.playlist_height, TRUE},
+    {"playlist_position", &config.playlist_position, TRUE},
+    {"equalizer_x", &config.equalizer_x, TRUE},
+    {"equalizer_y", &config.equalizer_y, TRUE},
+    {"mouse_wheel_change", &config.mouse_change, TRUE},
+    {"scroll_pl_by", &config.scroll_pl_by, TRUE},
+    {"colorize_r", &config.colorize_r, TRUE},
+    {"colorize_g", &config.colorize_g, TRUE},
+    {"colorize_b", &config.colorize_b, TRUE},
+    {"snap_distance", &config.snap_distance, TRUE},
+};
+
+static gint ncfgient = G_N_ELEMENTS(skins_numents);
+
+void skins_cfg_free() {
+    if (config.skin) { g_free(config.skin); config.skin = NULL; }
+}
+
+void skins_cfg_load() {
+    mcs_handle_t *cfgfile = aud_cfg_db_open();
+
+  /* if (!aud_cfg_db_get_int(cfgfile, "skins", "field_name", &(cfg->where)))
+         cfg->where = default value
+     if (!aud_cfg_db_get_string(cfgfile, "skins", "field_name", &(cfg->where)))
+         cfg->where = g_strdup("defaul");
+     if (!aud_cfg_db_get_bool(cfgfile, "skins", "field_name", &(cfg->where)))
+         cfg->where = FALSE / TRUE;
+  */
+  
+    memcpy(&config, &skins_default_config, sizeof(skins_cfg_t));
+    int i;
+    
+    for (i = 0; i < ncfgbent; ++i) {
+        aud_cfg_db_get_bool(cfgfile, "skins",
+                            skins_boolents[i].be_vname,
+                            skins_boolents[i].be_vloc);
+    }
+    
+    for (i = 0; i < ncfgient; ++i) {
+        aud_cfg_db_get_int(cfgfile, "skins",
+                           skins_numents[i].ie_vname,
+                           skins_numents[i].ie_vloc);
+    }
+
+    if (!aud_cfg_db_get_string(cfgfile, "skins", "skin", &(config.skin)))
+        config.skin = g_strdup(BMP_DEFAULT_SKIN_PATH);
+
+    if (!aud_cfg_db_get_float(cfgfile, "skins", "scale_factor", &(config.scale_factor)))
+        config.scale_factor = 2.0;
+
+    aud_cfg_db_close(cfgfile);
+}
+
+
+void skins_cfg_save(skins_cfg_t * cfg) {
+    mcs_handle_t *cfgfile = aud_cfg_db_open();
+
+/*
+    aud_cfg_db_set_int(cfgfile, "skins", "field_name", cfg->where);
+    aud_cfg_db_set_string(cfgfile, "skins", "field_name", cfg->where);
+    aud_cfg_db_set_bool(cfgfile, "skins", "field_name", cfg->where);
+*/
+    aud_cfg_db_set_string(cfgfile, "skins", "skin", cfg->skin);
+
+    int i;
+
+    for (i = 0; i < ncfgbent; ++i)
+        if (skins_boolents[i].be_wrt)
+            aud_cfg_db_set_bool(cfgfile, "skins",
+                                skins_boolents[i].be_vname,
+                                *skins_boolents[i].be_vloc);
+
+    for (i = 0; i < ncfgient; ++i)
+        if (skins_numents[i].ie_wrt)
+            aud_cfg_db_set_int(cfgfile, "skins",
+                               skins_numents[i].ie_vname,
+                               *skins_numents[i].ie_vloc);
+
+    aud_cfg_db_close(cfgfile);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/skins_cfg.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,86 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2008 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef SKINS_CFG_H
+#define SKINS_CFG_H
+
+#include <glib.h>
+
+#define MAINWIN_DEFAULT_POS_X    20
+#define MAINWIN_DEFAULT_POS_Y    20
+#define EQUALIZER_DEFAULT_POS_X  20
+#define EQUALIZER_DEFAULT_POS_Y  136
+#define PLAYLISTWIN_DEFAULT_WIDTH       275
+#define PLAYLISTWIN_DEFAULT_HEIGHT      232
+#define PLAYLISTWIN_DEFAULT_POS_X       295
+#define PLAYLISTWIN_DEFAULT_POS_Y       20
+
+
+typedef struct {
+    gint player_x, player_y;
+    gint equalizer_x, equalizer_y;
+    gint playlist_x, playlist_y;
+    gint playlist_width, playlist_height;
+    gint snap_distance;
+    gboolean snap_windows, save_window_position;
+    gboolean scaled, autoscroll;
+    gboolean always_on_top, sticky;
+    gfloat scale_factor;
+    gboolean always_show_cb;
+    gboolean close_dialog_open;
+    gboolean close_dialog_add;
+    gchar *skin;
+    gchar *filesel_path;
+    gboolean player_visible, equalizer_visible, playlist_visible;
+    gboolean player_shaded, equalizer_shaded, playlist_shaded;
+    gboolean dim_titlebar;
+    gboolean show_wm_decorations;
+    gboolean easy_move;
+    gboolean allow_broken_skins;
+    gboolean disable_inline_gtk;
+    gboolean analyzer_peaks;
+    gboolean twoway_scroll;
+    gint timer_mode;
+    gint vis_type;
+    gint analyzer_mode, analyzer_type;
+    gint scope_mode;
+    gint voiceprint_mode;
+    gint vu_mode, vis_refresh;
+    gint analyzer_falloff, peaks_falloff;
+    gint playlist_position;
+    gint mouse_change;
+    gint colorize_r; gint colorize_g; gint colorize_b;
+    gint scroll_pl_by;
+    gboolean warn_about_win_visibility;
+    gboolean warn_about_broken_gtk_engines;
+    gboolean mainwin_use_bitmapfont;
+    gboolean eq_scaled_linked;
+    gboolean use_xmms_style_fileselector;
+    gboolean show_numbers_in_pl, show_separator_in_pl;
+} skins_cfg_t;
+
+extern skins_cfg_t config;
+
+skins_cfg_t * skins_cfg_new(void);
+void skins_cfg_free();
+void skins_cfg_load();
+void skins_cfg_save();
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_dock.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,531 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_dock.h"
+#include "skins_cfg.h"
+#include <gdk/gdk.h>
+#include <stdlib.h>
+#include <audacious/plugin.h>
+#include "ui_skinned_window.h"
+
+#include "platform/smartinclude.h"
+
+static GList *dock_window_list = NULL;
+
+struct _DockedWindow {
+    GtkWindow *w;
+    gint offset_x, offset_y;
+};
+
+typedef struct _DockedWindow DockedWindow;
+
+
+static gint
+docked_list_compare(DockedWindow * a, DockedWindow * b)
+{
+    if (a->w == b->w)
+        return 0;
+    return 1;
+}
+
+static void
+snap_edge(gint * x, gint * y, gint w, gint h, gint bx, gint by,
+          gint bw, gint bh)
+{
+    gint sd = config.snap_distance;
+
+    if ((*x + w > bx - sd) && (*x + w < bx + sd) &&
+        (*y > by - h - sd) && (*y < by + bh + sd)) {
+        *x = bx - w;
+        if ((*y > by - sd) && (*y < by + sd))
+            *y = by;
+        if ((*y + h > by + bh - sd) && (*y + h < by + bh + sd))
+            *y = by + bh - h;
+    }
+    if ((*x > bx + bw - sd) && (*x < bx + bw + sd) &&
+        (*y > by - h - sd) && (*y < by + bh + sd)) {
+        *x = bx + bw;
+        if ((*y > by - sd) && (*y < by + sd))
+            *y = by;
+        if ((*y + h > by + bh - sd) && (*y + h < by + bh + sd))
+            *y = by + bh - h;
+    }
+}
+
+static void
+snap(gint * x, gint * y, gint w, gint h, gint bx, gint by, gint bw, gint bh)
+{
+    snap_edge(x, y, w, h, bx, by, bw, bh);
+    snap_edge(y, x, h, w, by, bx, bh, bw);
+}
+
+static void
+calc_snap_offset(GList * dlist, GList * wlist, gint x, gint y,
+                 gint * off_x, gint * off_y)
+{
+    gint nx, ny, nw, nh, sx, sy, sw, sh;
+    GtkWindow *w;
+    GList *dnode, *wnode;
+    DockedWindow temp, *dw;
+
+
+    *off_x = 0;
+    *off_y = 0;
+
+    if (!config.snap_windows)
+        return;
+
+    /*
+     * FIXME: Why not break out of the loop when we find someting
+     * to snap to?
+     */
+    for (dnode = dlist; dnode; dnode = g_list_next(dnode)) {
+        dw = dnode->data;
+        gtk_window_get_size(dw->w, &nw, &nh);
+
+        nx = dw->offset_x + *off_x + x;
+        ny = dw->offset_y + *off_y + y;
+
+        /* Snap to screen edges */
+        if (abs(nx) < config.snap_distance)
+            *off_x -= nx;
+        if (abs(ny) < config.snap_distance)
+            *off_y -= ny;
+        if (abs(nx + nw - gdk_screen_width()) < config.snap_distance)
+            *off_x -= nx + nw - gdk_screen_width();
+        if (abs(ny + nh - gdk_screen_height()) < config.snap_distance)
+            *off_y -= ny + nh - gdk_screen_height();
+
+        /* Snap to other windows */
+        for (wnode = wlist; wnode; wnode = g_list_next(wnode)) {
+            temp.w = wnode->data;
+            if (g_list_find_custom
+                (dlist, &temp, (GCompareFunc) docked_list_compare))
+                /* These windows are already docked */
+                continue;
+
+            w = GTK_WINDOW(wnode->data);
+            gtk_window_get_position(w, &sx, &sy);
+            gtk_window_get_size(w, &sw, &sh);
+
+            nx = dw->offset_x + *off_x + x;
+            ny = dw->offset_y + *off_y + y;
+
+            snap(&nx, &ny, nw, nh, sx, sy, sw, sh);
+
+            *off_x += nx - (dw->offset_x + *off_x + x);
+            *off_y += ny - (dw->offset_y + *off_y + y);
+        }
+    }
+}
+
+
+static gboolean
+is_docked(gint a_x, gint a_y, gint a_w, gint a_h,
+          gint b_x, gint b_y, gint b_w, gint b_h)
+{
+    if (((a_x == b_x + b_w) || (a_x + a_w == b_x)) &&
+        (b_y + b_h >= a_y) && (b_y <= a_y + a_h))
+        return TRUE;
+
+    if (((a_y == b_y + b_h) || (a_y + a_h == b_y)) &&
+        (b_x >= a_x - b_w) && (b_x <= a_x + a_w))
+        return TRUE;
+
+    return FALSE;
+}
+
+/*
+ * Builds a list of all windows that are docked to the window "w".
+ * Recursively adds all windows that are docked to the windows that are
+ * docked to "w" and so on...
+ * FIXME: init_off_?  ?
+ */
+
+static GList *
+get_docked_list(GList * dlist, GList * wlist, GtkWindow * w,
+                gint init_off_x, gint init_off_y)
+{
+    GList *node;
+    DockedWindow *dwin, temp;
+    gint w_x, w_y, w_width, w_height;
+    gint t_x, t_y, t_width, t_height;
+
+
+    gtk_window_get_position(w, &w_x, &w_y);
+    gtk_window_get_size(w, &w_width, &w_height);
+    if (!dlist) {
+        dwin = g_new0(DockedWindow, 1);
+        dwin->w = w;
+        dlist = g_list_append(dlist, dwin);
+    }
+
+    for (node = wlist; node; node = g_list_next(node)) {
+        temp.w = node->data;
+        if (g_list_find_custom
+            (dlist, &temp, (GCompareFunc) docked_list_compare))
+            continue;
+
+        gtk_window_get_position(GTK_WINDOW(node->data), &t_x, &t_y);
+        gtk_window_get_size(GTK_WINDOW(node->data), &t_width, &t_height);
+        if (is_docked
+            (w_x, w_y, w_width, w_height, t_x, t_y, t_width, t_height)) {
+            dwin = g_new0(DockedWindow, 1);
+            dwin->w = node->data;
+
+            dwin->offset_x = t_x - w_x + init_off_x;
+            dwin->offset_y = t_y - w_y + init_off_y;
+
+            dlist = g_list_append(dlist, dwin);
+
+            dlist =
+                get_docked_list(dlist, wlist, dwin->w, dwin->offset_x,
+                                dwin->offset_y);
+        }
+    }
+    return dlist;
+}
+
+static void
+free_docked_list(GList * dlist)
+{
+    GList *node;
+
+    for (node = dlist; node; node = g_list_next(node))
+        g_free(node->data);
+    g_list_free(dlist);
+}
+
+static void
+docked_list_move(GList * list, gint x, gint y)
+{
+    GList *node;
+    DockedWindow *dw;
+
+    for (node = list; node; node = g_list_next(node)) {
+        dw = node->data;
+        gtk_window_move(dw->w, x + dw->offset_x, y + dw->offset_y);
+
+        SkinnedWindow *window = SKINNED_WINDOW(dw->w);
+        if (window) {
+            switch(window->type) {
+
+            case WINDOW_MAIN:
+                config.player_x = x + dw->offset_x;
+                config.player_y = y + dw->offset_y;
+                break;
+            case WINDOW_EQ:
+                config.equalizer_x = x + dw->offset_x;
+                config.equalizer_y = y + dw->offset_y;
+                break;
+            case WINDOW_PLAYLIST:
+                config.playlist_x = x + dw->offset_x;
+                config.playlist_y = y + dw->offset_y;
+                break;
+            }
+
+            window->x = x + dw->offset_x;
+            window->y = y + dw->offset_y;
+        }
+    }
+}
+
+static GList *
+shade_move_list(GList * list, GtkWindow * widget, gint offset)
+{
+    gint x, y, w, h;
+    GList *node;
+    DockedWindow *dw;
+
+    gtk_window_get_position(widget, &x, &y);
+    gtk_window_get_size(widget, &w, &h);
+
+
+    for (node = list; node;) {
+        gint dx, dy, dwidth, dheight;
+
+        dw = node->data;
+        gtk_window_get_position(dw->w, &dx, &dy);
+        gtk_window_get_size(dw->w, &dwidth, &dheight);
+        if (is_docked(x, y, w, h, dx, dy, dwidth, dheight) &&
+            ((dx + dwidth) > x && dx < (x + w))) {
+            list = g_list_remove_link(list, node);
+            g_list_free_1(node);
+
+            node = list = shade_move_list(list, dw->w, offset);
+        }
+        else
+            node = g_list_next(node);
+    }
+    gtk_window_move(widget, x, y + offset);
+    return list;
+}
+
+/*
+ * Builds a list of the windows in the list of DockedWindows "winlist"
+ * that are docked to the top or bottom of the window, and recursively
+ * adds all windows that are docked to the top or bottom of that window,
+ * and so on...
+ * Note: The data in "winlist" is not copied.
+ */
+static GList *
+find_shade_list(GtkWindow * widget, GList * winlist, GList * shade_list)
+{
+    gint x, y, w, h;
+    gint dx, dy, dwidth, dheight;
+    GList *node;
+
+    gtk_window_get_position(widget, &x, &y);
+    gtk_window_get_size(widget, &w, &h);
+    for (node = winlist; node; node = g_list_next(node)) {
+        DockedWindow *dw = node->data;
+        if (g_list_find_custom
+            (shade_list, dw, (GCompareFunc) docked_list_compare))
+            continue;
+        gtk_window_get_position(dw->w, &dx, &dy);
+        gtk_window_get_size(dw->w, &dwidth, &dheight);
+
+        /* FIXME. Is the is_docked() necessary? */
+        if (is_docked(x, y, w, h, dx, dy, dwidth, dheight) &&
+            ((dx + dwidth) > x && dx < (x + w))) {
+            shade_list = g_list_append(shade_list, dw);
+            shade_list = find_shade_list(dw->w, winlist, shade_list);
+        }
+    }
+    return shade_list;
+}
+
+void
+dock_window_resize(GtkWindow * widget, gint new_w, gint new_h, gint w, gint h)
+{
+    gdk_window_set_hints(GTK_WIDGET(widget)->window, 0, 0, MIN(w, new_w),
+                         MIN(h, new_h), MAX(w, new_w), MAX(h, new_h),
+                         GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+    gdk_window_resize(GTK_WIDGET(widget)->window, new_w, new_h);
+    gdk_window_set_hints(GTK_WIDGET(widget)->window, 0, 0, new_w, new_h,
+                         new_w, new_h, GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+}
+
+void
+dock_shade(GList * window_list, GtkWindow * widget, gint new_h)
+{
+    gint x, y, w, h, off_y, orig_off_y;
+    GList *node, *docked_list, *slist;
+    DockedWindow *dw;
+
+    gtk_window_get_position(widget, &x, &y);
+    gtk_window_get_size(widget, &w, &h);
+
+    if (config.show_wm_decorations) {
+        dock_window_resize(widget, w, new_h, w, h);
+        return;
+    }
+
+    docked_list = get_docked_list(NULL, window_list, widget, 0, 0);
+    slist = find_shade_list(widget, docked_list, NULL);
+
+    off_y = new_h - h;
+    do {
+        orig_off_y = off_y;
+        for (node = slist; node; node = g_list_next(node)) {
+            gint dx, dy, dwidth, dheight;
+
+            dw = node->data;
+            if (dw->w == widget)
+                continue;
+            gtk_window_get_position(dw->w, &dx, &dy);
+            gtk_window_get_size(dw->w, &dwidth, &dheight);
+            if ((dy >= y) && ((dy + off_y + dheight) > gdk_screen_height()))
+                off_y -= (dy + off_y + dheight) - gdk_screen_height();
+            else if ((dy >= y) && ((dy + dheight) == gdk_screen_height()))
+                off_y = 0;
+
+            if (((dy >= y) && ((dy + off_y) < 0)))
+                off_y -= dy + off_y;
+            if ((dy < y) && ((dy + (off_y - (new_h - h))) < 0))
+                off_y -= dy + (off_y - (new_h - h));
+        }
+    } while (orig_off_y != off_y);
+    if (slist) {
+        GList *mlist = g_list_copy(slist);
+
+        /* Remove this widget from the list */
+        for (node = mlist; node; node = g_list_next(node)) {
+            dw = node->data;
+            if (dw->w == widget) {
+                mlist = g_list_remove_link(mlist, node);
+                g_list_free_1(node);
+                break;
+            }
+        }
+        for (node = mlist; node;) {
+            GList *temp;
+            gint dx, dy, dwidth, dheight;
+
+            dw = node->data;
+
+            gtk_window_get_position(dw->w, &dx, &dy);
+            gtk_window_get_size(dw->w, &dwidth, &dheight);
+            /*
+             * Find windows that are directly docked to this window,
+             * move it, and any windows docked to that window again
+             */
+            if (is_docked(x, y, w, h, dx, dy, dwidth, dheight) &&
+                ((dx + dwidth) > x && dx < (x + w))) {
+                mlist = g_list_remove_link(mlist, node);
+                g_list_free_1(node);
+                if (dy > y)
+                    temp = shade_move_list(mlist, dw->w, off_y);
+                else if (off_y - (new_h - h) != 0)
+                    temp = shade_move_list(mlist, dw->w, off_y - (new_h - h));
+                else
+                    temp = mlist;
+                node = mlist = temp;
+            }
+            else
+                node = g_list_next(node);
+        }
+        g_list_free(mlist);
+    }
+    g_list_free(slist);
+    free_docked_list(docked_list);
+    gtk_window_move(widget, x, y + off_y - (new_h - h));
+    dock_window_resize(widget, w, new_h, w, h);
+}
+
+void
+dock_move_press(GList * window_list, GtkWindow * w,
+                GdkEventButton * event, gboolean move_list)
+{
+    gint mx, my;
+    DockedWindow *dwin;
+
+    if (config.show_wm_decorations)
+        return;
+
+    gtk_window_present(w);
+    mx = event->x;
+    my = event->y;
+    gtk_object_set_data(GTK_OBJECT(w), "move_offset_x", GINT_TO_POINTER(mx));
+    gtk_object_set_data(GTK_OBJECT(w), "move_offset_y", GINT_TO_POINTER(my));
+    if (move_list)
+        gtk_object_set_data(GTK_OBJECT(w), "docked_list",
+                            get_docked_list(NULL, window_list, w, 0, 0));
+    else {
+        dwin = g_new0(DockedWindow, 1);
+        dwin->w = w;
+        gtk_object_set_data(GTK_OBJECT(w), "docked_list",
+                            g_list_append(NULL, dwin));
+    }
+    gtk_object_set_data(GTK_OBJECT(w), "window_list", window_list);
+    gtk_object_set_data(GTK_OBJECT(w), "is_moving", GINT_TO_POINTER(1));
+}
+
+void
+dock_move_motion(GtkWindow * w, GdkEventMotion * event)
+{
+    gint offset_x, offset_y, x, y;
+    GList *dlist;
+    GList *window_list;
+
+    if (!gtk_object_get_data(GTK_OBJECT(w), "is_moving"))
+        return;
+
+    offset_x =
+        GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(w), "move_offset_x"));
+    offset_y =
+        GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(w), "move_offset_y"));
+    dlist = gtk_object_get_data(GTK_OBJECT(w), "docked_list");
+    window_list = gtk_object_get_data(GTK_OBJECT(w), "window_list");
+
+    x = event->x_root - offset_x;
+    y = event->y_root - offset_y;
+
+    calc_snap_offset(dlist, window_list, x, y, &offset_x, &offset_y);
+    x += offset_x;
+    y += offset_y;
+
+    docked_list_move(dlist, x, y);
+}
+
+void
+dock_move_release(GtkWindow * w)
+{
+    GList *dlist;
+    gtk_object_remove_data(GTK_OBJECT(w), "is_moving");
+    gtk_object_remove_data(GTK_OBJECT(w), "move_offset_x");
+    gtk_object_remove_data(GTK_OBJECT(w), "move_offset_y");
+    if ((dlist = gtk_object_get_data(GTK_OBJECT(w), "docked_list")) != NULL)
+        free_docked_list(dlist);
+    gtk_object_remove_data(GTK_OBJECT(w), "docked_list");
+    gtk_object_remove_data(GTK_OBJECT(w), "window_list");
+}
+
+gboolean
+dock_is_moving(GtkWindow * w)
+{
+    if (gtk_object_get_data(GTK_OBJECT(w), "is_moving"))
+        return TRUE;
+    return FALSE;
+}
+
+GList *
+dock_add_window(GList * list, GtkWindow * window)
+{
+    return g_list_append(list, window);
+}
+
+GList *
+dock_remove_window(GList * list, GtkWindow * window)
+{
+    return g_list_remove(list, window);
+}
+
+GList *
+dock_window_set_decorated(GList * list, GtkWindow * window,
+                          gboolean decorated)
+{
+    if (gtk_window_get_decorated(window) == decorated)
+        return list;
+
+    if (decorated)
+        list = dock_remove_window(list, window);
+    else
+        list = dock_add_window(list, window);
+
+    gtk_window_set_decorated(window, decorated);
+
+    return list;
+}
+
+GList *
+get_dock_window_list() {
+    return dock_window_list;
+}
+
+void
+set_dock_window_list(GList * list) {
+    dock_window_list = list;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_dock.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,50 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef DOCK_H
+#define DOCK_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+void dock_set_uposition(GtkWindow * widget, gint x, gint y);
+GList *dock_add_window(GList * window_list, GtkWindow * window);
+GList *dock_remove_window(GList * window_list, GtkWindow * window);
+void dock_move_press(GList * window_list, GtkWindow * w,
+                     GdkEventButton * event, gboolean move_list);
+void dock_move_motion(GtkWindow * w, GdkEventMotion * event);
+void dock_move_release(GtkWindow * w);
+void dock_get_widget_pos(GtkWindow * w, gint * x, gint * y);
+gboolean dock_is_moving(GtkWindow * w);
+void dock_shade(GList * window_list, GtkWindow * widget, gint new_h);
+
+GList *dock_window_set_decorated(GList * list, GtkWindow * window,
+                                 gboolean decorated);
+void dock_window_resize(GtkWindow * widget, gint new_w, gint new_h, gint w, gint h);
+
+GList *get_dock_window_list();
+void set_dock_window_list(GList * list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_equalizer.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,1539 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+/*#define AUD_DEBUG*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "ui_equalizer.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "platform/smartinclude.h"
+#include "ui_skin.h"
+#include "ui_manager.h"
+#include "actions-equalizer.h"
+#include "util.h"
+#include "ui_main.h"
+#include <audacious/plugin.h>
+
+#include "images/audacious_eq.xpm"
+
+#include "ui_dock.h"
+#include "ui_skinned_window.h"
+#include "ui_skinned_button.h"
+#include "ui_skinned_horizontal_slider.h"
+#include "ui_skinned_equalizer_slider.h"
+#include "ui_skinned_equalizer_graph.h"
+#include "skins_cfg.h"
+
+enum PresetViewCols {
+    PRESET_VIEW_COL_NAME,
+    PRESET_VIEW_N_COLS
+};
+
+struct _EqualizerPreset {
+    gchar *name;
+    gfloat preamp, bands[10];
+};
+
+typedef struct _EqualizerPreset EqualizerPreset;
+
+
+GtkWidget *equalizerwin;
+GtkWidget *equalizerwin_graph;
+
+static GtkWidget *equalizerwin_load_window = NULL;
+static GtkWidget *equalizerwin_load_auto_window = NULL;
+static GtkWidget *equalizerwin_save_window = NULL;
+static GtkWidget *equalizerwin_save_entry = NULL;
+static GtkWidget *equalizerwin_save_auto_window = NULL;
+static GtkWidget *equalizerwin_save_auto_entry = NULL;
+static GtkWidget *equalizerwin_delete_window = NULL;
+static GtkWidget *equalizerwin_delete_auto_window = NULL;
+
+static GtkWidget *equalizerwin_on, *equalizerwin_auto;
+
+static GtkWidget *equalizerwin_close, *equalizerwin_presets, *equalizerwin_shade;
+static GtkWidget *equalizerwin_preamp,*equalizerwin_bands[10];
+static GtkWidget *equalizerwin_volume, *equalizerwin_balance;
+
+static GList *equalizer_presets = NULL, *equalizer_auto_presets = NULL;
+
+EqualizerPreset *
+equalizer_preset_new(const gchar * name)
+{
+    EqualizerPreset *preset = g_new0(EqualizerPreset, 1);
+    preset->name = g_strdup(name);
+    return preset;
+}
+
+void
+equalizer_preset_free(EqualizerPreset * preset)
+{
+    if (!preset)
+        return;
+
+    g_free(preset->name);
+    g_free(preset);
+}
+
+void
+equalizerwin_set_scaled(gboolean ds)
+{
+    gint height;
+
+    if (config.equalizer_shaded)
+        height = 14;
+    else
+        height = 116;
+
+    if (config.scaled) {
+        dock_window_resize(GTK_WINDOW(equalizerwin), 275 * config.scale_factor, 
+            height * config.scale_factor, 275 * config.scale_factor, height * config.scale_factor);
+    } else {
+        dock_window_resize(GTK_WINDOW(equalizerwin), 275, height, 275, height);
+    }
+
+    GList *iter;
+    for (iter = GTK_FIXED (SKINNED_WINDOW(equalizerwin)->fixed)->children; iter; iter = g_list_next (iter)) {
+        GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+        GtkWidget *child = child_data->widget;
+        g_signal_emit_by_name(child, "toggle-scaled");
+    }
+    gtk_widget_shape_combine_mask(equalizerwin, skin_get_mask(aud_active_skin, SKIN_MASK_EQ + config.equalizer_shaded), 0, 0);
+}
+
+void
+equalizerwin_set_shade_menu_cb(gboolean shaded)
+{
+    config.equalizer_shaded = shaded;
+
+    if (shaded) {
+        dock_shade(get_dock_window_list(), GTK_WINDOW(equalizerwin),
+                   14 * EQUALIZER_SCALE_FACTOR);
+        ui_skinned_set_push_button_data(equalizerwin_shade, -1, 3, -1, 47);
+        ui_skinned_button_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX);
+        ui_skinned_set_push_button_data(equalizerwin_close, 11, 38, 11, 47);
+        ui_skinned_button_set_skin_index(equalizerwin_close, SKIN_EQ_EX);
+        gtk_widget_show(equalizerwin_volume);
+        gtk_widget_show(equalizerwin_balance);
+    }
+    else {
+        dock_shade(get_dock_window_list(), GTK_WINDOW(equalizerwin),
+                   116 * EQUALIZER_SCALE_FACTOR);
+        ui_skinned_set_push_button_data(equalizerwin_shade, -1, 137, -1, 38);
+        ui_skinned_button_set_skin_index1(equalizerwin_shade, SKIN_EQMAIN);
+        ui_skinned_set_push_button_data(equalizerwin_close, 0, 116, 0, 125);
+        ui_skinned_button_set_skin_index(equalizerwin_close, SKIN_EQMAIN);
+        gtk_widget_hide(equalizerwin_volume);
+        gtk_widget_hide(equalizerwin_balance);
+    }
+
+    gtk_widget_shape_combine_mask(equalizerwin, skin_get_mask(aud_active_skin, SKIN_MASK_EQ + config.equalizer_shaded), 0, 0);
+}
+
+static void
+equalizerwin_set_shade(gboolean shaded)
+{
+    GtkAction *action = gtk_action_group_get_action(
+      toggleaction_group_others , "roll up equalizer" );
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , shaded );
+}
+
+static void
+equalizerwin_shade_toggle(void)
+{
+    equalizerwin_set_shade(!config.equalizer_shaded);
+}
+
+void
+equalizerwin_eq_changed(void)
+{
+    gint i;
+
+    aud_cfg->equalizer_preamp = ui_skinned_equalizer_slider_get_position(equalizerwin_preamp);
+    for (i = 0; i < 10; i++)
+        aud_cfg->equalizer_bands[i] = ui_skinned_equalizer_slider_get_position(equalizerwin_bands[i]);
+    /* um .. i think we need both of these for xmms compatibility ..
+       not sure. -larne */
+#if 0
+    input_set_eq(aud_cfg->equalizer_active, aud_cfg->equalizer_preamp,
+                 aud_cfg->equalizer_bands);
+    output_set_eq(aud_cfg->equalizer_active, aud_cfg->equalizer_preamp,
+                  aud_cfg->equalizer_bands);
+#endif
+    gtk_widget_queue_draw(equalizerwin_graph);
+}
+
+static void
+equalizerwin_on_pushed(void)
+{
+    aud_cfg->equalizer_active = UI_SKINNED_BUTTON(equalizerwin_on)->inside;
+    equalizerwin_eq_changed();
+}
+
+static void
+equalizerwin_presets_pushed(void)
+{
+    GdkModifierType modmask;
+    gint x, y;
+
+    gdk_window_get_pointer(NULL, &x, &y, &modmask);
+    ui_manager_popup_menu_show(GTK_MENU(equalizerwin_presets_menu), x, y, 1, GDK_CURRENT_TIME);
+}
+
+static void
+equalizerwin_auto_pushed(void)
+{
+    aud_cfg->equalizer_autoload = UI_SKINNED_BUTTON(equalizerwin_auto)->inside;
+}
+
+gboolean
+equalizerwin_press(GtkWidget * widget, GdkEventButton * event,
+                   gpointer callback_data)
+{
+    if (event->button == 1 && event->type == GDK_2BUTTON_PRESS
+             && event->y < 14) {
+        equalizerwin_set_shade(!config.equalizer_shaded);
+        if (dock_is_moving(GTK_WINDOW(equalizerwin)))
+            dock_move_release(GTK_WINDOW(equalizerwin));
+        return TRUE;
+    }
+    if (event->button == 3) {
+        /*
+         * Pop up the main menu a few pixels down to avoid
+         * anything to be selected initially.
+         */
+       ui_manager_popup_menu_show(GTK_MENU(mainwin_general_menu), event->x_root,
+                                event->y_root + 2, 3, event->time);
+       return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+equalizerwin_keypress(GtkWidget * widget,
+                      GdkEventKey * event,
+                      gpointer data)
+{
+#if 0
+    if (event->keyval == GDK_Tab && event->state & GDK_CONTROL_MASK) {
+        if (config.playlist_visible)
+            gtk_window_present(GTK_WINDOW(playlistwin));
+        else if (config.player_visible)
+            gtk_window_present(GTK_WINDOW(mainwin));
+        return TRUE;
+    }
+#endif
+    if (!config.equalizer_shaded) {
+        gtk_widget_event(mainwin, (GdkEvent *) event);
+        return TRUE;
+    }
+
+    switch (event->keyval) {
+    case GDK_Left:
+    case GDK_KP_Left:
+        mainwin_set_balance_diff(-4);
+        break;
+    case GDK_Right:
+    case GDK_KP_Right:
+        mainwin_set_balance_diff(4);
+        break;
+    default:
+        gtk_widget_event(mainwin, (GdkEvent *) event);
+        break;
+    }
+
+    return FALSE;
+}
+
+static void
+equalizerwin_close_cb(void)
+{
+    equalizerwin_show(FALSE);
+}
+
+static gboolean
+equalizerwin_delete(GtkWidget * widget,
+                    gpointer data)
+{
+    equalizerwin_show(FALSE);
+    return TRUE;
+}
+
+static GList *
+equalizerwin_read_presets(const gchar * basename)
+{
+#if 0
+    gchar *filename, *name;
+    RcFile *rcfile;
+    GList *list = NULL;
+    gint i, p = 0;
+    EqualizerPreset *preset;
+
+    /* START mod: add check for the default presets locate in system path ({prefix}/share/audacious)
+       by Massimo Cavalleri (submax) */
+
+    filename = g_build_filename(aud_paths[BMP_PATH_USER_DIR], basename, NULL);
+
+    if ((rcfile = aud_rcfile_open(filename)) == NULL) {
+        g_free(filename);
+        // DATA_DIR = "{prefix}/share/audacious" ; example is "/usr/share/audacious"
+        filename = g_build_filename(DATA_DIR, basename, NULL);
+        if ((rcfile = aud_rcfile_open(filename)) == NULL) {
+           g_free(filename);
+           return NULL;
+        }
+    }
+
+    // END mod
+
+    g_free(filename);
+
+    for (;;) {
+        gchar section[21];
+
+        g_snprintf(section, sizeof(section), "Preset%d", p++);
+        if (aud_rcfile_read_string(rcfile, "Presets", section, &name)) {
+            preset = g_new0(EqualizerPreset, 1);
+            preset->name = name;
+            aud_rcfile_read_float(rcfile, name, "Preamp", &preset->preamp);
+            for (i = 0; i < 10; i++) {
+                gchar band[7];
+                g_snprintf(band, sizeof(band), "Band%d", i);
+                aud_rcfile_read_float(rcfile, name, band, &preset->bands[i]);
+            }
+            list = g_list_prepend(list, preset);
+        }
+        else
+            break;
+    }
+    list = g_list_reverse(list);
+    aud_rcfile_free(rcfile);
+    return list;
+#endif
+}
+
+gint
+equalizerwin_volume_frame_cb(gint pos)
+{
+    if (equalizerwin_volume) {
+        gint x;
+        if (pos < 32)
+            x = 1;
+        else if (pos < 63)
+            x = 4;
+        else
+            x = 7;
+
+        UI_SKINNED_HORIZONTAL_SLIDER(equalizerwin_volume)->knob_nx = x;
+        UI_SKINNED_HORIZONTAL_SLIDER(equalizerwin_volume)->knob_px = x;
+    }
+    return 1;
+}
+
+static void
+equalizerwin_volume_motion_cb(GtkWidget *widget, gint pos)
+{
+    gint v = (gint) rint(pos * 100 / 94.0);
+    mainwin_adjust_volume_motion(v);
+    mainwin_set_volume_slider(v);
+}
+
+static void
+equalizerwin_volume_release_cb(GtkWidget *widget, gint pos)
+{
+    mainwin_adjust_volume_release();
+}
+
+static gint
+equalizerwin_balance_frame_cb(gint pos)
+{
+    if (equalizerwin_balance) {
+        gint x;
+        if (pos < 13)
+            x = 11;
+        else if (pos < 26)
+            x = 14;
+        else
+            x = 17;
+
+        UI_SKINNED_HORIZONTAL_SLIDER(equalizerwin_balance)->knob_nx = x;
+        UI_SKINNED_HORIZONTAL_SLIDER(equalizerwin_balance)->knob_px = x;
+    }
+
+    return 1;
+}
+
+static void
+equalizerwin_balance_motion_cb(GtkWidget *widget, gint pos)
+{
+    gint b;
+    pos = MIN(pos, 38);         /* The skin uses a even number of pixels
+                                   for the balance-slider *sigh* */
+    b = (gint) rint((pos - 19) * 100 / 19.0);
+    mainwin_adjust_balance_motion(b);
+    mainwin_set_balance_slider(b);
+}
+
+static void
+equalizerwin_balance_release_cb(GtkWidget *widget, gint pos)
+{
+    mainwin_adjust_balance_release();
+}
+
+void
+equalizerwin_set_balance_slider(gint percent)
+{
+    ui_skinned_horizontal_slider_set_position(equalizerwin_balance,
+                         (gint) rint((percent * 19 / 100.0) + 19));
+}
+
+void
+equalizerwin_set_volume_slider(gint percent)
+{
+    ui_skinned_horizontal_slider_set_position(equalizerwin_volume,
+                         (gint) rint(percent * 94 / 100.0));
+}
+
+static void
+equalizerwin_create_widgets(void)
+{
+    gint i;
+
+    equalizerwin_on = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(equalizerwin_on, SKINNED_WINDOW(equalizerwin)->fixed,
+                                   14, 18, 25, 12, 10, 119, 128, 119, 69, 119, 187, 119, SKIN_EQMAIN);
+    g_signal_connect(equalizerwin_on, "clicked", equalizerwin_on_pushed, NULL);
+    UI_SKINNED_BUTTON(equalizerwin_on)->inside = aud_cfg->equalizer_active;
+
+    equalizerwin_auto = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(equalizerwin_auto, SKINNED_WINDOW(equalizerwin)->fixed,
+                                   39, 18, 33, 12, 35, 119, 153, 119, 94, 119, 212, 119, SKIN_EQMAIN);
+    g_signal_connect(equalizerwin_auto, "clicked", equalizerwin_auto_pushed, NULL);
+    UI_SKINNED_BUTTON(equalizerwin_auto)->inside = aud_cfg->equalizer_autoload;
+
+    equalizerwin_presets = ui_skinned_button_new();
+    ui_skinned_push_button_setup(equalizerwin_presets, SKINNED_WINDOW(equalizerwin)->fixed,
+                                 217, 18, 44, 12, 224, 164, 224, 176, SKIN_EQMAIN);
+    g_signal_connect(equalizerwin_presets, "clicked", equalizerwin_presets_pushed, NULL );
+
+    equalizerwin_close = ui_skinned_button_new();
+    ui_skinned_push_button_setup(equalizerwin_close, SKINNED_WINDOW(equalizerwin)->fixed,
+                                 264, 3, 9, 9, 0, 116, 0, 125, SKIN_EQMAIN);
+    g_signal_connect(equalizerwin_close, "clicked", equalizerwin_close_cb, NULL );
+
+    equalizerwin_shade = ui_skinned_button_new();
+    ui_skinned_push_button_setup(equalizerwin_shade, SKINNED_WINDOW(equalizerwin)->fixed,
+                                 254, 3, 9, 9, 254, 137, 1, 38, SKIN_EQMAIN);
+    ui_skinned_button_set_skin_index2(equalizerwin_shade, SKIN_EQ_EX);
+    g_signal_connect(equalizerwin_shade, "clicked", equalizerwin_shade_toggle, NULL );
+
+    equalizerwin_graph = ui_skinned_equalizer_graph_new(SKINNED_WINDOW(equalizerwin)->fixed, 86, 17);
+
+    equalizerwin_preamp = ui_skinned_equalizer_slider_new(SKINNED_WINDOW(equalizerwin)->fixed, 21, 38);
+    ui_skinned_equalizer_slider_set_position(equalizerwin_preamp, aud_cfg->equalizer_preamp);
+
+    for (i = 0; i < 10; i++) {
+        equalizerwin_bands[i] =
+            ui_skinned_equalizer_slider_new(SKINNED_WINDOW(equalizerwin)->fixed, 78 + (i * 18), 38);
+        ui_skinned_equalizer_slider_set_position(equalizerwin_bands[i], aud_cfg->equalizer_bands[i]);
+    }
+
+    equalizerwin_volume =
+        ui_skinned_horizontal_slider_new(SKINNED_WINDOW(equalizerwin)->fixed,
+                                         61, 4, 97, 8, 1, 30, 1, 30, 3, 7, 4, 61, 0, 94,
+                                         equalizerwin_volume_frame_cb, SKIN_EQ_EX);
+    g_signal_connect(equalizerwin_volume, "motion", G_CALLBACK(equalizerwin_volume_motion_cb), NULL);
+    g_signal_connect(equalizerwin_volume, "release", G_CALLBACK(equalizerwin_volume_release_cb), NULL);
+
+
+    equalizerwin_balance =
+        ui_skinned_horizontal_slider_new(SKINNED_WINDOW(equalizerwin)->fixed,
+                       164, 4, 42, 8, 11, 30, 11, 30, 3, 7, 4, 164, 0, 39,
+                       equalizerwin_balance_frame_cb, SKIN_EQ_EX);
+    g_signal_connect(equalizerwin_balance, "motion", G_CALLBACK(equalizerwin_balance_motion_cb), NULL);
+    g_signal_connect(equalizerwin_balance, "release", G_CALLBACK(equalizerwin_balance_release_cb), NULL);
+}
+
+
+static void
+equalizerwin_create_window(void)
+{
+    GdkPixbuf *icon;
+    gint width, height;
+
+    width = 275;
+    height = config.equalizer_shaded ? 14 : 116;
+
+    equalizerwin = ui_skinned_window_new("equalizer");
+    gtk_window_set_title(GTK_WINDOW(equalizerwin), _("Audacious Equalizer"));
+    gtk_window_set_role(GTK_WINDOW(equalizerwin), "equalizer");
+    gtk_window_set_resizable(GTK_WINDOW(equalizerwin), FALSE);
+
+    if (config.scaled && config.eq_scaled_linked) {
+        width *= config.scale_factor;
+        height *= config.scale_factor;
+    }
+
+    gtk_widget_set_size_request(equalizerwin, width, height);
+
+    /* this will hide only mainwin. it's annoying! yaz */
+    gtk_window_set_transient_for(GTK_WINDOW(equalizerwin),
+                                 GTK_WINDOW(mainwin));
+    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(equalizerwin), TRUE);
+
+    icon = gdk_pixbuf_new_from_xpm_data((const gchar **) audacious_eq_icon);
+    gtk_window_set_icon(GTK_WINDOW(equalizerwin), icon);
+    g_object_unref(icon);
+
+    gtk_widget_set_app_paintable(equalizerwin, TRUE);
+
+    if (config.equalizer_x != -1 && config.save_window_position)
+        gtk_window_move(GTK_WINDOW(equalizerwin),
+                        config.equalizer_x, config.equalizer_y);
+
+    g_signal_connect(equalizerwin, "delete_event",
+                     G_CALLBACK(equalizerwin_delete), NULL);
+    g_signal_connect(equalizerwin, "button_press_event",
+                     G_CALLBACK(equalizerwin_press), NULL);
+    g_signal_connect(equalizerwin, "key_press_event",
+                     G_CALLBACK(equalizerwin_keypress), NULL);
+}
+
+void
+equalizerwin_create(void)
+{
+    equalizer_presets = equalizerwin_read_presets("eq.preset");
+    equalizer_auto_presets = equalizerwin_read_presets("eq.auto_preset");
+
+    equalizerwin_create_window();
+
+    gtk_window_add_accel_group( GTK_WINDOW(equalizerwin) , ui_manager_get_accel_group() );
+
+    equalizerwin_create_widgets();
+}
+
+
+void
+equalizerwin_show(gboolean show)
+{
+    GtkAction *action = gtk_action_group_get_action(
+      toggleaction_group_others , "show equalizer" );
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , show );
+
+    if (show)
+        equalizerwin_real_show();
+    else
+        equalizerwin_real_hide();
+}
+
+void
+equalizerwin_real_show(void)
+{
+    gtk_window_move(GTK_WINDOW(equalizerwin), config.equalizer_x, config.equalizer_y);
+    if (config.scaled && config.eq_scaled_linked)
+        gtk_widget_set_size_request(equalizerwin, 275 * config.scale_factor,
+                                    ((config.equalizer_shaded ? 14 : 116) * config.scale_factor));
+    else
+        gtk_widget_set_size_request(equalizerwin, 275,
+                                    (config.equalizer_shaded ? 14 : 116));
+    config.equalizer_visible = TRUE;
+    UI_SKINNED_BUTTON(mainwin_eq)->inside = TRUE;
+    gtk_widget_show_all(equalizerwin);
+
+    if (!config.equalizer_shaded) {
+        gtk_widget_hide(equalizerwin_volume);
+        gtk_widget_hide(equalizerwin_balance);
+    }
+    else {
+        ui_skinned_set_push_button_data(equalizerwin_shade, -1, 3, -1, 47);
+        ui_skinned_button_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX);
+        ui_skinned_set_push_button_data(equalizerwin_close, 11, 38, 11, 47);
+        ui_skinned_button_set_skin_index(equalizerwin_close, SKIN_EQ_EX);
+    }
+
+    gtk_window_present(GTK_WINDOW(equalizerwin));
+}
+
+void
+equalizerwin_real_hide(void)
+{
+    /*
+     * This function should only be called from the
+     * main menu signal handler
+     */
+    gtk_widget_hide(equalizerwin);
+    config.equalizer_visible = FALSE;
+    UI_SKINNED_BUTTON(mainwin_eq)->inside = FALSE;
+    gtk_widget_queue_draw(mainwin_eq);
+}
+
+static EqualizerPreset *
+equalizerwin_find_preset(GList * list, const gchar * name)
+{
+    GList *node = list;
+    EqualizerPreset *preset;
+
+    while (node) {
+        preset = node->data;
+        if (!strcasecmp(preset->name, name))
+            return preset;
+        node = g_list_next(node);
+    }
+    return NULL;
+}
+
+static void
+equalizerwin_write_preset_file(GList * list, const gchar * basename)
+{
+#if 0
+    gchar *filename, *tmp;
+    gint i, p;
+    EqualizerPreset *preset;
+    RcFile *rcfile;
+    GList *node;
+
+    rcfile = aud_rcfile_new();
+    p = 0;
+    for (node = list; node; node = g_list_next(node)) {
+        preset = node->data;
+        tmp = g_strdup_printf("Preset%d", p++);
+        aud_rcfile_write_string(rcfile, "Presets", tmp, preset->name);
+        g_free(tmp);
+        aud_rcfile_write_float(rcfile, preset->name, "Preamp",
+                               preset->preamp);
+        for (i = 0; i < 10; i++) {
+            tmp = g_strdup_printf("Band%d\n", i);
+            aud_rcfile_write_float(rcfile, preset->name, tmp,
+                                   preset->bands[i]);
+            g_free(tmp);
+        }
+    }
+
+    filename = g_build_filename(aud_paths[BMP_PATH_USER_DIR], basename, NULL);
+    aud_rcfile_write(rcfile, filename);
+    aud_rcfile_free(rcfile);
+    g_free(filename);
+#endif
+}
+
+static gboolean
+equalizerwin_load_preset(GList * list, const gchar * name)
+{
+    EqualizerPreset *preset;
+    gint i;
+
+    if ((preset = equalizerwin_find_preset(list, name)) != NULL) {
+        ui_skinned_equalizer_slider_set_position(equalizerwin_preamp, preset->preamp);
+        for (i = 0; i < 10; i++)
+            ui_skinned_equalizer_slider_set_position(equalizerwin_bands[i], preset->bands[i]);
+        equalizerwin_eq_changed();
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static GList *
+equalizerwin_save_preset(GList * list, const gchar * name,
+                         const gchar * filename)
+{
+    gint i;
+    EqualizerPreset *preset;
+
+    if (!(preset = equalizerwin_find_preset(list, name))) {
+        preset = g_new0(EqualizerPreset, 1);
+        preset->name = g_strdup(name);
+        list = g_list_append(list, preset);
+    }
+
+    preset->preamp = ui_skinned_equalizer_slider_get_position(equalizerwin_preamp);
+    for (i = 0; i < 10; i++)
+        preset->bands[i] = ui_skinned_equalizer_slider_get_position(equalizerwin_bands[i]);
+
+    equalizerwin_write_preset_file(list, filename);
+
+    return list;
+}
+
+static GList *
+equalizerwin_delete_preset(GList * list, gchar * name, gchar * filename)
+{
+    EqualizerPreset *preset;
+    GList *node;
+
+    if (!(preset = equalizerwin_find_preset(list, name)))
+        return list;
+
+    if (!(node = g_list_find(list, preset)))
+        return list;
+
+    list = g_list_remove_link(list, node);
+    equalizer_preset_free(preset);
+    g_list_free_1(node);
+
+    equalizerwin_write_preset_file(list, filename);
+
+    return list;
+}
+
+static void
+equalizerwin_delete_selected_presets(GtkTreeView *view, gchar *filename)
+{
+    gchar *text;
+
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
+    GtkTreeModel *model = gtk_tree_view_get_model(view);
+
+    /*
+     * first we are making a list of the selected rows, then we convert this
+     * list into a list of GtkTreeRowReferences, so that when you delete an
+     * item you can still access the other items
+     * finally we iterate through all GtkTreeRowReferences, convert them to
+     * GtkTreeIters and delete those one after the other
+     */
+
+    GList *list = gtk_tree_selection_get_selected_rows(selection, &model);
+    GList *rrefs = NULL;
+    GList *litr;
+
+    for (litr = list; litr; litr = litr->next)
+    {
+        GtkTreePath *path = litr->data;
+        rrefs = g_list_append(rrefs, gtk_tree_row_reference_new(model, path));
+    }
+
+    for (litr = rrefs; litr; litr = litr->next)
+    {
+        GtkTreeRowReference *ref = litr->data;
+        GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
+        GtkTreeIter iter;
+        gtk_tree_model_get_iter(model, &iter, path);
+
+        gtk_tree_model_get(model, &iter, 0, &text, -1);
+
+        if (!strcmp(filename, "eq.preset"))
+            equalizer_presets = equalizerwin_delete_preset(equalizer_presets, text, filename);
+        else if (!strcmp(filename, "eq.auto_preset"))
+            equalizer_auto_presets = equalizerwin_delete_preset(equalizer_auto_presets, text, filename);
+
+        gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+    }
+}
+
+static GList *
+import_winamp_eqf(VFSFile * file)
+{
+#if 0
+    gchar header[31];
+    gchar bands[11];
+    gint i = 0;
+    EqualizerPreset *preset = NULL;
+    GList *list = NULL;
+    GtkWidget *dialog;
+    gchar *realfn;
+    gchar preset_name[0xb4];
+
+    vfs_fread(header, 1, 31, file);
+    if (strncmp(header, "Winamp EQ library file v1.1", 27)) goto error;
+
+    AUDDBG("The EQF header is OK\n");
+
+    if (vfs_fseek(file, 0x1f, SEEK_SET) == -1) goto error;
+
+    while (vfs_fread(preset_name, 1, 0xb4, file) == 0xb4) {
+        AUDDBG("The preset name is '%s'\n", preset_name);
+        vfs_fseek(file, 0x4d, SEEK_CUR); /* unknown crap --asphyx */
+        if (vfs_fread(bands, 1, 11, file) != 11) break;
+
+        preset = equalizer_preset_new(preset_name);
+        /*this was divided by 63, but shouldn't it be 64? --majeru*/
+        preset->preamp = EQUALIZER_MAX_GAIN - ((bands[10] * EQUALIZER_MAX_GAIN * 2) / 64.0);
+
+        for (i = 0; i < 10; i++)
+            preset->bands[i] = EQUALIZER_MAX_GAIN - ((bands[i] * EQUALIZER_MAX_GAIN * 2) / 64.0);
+        
+        list = g_list_prepend(list, preset);
+    }
+    
+    list = g_list_reverse(list);
+    if (list == NULL) goto error;
+
+    return list;
+
+error:
+    realfn = g_filename_from_uri(file->uri, NULL, NULL);
+    dialog = gtk_message_dialog_new (GTK_WINDOW(mainwin),
+                                     GTK_DIALOG_DESTROY_WITH_PARENT,
+                                     GTK_MESSAGE_ERROR,
+                                     GTK_BUTTONS_CLOSE,
+                                     _("Error importing Winamp EQF file '%s'"),
+                                     realfn);
+    gtk_dialog_run (GTK_DIALOG (dialog));
+    gtk_widget_destroy (dialog);
+    g_free(realfn);
+    return NULL;
+#endif
+}
+
+static void
+free_cb (gpointer data, gpointer user_data)
+{
+    equalizer_preset_free((EqualizerPreset*)data);
+}
+
+static void
+equalizerwin_read_winamp_eqf(VFSFile * file)
+{
+    GList *presets;
+    gint i;
+
+    if ((presets = import_winamp_eqf(file)) == NULL)
+        return;
+
+    /* just get the first preset --asphyx */
+    EqualizerPreset *preset = (EqualizerPreset*)presets->data;
+    ui_skinned_equalizer_slider_set_position(equalizerwin_preamp,
+                                             preset->preamp);
+
+    for (i = 0; i < 10; i++)
+        ui_skinned_equalizer_slider_set_position(equalizerwin_bands[i],
+                                                 preset->bands[i]);
+
+    g_list_foreach(presets, free_cb, NULL);
+    g_list_free(presets);
+
+    equalizerwin_eq_changed();
+}
+#if 0
+static void
+equalizerwin_read_aud_preset(RcFile * rcfile)
+{
+    gfloat val;
+    gint i;
+
+    if (aud_rcfile_read_float(rcfile, "Equalizer preset", "Preamp", &val))
+        ui_skinned_equalizer_slider_set_position(equalizerwin_preamp, val);
+    for (i = 0; i < 10; i++) {
+        gchar tmp[7];
+        g_snprintf(tmp, sizeof(tmp), "Band%d", i);
+        if (aud_rcfile_read_float(rcfile, "Equalizer preset", tmp, &val))
+            ui_skinned_equalizer_slider_set_position(equalizerwin_bands[i], val);
+    }
+    equalizerwin_eq_changed();
+}
+#endif
+
+static void
+equalizerwin_save_ok(GtkWidget * widget, gpointer data)
+{
+    const gchar *text;
+
+    text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_entry));
+    if (strlen(text) != 0)
+        equalizer_presets =
+            equalizerwin_save_preset(equalizer_presets, text, "eq.preset");
+    gtk_widget_destroy(equalizerwin_save_window);
+}
+
+static void
+equalizerwin_save_select(GtkTreeView *treeview, GtkTreePath *path,
+                         GtkTreeViewColumn *col, gpointer data)
+{
+    gchar *text;
+
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    if (selection)
+    {
+        if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        {
+            gtk_tree_model_get(model, &iter, 0, &text, -1);
+            gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_entry), text);
+            equalizerwin_save_ok(NULL, NULL);
+
+            g_free(text);
+        }
+    }
+}
+
+static void
+equalizerwin_load_ok(GtkWidget *widget, gpointer data)
+{
+    gchar *text;
+
+    GtkTreeView* view = GTK_TREE_VIEW(data);
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    if (selection)
+    {
+        if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        {
+            gtk_tree_model_get(model, &iter, 0, &text, -1);
+            equalizerwin_load_preset(equalizer_presets, text);
+
+            g_free(text);
+        }
+    }
+    gtk_widget_destroy(equalizerwin_load_window);
+}
+
+static void
+equalizerwin_load_select(GtkTreeView *treeview, GtkTreePath *path,
+                         GtkTreeViewColumn *col, gpointer data)
+{
+    equalizerwin_load_ok(NULL, treeview);
+}
+
+static void
+equalizerwin_delete_delete(GtkWidget *widget, gpointer data)
+{
+    equalizerwin_delete_selected_presets(GTK_TREE_VIEW(data), "eq.preset");
+}
+
+static void
+equalizerwin_save_auto_ok(GtkWidget *widget, gpointer data)
+{
+    const gchar *text;
+
+    text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_auto_entry));
+    if (strlen(text) != 0)
+        equalizer_auto_presets =
+            equalizerwin_save_preset(equalizer_auto_presets, text,
+                                     "eq.auto_preset");
+    gtk_widget_destroy(equalizerwin_save_auto_window);
+}
+
+static void
+equalizerwin_save_auto_select(GtkTreeView *treeview, GtkTreePath *path,
+                              GtkTreeViewColumn *col, gpointer data)
+{
+    gchar *text;
+
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    if (selection)
+    {
+        if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        {
+            gtk_tree_model_get(model, &iter, 0, &text, -1);
+            gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry), text);
+            equalizerwin_save_auto_ok(NULL, NULL);
+
+            g_free(text);
+        }
+    }
+}
+
+static void
+equalizerwin_load_auto_ok(GtkWidget *widget, gpointer data)
+{
+    gchar *text;
+
+    GtkTreeView *view = GTK_TREE_VIEW(data);
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+
+    if (selection)
+    {
+        if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        {
+            gtk_tree_model_get(model, &iter, 0, &text, -1);
+            equalizerwin_load_preset(equalizer_auto_presets, text);
+
+            g_free(text);
+        }
+    }
+    gtk_widget_destroy(equalizerwin_load_auto_window);
+}
+
+static void
+equalizerwin_load_auto_select(GtkTreeView *treeview, GtkTreePath *path,
+                              GtkTreeViewColumn *col, gpointer data)
+{
+    equalizerwin_load_auto_ok(NULL, treeview);
+}
+
+static void
+equalizerwin_delete_auto_delete(GtkWidget *widget, gpointer data)
+{
+    equalizerwin_delete_selected_presets(GTK_TREE_VIEW(data), "eq.auto_preset");
+}
+
+
+static void
+load_preset_file(const gchar *filename)
+{
+#if 0
+    RcFile *rcfile;
+
+    if ((rcfile = aud_rcfile_open(filename)) != NULL) {
+        equalizerwin_read_aud_preset(rcfile);
+        aud_rcfile_free(rcfile);
+    }
+#endif
+}
+
+static VFSFile *
+open_vfs_file(const gchar *filename, const gchar *mode)
+{
+    VFSFile *file;
+    GtkWidget *dialog;
+
+    if (!(file = vfs_fopen(filename, mode))) {
+        dialog = gtk_message_dialog_new (GTK_WINDOW (mainwin),
+                                         GTK_DIALOG_DESTROY_WITH_PARENT,
+                                         GTK_MESSAGE_ERROR,
+                                         GTK_BUTTONS_CLOSE,
+                                         "Error loading file '%s'",
+                                         filename);
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+    }
+
+    return file;
+}
+
+static void
+load_winamp_file(const gchar * filename)
+{
+#if 0
+    VFSFile *file;
+
+    if (!(file = open_vfs_file(filename, "rb")))
+        return;
+
+    equalizerwin_read_winamp_eqf(file);
+    vfs_fclose(file);
+#endif
+}
+
+static void
+import_winamp_file(const gchar * filename)
+{
+#if 0
+    VFSFile *file;
+    GList *list;
+
+    if (!(file = open_vfs_file(filename, "rb")) ||
+        !(list = import_winamp_eqf(file)))
+        return;
+
+    equalizer_presets = g_list_concat(equalizer_presets, list);
+    equalizerwin_write_preset_file(equalizer_presets, "eq.preset");
+
+    vfs_fclose(file);
+#endif
+}
+
+static void
+save_preset_file(const gchar * filename)
+{
+#if 0
+    RcFile *rcfile;
+    gint i;
+
+    rcfile = aud_rcfile_new();
+    aud_rcfile_write_float(rcfile, "Equalizer preset", "Preamp",
+                           ui_skinned_equalizer_slider_get_position(equalizerwin_preamp));
+
+    for (i = 0; i < 10; i++) {
+        gchar tmp[7];
+        g_snprintf(tmp, sizeof(tmp), "Band%d", i);
+        aud_rcfile_write_float(rcfile, "Equalizer preset", tmp,
+                               ui_skinned_equalizer_slider_get_position(equalizerwin_bands[i]));
+    }
+
+    aud_rcfile_write(rcfile, filename);
+    aud_rcfile_free(rcfile);
+#endif
+}
+
+static void
+save_winamp_file(const gchar * filename)
+{
+#if 0
+    VFSFile *file;
+
+    gchar name[257];
+    gint i;
+    guchar bands[11];
+
+    if (!(file = open_vfs_file(filename, "wb")))
+        return;
+
+    vfs_fwrite("Winamp EQ library file v1.1\x1a!--", 1, 31, file);
+
+    memset(name, 0, 257);
+    g_strlcpy(name, "Entry1", 257);
+    vfs_fwrite(name, 1, 257, file);
+
+    for (i = 0; i < 10; i++)
+        bands[i] = 63 - (((ui_skinned_equalizer_slider_get_position(equalizerwin_bands[i]) + EQUALIZER_MAX_GAIN) * 63) / EQUALIZER_MAX_GAIN / 2);
+    bands[10] = 63 - (((ui_skinned_equalizer_slider_get_position(equalizerwin_preamp) + EQUALIZER_MAX_GAIN) * 63) / EQUALIZER_MAX_GAIN / 2);
+    vfs_fwrite(bands, 1, 11, file);
+
+    vfs_fclose(file);
+#endif
+}
+
+static GtkWidget *
+equalizerwin_create_list_window(GList *preset_list,
+                                const gchar *title,
+                                GtkWidget **window,
+                                GtkSelectionMode sel_mode,
+                                GtkWidget **entry,
+                                const gchar *action_name,
+                                GCallback action_func,
+                                GCallback select_row_func)
+{
+    GtkWidget *vbox, *scrolled_window, *bbox, *view;
+    GtkWidget *button_cancel, *button_action;
+    GList *node;
+
+    GtkListStore *store;
+    GtkTreeIter iter;
+    GtkTreeModel *model;
+    GtkCellRenderer *renderer;
+    GtkTreeSelection *selection;
+    GtkTreeSortable *sortable;
+
+
+
+    *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(*window), title);
+    gtk_window_set_type_hint(GTK_WINDOW(*window), GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_window_set_default_size(GTK_WINDOW(*window), 350, 300);
+    gtk_window_set_position(GTK_WINDOW(*window), GTK_WIN_POS_CENTER);
+    gtk_container_set_border_width(GTK_CONTAINER(*window), 10);
+    gtk_window_set_transient_for(GTK_WINDOW(*window),
+                                 GTK_WINDOW(equalizerwin));
+    g_signal_connect(*window, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), window);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(*window), vbox);
+
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+
+    /* fill the store with the names of all available presets */
+    store = gtk_list_store_new(1, G_TYPE_STRING);
+    for (node = preset_list; node; node = g_list_next(node))
+    {
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter,
+                           0, ((EqualizerPreset*)node->data)->name,
+                           -1);
+    }
+    model = GTK_TREE_MODEL(store);
+
+
+    sortable = GTK_TREE_SORTABLE(store);
+    gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);
+
+
+    view = gtk_tree_view_new();
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1,
+                                                _("Presets"), renderer,
+                                                "text", 0, NULL);
+    gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
+    g_object_unref(model);
+
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+    gtk_tree_selection_set_mode(selection, sel_mode);
+
+
+
+
+    gtk_container_add(GTK_CONTAINER(scrolled_window), view);
+    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+
+    if (entry) {
+        *entry = gtk_entry_new();
+        g_signal_connect(*entry, "activate", action_func, NULL);
+        gtk_box_pack_start(GTK_BOX(vbox), *entry, FALSE, FALSE, 0);
+    }
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+    button_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    g_signal_connect_swapped(button_cancel, "clicked",
+                             G_CALLBACK(gtk_widget_destroy),
+                             GTK_OBJECT(*window));
+    gtk_box_pack_start(GTK_BOX(bbox), button_cancel, TRUE, TRUE, 0);
+
+    button_action = gtk_button_new_from_stock(action_name);
+    g_signal_connect(button_action, "clicked", G_CALLBACK(action_func), view);
+    GTK_WIDGET_SET_FLAGS(button_action, GTK_CAN_DEFAULT);
+
+    if (select_row_func)
+        g_signal_connect(view, "row-activated", G_CALLBACK(select_row_func), NULL);
+
+        
+    gtk_box_pack_start(GTK_BOX(bbox), button_action, TRUE, TRUE, 0);
+
+    gtk_widget_grab_default(button_action);
+
+
+    gtk_widget_show_all(*window);
+
+    return *window;
+}
+
+void
+equalizerwin_load_auto_preset(const gchar * filename)
+{
+#if 0
+    gchar *presetfilename, *directory;
+    RcFile *rcfile;
+
+    g_return_if_fail(filename != NULL);
+
+    if (!aud_cfg->equalizer_autoload)
+        return;
+
+    presetfilename = g_strconcat(filename, ".", aud_cfg->eqpreset_extension, NULL);
+
+    /* First try to find a per file preset file */
+    if (strlen(aud_cfg->eqpreset_extension) > 0 &&
+        (rcfile = aud_rcfile_open(presetfilename)) != NULL) {
+        g_free(presetfilename);
+        equalizerwin_read_aud_preset(rcfile);
+        aud_rcfile_free(rcfile);
+        return;
+    }
+
+    g_free(presetfilename);
+
+    directory = g_path_get_dirname(filename);
+    presetfilename = g_build_filename(directory, aud_cfg->eqpreset_default_file,
+                                      NULL);
+    g_free(directory);
+
+    /* Try to find a per directory preset file */
+    if (strlen(aud_cfg->eqpreset_default_file) > 0 &&
+        (rcfile = aud_rcfile_open(presetfilename)) != NULL) {
+        equalizerwin_read_aud_preset(rcfile);
+        aud_rcfile_free(rcfile);
+    }
+    else if (!equalizerwin_load_preset
+             (equalizer_auto_presets, g_basename(filename))) {
+        /* Fall back to the oldstyle auto presets */
+        equalizerwin_load_preset(equalizer_presets, "Default");
+    }
+
+    g_free(presetfilename);
+#endif
+}
+
+void
+equalizerwin_set_preamp(gfloat preamp)
+{
+    ui_skinned_equalizer_slider_set_position(equalizerwin_preamp, preamp);
+    equalizerwin_eq_changed();
+}
+
+void
+equalizerwin_set_band(gint band, gfloat value)
+{
+    g_return_if_fail(band >= 0 && band < 10);
+    ui_skinned_equalizer_slider_set_position(equalizerwin_bands[band], value);
+}
+
+gfloat
+equalizerwin_get_preamp(void)
+{
+    return ui_skinned_equalizer_slider_get_position(equalizerwin_preamp);
+}
+
+gfloat
+equalizerwin_get_band(gint band)
+{
+    g_return_val_if_fail(band >= 0 && band < 10, 0.0);
+    return ui_skinned_equalizer_slider_get_position(equalizerwin_bands[band]);
+}
+
+void
+action_equ_load_preset(void)
+{
+    if (equalizerwin_load_window) {
+        gtk_window_present(GTK_WINDOW(equalizerwin_load_window));
+        return;
+    }
+    
+    equalizerwin_create_list_window(equalizer_presets,
+                                    Q_("Load preset"),
+                                    &equalizerwin_load_window,
+                                    GTK_SELECTION_SINGLE, NULL,
+                                    GTK_STOCK_OK,
+                                    G_CALLBACK(equalizerwin_load_ok),
+                                    G_CALLBACK(equalizerwin_load_select));
+}
+
+void
+action_equ_load_auto_preset(void)
+{
+    if (equalizerwin_load_auto_window) {
+        gtk_window_present(GTK_WINDOW(equalizerwin_load_auto_window));
+        return;
+    }
+
+    equalizerwin_create_list_window(equalizer_auto_presets,
+                                    Q_("Load auto-preset"),
+                                    &equalizerwin_load_auto_window,
+                                    GTK_SELECTION_SINGLE, NULL,
+                                    GTK_STOCK_OK,
+                                    G_CALLBACK(equalizerwin_load_auto_ok),
+                                    G_CALLBACK(equalizerwin_load_auto_select));
+}
+
+void
+action_equ_load_default_preset(void)
+{
+    equalizerwin_load_preset(equalizer_presets, "Default");
+}
+
+void
+action_equ_zero_preset(void)
+{
+    gint i;
+
+    ui_skinned_equalizer_slider_set_position(equalizerwin_preamp, 0);
+    for (i = 0; i < 10; i++)
+        ui_skinned_equalizer_slider_set_position(equalizerwin_bands[i], 0);
+
+    equalizerwin_eq_changed();
+}
+
+void
+action_equ_load_preset_file(void)
+{
+    GtkWidget *dialog;
+    gchar *file_uri;
+
+    dialog = make_filebrowser(Q_("Load equalizer preset"), FALSE);
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
+        load_preset_file(file_uri);
+        g_free(file_uri);
+    }
+    gtk_widget_destroy(dialog);
+}
+
+void
+action_equ_load_preset_eqf(void)
+{
+    GtkWidget *dialog;
+    gchar *file_uri;
+
+    dialog = make_filebrowser(Q_("Load equalizer preset"), FALSE);
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
+        load_winamp_file(file_uri);
+        g_free(file_uri);
+    }
+    gtk_widget_destroy(dialog);
+}
+
+void
+action_equ_import_winamp_presets(void)
+{
+    GtkWidget *dialog;
+    gchar *file_uri;
+
+    dialog = make_filebrowser(Q_("Load equalizer preset"), FALSE);
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
+        import_winamp_file(file_uri);
+        g_free(file_uri);
+    }
+    gtk_widget_destroy(dialog);
+}
+
+void
+action_equ_save_preset(void)
+{
+    if (equalizerwin_save_window) {
+        gtk_window_present(GTK_WINDOW(equalizerwin_save_window));
+        return;
+    }
+     
+    equalizerwin_create_list_window(equalizer_presets,
+                                    Q_("Save preset"),
+                                    &equalizerwin_save_window,
+                                    GTK_SELECTION_SINGLE,
+                                    &equalizerwin_save_entry,
+                                    GTK_STOCK_OK,
+                                    G_CALLBACK(equalizerwin_save_ok),
+                                    G_CALLBACK(equalizerwin_save_select));
+}
+
+void
+action_equ_save_auto_preset(void)
+{
+    gchar *name;
+    Playlist *playlist = aud_playlist_get_active();
+
+    if (equalizerwin_save_auto_window)
+        gtk_window_present(GTK_WINDOW(equalizerwin_save_auto_window));
+    else
+        equalizerwin_create_list_window(equalizer_auto_presets,
+                                        Q_("Save auto-preset"),
+                                        &equalizerwin_save_auto_window,
+                                        GTK_SELECTION_SINGLE,
+                                        &equalizerwin_save_auto_entry,
+                                        GTK_STOCK_OK,
+                                        G_CALLBACK(equalizerwin_save_auto_ok),
+                                        G_CALLBACK(equalizerwin_save_auto_select));
+
+    name = aud_playlist_get_filename(playlist, aud_playlist_get_position(playlist));
+    if (name) {
+        gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry),
+                           g_basename(name));
+        g_free(name);
+    }
+}
+
+void
+action_equ_save_default_preset(void)
+{
+    equalizer_presets =
+        equalizerwin_save_preset(equalizer_presets, Q_("Default"), "eq.preset");
+}
+
+void
+action_equ_save_preset_file(void)
+{
+    GtkWidget *dialog;
+    gchar *file_uri;
+    gchar *songname;
+    Playlist *playlist = aud_playlist_get_active();
+
+    dialog = make_filebrowser(Q_("Save equalizer preset"), TRUE);
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
+        save_preset_file(file_uri);
+        g_free(file_uri);
+    }
+
+    songname = aud_playlist_get_filename(playlist, aud_playlist_get_position(playlist));
+    if (songname) {
+        gchar *eqname = g_strdup_printf("%s.%s", songname,
+                                        aud_cfg->eqpreset_extension);
+        g_free(songname);
+        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
+                                      eqname);
+        g_free(eqname);
+    }
+
+    gtk_widget_destroy(dialog);
+}
+
+void
+action_equ_save_preset_eqf(void)
+{
+    GtkWidget *dialog;
+    gchar *file_uri;
+
+    dialog = make_filebrowser(Q_("Save equalizer preset"), TRUE);
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+    {
+        file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
+        save_winamp_file(file_uri);
+        g_free(file_uri);
+    }
+    gtk_widget_destroy(dialog);
+}
+
+void
+action_equ_delete_preset(void)
+{
+    if (equalizerwin_delete_window) {
+        gtk_window_present(GTK_WINDOW(equalizerwin_delete_window));
+        return;
+    }
+    
+    equalizerwin_create_list_window(equalizer_presets,
+                                    Q_("Delete preset"),
+                                    &equalizerwin_delete_window,
+                                    GTK_SELECTION_EXTENDED, NULL,
+                                    GTK_STOCK_DELETE,
+                                    G_CALLBACK(equalizerwin_delete_delete),
+                                    NULL);
+}
+
+void
+action_equ_delete_auto_preset(void)
+{
+    if (equalizerwin_delete_auto_window) {
+        gtk_window_present(GTK_WINDOW(equalizerwin_delete_auto_window));
+        return;
+    }
+    
+    equalizerwin_create_list_window(equalizer_auto_presets,
+                                    Q_("Delete auto-preset"),
+                                    &equalizerwin_delete_auto_window,
+                                    GTK_SELECTION_EXTENDED, NULL,
+                                    GTK_STOCK_DELETE,
+                                    G_CALLBACK(equalizerwin_delete_auto_delete),
+                                    NULL);
+}
+
+void
+equalizer_activate(gboolean active)
+{
+    aud_cfg->equalizer_active = active;
+    UI_SKINNED_BUTTON(equalizerwin_on)->inside = active;
+    gtk_widget_queue_draw(equalizerwin_on);
+
+    equalizerwin_eq_changed();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_equalizer.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,69 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_EQUALIZER_H
+#define AUDACIOUS_UI_EQUALIZER_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include "skins_cfg.h"
+
+#define EQUALIZER_SCALED     (config.scaled && config.eq_scaled_linked)
+#define EQUALIZER_SCALE_FACTOR (EQUALIZER_SCALED ? config.scale_factor : 1)
+
+#define EQUALIZER_HEIGHT         ((config.equalizer_shaded ? 14 : 116) * (EQUALIZER_SCALE_FACTOR))
+#define EQUALIZER_WIDTH          (275 * EQUALIZER_SCALE_FACTOR)
+
+#define EQUALIZER_DEFAULT_POS_X  20
+#define EQUALIZER_DEFAULT_POS_Y  136
+
+#define EQUALIZER_DEFAULT_DIR_PRESET "dir_default.preset"
+#define EQUALIZER_DEFAULT_PRESET_EXT "preset"
+
+void equalizerwin_set_scaled(gboolean ds);
+void equalizerwin_set_shade_menu_cb(gboolean shaded);
+void draw_equalizer_window(gboolean force);
+void equalizerwin_create(void);
+void equalizerwin_show(gboolean show);
+void equalizerwin_real_show(void);
+void equalizerwin_real_hide(void);
+void equalizerwin_load_auto_preset(const gchar * filename);
+void equalizerwin_set_volume_slider(gint percent);
+void equalizerwin_set_balance_slider(gint percent);
+void equalizerwin_eq_changed(void);
+void equalizerwin_set_preamp(gfloat preamp);
+void equalizerwin_set_band(gint band, gfloat value);
+gfloat equalizerwin_get_preamp(void);
+gfloat equalizerwin_get_band(gint band);
+
+gboolean equalizerwin_has_focus(void);
+
+extern GtkWidget *equalizerwin;
+extern GtkWidget *equalizerwin_graph;
+extern gboolean equalizerwin_focus;
+
+void equalizer_activate(gboolean active);
+
+#endif /* AUDACIOUS_UI_EQUALIZER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_fileopener.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,489 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_fileopener.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <audacious/input.h>
+#include <audacious/main.h>
+#include <audacious/playback.h>
+#include <audacious/playlist.h>
+#include <audacious/strings.h>
+#include "ui_playlist.h"
+#include "skins_cfg.h"
+
+static void
+filebrowser_add_files(GtkFileChooser * browser,
+                      GSList * files)
+{
+    GSList *cur;
+    gchar *ptr;
+    Playlist *playlist = aud_playlist_get_active();
+
+    for (cur = files; cur; cur = g_slist_next(cur)) {
+        gchar *filename = g_filename_to_uri((const gchar *) cur->data, NULL, NULL);
+
+        if (aud_vfs_file_test(cur->data, G_FILE_TEST_IS_DIR)) {
+            aud_playlist_add_dir(playlist, filename ? filename : (const gchar *) cur->data);
+        } else {
+            aud_playlist_add(playlist, filename ? filename : (const gchar *) cur->data);
+        }
+
+        g_free(filename);
+    }
+
+    playlistwin_update_list(playlist);
+
+    ptr = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(browser));
+
+    g_free(config.filesel_path);
+    config.filesel_path = ptr;
+}
+
+static void
+action_button_cb(GtkWidget *widget, gpointer data)
+{
+    GtkWidget *window = g_object_get_data(data, "window");
+    GtkWidget *chooser = g_object_get_data(data, "chooser");
+    GtkWidget *toggle = g_object_get_data(data, "toggle-button");
+    gboolean play_button;
+    GSList *files;
+    config.close_dialog_open =
+        gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle));
+
+    play_button =
+        GPOINTER_TO_INT(g_object_get_data(data, "play-button"));
+
+    files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(chooser));
+    if (!files) return;
+
+    if (play_button)
+        aud_playlist_clear(aud_playlist_get_active());
+
+    filebrowser_add_files(GTK_FILE_CHOOSER(chooser), files);
+    g_slist_foreach(files, (GFunc) g_free, NULL);
+    g_slist_free(files);
+
+    if (play_button)
+        audacious_drct_initiate();
+
+    if (config.close_dialog_open)
+        gtk_widget_destroy(window);
+}
+
+
+static void
+close_button_cb(GtkWidget *widget, gpointer data)
+{
+    gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+static gboolean
+filebrowser_on_keypress(GtkWidget * browser,
+                        GdkEventKey * event,
+                        gpointer data)
+{
+    if (event->keyval == GDK_Escape) {
+        gtk_widget_destroy(browser);
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void
+util_run_filebrowser_gtk2style(gboolean play_button, gboolean show)
+{
+    static GtkWidget *window = NULL;
+    GtkWidget *vbox, *hbox, *bbox;
+    GtkWidget *chooser;
+    GtkWidget *action_button, *close_button;
+    GtkWidget *toggle;
+    gchar *window_title, *toggle_text;
+    gpointer action_stock, storage;
+
+    if(!show) {
+        if(window){
+            gtk_widget_hide(window);
+            return;
+        }
+        else
+            return;
+    }
+    else {
+        if(window) {
+            gtk_window_present(GTK_WINDOW(window)); /* raise filebrowser */
+            return;
+        }
+    }
+    
+    window_title = play_button ? _("Open Files") : _("Add Files");
+    toggle_text = play_button ?
+        _("Close dialog on Open") : _("Close dialog on Add");
+    action_stock = play_button ? GTK_STOCK_OPEN : GTK_STOCK_ADD;
+
+    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(window), window_title);
+    gtk_window_set_default_size(GTK_WINDOW(window), 700, 450);
+    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+    gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+
+    vbox = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(window), vbox);
+
+    chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
+    gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), TRUE);
+    if (config.filesel_path)
+        gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser),
+                                            config.filesel_path);
+    gtk_box_pack_start(GTK_BOX(vbox), chooser, TRUE, TRUE, 3);
+
+    hbox = gtk_hbox_new(TRUE, 0);
+    gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
+
+    toggle = gtk_check_button_new_with_label(toggle_text);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
+                                 config.close_dialog_open ? TRUE : FALSE);
+    gtk_box_pack_start(GTK_BOX(hbox), toggle, TRUE, TRUE, 3);
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_box_set_spacing(GTK_BOX(bbox), 6);
+    gtk_box_pack_end(GTK_BOX(hbox), bbox, TRUE, TRUE, 3);
+
+    close_button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    action_button = gtk_button_new_from_stock(action_stock);
+    gtk_container_add(GTK_CONTAINER(bbox), close_button);
+    gtk_container_add(GTK_CONTAINER(bbox), action_button);
+
+    /* this storage object holds several other objects which are used in the
+     * callback functions
+     */
+    storage = g_object_new(G_TYPE_OBJECT, NULL);
+    g_object_set_data(storage, "window", window);
+    g_object_set_data(storage, "chooser", chooser);
+    g_object_set_data(storage, "toggle-button", toggle);
+    g_object_set_data(storage, "play-button", GINT_TO_POINTER(play_button));
+
+    g_signal_connect(chooser, "file-activated",
+                     G_CALLBACK(action_button_cb), storage);
+    g_signal_connect(action_button, "clicked",
+                     G_CALLBACK(action_button_cb), storage);
+    g_signal_connect(close_button, "clicked",
+                     G_CALLBACK(close_button_cb), window);
+    g_signal_connect(window, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &window);
+
+    g_signal_connect(window, "key_press_event",
+                     G_CALLBACK(filebrowser_on_keypress),
+                     NULL);
+
+    gtk_widget_show_all(window);
+}
+
+/*
+ * Derived from Beep Media Player 0.9.6.1.
+ * Which is (C) 2003 - 2006 Milosz Derezynski &c
+ *
+ * Although I changed it quite a bit. -nenolod
+ */
+static void filebrowser_changed_classic(GtkFileSelection * filesel)
+{
+    GList *list;
+    GList *node;
+    char *filename = (char *)
+    gtk_file_selection_get_filename(GTK_FILE_SELECTION(filesel));
+    GtkListStore *store;
+    GtkTreeIter iter;
+
+#if 0
+    if ((list = input_scan_dir(filename)) != NULL)
+    {
+        /*
+         * We enter a directory that has been "hijacked" by an
+         * input-plugin. This is used by the CDDA plugin
+         */
+        store =
+            GTK_LIST_STORE(gtk_tree_view_get_model
+                   (GTK_TREE_VIEW(filesel->file_list)));
+        gtk_list_store_clear(store);
+
+        node = list;
+        while (node) {
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, node->data, -1);
+            g_free(node->data);
+            node = g_list_next(node);
+        }
+
+        g_list_free(list);
+    }
+#endif
+}
+
+static void filebrowser_entry_changed_classic(GtkEditable * entry, gpointer data)
+{
+    filebrowser_changed_classic(GTK_FILE_SELECTION(data));
+}
+
+
+static gboolean
+util_filebrowser_is_dir_classic(GtkFileSelection * filesel)
+{
+    char *text;
+    struct stat buf;
+    gboolean retv = FALSE;
+
+    text = g_strdup(gtk_file_selection_get_filename(filesel));
+
+    if (stat(text, &buf) == 0 && S_ISDIR(buf.st_mode)) {
+    /* Selected directory */
+    int len = strlen(text);
+    if (len > 3 && !strcmp(text + len - 4, "/../")) {
+        if (len == 4)
+        /* At the root already */
+        *(text + len - 3) = '\0';
+        else {
+        char *ptr;
+        *(text + len - 4) = '\0';
+        ptr = strrchr(text, '/');
+        *(ptr + 1) = '\0';
+        }
+    } else if (len > 2 && !strcmp(text + len - 3, "/./"))
+        *(text + len - 2) = '\0';
+    gtk_file_selection_set_filename(filesel, text);
+    retv = TRUE;
+    }
+    g_free(text);
+    return retv;
+}
+
+static void filebrowser_add_files_classic(gchar ** files,
+                  GtkFileSelection * filesel)
+{
+    int ctr = 0;
+    char *ptr;
+    Playlist *playlist = aud_playlist_get_active();
+
+    while (files[ctr] != NULL) {
+        gchar *filename = g_filename_to_uri((const gchar *) files[ctr++], NULL, NULL);
+        aud_playlist_add(playlist, filename);
+        g_free(filename);
+    }
+    playlistwin_update_list(playlist);
+
+    gtk_label_get(GTK_LABEL(GTK_BIN(filesel->history_pulldown)->child),
+          &ptr);
+
+    /* This will give an extra slash if the current dir is the root. */
+    config.filesel_path = g_strconcat(ptr, "/", NULL);
+}
+
+static void filebrowser_ok_classic(GtkWidget * w, GtkWidget * filesel)
+{
+    gchar **files;
+
+    if (util_filebrowser_is_dir_classic(GTK_FILE_SELECTION(filesel)))
+    return;
+    files = gtk_file_selection_get_selections(GTK_FILE_SELECTION(filesel));
+    filebrowser_add_files_classic(files, GTK_FILE_SELECTION(filesel));
+    gtk_widget_destroy(filesel);
+}
+
+static void filebrowser_play_classic(GtkWidget * w, GtkWidget * filesel)
+{
+    gchar **files;
+
+    if (util_filebrowser_is_dir_classic
+    (GTK_FILE_SELECTION(GTK_FILE_SELECTION(filesel))))
+    return;
+    aud_playlist_clear(aud_playlist_get_active());
+    files = gtk_file_selection_get_selections(GTK_FILE_SELECTION(filesel));
+    filebrowser_add_files_classic(files, GTK_FILE_SELECTION(filesel));
+    gtk_widget_destroy(filesel);
+
+    audacious_drct_initiate();
+}
+
+static void filebrowser_add_selected_files_classic(GtkWidget * w, gpointer data)
+{
+    gchar **files;
+
+    GtkFileSelection *filesel = GTK_FILE_SELECTION(data);
+    files = gtk_file_selection_get_selections(filesel);
+
+    filebrowser_add_files_classic(files, filesel);
+    gtk_tree_selection_unselect_all(gtk_tree_view_get_selection
+                    (GTK_TREE_VIEW(filesel->file_list)));
+
+    gtk_entry_set_text(GTK_ENTRY(filesel->selection_entry), "");
+}
+
+static void filebrowser_add_all_files_classic(GtkWidget * w, gpointer data)
+{
+    gchar **files;
+    GtkFileSelection *filesel;
+
+    filesel = data;
+    gtk_tree_selection_select_all(gtk_tree_view_get_selection
+                  (GTK_TREE_VIEW(filesel->file_list)));
+    files = gtk_file_selection_get_selections(filesel);
+    filebrowser_add_files_classic(files, filesel);
+    gtk_tree_selection_unselect_all(gtk_tree_view_get_selection
+                    (GTK_TREE_VIEW(filesel->file_list)));
+    gtk_entry_set_text(GTK_ENTRY(filesel->selection_entry), "");
+}
+
+static void
+util_run_filebrowser_classic(gboolean play_button, gboolean show)
+{
+    static GtkWidget *dialog;
+    GtkWidget *button_add_selected, *button_add_all, *button_close,
+    *button_add;
+    char *title;
+
+    if (!show) {
+        if(dialog) {
+            gtk_widget_hide(dialog);
+            return;
+        }
+        else
+            return;
+    }
+    else {
+        if (dialog) {
+            gtk_window_present(GTK_WINDOW(dialog));
+            return;
+        }
+    }
+
+    if (play_button)
+    title = _("Play files");
+    else
+    title = _("Load files");
+
+    dialog = gtk_file_selection_new(title);
+
+    gtk_file_selection_set_select_multiple
+    (GTK_FILE_SELECTION(dialog), TRUE);
+
+    if (config.filesel_path)
+    gtk_file_selection_set_filename(GTK_FILE_SELECTION(dialog),
+                    config.filesel_path);
+
+    gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(dialog));
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+
+    gtk_widget_hide(GTK_FILE_SELECTION(dialog)->ok_button);
+    gtk_widget_destroy(GTK_FILE_SELECTION(dialog)->cancel_button);
+
+    /*
+     * The mnemonics are quite unorthodox, but that should guarantee they're unique in any locale
+     * plus kinda easy to use
+     */
+    button_add_selected =
+    gtk_dialog_add_button(GTK_DIALOG(dialog), "Add selected",
+                  GTK_RESPONSE_NONE);
+    gtk_button_set_use_underline(GTK_BUTTON(button_add_selected), TRUE);
+    g_signal_connect(G_OBJECT(button_add_selected), "clicked",
+             G_CALLBACK(filebrowser_add_selected_files_classic), dialog);
+
+    button_add_all =
+    gtk_dialog_add_button(GTK_DIALOG(dialog), "Add all",
+                  GTK_RESPONSE_NONE);
+    gtk_button_set_use_underline(GTK_BUTTON(button_add_all), TRUE);
+    g_signal_connect(G_OBJECT(button_add_all), "clicked",
+             G_CALLBACK(filebrowser_add_all_files_classic), dialog);
+
+    if (play_button) {
+    button_add =
+        gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_MEDIA_PLAY,
+                  GTK_RESPONSE_NONE);
+    gtk_button_set_use_stock(GTK_BUTTON(button_add), TRUE);
+    g_signal_connect(G_OBJECT(button_add), "clicked",
+             G_CALLBACK(filebrowser_play_classic), dialog);
+    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dialog)->ok_button),
+             "clicked", G_CALLBACK(filebrowser_play_classic), dialog);
+    } else {
+    button_add =
+        gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_ADD,
+                  GTK_RESPONSE_NONE);
+    gtk_button_set_use_stock(GTK_BUTTON(button_add), TRUE);
+    g_signal_connect(G_OBJECT(button_add), "clicked",
+             G_CALLBACK(filebrowser_ok_classic), dialog);
+    g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dialog)->ok_button),
+             "clicked", G_CALLBACK(filebrowser_ok_classic), dialog);
+    }
+
+    button_close =
+    gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE,
+                  GTK_RESPONSE_NONE);
+    gtk_button_set_use_stock(GTK_BUTTON(button_close), TRUE);
+    g_signal_connect_swapped(G_OBJECT(button_close), "clicked",
+                 G_CALLBACK(gtk_widget_destroy),
+                 G_OBJECT(dialog));
+
+    gtk_widget_set_size_request(dialog, 600, 450);
+    gtk_widget_realize(dialog);
+
+    g_signal_connect(G_OBJECT
+             (GTK_FILE_SELECTION(dialog)->history_pulldown),
+             "changed", G_CALLBACK(filebrowser_entry_changed_classic),
+             dialog);
+
+    g_signal_connect(G_OBJECT(dialog), "destroy",
+             G_CALLBACK(gtk_widget_destroyed), &dialog);
+
+    filebrowser_changed_classic(GTK_FILE_SELECTION(dialog));
+
+    gtk_widget_show(dialog);
+}
+
+/*
+ * util_run_filebrowser(gboolean play_button)
+ *
+ * Inputs:
+ *     - whether or not a play button should be used
+ *
+ * Outputs:
+ *     - none
+ *
+ * Side Effects:
+ *     - either a GTK1 or a GTK2 fileselector is launched
+ */
+void
+run_filebrowser(gboolean play_button)
+{
+    if (!config.use_xmms_style_fileselector)
+        util_run_filebrowser_gtk2style(play_button, TRUE);
+    else
+        util_run_filebrowser_classic(play_button, TRUE);
+}
+
+void
+hide_filebrowser(void)
+{
+    if (!config.use_xmms_style_fileselector)
+        util_run_filebrowser_gtk2style(FALSE, FALSE);
+    else
+        util_run_filebrowser_classic(FALSE, FALSE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_fileopener.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,31 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_FILEOPENER_H
+#define AUDACIOUS_UI_FILEOPENER_H
+
+#include <gtk/gtk.h>
+
+#define NO_PLAY_BUTTON  FALSE
+#define PLAY_BUTTON     TRUE
+
+void run_filebrowser(gboolean clear_pl_on_ok);
+void hide_filebrowser(void);
+
+#endif /* AUDACIOUS_UI_FILEOPENER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_hints.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,59 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_hints.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "ui_equalizer.h"
+#include "ui_main.h"
+#include "ui_playlist.h"
+
+#include "platform/smartinclude.h"
+
+void
+hint_set_always(gboolean always)
+{
+    gtk_window_set_keep_above(GTK_WINDOW(mainwin), always);
+    gtk_window_set_keep_above(GTK_WINDOW(equalizerwin), always);
+    gtk_window_set_keep_above(GTK_WINDOW(playlistwin), always);
+}
+
+void
+hint_set_sticky(gboolean sticky)
+{
+    if (sticky) {
+        gtk_window_stick(GTK_WINDOW(mainwin));
+        gtk_window_stick(GTK_WINDOW(equalizerwin));
+        gtk_window_stick(GTK_WINDOW(playlistwin));
+    }
+    else {
+        gtk_window_unstick(GTK_WINDOW(mainwin));
+        gtk_window_unstick(GTK_WINDOW(equalizerwin));
+        gtk_window_unstick(GTK_WINDOW(playlistwin));
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_hints.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,35 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_HINTS_H
+#define AUDACIOUS_UI_HINTS_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+void hint_set_always(gboolean always);
+void hint_set_sticky(gboolean sticky);
+
+#endif /* AUDACIOUS_UI_HINTS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_main.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,2909 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2006  Audacious development team.
+ *
+ *  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkmessagedialog.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+/* GDK including */
+#include "platform/smartinclude.h"
+
+#if defined(USE_REGEX_ONIGURUMA)
+#include <onigposix.h>
+#elif defined(USE_REGEX_PCRE)
+#include <pcreposix.h>
+#else
+#include <regex.h>
+#endif
+
+#include "ui_main.h"
+#include "ui_dock.h"
+#include "icons-stock.h"
+#include "actions-mainwin.h"
+#include "ui_manager.h"
+#include "ui_equalizer.h"
+#include "ui_playlist.h"
+#include "ui_hints.h"
+#include "dnd.h"
+#if 0
+#include "configdb.h"
+#include "input.h"
+#include "main.h"
+#include "playback.h"
+#include "playlist.h"
+#include "pluginenum.h"
+#include "strings.h"
+#include "ui_credits.h"
+#include "ui_dock.h"
+#include "ui_fileinfo.h"
+#include "ui_jumptotrack.h"
+#include "ui_main_evlisteners.h"
+#include "ui_preferences.h"
+#include "ui_skinselector.h"
+#include "ui_urlopener.h"
+#include "util.h"
+#include "visualization.h"
+#endif
+#include "ui_fileopener.h"
+#include "ui_skinned_window.h"
+#include "ui_skinned_button.h"
+#include "ui_skinned_textbox.h"
+#include "ui_skinned_number.h"
+#include "ui_skinned_horizontal_slider.h"
+#include "ui_skinned_menurow.h"
+#include "ui_skinned_playstatus.h"
+#include "ui_skinned_monostereo.h"
+#include "ui_skinned_playlist.h"
+#include <audacious/plugin.h>
+#include "skins_cfg.h"
+
+static GTimeVal cb_time; 
+static const int TRISTATE_THRESHOLD = 200;
+
+enum {
+    MAINWIN_SEEK_REV = -1,
+    MAINWIN_SEEK_NIL,
+    MAINWIN_SEEK_FWD
+};
+
+GtkWidget *mainwin = NULL;
+
+static gint balance;
+
+static GtkWidget *mainwin_jtt = NULL;
+
+static gint seek_state = MAINWIN_SEEK_NIL;
+static gint seek_initial_pos = 0;
+
+static GtkWidget *mainwin_menubtn;
+static GtkWidget *mainwin_minimize, *mainwin_shade, *mainwin_close;
+
+static GtkWidget *mainwin_rew, *mainwin_fwd;
+static GtkWidget *mainwin_eject;
+static GtkWidget *mainwin_play, *mainwin_pause, *mainwin_stop;
+
+static GtkWidget *mainwin_shuffle, *mainwin_repeat;
+GtkWidget *mainwin_eq, *mainwin_pl;
+
+GtkWidget *mainwin_info;
+GtkWidget *mainwin_stime_min, *mainwin_stime_sec;
+
+static GtkWidget *mainwin_rate_text, *mainwin_freq_text, *mainwin_othertext;
+
+GtkWidget *mainwin_playstatus;
+
+GtkWidget *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num;
+GtkWidget *mainwin_10sec_num, *mainwin_sec_num;
+
+GtkWidget *mainwin_vis;
+GtkWidget *mainwin_svis;
+
+GtkWidget *mainwin_sposition = NULL;
+
+static GtkWidget *mainwin_menurow;
+static GtkWidget *mainwin_volume, *mainwin_balance;
+GtkWidget *mainwin_position;
+
+static GtkWidget *mainwin_monostereo;
+static GtkWidget *mainwin_srew, *mainwin_splay, *mainwin_spause;
+static GtkWidget *mainwin_sstop, *mainwin_sfwd, *mainwin_seject, *mainwin_about;
+
+static gint mainwin_timeout_id;
+
+static gboolean mainwin_info_text_locked = FALSE;
+static guint mainwin_volume_release_timeout = 0;
+
+static int ab_position_a = -1;
+static int ab_position_b = -1;
+
+static void mainwin_refresh_visible(void);
+static gint mainwin_idle_func(gpointer data);
+
+static void set_timer_mode_menu_cb(TimerMode mode);
+static void set_timer_mode(TimerMode mode);
+static void change_timer_mode(void);
+
+static void mainwin_position_motion_cb(GtkWidget *widget, gint pos);
+static void mainwin_position_release_cb(GtkWidget *widget, gint pos);
+
+static void set_scaled(gboolean scaled);
+static void mainwin_eq_pushed(gboolean toggled);
+static void mainwin_pl_pushed(gboolean toggled);
+
+static void
+mainwin_set_title_scroll(gboolean scroll)
+{
+    config.autoscroll = scroll;
+    ui_skinned_textbox_set_scroll(mainwin_info, config.autoscroll);
+}
+
+
+void
+mainwin_set_always_on_top(gboolean always)
+{
+    GtkAction *action = gtk_action_group_get_action(toggleaction_group_others,
+                                                    "view always on top");
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , always );
+}
+
+static void
+mainwin_set_shade(gboolean shaded)
+{
+    GtkAction *action = gtk_action_group_get_action(toggleaction_group_others,
+                                                    "roll up player");
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , shaded );
+}
+
+static void
+mainwin_set_shade_menu_cb(gboolean shaded)
+{
+    config.player_shaded = shaded;
+
+    if (shaded) {
+        dock_shade(get_dock_window_list(), GTK_WINDOW(mainwin),
+                   MAINWIN_SHADED_HEIGHT * MAINWIN_SCALE_FACTOR);
+    } else {
+        gint height = !aud_active_skin->properties.mainwin_height ? MAINWIN_HEIGHT :
+            aud_active_skin->properties.mainwin_height;
+
+        dock_shade(get_dock_window_list(), GTK_WINDOW(mainwin), height * MAINWIN_SCALE_FACTOR);
+    }
+
+    mainwin_refresh_hints();
+    ui_skinned_set_push_button_data(mainwin_shade, 0, config.player_shaded ? 27 : 18, 9, config.player_shaded ? 27 : 18);
+    gtk_widget_shape_combine_mask(mainwin, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + config.player_shaded), 0, 0);
+}
+
+static void
+mainwin_vis_set_refresh(RefreshRate rate)
+{
+    config.vis_refresh = rate;
+}
+
+static void
+mainwin_vis_set_afalloff(FalloffSpeed speed)
+{
+    config.analyzer_falloff = speed;
+}
+
+static void
+mainwin_vis_set_pfalloff(FalloffSpeed speed)
+{
+    config.peaks_falloff = speed;
+}
+
+static void
+mainwin_vis_set_analyzer_mode(AnalyzerMode mode)
+{
+    config.analyzer_mode = mode;
+}
+
+static void
+mainwin_vis_set_analyzer_type(AnalyzerType mode)
+{
+    config.analyzer_type = mode;
+}
+
+void
+mainwin_vis_set_type(VisType mode)
+{
+    GtkAction *action;
+
+    switch ( mode )
+    {
+        case VIS_ANALYZER:
+            action = gtk_action_group_get_action(radioaction_group_vismode,
+                                                 "vismode analyzer");
+            break;
+        case VIS_SCOPE:
+            action = gtk_action_group_get_action(radioaction_group_vismode,
+                                                 "vismode scope");
+            break;
+        case VIS_VOICEPRINT:
+            action = gtk_action_group_get_action(radioaction_group_vismode,
+                                                 "vismode voiceprint");
+            break;
+        case VIS_OFF:
+        default:
+            action = gtk_action_group_get_action(radioaction_group_vismode,
+                                                 "vismode off");
+            break;
+    }
+
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , TRUE );
+}
+
+static void
+mainwin_vis_set_type_menu_cb(VisType mode)
+{
+    config.vis_type = mode;
+
+    if (mode == VIS_OFF) {
+        if (config.player_shaded) {
+            ui_svis_set_visible(mainwin_svis, FALSE);
+            ui_vis_set_visible(mainwin_vis, TRUE);
+        } else {
+            ui_svis_set_visible(mainwin_svis, TRUE);
+            ui_vis_set_visible(mainwin_vis, FALSE);
+        }
+    }
+    if (mode == VIS_ANALYZER || mode == VIS_SCOPE || mode == VIS_VOICEPRINT) {
+        if (config.player_shaded) {
+            ui_svis_clear_data(mainwin_svis);
+            ui_svis_set_visible(mainwin_svis, TRUE);
+            ui_vis_clear_data(mainwin_vis);
+            ui_vis_set_visible(mainwin_vis, FALSE);
+        } else {
+            ui_svis_clear_data(mainwin_svis);
+            ui_svis_set_visible(mainwin_svis, FALSE);
+            ui_vis_clear_data(mainwin_vis);
+            ui_vis_set_visible(mainwin_vis, TRUE);
+        }
+    }
+}
+
+static void
+mainwin_menubtn_cb(void)
+{
+    gint x, y;
+    gtk_window_get_position(GTK_WINDOW(mainwin), &x, &y);
+    ui_manager_popup_menu_show(GTK_MENU(mainwin_general_menu),
+                               x + 6 * MAINWIN_SCALE_FACTOR ,
+                               y + MAINWIN_SHADED_HEIGHT * MAINWIN_SCALE_FACTOR,
+                               1, GDK_CURRENT_TIME);
+}
+
+void
+mainwin_minimize_cb(void)
+{
+    if (!mainwin)
+        return;
+
+    gtk_window_iconify(GTK_WINDOW(mainwin));
+}
+
+static void
+mainwin_shade_toggle(void)
+{
+    mainwin_set_shade(!config.player_shaded);
+}
+
+void
+mainwin_quit_cb(void)
+{
+    if (mainwin_timeout_id)
+        g_source_remove(mainwin_timeout_id);
+
+    audacious_drct_quit();
+}
+
+gboolean
+mainwin_vis_cb(GtkWidget *widget, GdkEventButton *event)
+{
+    if (event->button == 1) {
+        config.vis_type++;
+
+        if (config.vis_type > VIS_OFF)
+            config.vis_type = VIS_ANALYZER;
+
+        mainwin_vis_set_type(config.vis_type);
+    } else if (event->button == 3) {
+       ui_manager_popup_menu_show(GTK_MENU(mainwin_visualization_menu),
+                                   event->x_root, event->y_root, 3,
+                                   event->time);
+    }
+    return TRUE;
+}
+
+static void
+mainwin_destroy(GtkWidget * widget, gpointer data)
+{
+/* we should detect whether plugin got unloaded and when user indeed
+   wants to close audacious */
+#if 0
+    mainwin_quit_cb();
+#endif
+}
+
+static gchar *mainwin_tb_old_text = NULL;
+
+void
+mainwin_lock_info_text(const gchar * text)
+{
+    if (mainwin_info_text_locked != TRUE)
+        mainwin_tb_old_text = g_strdup(aud_active_skin->properties.mainwin_othertext_is_status ?
+                                       UI_SKINNED_TEXTBOX(mainwin_othertext)->text : UI_SKINNED_TEXTBOX(mainwin_info)->text);
+
+    mainwin_info_text_locked = TRUE;
+    if (aud_active_skin->properties.mainwin_othertext_is_status)
+        ui_skinned_textbox_set_text(mainwin_othertext, text);
+    else
+        ui_skinned_textbox_set_text(mainwin_info, text);
+}
+
+void
+mainwin_release_info_text(void)
+{
+    mainwin_info_text_locked = FALSE;
+
+    if (mainwin_tb_old_text != NULL)
+    {
+        if (aud_active_skin->properties.mainwin_othertext_is_status)
+            ui_skinned_textbox_set_text(mainwin_othertext, mainwin_tb_old_text);
+        else
+            ui_skinned_textbox_set_text(mainwin_info, mainwin_tb_old_text);
+        g_free(mainwin_tb_old_text);
+        mainwin_tb_old_text = NULL;
+    }
+}
+
+
+static gchar *
+make_mainwin_title(const gchar * title)
+{
+    if (title)
+        return g_strdup_printf(_("%s - Audacious"), title);
+    else
+        return g_strdup(_("Audacious"));
+}
+
+void
+mainwin_set_song_title(const gchar * title)
+{
+    gchar *mainwin_title_text = make_mainwin_title(title);
+    gtk_window_set_title(GTK_WINDOW(mainwin), mainwin_title_text);
+    g_free(mainwin_title_text);
+}
+
+static void
+mainwin_refresh_visible(void)
+{
+    if (!aud_active_skin || !config.player_visible)
+        return;
+
+    gtk_widget_show_all(mainwin);
+
+    if (!aud_active_skin->properties.mainwin_text_visible)
+        gtk_widget_hide(mainwin_info);
+
+    if (!aud_active_skin->properties.mainwin_vis_visible)
+        gtk_widget_hide(mainwin_vis);
+
+    if (!aud_active_skin->properties.mainwin_menurow_visible)
+        gtk_widget_hide(mainwin_menurow);
+
+    if (aud_active_skin->properties.mainwin_othertext) {
+        gtk_widget_hide(mainwin_rate_text);
+        gtk_widget_hide(mainwin_freq_text);
+        gtk_widget_hide(mainwin_monostereo);
+
+        if (!aud_active_skin->properties.mainwin_othertext_visible)
+            gtk_widget_hide(mainwin_othertext);
+    } else {
+        gtk_widget_hide(mainwin_othertext);
+    }
+
+    if (!aud_active_skin->properties.mainwin_vis_visible)
+        gtk_widget_hide(mainwin_vis);
+
+    if (!audacious_drct_get_playing()) {
+        gtk_widget_hide(mainwin_minus_num);
+        gtk_widget_hide(mainwin_10min_num);
+        gtk_widget_hide(mainwin_min_num);
+        gtk_widget_hide(mainwin_10sec_num);
+        gtk_widget_hide(mainwin_sec_num);
+
+        gtk_widget_hide(mainwin_stime_min);
+        gtk_widget_hide(mainwin_stime_sec);
+
+        gtk_widget_hide(mainwin_position);
+        gtk_widget_hide(mainwin_sposition);
+    }
+
+    if (config.player_shaded) {
+        ui_svis_clear_data(mainwin_svis);
+        if (config.vis_type != VIS_OFF)
+            ui_svis_set_visible(mainwin_svis, TRUE);
+        else
+            ui_svis_set_visible(mainwin_svis, FALSE);
+
+        ui_skinned_textbox_set_scroll(mainwin_info, FALSE);
+        if (!audacious_drct_get_playing()) {
+            gtk_widget_hide(mainwin_sposition);
+            gtk_widget_hide(mainwin_stime_min);
+            gtk_widget_hide(mainwin_stime_sec);
+        }
+    } else {
+        gtk_widget_hide(mainwin_srew);
+        gtk_widget_hide(mainwin_splay);
+        gtk_widget_hide(mainwin_spause);
+        gtk_widget_hide(mainwin_sstop);
+        gtk_widget_hide(mainwin_sfwd);
+        gtk_widget_hide(mainwin_seject);
+        gtk_widget_hide(mainwin_stime_min);
+        gtk_widget_hide(mainwin_stime_sec);
+        gtk_widget_hide(mainwin_svis);
+        gtk_widget_hide(mainwin_sposition);
+        ui_vis_clear_data(mainwin_vis);
+        if (config.vis_type != VIS_OFF)
+            ui_vis_set_visible(mainwin_vis, TRUE);
+        else
+            ui_vis_set_visible(mainwin_vis, FALSE);
+
+        ui_skinned_textbox_set_scroll(mainwin_info, config.autoscroll);
+    }
+}
+
+void
+mainwin_refresh_hints(void)
+{
+    /* positioning and size attributes */
+    if (aud_active_skin->properties.mainwin_vis_x && aud_active_skin->properties.mainwin_vis_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_vis), aud_active_skin->properties.mainwin_vis_x,
+                       aud_active_skin->properties.mainwin_vis_y);
+
+    if (aud_active_skin->properties.mainwin_vis_width)
+        gtk_widget_set_size_request(mainwin_vis, aud_active_skin->properties.mainwin_vis_width * MAINWIN_SCALE_FACTOR,
+                                    UI_VIS(mainwin_vis)->height* MAINWIN_SCALE_FACTOR);
+
+    if (aud_active_skin->properties.mainwin_text_x && aud_active_skin->properties.mainwin_text_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_info), aud_active_skin->properties.mainwin_text_x,
+                       aud_active_skin->properties.mainwin_text_y);
+
+    if (aud_active_skin->properties.mainwin_text_width) {
+        UI_SKINNED_TEXTBOX(mainwin_info)->width = aud_active_skin->properties.mainwin_text_width;
+        gtk_widget_set_size_request(mainwin_info, aud_active_skin->properties.mainwin_text_width * MAINWIN_SCALE_FACTOR,
+                                    UI_SKINNED_TEXTBOX(mainwin_info)->height * MAINWIN_SCALE_FACTOR );
+    }
+
+    if (aud_active_skin->properties.mainwin_infobar_x && aud_active_skin->properties.mainwin_infobar_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_othertext), aud_active_skin->properties.mainwin_infobar_x,
+                       aud_active_skin->properties.mainwin_infobar_y);
+
+    if (aud_active_skin->properties.mainwin_number_0_x && aud_active_skin->properties.mainwin_number_0_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_minus_num), aud_active_skin->properties.mainwin_number_0_x,
+                       aud_active_skin->properties.mainwin_number_0_y);
+
+    if (aud_active_skin->properties.mainwin_number_1_x && aud_active_skin->properties.mainwin_number_1_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_10min_num), aud_active_skin->properties.mainwin_number_1_x,
+                       aud_active_skin->properties.mainwin_number_1_y);
+
+    if (aud_active_skin->properties.mainwin_number_2_x && aud_active_skin->properties.mainwin_number_2_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_min_num), aud_active_skin->properties.mainwin_number_2_x,
+                       aud_active_skin->properties.mainwin_number_2_y);
+
+    if (aud_active_skin->properties.mainwin_number_3_x && aud_active_skin->properties.mainwin_number_3_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_10sec_num), aud_active_skin->properties.mainwin_number_3_x,
+                       aud_active_skin->properties.mainwin_number_3_y);
+
+    if (aud_active_skin->properties.mainwin_number_4_x && aud_active_skin->properties.mainwin_number_4_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_sec_num), aud_active_skin->properties.mainwin_number_4_x,
+                       aud_active_skin->properties.mainwin_number_4_y);
+
+    if (aud_active_skin->properties.mainwin_playstatus_x && aud_active_skin->properties.mainwin_playstatus_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), mainwin_playstatus, aud_active_skin->properties.mainwin_playstatus_x,
+                       aud_active_skin->properties.mainwin_playstatus_y);
+
+    if (aud_active_skin->properties.mainwin_volume_x && aud_active_skin->properties.mainwin_volume_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_volume), aud_active_skin->properties.mainwin_volume_x,
+                       aud_active_skin->properties.mainwin_volume_y);
+
+    if (aud_active_skin->properties.mainwin_balance_x && aud_active_skin->properties.mainwin_balance_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_balance), aud_active_skin->properties.mainwin_balance_x,
+                       aud_active_skin->properties.mainwin_balance_y);
+
+    if (aud_active_skin->properties.mainwin_position_x && aud_active_skin->properties.mainwin_position_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_position), aud_active_skin->properties.mainwin_position_x,
+                       aud_active_skin->properties.mainwin_position_y);
+
+    if (aud_active_skin->properties.mainwin_previous_x && aud_active_skin->properties.mainwin_previous_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), mainwin_rew, aud_active_skin->properties.mainwin_previous_x,
+                       aud_active_skin->properties.mainwin_previous_y);
+
+    if (aud_active_skin->properties.mainwin_play_x && aud_active_skin->properties.mainwin_play_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_play), aud_active_skin->properties.mainwin_play_x,
+                       aud_active_skin->properties.mainwin_play_y);
+
+    if (aud_active_skin->properties.mainwin_pause_x && aud_active_skin->properties.mainwin_pause_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_pause), aud_active_skin->properties.mainwin_pause_x,
+                       aud_active_skin->properties.mainwin_pause_y);
+
+    if (aud_active_skin->properties.mainwin_stop_x && aud_active_skin->properties.mainwin_stop_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_stop), aud_active_skin->properties.mainwin_stop_x,
+                       aud_active_skin->properties.mainwin_stop_y);
+
+    if (aud_active_skin->properties.mainwin_next_x && aud_active_skin->properties.mainwin_next_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_fwd), aud_active_skin->properties.mainwin_next_x,
+                       aud_active_skin->properties.mainwin_next_y);
+
+    if (aud_active_skin->properties.mainwin_eject_x && aud_active_skin->properties.mainwin_eject_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_eject), aud_active_skin->properties.mainwin_eject_x,
+                       aud_active_skin->properties.mainwin_eject_y);
+
+    if (aud_active_skin->properties.mainwin_eqbutton_x && aud_active_skin->properties.mainwin_eqbutton_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_eq), aud_active_skin->properties.mainwin_eqbutton_x,
+                       aud_active_skin->properties.mainwin_eqbutton_y);
+
+    if (aud_active_skin->properties.mainwin_plbutton_x && aud_active_skin->properties.mainwin_plbutton_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_pl), aud_active_skin->properties.mainwin_plbutton_x,
+                       aud_active_skin->properties.mainwin_plbutton_y);
+
+    if (aud_active_skin->properties.mainwin_shuffle_x && aud_active_skin->properties.mainwin_shuffle_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_shuffle), aud_active_skin->properties.mainwin_shuffle_x,
+                       aud_active_skin->properties.mainwin_shuffle_y);
+
+    if (aud_active_skin->properties.mainwin_repeat_x && aud_active_skin->properties.mainwin_repeat_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_repeat), aud_active_skin->properties.mainwin_repeat_x,
+                       aud_active_skin->properties.mainwin_repeat_y);
+
+    if (aud_active_skin->properties.mainwin_about_x && aud_active_skin->properties.mainwin_about_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_about), aud_active_skin->properties.mainwin_about_x,
+                       aud_active_skin->properties.mainwin_about_y);
+
+    if (aud_active_skin->properties.mainwin_minimize_x && aud_active_skin->properties.mainwin_minimize_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_minimize), config.player_shaded ? 244 : aud_active_skin->properties.mainwin_minimize_x,
+                       config.player_shaded ? 3 : aud_active_skin->properties.mainwin_minimize_y);
+
+    if (aud_active_skin->properties.mainwin_shade_x && aud_active_skin->properties.mainwin_shade_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_shade), config.player_shaded ? 254 : aud_active_skin->properties.mainwin_shade_x,
+                       config.player_shaded ? 3 : aud_active_skin->properties.mainwin_shade_y);
+
+    if (aud_active_skin->properties.mainwin_close_x && aud_active_skin->properties.mainwin_close_y)
+        gtk_fixed_move(GTK_FIXED(SKINNED_WINDOW(mainwin)->fixed), GTK_WIDGET(mainwin_close), config.player_shaded ? 264 : aud_active_skin->properties.mainwin_close_x,
+                       config.player_shaded ? 3 : aud_active_skin->properties.mainwin_close_y);
+
+    mainwin_refresh_visible();
+
+    /* window size, mainwinWidth && mainwinHeight properties */
+    if (aud_active_skin->properties.mainwin_height && aud_active_skin->properties.mainwin_width)
+    {
+        dock_window_resize(GTK_WINDOW(mainwin), config.player_shaded ? MAINWIN_SHADED_WIDTH * MAINWIN_SCALE_FACTOR : aud_active_skin->properties.mainwin_width * MAINWIN_SCALE_FACTOR,
+                           config.player_shaded ? MAINWIN_SHADED_HEIGHT * MAINWIN_SCALE_FACTOR : aud_active_skin->properties.mainwin_height * MAINWIN_SCALE_FACTOR,
+                           aud_active_skin->properties.mainwin_width * MAINWIN_SCALE_FACTOR,
+                           aud_active_skin->properties.mainwin_height * MAINWIN_SCALE_FACTOR);
+
+        gdk_flush();
+    }
+}
+
+void
+mainwin_set_song_info(gint bitrate,
+                      gint frequency,
+                      gint n_channels)
+{
+    gchar *text;
+    gchar *title;
+    Playlist *playlist = aud_playlist_get_active();
+
+    GDK_THREADS_ENTER();
+    if (bitrate != -1) {
+        bitrate /= 1000;
+
+        if (bitrate < 1000) {
+            /* Show bitrate in 1000s */
+            text = g_strdup_printf("%3d", bitrate);
+        }
+        else {
+            /* Show bitrate in 100,000s */
+            text = g_strdup_printf("%2dH", bitrate / 100);
+        }
+        ui_skinned_textbox_set_text(mainwin_rate_text, text);
+        g_free(text);
+    }
+    else
+        ui_skinned_textbox_set_text(mainwin_rate_text, _("VBR"));
+
+    /* Show sampling frequency in kHz */
+    text = g_strdup_printf("%2d", frequency / 1000);
+    ui_skinned_textbox_set_text(mainwin_freq_text, text);
+    g_free(text);
+
+    ui_skinned_monostereo_set_num_channels(mainwin_monostereo, n_channels);
+
+    if (!audacious_drct_get_paused() && mainwin_playstatus != NULL)
+        ui_skinned_playstatus_set_status(mainwin_playstatus, STATUS_PLAY);
+
+    if (aud_active_skin && aud_active_skin->properties.mainwin_othertext)
+    {
+        if (bitrate != -1)
+            text = g_strdup_printf("%d kbps, %0.1f kHz, %s",
+                                   bitrate,
+                                   (gfloat) frequency / 1000,
+                                   (n_channels > 1) ? _("stereo") : _("mono"));
+        else
+            text = g_strdup_printf("VBR, %0.1f kHz, %s",
+                                   (gfloat) frequency / 1000,
+                                   (n_channels > 1) ? _("stereo") : _("mono"));
+
+        ui_skinned_textbox_set_text(mainwin_othertext, text);
+        g_free(text);
+    }
+
+    title = aud_playlist_get_info_text(playlist);
+    mainwin_set_song_title(title);
+    g_free(title);
+    GDK_THREADS_LEAVE();
+}
+
+void
+mainwin_clear_song_info(void)
+{
+    if (!mainwin)
+        return;
+
+    /* clear title */
+    mainwin_set_song_title(NULL);
+
+#if 0
+    /* clear sampling parameters */
+    playback_set_sample_params(0, 0, 0);
+#endif
+    UI_SKINNED_HORIZONTAL_SLIDER(mainwin_position)->pressed = FALSE;
+    UI_SKINNED_HORIZONTAL_SLIDER(mainwin_sposition)->pressed = FALSE;
+
+    /* clear sampling parameter displays */
+    ui_skinned_textbox_set_text(mainwin_rate_text, "   ");
+    ui_skinned_textbox_set_text(mainwin_freq_text, "  ");
+    ui_skinned_monostereo_set_num_channels(mainwin_monostereo, 0);
+
+    if (mainwin_playstatus != NULL)
+        ui_skinned_playstatus_set_status(mainwin_playstatus, STATUS_STOP);
+
+    mainwin_refresh_visible();
+
+    playlistwin_hide_timer();
+
+    ui_vis_clear_data(mainwin_vis);
+    ui_svis_clear_data(mainwin_svis);
+}
+
+void
+mainwin_disable_seekbar(void)
+{
+    if (!mainwin)
+        return;
+
+    gtk_widget_hide(mainwin_position);
+    gtk_widget_hide(mainwin_sposition);
+}
+
+static gboolean
+mainwin_mouse_button_release(GtkWidget * widget,
+                             GdkEventButton * event,
+                             gpointer callback_data)
+{
+    if (dock_is_moving(GTK_WINDOW(mainwin))) {
+        dock_move_release(GTK_WINDOW(mainwin));
+    }
+
+    return FALSE;
+}
+
+void
+mainwin_scrolled(GtkWidget *widget, GdkEventScroll *event,
+                 gpointer callback_data)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    switch (event->direction) {
+        case GDK_SCROLL_UP:
+            mainwin_set_volume_diff(config.mouse_change);
+            break;
+        case GDK_SCROLL_DOWN:
+            mainwin_set_volume_diff(-config.mouse_change);
+            break;
+        case GDK_SCROLL_LEFT:
+            if (aud_playlist_get_current_length(playlist) != -1)
+                audacious_drct_seek(CLAMP(audacious_drct_get_time() - 1000,
+                                    0, aud_playlist_get_current_length(playlist)) / 1000);
+            break;
+        case GDK_SCROLL_RIGHT:
+            if (aud_playlist_get_current_length(playlist) != -1)
+                audacious_drct_seek(CLAMP(audacious_drct_get_time() + 1000,
+                                    0, aud_playlist_get_current_length(playlist)) / 1000);
+            break;
+    }
+}
+
+static gboolean
+mainwin_widget_contained(GdkEventButton *event, int x, int y, int w, int h)
+{
+    if ((event->x > x && event->y > y) &&
+        (event->x < x+w && event->y < y+h))
+        return TRUE;
+
+    return FALSE;
+}
+
+static gboolean
+mainwin_mouse_button_press(GtkWidget * widget,
+                           GdkEventButton * event,
+                           gpointer callback_data)
+{
+    if (config.scaled) {
+        /*
+         * A hack to make scaling transparent to callbacks.
+         * We should make a copy of this data instead of
+         * tampering with the data we get from gtk+
+         */
+        event->x /= config.scale_factor;
+        event->y /= config.scale_factor;
+    }
+
+    if (event->button == 1 && event->type == GDK_2BUTTON_PRESS && event->y < 14) {
+        mainwin_set_shade(!config.player_shaded);
+        if (dock_is_moving(GTK_WINDOW(mainwin)))
+            dock_move_release(GTK_WINDOW(mainwin));
+        return TRUE;
+    }
+
+    if (event->button == 3) {
+        /* Pop up playback menu if right clicked over playback-control widgets,
+         * otherwise popup general menu
+         */
+        if (mainwin_widget_contained(event, aud_active_skin->properties.mainwin_position_x,
+                                     aud_active_skin->properties.mainwin_position_y, 248, 10) ||
+            mainwin_widget_contained(event, aud_active_skin->properties.mainwin_previous_x,
+                                     aud_active_skin->properties.mainwin_previous_y, 23, 18) ||
+            mainwin_widget_contained(event, aud_active_skin->properties.mainwin_play_x,
+                                     aud_active_skin->properties.mainwin_play_y, 23, 18) ||
+            mainwin_widget_contained(event, aud_active_skin->properties.mainwin_pause_x,
+                                     aud_active_skin->properties.mainwin_pause_y, 23, 18) ||
+            mainwin_widget_contained(event, aud_active_skin->properties.mainwin_stop_x,
+                                     aud_active_skin->properties.mainwin_stop_y, 23, 18) ||
+            mainwin_widget_contained(event, aud_active_skin->properties.mainwin_next_x,
+                                     aud_active_skin->properties.mainwin_next_y, 23, 18))
+        {
+
+            ui_manager_popup_menu_show(GTK_MENU(mainwin_playback_menu),
+                                       event->x_root,
+                                       event->y_root, 3, event->time);
+
+        } else {
+            /*
+             * Pop up the main menu a few pixels down.
+             * This will avoid that anything is selected
+             * if one right-clicks to focus the window
+             * without raising it.
+             *
+             ***MD I think the above is stupid, people don't expect this
+             *
+             */
+
+            ui_manager_popup_menu_show(GTK_MENU(mainwin_general_menu),
+                                       event->x_root,
+                                       event->y_root, 3, event->time);
+
+        }
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+mainwin_keypress(GtkWidget * grab_widget,
+                 GdkEventKey * event,
+                 gpointer data)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    switch (event->keyval) {
+
+        case GDK_Up:
+        case GDK_KP_Up:
+        case GDK_KP_8:
+            mainwin_set_volume_diff(2);
+            break;
+        case GDK_Down:
+        case GDK_KP_Down:
+        case GDK_KP_2:
+            mainwin_set_volume_diff(-2);
+            break;
+        case GDK_Left:
+        case GDK_KP_Left:
+        case GDK_KP_7:
+            if (aud_playlist_get_current_length(playlist) != -1)
+                audacious_drct_seek(CLAMP
+                              (audacious_drct_get_time() - 5000, 0,
+                               aud_playlist_get_current_length(playlist)) / 1000);
+            break;
+        case GDK_Right:
+        case GDK_KP_Right:
+        case GDK_KP_9:
+            if (aud_playlist_get_current_length(playlist) != -1)
+                audacious_drct_seek(CLAMP
+                              (audacious_drct_get_time() + 5000, 0,
+                               aud_playlist_get_current_length(playlist)) / 1000);
+            break;
+        case GDK_KP_4:
+            aud_playlist_prev(playlist);
+            break;
+        case GDK_KP_6:
+            aud_playlist_next(playlist);
+            break;
+        case GDK_KP_Insert:
+#if 0
+            ui_jump_to_track();
+#endif
+            break;
+        case GDK_Return:
+        case GDK_KP_Enter:
+        case GDK_KP_5:
+            mainwin_play_pushed();
+            break;
+        case GDK_space:
+            audacious_drct_pause();
+            break;
+        case GDK_Escape:
+            mainwin_minimize_cb();
+            break;
+        case GDK_Tab:
+            if (event->state & GDK_CONTROL_MASK) {
+                if (config.equalizer_visible)
+                    gtk_window_present(GTK_WINDOW(equalizerwin));
+                else if (config.playlist_visible)
+                    gtk_window_present(GTK_WINDOW(playlistwin));
+            }
+            break;
+        case GDK_c:
+            if (event->state & GDK_CONTROL_MASK) {
+                Playlist *playlist = aud_playlist_get_active();
+                gint pos = aud_playlist_get_position(playlist);
+                gchar *title = aud_playlist_get_songtitle(playlist, pos);
+
+                if (title != NULL) {
+                    GtkClipboard *clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+                    gtk_clipboard_set_text(clip, title, -1);
+                    gtk_clipboard_store(clip);
+                }
+
+                return TRUE;
+            }
+            return FALSE;
+        default:
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+mainwin_jump_to_time_cb(GtkWidget * widget,
+                        GtkWidget * entry)
+{
+    guint min = 0, sec = 0, params;
+    gint time;
+    Playlist *playlist = aud_playlist_get_active();
+
+    params = sscanf(gtk_entry_get_text(GTK_ENTRY(entry)), "%u:%u",
+                    &min, &sec);
+    if (params == 2)
+        time = (min * 60) + sec;
+    else if (params == 1)
+        time = min;
+    else
+        return;
+
+    if (aud_playlist_get_current_length(playlist) > -1 &&
+        time <= (aud_playlist_get_current_length(playlist) / 1000))
+    {
+        audacious_drct_seek(time);
+        gtk_widget_destroy(mainwin_jtt);
+    }
+}
+
+
+void
+mainwin_jump_to_time(void)
+{
+    GtkWidget *vbox, *hbox_new, *hbox_total;
+    GtkWidget *time_entry, *label, *bbox, *jump, *cancel;
+    GtkWidget *dialog;
+    guint tindex;
+    gchar time_str[10];
+
+    if (!audacious_drct_get_playing()) {
+        dialog =
+            gtk_message_dialog_new (GTK_WINDOW (mainwin),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_CLOSE,
+                                    _("Can't jump to time when no track is being played.\n"));
+        gtk_dialog_run (GTK_DIALOG (dialog));
+        gtk_widget_destroy (dialog);
+        return;
+    }
+
+    if (mainwin_jtt) {
+        gtk_window_present(GTK_WINDOW(mainwin_jtt));
+        return;
+    }
+
+    mainwin_jtt = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_type_hint(GTK_WINDOW(mainwin_jtt),
+                             GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    gtk_window_set_title(GTK_WINDOW(mainwin_jtt), _("Jump to Time"));
+    gtk_window_set_position(GTK_WINDOW(mainwin_jtt), GTK_WIN_POS_CENTER);
+    gtk_window_set_transient_for(GTK_WINDOW(mainwin_jtt),
+                                 GTK_WINDOW(mainwin));
+
+    g_signal_connect(mainwin_jtt, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &mainwin_jtt);
+    gtk_container_border_width(GTK_CONTAINER(mainwin_jtt), 10);
+
+    vbox = gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(mainwin_jtt), vbox);
+
+    hbox_new = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox_new, TRUE, TRUE, 5);
+
+    time_entry = gtk_entry_new();
+    gtk_box_pack_start(GTK_BOX(hbox_new), time_entry, FALSE, FALSE, 5);
+    g_signal_connect(time_entry, "activate",
+                     G_CALLBACK(mainwin_jump_to_time_cb), time_entry);
+
+    gtk_widget_set_size_request(time_entry, 70, -1);
+    label = gtk_label_new(_("minutes:seconds"));
+    gtk_box_pack_start(GTK_BOX(hbox_new), label, FALSE, FALSE, 5);
+
+    hbox_total = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox_total, TRUE, TRUE, 5);
+    gtk_widget_show(hbox_total);
+
+    /* FIXME: Disable display of current track length. It's not
+       updated when track changes */
+
+    label = gtk_label_new(_("Track length:"));
+    gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 5);
+
+    gint len = aud_playlist_get_current_length(aud_playlist_get_active()) / 1000;
+    g_snprintf(time_str, sizeof(time_str), "%u:%2.2u", len / 60, len % 60);
+    label = gtk_label_new(time_str);
+
+    gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 10);
+
+    bbox = gtk_hbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, TRUE, TRUE, 0);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+    gtk_container_add(GTK_CONTAINER(bbox), cancel);
+    g_signal_connect_swapped(cancel, "clicked",
+                             G_CALLBACK(gtk_widget_destroy), mainwin_jtt);
+
+    jump = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+    GTK_WIDGET_SET_FLAGS(jump, GTK_CAN_DEFAULT);
+    gtk_container_add(GTK_CONTAINER(bbox), jump);
+    g_signal_connect(jump, "clicked",
+                     G_CALLBACK(mainwin_jump_to_time_cb), time_entry);
+
+    tindex = audacious_drct_get_time() / 1000;
+    g_snprintf(time_str, sizeof(time_str), "%u:%2.2u", tindex / 60,
+               tindex % 60);
+    gtk_entry_set_text(GTK_ENTRY(time_entry), time_str);
+
+    gtk_entry_select_region(GTK_ENTRY(time_entry), 0, strlen(time_str));
+
+    gtk_widget_show_all(mainwin_jtt);
+
+    gtk_widget_grab_focus(time_entry);
+    gtk_widget_grab_default(jump);
+}
+
+/*
+ * Rewritten 09/13/06:
+ *
+ * Remove all of this flaky iter/sourcelist/strsplit stuff.
+ * All we care about is the filepath.
+ *
+ * We can figure this out and easily pass it to g_filename_from_uri().
+ *   - nenolod
+ */
+void
+mainwin_drag_data_received(GtkWidget * widget,
+                           GdkDragContext * context,
+                           gint x,
+                           gint y,
+                           GtkSelectionData * selection_data,
+                           guint info,
+                           guint time,
+                           gpointer user_data)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    g_return_if_fail(selection_data != NULL);
+    g_return_if_fail(selection_data->data != NULL);
+#if 0
+    if (aud_str_has_prefix_nocase((gchar *) selection_data->data, "fonts:///"))
+    {
+        gchar *path = (gchar *) selection_data->data;
+        gchar *decoded = g_filename_from_uri(path, NULL, NULL);
+
+        if (decoded == NULL)
+            return;
+
+        aud_cfg->playlist_font = g_strconcat(decoded, strrchr(aud_cfg->playlist_font, ' '), NULL);
+        ui_skinned_playlist_set_font(aud_cfg->playlist_font);
+        playlistwin_update_list(playlist);
+
+        g_free(decoded);
+
+        return;
+    }
+
+    /* perhaps make suffix check case-insensitive -- desowin */
+    if (aud_str_has_prefix_nocase((char*)selection_data->data, "file:///")) {
+        if (str_has_suffix_nocase((char*)selection_data->data, ".wsz\r\n") ||
+            str_has_suffix_nocase((char*)selection_data->data, ".zip\r\n")) {
+            on_skin_view_drag_data_received(GTK_WIDGET(user_data), context, x, y, selection_data, info, time, NULL);
+            return;
+        }
+    }
+#endif
+    aud_playlist_clear(playlist);
+    aud_playlist_add_url(playlist, (gchar *) selection_data->data);
+    audacious_drct_initiate();
+}
+
+static void
+on_add_url_add_clicked(GtkWidget * widget,
+                       GtkWidget * entry)
+{
+    const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (text && *text)
+        aud_playlist_add_url(aud_playlist_get_active(), text);
+}
+
+static void
+on_add_url_ok_clicked(GtkWidget * widget,
+                      GtkWidget * entry)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (text && *text)
+    {
+        aud_playlist_clear(playlist);
+        aud_playlist_add_url(playlist, text);
+        audacious_drct_initiate();
+    }
+}
+
+static void
+on_visibility_warning_toggle(GtkToggleButton *tbt, gpointer unused)
+{
+    config.warn_about_win_visibility = !gtk_toggle_button_get_active(tbt);
+}
+
+static void
+on_visibility_warning_response(GtkDialog *dlg, gint r_id, gpointer unused)
+{
+    switch (r_id)
+    {
+        case GTK_RESPONSE_OK:
+            mainwin_show(TRUE);
+            break;
+        case GTK_RESPONSE_CANCEL:
+        default:
+            break;
+    }
+    gtk_widget_destroy(GTK_WIDGET(dlg));
+}
+
+void
+mainwin_show_visibility_warning(void)
+{
+    if (config.warn_about_win_visibility)
+    {
+        GtkWidget *label, *checkbt, *vbox;
+        GtkWidget *warning_dlg =
+            gtk_dialog_new_with_buttons( _("Audacious - visibility warning") ,
+                                         GTK_WINDOW(mainwin) ,
+                                         GTK_DIALOG_DESTROY_WITH_PARENT ,
+                                         _("Show main player window") ,
+                                         GTK_RESPONSE_OK , _("Ignore") ,
+                                         GTK_RESPONSE_CANCEL , NULL );
+
+        vbox = gtk_vbox_new( FALSE , 4 );
+        gtk_container_set_border_width( GTK_CONTAINER(vbox) , 4 );
+        gtk_box_pack_start( GTK_BOX(GTK_DIALOG(warning_dlg)->vbox) , vbox , TRUE , TRUE , 0 );
+        label = gtk_label_new( _("Audacious has been started with all of its windows hidden.\n"
+                                 "You may want to show the player window again to control Audacious; "
+                                 "otherwise, you'll have to control it remotely via audtool or "
+                                 "enabled plugins (such as the statusicon plugin).") );
+        gtk_label_set_line_wrap( GTK_LABEL(label) , TRUE );
+        gtk_misc_set_alignment( GTK_MISC(label) , 0.0 , 0.0 );
+        checkbt = gtk_check_button_new_with_label( _("Always ignore, show/hide is controlled remotely") );
+        gtk_box_pack_start( GTK_BOX(vbox) , label , TRUE , TRUE , 0 );
+        gtk_box_pack_start( GTK_BOX(vbox) , checkbt , TRUE , TRUE , 0 );
+        g_signal_connect( G_OBJECT(checkbt) , "toggled" ,
+                          G_CALLBACK(on_visibility_warning_toggle) , NULL );
+        g_signal_connect( G_OBJECT(warning_dlg) , "response" ,
+                          G_CALLBACK(on_visibility_warning_response) , NULL );
+        gtk_widget_show_all(warning_dlg);
+    }
+}
+
+static void
+on_broken_gtk_engine_warning_toggle(GtkToggleButton *tbt, gpointer unused)
+{
+    config.warn_about_broken_gtk_engines = !gtk_toggle_button_get_active(tbt);
+}
+
+void
+ui_main_check_theme_engine(void)
+{
+    GtkSettings *settings;
+    GtkWidget *widget;
+    gchar *theme = NULL;
+
+    widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_widget_ensure_style(widget);
+
+    settings = gtk_settings_get_default();
+    g_object_get(G_OBJECT(settings), "gtk-theme-name", &theme, NULL);
+    gtk_widget_destroy(widget);
+
+    if (theme == NULL)
+        return;
+
+    if (g_ascii_strcasecmp(theme, "Qt"))
+    {
+        g_free(theme);
+        return;
+    }
+
+    if (config.warn_about_broken_gtk_engines)
+    {
+        gchar *msg;
+        GtkWidget *label, *checkbt, *vbox;
+        GtkWidget *warning_dlg =
+            gtk_dialog_new_with_buttons( _("Audacious - broken GTK engine usage warning") ,
+                                         GTK_WINDOW(mainwin) , GTK_DIALOG_DESTROY_WITH_PARENT ,
+                                         GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL );
+        vbox = gtk_vbox_new( FALSE , 4 );
+        gtk_container_set_border_width( GTK_CONTAINER(vbox) , 4 );
+        gtk_box_pack_start( GTK_BOX(GTK_DIALOG(warning_dlg)->vbox) , vbox ,
+                            TRUE , TRUE , 0 );
+
+        msg = g_strdup_printf(_("<big><b>Broken GTK engine in use</b></big>\n\n"
+                                "Audacious has detected that you are using a broken GTK engine.\n\n"
+                                "The theme engine you are using, <i>%s</i>, is incompatible with some of the features "
+                                "used by modern skins. The incompatible features have been disabled for this session.\n\n"
+                                "To use these features, please consider using a different GTK theme engine."), theme);
+        label = gtk_label_new(msg);
+        gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
+        g_free(msg);
+
+        gtk_label_set_line_wrap( GTK_LABEL(label) , TRUE );
+        gtk_misc_set_alignment( GTK_MISC(label) , 0.0 , 0.0 );
+        checkbt = gtk_check_button_new_with_label( _("Do not display this warning again") );
+        gtk_box_pack_start( GTK_BOX(vbox) , label , TRUE , TRUE , 0 );
+        gtk_box_pack_start( GTK_BOX(vbox) , checkbt , TRUE , TRUE , 0 );
+        g_signal_connect( G_OBJECT(checkbt) , "toggled" ,
+                          G_CALLBACK(on_broken_gtk_engine_warning_toggle) , NULL );
+        g_signal_connect( G_OBJECT(warning_dlg) , "response" ,
+                          G_CALLBACK(gtk_widget_destroy) , NULL );        
+        gtk_widget_show_all(warning_dlg);
+        gtk_window_stick(GTK_WINDOW(warning_dlg));
+    }
+
+    config.disable_inline_gtk = TRUE;
+
+    g_free(theme);
+}
+
+void
+mainwin_show_add_url_window(void)
+{
+#if 0
+    static GtkWidget *url_window = NULL;
+
+    if (!url_window) {
+        url_window =
+            util_add_url_dialog_new(_("Enter location to play:"),
+                                    G_CALLBACK(on_add_url_ok_clicked),
+                                    G_CALLBACK(on_add_url_add_clicked));
+        gtk_window_set_transient_for(GTK_WINDOW(url_window),
+                                     GTK_WINDOW(mainwin));
+        g_signal_connect(url_window, "destroy",
+                         G_CALLBACK(gtk_widget_destroyed),
+                         &url_window);
+    }
+
+    gtk_window_present(GTK_WINDOW(url_window));
+#endif
+}
+
+static void
+check_set( GtkActionGroup * action_group ,
+           const gchar * action_name ,
+           gboolean is_on )
+{
+    /* check_set noew uses gtkaction */
+    GtkAction *action = gtk_action_group_get_action( action_group , action_name );
+    if ( action != NULL )
+        gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , is_on );
+    return;
+}
+
+void
+mainwin_eject_pushed(void)
+{
+    run_filebrowser(PLAY_BUTTON);
+}
+
+void
+mainwin_rev_pushed(void)
+{
+    g_get_current_time(&cb_time);
+
+    seek_initial_pos = ui_skinned_horizontal_slider_get_position(mainwin_position);
+    seek_state = MAINWIN_SEEK_REV;
+    mainwin_timeout_id = g_timeout_add(MAINWIN_UPDATE_INTERVAL,
+                                       (GSourceFunc) mainwin_idle_func, NULL);
+}
+
+void
+mainwin_rev_release(void)
+{
+    GTimeVal now_time;
+    GTimeVal delta_time;
+    gulong now_dur;
+
+    g_get_current_time(&now_time);
+
+    delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+    delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+    now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+    if ( now_dur <= TRISTATE_THRESHOLD )
+    {
+        /* interpret as 'skip to previous song' */
+        aud_playlist_prev(aud_playlist_get_active());
+    }
+    else
+    {
+        /* interpret as 'seek' */
+        mainwin_position_release_cb( mainwin_position, ui_skinned_horizontal_slider_get_position(mainwin_position) );
+    }
+
+    seek_state = MAINWIN_SEEK_NIL;
+
+    g_source_remove(mainwin_timeout_id);
+    mainwin_timeout_id = 0;
+}
+
+void
+mainwin_fwd_pushed(void)
+{
+    g_get_current_time(&cb_time);
+
+    seek_initial_pos = ui_skinned_horizontal_slider_get_position(mainwin_position);
+    seek_state = MAINWIN_SEEK_FWD;
+    mainwin_timeout_id = g_timeout_add(MAINWIN_UPDATE_INTERVAL,
+                                       (GSourceFunc) mainwin_idle_func, NULL);
+}
+
+void
+mainwin_fwd_release(void)
+{
+    GTimeVal now_time;
+    GTimeVal delta_time;
+    gulong now_dur;
+
+    g_get_current_time(&now_time);
+
+    delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+    delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+    now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+    if ( now_dur <= TRISTATE_THRESHOLD )
+    {
+        /* interpret as 'skip to next song' */
+        aud_playlist_next(aud_playlist_get_active());
+    }
+    else
+    {
+        /* interpret as 'seek' */
+        mainwin_position_release_cb( mainwin_position, ui_skinned_horizontal_slider_get_position(mainwin_position) );
+    }
+
+    seek_state = MAINWIN_SEEK_NIL;
+
+    g_source_remove(mainwin_timeout_id);
+    mainwin_timeout_id = 0;
+}
+
+void
+mainwin_play_pushed(void)
+{
+    if (ab_position_a != -1)
+        audacious_drct_seek(ab_position_a / 1000);
+    if (audacious_drct_get_paused()) {
+        audacious_drct_pause();
+        return;
+    }
+
+    if (aud_playlist_get_length(aud_playlist_get_active()))
+        audacious_drct_initiate();
+    else
+        mainwin_eject_pushed();
+}
+
+void
+mainwin_stop_pushed(void)
+{
+    audacious_drct_stop();
+    mainwin_clear_song_info();
+    ab_position_a = ab_position_b = -1;
+}
+
+void
+mainwin_shuffle_pushed(gboolean toggled)
+{
+    check_set( toggleaction_group_others , "playback shuffle" , toggled );
+}
+
+void mainwin_shuffle_pushed_cb(void) {
+    mainwin_shuffle_pushed(UI_SKINNED_BUTTON(mainwin_shuffle)->inside);
+}
+
+void
+mainwin_repeat_pushed(gboolean toggled)
+{
+    check_set( toggleaction_group_others , "playback repeat" , toggled );
+}
+
+void mainwin_repeat_pushed_cb(void) {
+    mainwin_repeat_pushed(UI_SKINNED_BUTTON(mainwin_repeat)->inside);
+}
+
+void mainwin_equalizer_pushed_cb(void) {
+    mainwin_eq_pushed(UI_SKINNED_BUTTON(mainwin_eq)->inside);
+}
+
+void mainwin_playlist_pushed_cb(void) {
+    mainwin_pl_pushed(UI_SKINNED_BUTTON(mainwin_pl)->inside);
+}
+
+void
+mainwin_eq_pushed(gboolean toggled)
+{
+    equalizerwin_show(toggled);
+}
+
+void
+mainwin_pl_pushed(gboolean toggled)
+{
+    if (toggled)
+        playlistwin_show();
+    else
+        playlistwin_hide();
+}
+
+gint
+mainwin_spos_frame_cb(gint pos)
+{
+    if (mainwin_sposition) {
+        gint x = 0;
+        if (pos < 6)
+            x = 17;
+        else if (pos < 9)
+            x = 20;
+        else
+            x = 23;
+
+        UI_SKINNED_HORIZONTAL_SLIDER(mainwin_sposition)->knob_nx = x;
+        UI_SKINNED_HORIZONTAL_SLIDER(mainwin_sposition)->knob_px = x;
+    }
+    return 1;
+}
+
+void
+mainwin_spos_motion_cb(GtkWidget *widget, gint pos)
+{
+    gint time;
+    gchar *time_msg;
+    Playlist *playlist = aud_playlist_get_active();
+
+    pos--;
+
+    time = ((aud_playlist_get_current_length(playlist) / 1000) * pos) / 12;
+
+    if (config.timer_mode == TIMER_REMAINING) {
+        time = (aud_playlist_get_current_length(playlist) / 1000) - time;
+        time_msg = g_strdup_printf("-%2.2d", time / 60);
+        ui_skinned_textbox_set_text(mainwin_stime_min, time_msg);
+        g_free(time_msg);
+    }
+    else {
+        time_msg = g_strdup_printf(" %2.2d", time / 60);
+        ui_skinned_textbox_set_text(mainwin_stime_min, time_msg);
+        g_free(time_msg);
+    }
+
+    time_msg = g_strdup_printf("%2.2d", time % 60);
+    ui_skinned_textbox_set_text(mainwin_stime_sec, time_msg);
+    g_free(time_msg);
+}
+
+void
+mainwin_spos_release_cb(GtkWidget *widget, gint pos)
+{
+    audacious_drct_seek(((aud_playlist_get_current_length(aud_playlist_get_active()) / 1000) *
+                   (pos - 1)) / 12);
+}
+
+void
+mainwin_position_motion_cb(GtkWidget *widget, gint pos)
+{
+    gint length, time;
+    gchar *seek_msg;
+
+    length = aud_playlist_get_current_length(aud_playlist_get_active()) / 1000;
+    time = (length * pos) / 219;
+    seek_msg = g_strdup_printf(_("Seek to: %d:%-2.2d/%d:%-2.2d (%d%%)"),
+                               time / 60, time % 60,
+                               length / 60, length % 60,
+                               (length != 0) ? (time * 100) / length : 0);
+    mainwin_lock_info_text(seek_msg);
+    g_free(seek_msg);
+}
+
+void
+mainwin_position_release_cb(GtkWidget *widget, gint pos)
+{
+    gint length, time;
+
+    length = audacious_drct_get_length();
+    time = (length * pos) / 219;
+    audacious_drct_seek(time);
+    mainwin_release_info_text();
+}
+
+gint
+mainwin_volume_frame_cb(gint pos)
+{
+    return (gint) rint((pos / 52.0) * 28);
+}
+
+void
+mainwin_adjust_volume_motion(gint v)
+{
+    gchar *volume_msg;
+
+    volume_msg = g_strdup_printf(_("Volume: %d%%"), v);
+    mainwin_lock_info_text(volume_msg);
+    g_free(volume_msg);
+
+    if (balance < 0)
+        audacious_drct_set_volume(v, (v * (100 - abs(balance))) / 100);
+    else if (balance > 0)
+        audacious_drct_set_volume((v * (100 - abs(balance))) / 100, v);
+    else
+        audacious_drct_set_volume(v, v);
+}
+
+void
+mainwin_adjust_volume_release(void)
+{
+    mainwin_release_info_text();
+}
+
+void
+mainwin_adjust_balance_motion(gint b)
+{
+    gchar *balance_msg;
+    gint v, pvl, pvr;
+
+    balance = b;
+    aud_input_get_volume(&pvl, &pvr);
+    v = MAX(pvl, pvr);
+    if (b < 0) {
+        balance_msg = g_strdup_printf(_("Balance: %d%% left"), -b);
+        audacious_drct_set_volume(v, (gint) rint(((100 + b) / 100.0) * v));
+    }
+    else if (b == 0) {
+        balance_msg = g_strdup_printf(_("Balance: center"));
+        audacious_drct_set_volume(v, v);
+    }
+    else {                      /* b > 0 */
+        balance_msg = g_strdup_printf(_("Balance: %d%% right"), b);
+        audacious_drct_set_volume((gint) rint(((100 - b) / 100.0) * v), v);
+    }
+    mainwin_lock_info_text(balance_msg);
+    g_free(balance_msg);
+}
+
+void
+mainwin_adjust_balance_release(void)
+{
+    mainwin_release_info_text();
+}
+
+void
+mainwin_set_volume_slider(gint percent)
+{
+    ui_skinned_horizontal_slider_set_position(mainwin_volume, (gint) rint((percent * 51) / 100.0));
+}
+
+void
+mainwin_set_balance_slider(gint percent)
+{
+    ui_skinned_horizontal_slider_set_position(mainwin_balance, (gint) rint(((percent * 12) / 100.0) + 12));
+}
+
+void
+mainwin_volume_motion_cb(GtkWidget *widget, gint pos)
+{
+
+    gint vol = (pos * 100) / 51;
+    mainwin_adjust_volume_motion(vol);
+    equalizerwin_set_volume_slider(vol);
+}
+
+gboolean
+mainwin_volume_release_cb(GtkWidget *widget, gint pos)
+{
+    mainwin_adjust_volume_release();
+    return FALSE;
+}
+
+gint
+mainwin_balance_frame_cb(gint pos)
+{
+    return ((abs(pos - 12) * 28) / 13);
+}
+
+void
+mainwin_balance_motion_cb(GtkWidget *widget, gint pos)
+{
+    gint bal = ((pos - 12) * 100) / 12;
+    mainwin_adjust_balance_motion(bal);
+    equalizerwin_set_balance_slider(bal);
+}
+
+void
+mainwin_balance_release_cb(GtkWidget *widget, gint pos)
+{
+    mainwin_adjust_volume_release();
+}
+
+void
+mainwin_set_volume_diff(gint diff)
+{
+    gint vl, vr, vol;
+
+    aud_input_get_volume(&vl, &vr);
+    vol = MAX(vl, vr);
+    vol = CLAMP(vol + diff, 0, 100);
+
+    mainwin_adjust_volume_motion(vol);
+    mainwin_set_volume_slider(vol);
+    equalizerwin_set_volume_slider(vol);
+
+    if (mainwin_volume_release_timeout)
+        g_source_remove(mainwin_volume_release_timeout);
+    mainwin_volume_release_timeout =
+        g_timeout_add(700, (GSourceFunc)(mainwin_volume_release_cb), NULL);
+}
+
+void
+mainwin_set_balance_diff(gint diff)
+{
+    gint b;
+    b = CLAMP(balance + diff, -100, 100);
+    mainwin_adjust_balance_motion(b);
+    mainwin_set_balance_slider(b);
+    equalizerwin_set_balance_slider(b);
+}
+
+void
+mainwin_show(gboolean show)
+{
+    if (show)
+        mainwin_real_show();
+    else
+        mainwin_real_hide();
+}
+
+void
+mainwin_real_show(void)
+{
+    config.player_visible = TRUE;
+
+    check_set( toggleaction_group_others , "show player" , TRUE );
+
+    if (config.player_shaded)
+        ui_vis_clear_data(mainwin_vis);
+
+    if (config.show_wm_decorations) {
+        if (config.player_x != -1 && config.save_window_position)
+            gtk_window_move(GTK_WINDOW(mainwin), config.player_x, config.player_y);
+
+        gtk_widget_show(mainwin);
+        return;
+    }
+
+    if (config.player_x != -1 && config.save_window_position)
+        gtk_window_move(GTK_WINDOW(mainwin), config.player_x, config.player_y);
+
+    mainwin_refresh_hints();
+    gtk_window_present(GTK_WINDOW(mainwin));
+}
+
+void
+mainwin_real_hide(void)
+{
+
+    check_set( toggleaction_group_others , "show player", FALSE);
+
+    if (config.player_shaded)
+        ui_svis_clear_data(mainwin_svis);
+
+    gtk_widget_hide(mainwin);
+
+    config.player_visible = FALSE;
+}
+
+
+void
+mainwin_set_stopaftersong(gboolean stop)
+{
+    aud_cfg->stopaftersong = stop;
+    check_set(toggleaction_group_others, "stop after current song", aud_cfg->stopaftersong);
+}
+
+void
+mainwin_set_noplaylistadvance(gboolean no_advance)
+{
+    aud_cfg->no_playlist_advance = no_advance;
+    check_set(toggleaction_group_others, "playback no playlist advance", aud_cfg->no_playlist_advance);
+}
+
+static void
+mainwin_set_scaled(gboolean scaled)
+{
+    gint height;
+
+    if (config.player_shaded)
+        height = MAINWIN_SHADED_HEIGHT;
+    else
+        height = aud_active_skin->properties.mainwin_height;
+
+    dock_window_resize(GTK_WINDOW(mainwin), config.player_shaded ? MAINWIN_SHADED_WIDTH : aud_active_skin->properties.mainwin_width,
+                       config.player_shaded ? MAINWIN_SHADED_HEIGHT : aud_active_skin->properties.mainwin_height,
+                       aud_active_skin->properties.mainwin_width * config.scale_factor , aud_active_skin->properties.mainwin_height * config.scale_factor);
+
+    GList *iter;
+    for (iter = GTK_FIXED (SKINNED_WINDOW(mainwin)->fixed)->children; iter; iter = g_list_next (iter)) {
+        GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+        GtkWidget *child = child_data->widget;
+        g_signal_emit_by_name(child, "toggle-scaled");
+    }
+
+    mainwin_refresh_hints();
+    gtk_widget_shape_combine_mask(mainwin, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + config.player_shaded), 0, 0);
+}
+
+void
+set_scaled(gboolean scaled)
+{
+    config.scaled = scaled;
+
+    mainwin_set_scaled(scaled);
+
+    if (config.eq_scaled_linked)
+        equalizerwin_set_scaled(scaled);
+}
+
+
+
+void
+mainwin_general_menu_callback(gpointer data,
+                              guint action,
+                              GtkWidget * item)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    switch (action) {
+#if 0
+        case MAINWIN_GENERAL_PREFS:
+            show_prefs_window();
+            break;
+        case MAINWIN_GENERAL_ABOUT:
+            show_about_window();
+            break;
+#endif
+        case MAINWIN_GENERAL_PLAYFILE:
+            run_filebrowser(NO_PLAY_BUTTON);
+            break;
+        case MAINWIN_GENERAL_PLAYLOCATION:
+            mainwin_show_add_url_window();
+            break;
+#if 0
+        case MAINWIN_GENERAL_FILEINFO:
+            ui_fileinfo_show_current(playlist);
+            break;
+#endif
+        case MAINWIN_GENERAL_FOCUSPLWIN:
+            gtk_window_present(GTK_WINDOW(playlistwin));
+            break;
+        case MAINWIN_GENERAL_SHOWMWIN:
+            mainwin_show(GTK_CHECK_MENU_ITEM(item)->active);
+            break;
+        case MAINWIN_GENERAL_SHOWPLWIN:
+            if (GTK_CHECK_MENU_ITEM(item)->active)
+                playlistwin_show();
+            else
+                playlistwin_hide();
+            break;
+        case MAINWIN_GENERAL_SHOWEQWIN:
+            if (GTK_CHECK_MENU_ITEM(item)->active)
+                equalizerwin_real_show();
+            else
+                equalizerwin_real_hide();
+            break;
+        case MAINWIN_GENERAL_PREV:
+            aud_playlist_prev(playlist);
+            break;
+        case MAINWIN_GENERAL_PLAY:
+            mainwin_play_pushed();
+            break;
+        case MAINWIN_GENERAL_PAUSE:
+            audacious_drct_pause();
+            break;
+        case MAINWIN_GENERAL_STOP:
+            mainwin_stop_pushed();
+            break;
+        case MAINWIN_GENERAL_NEXT:
+            aud_playlist_next(playlist);
+            break;
+#if 0
+        case MAINWIN_GENERAL_BACK5SEC:
+            if (audacious_drct_get_playing()
+                && aud_playlist_get_current_length(playlist) != -1)
+                playback_seek_relative(-5);
+            break;
+        case MAINWIN_GENERAL_FWD5SEC:
+            if (audacious_drct_get_playing()
+                && aud_playlist_get_current_length(playlist) != -1)
+                playback_seek_relative(5);
+            break;
+#endif
+        case MAINWIN_GENERAL_START:
+            aud_playlist_set_position(playlist, 0);
+            break;
+        case MAINWIN_GENERAL_JTT:
+            mainwin_jump_to_time();
+            break;
+        case MAINWIN_GENERAL_JTF:
+#if 0
+            ui_jump_to_track();
+#endif
+            break;
+        case MAINWIN_GENERAL_EXIT:
+            mainwin_quit_cb();
+            break;
+        case MAINWIN_GENERAL_SETAB:
+            if (aud_playlist_get_current_length(playlist) != -1) {
+                if (ab_position_a == -1) {
+                    ab_position_a = audacious_drct_get_time();
+                    ab_position_b = -1;
+                    mainwin_lock_info_text("'Loop-Point A Position' set.");
+                } else if (ab_position_b == -1) {
+                    int time = audacious_drct_get_time();
+                    if (time > ab_position_a)
+                        ab_position_b = time;
+                    mainwin_release_info_text();
+                } else {
+                    ab_position_a = audacious_drct_get_time();
+                    ab_position_b = -1;
+                    mainwin_lock_info_text("'Loop-Point A Position' reset.");
+                }
+            }
+            break;
+        case MAINWIN_GENERAL_CLEARAB:
+            if (aud_playlist_get_current_length(playlist) != -1) {
+                ab_position_a = ab_position_b = -1;
+                mainwin_release_info_text();
+            }
+            break;
+        case MAINWIN_GENERAL_NEW_PL:
+            {
+                Playlist *new_pl = aud_playlist_new();
+                aud_playlist_add_playlist(new_pl);
+                aud_playlist_select_playlist(new_pl);
+            }
+            break;
+        case MAINWIN_GENERAL_PREV_PL:
+            aud_playlist_select_prev();
+            break;
+        case MAINWIN_GENERAL_NEXT_PL:
+            aud_playlist_select_next();
+            break;
+    }
+}
+
+static void
+mainwin_mr_change(GtkWidget *widget, MenuRowItem i)
+{
+    switch (i) {
+        case MENUROW_OPTIONS:
+            mainwin_lock_info_text(_("Options Menu"));
+            break;
+        case MENUROW_ALWAYS:
+            if (UI_SKINNED_MENUROW(mainwin_menurow)->always_selected)
+                mainwin_lock_info_text(_("Disable 'Always On Top'"));
+            else
+                mainwin_lock_info_text(_("Enable 'Always On Top'"));
+            break;
+        case MENUROW_FILEINFOBOX:
+            mainwin_lock_info_text(_("File Info Box"));
+            break;
+        case MENUROW_SCALE:
+            if (UI_SKINNED_MENUROW(mainwin_menurow)->scale_selected)
+                mainwin_lock_info_text(_("Disable 'GUI Scaling'"));
+            else
+                mainwin_lock_info_text(_("Enable 'GUI Scaling'"));
+            break;
+        case MENUROW_VISUALIZATION:
+            mainwin_lock_info_text(_("Visualization Menu"));
+            break;
+        case MENUROW_NONE:
+            break;
+    }
+}
+
+static void
+mainwin_mr_release(GtkWidget *widget, MenuRowItem i, GdkEventButton *event)
+{
+    switch (i) {
+        case MENUROW_OPTIONS:
+            ui_manager_popup_menu_show(GTK_MENU(mainwin_view_menu),
+                                       event->x_root, event->y_root, 1,
+                                       event->time);
+            break;
+        case MENUROW_ALWAYS:
+            gtk_toggle_action_set_active(
+                                         GTK_TOGGLE_ACTION(gtk_action_group_get_action(
+                                                                                       toggleaction_group_others , "view always on top" )) ,
+                                         UI_SKINNED_MENUROW(mainwin_menurow)->always_selected );
+            break;
+        case MENUROW_FILEINFOBOX:
+#if 0
+            ui_fileinfo_show_current(aud_playlist_get_active());
+#endif
+            break;
+        case MENUROW_SCALE:
+            gtk_toggle_action_set_active(
+                                         GTK_TOGGLE_ACTION(gtk_action_group_get_action(
+                                                                                       toggleaction_group_others , "view scaled" )) ,
+                                         UI_SKINNED_MENUROW(mainwin_menurow)->scale_selected );
+            break;
+        case MENUROW_VISUALIZATION:
+            ui_manager_popup_menu_show(GTK_MENU(mainwin_visualization_menu),
+                                       event->x_root, event->y_root, 1,
+                                       event->time);
+            break;
+        case MENUROW_NONE:
+            break;
+    }
+
+    mainwin_release_info_text();
+}
+
+void
+run_no_output_device_dialog(gpointer hook_data, gpointer user_data)
+{
+    const gchar *markup =
+        N_("<b><big>Couldn't open audio.</big></b>\n\n"
+           "Please check that:\n"
+           "1. You have the correct output plugin selected.\n"
+           "2. No other programs is blocking the soundcard.\n"
+           "3. Your soundcard is configured properly.\n");
+
+    GDK_THREADS_ENTER();
+    GtkWidget *dialog =
+        gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin),
+                                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                                           GTK_MESSAGE_ERROR,
+                                           GTK_BUTTONS_OK,
+                                           _(markup));
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+    GDK_THREADS_LEAVE();
+}
+
+void
+ui_main_set_initial_volume(void)
+{
+    gint vl, vr, b, v;
+
+    aud_input_get_volume(&vl, &vr);
+
+    vl = CLAMP(vl, 0, 100);
+    vr = CLAMP(vr, 0, 100);
+    v = MAX(vl, vr);
+    if (vl > vr)
+        b = (gint) rint(((gdouble) vr / vl) * 100) - 100;
+    else if (vl < vr)
+        b = 100 - (gint) rint(((gdouble) vl / vr) * 100);
+    else
+        b = 0;
+
+    mainwin_set_volume_slider(v);
+    equalizerwin_set_volume_slider(v);
+    mainwin_set_balance_slider(b);
+    equalizerwin_set_balance_slider(b);
+}
+
+static void
+set_timer_mode(TimerMode mode)
+{
+    if (mode == TIMER_ELAPSED)
+        check_set(radioaction_group_viewtime, "view time elapsed", TRUE);
+    else
+        check_set(radioaction_group_viewtime, "view time remaining", TRUE);
+}
+
+static void
+set_timer_mode_menu_cb(TimerMode mode)
+{
+    config.timer_mode = mode;
+}
+
+gboolean
+change_timer_mode_cb(GtkWidget *widget, GdkEventButton *event)
+{
+    if (event->button == 1) {
+        change_timer_mode();
+    } else if (event->button == 3)
+        return FALSE;
+
+    return TRUE;
+}
+
+static void change_timer_mode(void) {
+    if (config.timer_mode == TIMER_ELAPSED)
+        set_timer_mode(TIMER_REMAINING);
+    else
+        set_timer_mode(TIMER_ELAPSED);
+    if (audacious_drct_get_playing())
+        mainwin_update_song_info();
+}
+
+static void
+mainwin_aud_playlist_prev(void)
+{
+    aud_playlist_prev(aud_playlist_get_active());
+}
+
+static void
+mainwin_aud_playlist_next(void)
+{
+    aud_playlist_next(aud_playlist_get_active());
+}
+
+void
+mainwin_setup_menus(void)
+{
+    set_timer_mode(config.timer_mode);
+
+    /* View menu */
+
+    check_set(toggleaction_group_others, "view always on top", config.always_on_top);
+    check_set(toggleaction_group_others, "view put on all workspaces", config.sticky);
+    check_set(toggleaction_group_others, "roll up player", config.player_shaded);
+    check_set(toggleaction_group_others, "roll up playlist editor", config.playlist_shaded);
+    check_set(toggleaction_group_others, "roll up equalizer", config.equalizer_shaded);
+    check_set(toggleaction_group_others, "view easy move", config.easy_move);
+    check_set(toggleaction_group_others, "view scaled", config.scaled);
+
+    /* Songname menu */
+
+    check_set(toggleaction_group_others, "autoscroll songname", config.autoscroll);
+    check_set(toggleaction_group_others, "stop after current song", aud_cfg->stopaftersong);
+
+    /* Playback menu */
+
+    check_set(toggleaction_group_others, "playback repeat", aud_cfg->repeat);
+    check_set(toggleaction_group_others, "playback shuffle", aud_cfg->shuffle);
+    check_set(toggleaction_group_others, "playback no playlist advance", aud_cfg->no_playlist_advance);
+
+    /* Visualization menu */
+
+    switch ( config.vis_type )
+    {
+        case VIS_ANALYZER:
+            check_set(radioaction_group_vismode, "vismode analyzer", TRUE);
+            break;
+        case VIS_SCOPE:
+            check_set(radioaction_group_vismode, "vismode scope", TRUE);
+            break;
+        case VIS_VOICEPRINT:
+            check_set(radioaction_group_vismode, "vismode voiceprint", TRUE);
+            break;
+        case VIS_OFF:
+        default:
+            check_set(radioaction_group_vismode, "vismode off", TRUE);
+            break;
+    }
+
+    switch ( config.analyzer_mode )
+    {
+        case ANALYZER_FIRE:
+            check_set(radioaction_group_anamode, "anamode fire", TRUE);
+            break;
+        case ANALYZER_VLINES:
+            check_set(radioaction_group_anamode, "anamode vertical lines", TRUE);
+            break;
+        case ANALYZER_NORMAL:
+        default:
+            check_set(radioaction_group_anamode, "anamode normal", TRUE);
+            break;
+    }
+
+    switch ( config.analyzer_type )
+    {
+        case ANALYZER_BARS:
+            check_set(radioaction_group_anatype, "anatype bars", TRUE);
+            break;
+        case ANALYZER_LINES:
+        default:
+            check_set(radioaction_group_anatype, "anatype lines", TRUE);
+            break;
+    }
+
+    check_set(toggleaction_group_others, "anamode peaks", config.analyzer_peaks );
+
+    switch ( config.scope_mode )
+    {
+        case SCOPE_LINE:
+            check_set(radioaction_group_scomode, "scomode line", TRUE);
+            break;
+        case SCOPE_SOLID:
+            check_set(radioaction_group_scomode, "scomode solid", TRUE);
+            break;
+        case SCOPE_DOT:
+        default:
+            check_set(radioaction_group_scomode, "scomode dot", TRUE);
+            break;
+    }
+
+    switch ( config.voiceprint_mode )
+    {
+        case VOICEPRINT_FIRE:
+            check_set(radioaction_group_vprmode, "vprmode fire", TRUE);
+            break;
+        case VOICEPRINT_ICE:
+            check_set(radioaction_group_vprmode, "vprmode ice", TRUE);
+            break;
+        case VOICEPRINT_NORMAL:
+        default:
+            check_set(radioaction_group_vprmode, "vprmode normal", TRUE);
+            break;
+    }
+
+    switch ( config.vu_mode )
+    {
+        case VU_SMOOTH:
+            check_set(radioaction_group_wshmode, "wshmode smooth", TRUE);
+            break;
+        case VU_NORMAL:
+        default:
+            check_set(radioaction_group_wshmode, "wshmode normal", TRUE);
+            break;
+    }
+
+    switch ( config.vis_refresh )
+    {
+        case REFRESH_HALF:
+            check_set(radioaction_group_refrate, "refrate half", TRUE);
+            break;
+        case REFRESH_QUARTER:
+            check_set(radioaction_group_refrate, "refrate quarter", TRUE);
+            break;
+        case REFRESH_EIGTH:
+            check_set(radioaction_group_refrate, "refrate eighth", TRUE);
+            break;
+        case REFRESH_FULL:
+        default:
+            check_set(radioaction_group_refrate, "refrate full", TRUE);
+            break;
+    }
+
+    switch ( config.analyzer_falloff )
+    {
+        case FALLOFF_SLOW:
+            check_set(radioaction_group_anafoff, "anafoff slow", TRUE);
+            break;
+        case FALLOFF_MEDIUM:
+            check_set(radioaction_group_anafoff, "anafoff medium", TRUE);
+            break;
+        case FALLOFF_FAST:
+            check_set(radioaction_group_anafoff, "anafoff fast", TRUE);
+            break;
+        case FALLOFF_FASTEST:
+            check_set(radioaction_group_anafoff, "anafoff fastest", TRUE);
+            break;
+        case FALLOFF_SLOWEST:
+        default:
+            check_set(radioaction_group_anafoff, "anafoff slowest", TRUE);
+            break;
+    }
+
+    switch ( config.peaks_falloff )
+    {
+        case FALLOFF_SLOW:
+            check_set(radioaction_group_peafoff, "peafoff slow", TRUE);
+            break;
+        case FALLOFF_MEDIUM:
+            check_set(radioaction_group_peafoff, "peafoff medium", TRUE);
+            break;
+        case FALLOFF_FAST:
+            check_set(radioaction_group_peafoff, "peafoff fast", TRUE);
+            break;
+        case FALLOFF_FASTEST:
+            check_set(radioaction_group_peafoff, "peafoff fastest", TRUE);
+            break;
+        case FALLOFF_SLOWEST:
+        default:
+            check_set(radioaction_group_peafoff, "peafoff slowest", TRUE);
+            break;
+    }
+}
+
+static void mainwin_info_double_clicked_cb(void) {
+#if 0
+    ui_fileinfo_show_current(aud_playlist_get_active());
+#endif
+}
+
+static void
+mainwin_info_right_clicked_cb(GtkWidget *widget, GdkEventButton *event)
+{
+    ui_manager_popup_menu_show(GTK_MENU(mainwin_songname_menu),
+                               event->x_root, event->y_root, 3, event->time);
+}
+
+static void
+mainwin_create_widgets(void)
+{
+    mainwin_menubtn = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_menubtn, SKINNED_WINDOW(mainwin)->fixed,
+                                 6, 3, 9, 9, 0, 0, 0, 9, SKIN_TITLEBAR);
+    g_signal_connect(mainwin_menubtn, "clicked", mainwin_menubtn_cb, NULL );
+
+    mainwin_minimize = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_minimize, SKINNED_WINDOW(mainwin)->fixed,
+                                 244, 3, 9, 9, 9, 0, 9, 9, SKIN_TITLEBAR);
+    g_signal_connect(mainwin_minimize, "clicked", mainwin_minimize_cb, NULL );
+
+    mainwin_shade = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_shade, SKINNED_WINDOW(mainwin)->fixed,
+                                 254, 3, 9, 9, 0,
+                                 config.player_shaded ? 27 : 18, 9, config.player_shaded ? 27 : 18, SKIN_TITLEBAR);
+    g_signal_connect(mainwin_shade, "clicked", mainwin_shade_toggle, NULL );
+
+    mainwin_close = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_close, SKINNED_WINDOW(mainwin)->fixed,
+                                 264, 3, 9, 9, 18, 0, 18, 9, SKIN_TITLEBAR);
+    g_signal_connect(mainwin_close, "clicked", mainwin_quit_cb, NULL );
+
+    mainwin_rew = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_rew, SKINNED_WINDOW(mainwin)->fixed,
+                                 16, 88, 23, 18, 0, 0, 0, 18, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_rew, "pressed", mainwin_rev_pushed, NULL);
+    g_signal_connect(mainwin_rew, "released", mainwin_rev_release, NULL);
+
+    mainwin_fwd = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_fwd, SKINNED_WINDOW(mainwin)->fixed,
+                                 108, 88, 22, 18, 92, 0, 92, 18, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_fwd, "pressed", mainwin_fwd_pushed, NULL);
+    g_signal_connect(mainwin_fwd, "released", mainwin_fwd_release, NULL);
+
+    mainwin_play = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_play, SKINNED_WINDOW(mainwin)->fixed,
+                                 39, 88, 23, 18, 23, 0, 23, 18, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_play, "clicked", mainwin_play_pushed, NULL );
+
+    mainwin_pause = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_pause, SKINNED_WINDOW(mainwin)->fixed,
+                                 62, 88, 23, 18, 46, 0, 46, 18, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_pause, "clicked", audacious_drct_pause, NULL );
+
+    mainwin_stop = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_stop, SKINNED_WINDOW(mainwin)->fixed,
+                                 85, 88, 23, 18, 69, 0, 69, 18, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_stop, "clicked", mainwin_stop_pushed, NULL );
+
+    mainwin_eject = ui_skinned_button_new();
+    ui_skinned_push_button_setup(mainwin_eject, SKINNED_WINDOW(mainwin)->fixed,
+                                 136, 89, 22, 16, 114, 0, 114, 16, SKIN_CBUTTONS);
+    g_signal_connect(mainwin_eject, "clicked", mainwin_eject_pushed, NULL);
+
+    mainwin_srew = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_srew, SKINNED_WINDOW(mainwin)->fixed, 169, 4, 8, 7);
+    g_signal_connect(mainwin_srew, "clicked", mainwin_aud_playlist_prev, NULL);
+
+    mainwin_splay = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_splay, SKINNED_WINDOW(mainwin)->fixed, 177, 4, 10, 7);
+    g_signal_connect(mainwin_splay, "clicked", mainwin_play_pushed, NULL);
+
+    mainwin_spause = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_spause, SKINNED_WINDOW(mainwin)->fixed, 187, 4, 10, 7);
+    g_signal_connect(mainwin_spause, "clicked", audacious_drct_pause, NULL);
+
+    mainwin_sstop = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_sstop, SKINNED_WINDOW(mainwin)->fixed, 197, 4, 9, 7);
+    g_signal_connect(mainwin_sstop, "clicked", mainwin_stop_pushed, NULL);
+
+    mainwin_sfwd = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_sfwd, SKINNED_WINDOW(mainwin)->fixed, 206, 4, 8, 7);
+    g_signal_connect(mainwin_sfwd, "clicked", mainwin_aud_playlist_next, NULL);
+
+    mainwin_seject = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_seject, SKINNED_WINDOW(mainwin)->fixed, 216, 4, 9, 7);
+    g_signal_connect(mainwin_seject, "clicked", mainwin_eject_pushed, NULL);
+
+    mainwin_shuffle = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(mainwin_shuffle, SKINNED_WINDOW(mainwin)->fixed,
+                                   164, 89, 46, 15, 28, 0, 28, 15, 28, 30, 28, 45, SKIN_SHUFREP);
+    g_signal_connect(mainwin_shuffle, "clicked", mainwin_shuffle_pushed_cb, NULL);
+
+    mainwin_repeat = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(mainwin_repeat, SKINNED_WINDOW(mainwin)->fixed,
+                                   210, 89, 28, 15, 0, 0, 0, 15, 0, 30, 0, 45, SKIN_SHUFREP);
+    g_signal_connect(mainwin_repeat, "clicked", mainwin_repeat_pushed_cb, NULL);
+
+    mainwin_eq = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(mainwin_eq, SKINNED_WINDOW(mainwin)->fixed,
+                                   219, 58, 23, 12, 0, 61, 46, 61, 0, 73, 46, 73, SKIN_SHUFREP);
+    g_signal_connect(mainwin_eq, "clicked", mainwin_equalizer_pushed_cb, NULL);
+    UI_SKINNED_BUTTON(mainwin_eq)->inside = config.equalizer_visible;
+
+    mainwin_pl = ui_skinned_button_new();
+    ui_skinned_toggle_button_setup(mainwin_pl, SKINNED_WINDOW(mainwin)->fixed,
+                                   242, 58, 23, 12, 23, 61, 69, 61, 23, 73, 69, 73, SKIN_SHUFREP);
+    g_signal_connect(mainwin_pl, "clicked", mainwin_playlist_pushed_cb, NULL);
+    UI_SKINNED_BUTTON(mainwin_pl)->inside = config.playlist_visible;
+
+    mainwin_info = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 112, 27, 153, 1, SKIN_TEXT);
+    ui_skinned_textbox_set_scroll(mainwin_info, config.autoscroll);
+    ui_skinned_textbox_set_xfont(mainwin_info, !config.mainwin_use_bitmapfont, aud_cfg->mainwin_font);
+    g_signal_connect(mainwin_info, "double-clicked", mainwin_info_double_clicked_cb, NULL);
+    g_signal_connect(mainwin_info, "right-clicked", G_CALLBACK(mainwin_info_right_clicked_cb), NULL);
+
+    mainwin_othertext = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 112, 43, 153, 1, SKIN_TEXT);
+
+    mainwin_rate_text = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 111, 43, 15, 0, SKIN_TEXT);
+
+    mainwin_freq_text = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 156, 43, 10, 0, SKIN_TEXT);
+
+    mainwin_menurow = ui_skinned_menurow_new(SKINNED_WINDOW(mainwin)->fixed, 10, 22, 304, 0, 304, 44,  SKIN_TITLEBAR);
+    g_signal_connect(mainwin_menurow, "change", G_CALLBACK(mainwin_mr_change), NULL);
+    g_signal_connect(mainwin_menurow, "release", G_CALLBACK(mainwin_mr_release), NULL);
+
+    mainwin_volume = ui_skinned_horizontal_slider_new(SKINNED_WINDOW(mainwin)->fixed, 107, 57, 68,
+                                                      13, 15, 422, 0, 422, 14, 11, 15, 0, 0, 51,
+                                                      mainwin_volume_frame_cb, SKIN_VOLUME);
+    g_signal_connect(mainwin_volume, "motion", G_CALLBACK(mainwin_volume_motion_cb), NULL);
+    g_signal_connect(mainwin_volume, "release", G_CALLBACK(mainwin_volume_release_cb), NULL);
+
+    mainwin_balance = ui_skinned_horizontal_slider_new(SKINNED_WINDOW(mainwin)->fixed, 177, 57, 38,
+                                                       13, 15, 422, 0, 422, 14, 11, 15, 9, 0, 24,
+                                                       mainwin_balance_frame_cb, SKIN_BALANCE);
+    g_signal_connect(mainwin_balance, "motion", G_CALLBACK(mainwin_balance_motion_cb), NULL);
+    g_signal_connect(mainwin_balance, "release", G_CALLBACK(mainwin_balance_release_cb), NULL);
+
+    mainwin_monostereo = ui_skinned_monostereo_new(SKINNED_WINDOW(mainwin)->fixed, 212, 41, SKIN_MONOSTEREO);
+
+    mainwin_playstatus = ui_skinned_playstatus_new(SKINNED_WINDOW(mainwin)->fixed, 24, 28);
+
+    mainwin_minus_num = ui_skinned_number_new(SKINNED_WINDOW(mainwin)->fixed, 36, 26, SKIN_NUMBERS);
+    g_signal_connect(mainwin_minus_num, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_10min_num = ui_skinned_number_new(SKINNED_WINDOW(mainwin)->fixed, 48, 26, SKIN_NUMBERS);
+    g_signal_connect(mainwin_10min_num, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_min_num = ui_skinned_number_new(SKINNED_WINDOW(mainwin)->fixed, 60, 26, SKIN_NUMBERS);
+    g_signal_connect(mainwin_min_num, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_10sec_num = ui_skinned_number_new(SKINNED_WINDOW(mainwin)->fixed, 78, 26, SKIN_NUMBERS);
+    g_signal_connect(mainwin_10sec_num, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_sec_num = ui_skinned_number_new(SKINNED_WINDOW(mainwin)->fixed, 90, 26, SKIN_NUMBERS);
+    g_signal_connect(mainwin_sec_num, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_about = ui_skinned_button_new();
+    ui_skinned_small_button_setup(mainwin_about, SKINNED_WINDOW(mainwin)->fixed, 247, 83, 20, 25);
+#if 0
+    g_signal_connect(mainwin_about, "clicked", show_about_window, NULL);
+#endif
+    mainwin_vis = ui_vis_new(SKINNED_WINDOW(mainwin)->fixed, 24, 43, 76);
+    g_signal_connect(mainwin_vis, "button-press-event", G_CALLBACK(mainwin_vis_cb), NULL);
+    mainwin_svis = ui_svis_new(SKINNED_WINDOW(mainwin)->fixed, 79, 5);
+    g_signal_connect(mainwin_svis, "button-press-event", G_CALLBACK(mainwin_vis_cb), NULL);
+
+    mainwin_position = ui_skinned_horizontal_slider_new(SKINNED_WINDOW(mainwin)->fixed, 16, 72, 248,
+                                                        10, 248, 0, 278, 0, 29, 10, 10, 0, 0, 219,
+                                                        NULL, SKIN_POSBAR);
+    g_signal_connect(mainwin_position, "motion", G_CALLBACK(mainwin_position_motion_cb), NULL);
+    g_signal_connect(mainwin_position, "release", G_CALLBACK(mainwin_position_release_cb), NULL);
+
+    mainwin_sposition = ui_skinned_horizontal_slider_new(SKINNED_WINDOW(mainwin)->fixed, 226, 4, 17,
+                                                         7, 17, 36, 17, 36, 3, 7, 36, 0, 1, 13,
+                                                         mainwin_spos_frame_cb, SKIN_TITLEBAR);
+    g_signal_connect(mainwin_sposition, "motion", G_CALLBACK(mainwin_spos_motion_cb), NULL);
+    g_signal_connect(mainwin_sposition, "release", G_CALLBACK(mainwin_spos_release_cb), NULL);
+
+    mainwin_stime_min = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 130, 4, 15, FALSE, SKIN_TEXT);
+    g_signal_connect(mainwin_stime_min, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    mainwin_stime_sec = ui_skinned_textbox_new(SKINNED_WINDOW(mainwin)->fixed, 147, 4, 10, FALSE, SKIN_TEXT);
+    g_signal_connect(mainwin_stime_sec, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+
+    aud_hook_associate("playback audio error", (void *) mainwin_stop_pushed, NULL);
+    aud_hook_associate("playback audio error", (void *) run_no_output_device_dialog, NULL);
+
+    aud_hook_associate("playback seek", (HookFunction) mainwin_update_song_info, NULL);
+}
+
+static void
+mainwin_create_window(void)
+{
+    gint width, height;
+
+    mainwin = ui_skinned_window_new("player");
+    gtk_window_set_title(GTK_WINDOW(mainwin), _("Audacious"));
+    gtk_window_set_role(GTK_WINDOW(mainwin), "player");
+    gtk_window_set_resizable(GTK_WINDOW(mainwin), FALSE);
+
+    width = config.player_shaded ? MAINWIN_SHADED_WIDTH : aud_active_skin->properties.mainwin_width;
+    height = config.player_shaded ? MAINWIN_SHADED_HEIGHT : aud_active_skin->properties.mainwin_height;
+
+    if (config.scaled) {
+        width *= config.scale_factor;
+        height *= config.scale_factor;
+    }
+
+    gtk_widget_set_size_request(mainwin, width, height);
+
+    if (config.player_x != -1 && config.save_window_position)
+        gtk_window_move(GTK_WINDOW(mainwin), config.player_x, config.player_y);
+
+    g_signal_connect(mainwin, "destroy", G_CALLBACK(mainwin_destroy), NULL);
+    g_signal_connect(mainwin, "button_press_event",
+                     G_CALLBACK(mainwin_mouse_button_press), NULL);
+    g_signal_connect(mainwin, "scroll_event",
+                     G_CALLBACK(mainwin_scrolled), NULL);
+    g_signal_connect(mainwin, "button_release_event",
+                     G_CALLBACK(mainwin_mouse_button_release), NULL);
+
+    aud_drag_dest_set(mainwin);
+
+    g_signal_connect(mainwin, "key_press_event",
+                     G_CALLBACK(mainwin_keypress), NULL);
+
+    ui_main_evlistener_init();
+}
+
+void
+mainwin_create(void)
+{
+    mainwin_create_window();
+
+    gtk_window_add_accel_group( GTK_WINDOW(mainwin) , ui_manager_get_accel_group() );
+
+    mainwin_create_widgets();
+}
+
+gboolean
+mainwin_update_song_info(void)
+{
+    if (!audacious_drct_get_playing())
+        return FALSE;
+
+    gint time = audacious_drct_get_time();
+    gint length = audacious_drct_get_length();
+    gint t;
+    gchar stime_prefix;
+
+    if (ab_position_a != -1 && ab_position_b != -1 && time > ab_position_b)
+        audacious_drct_seek(ab_position_a/1000);
+
+    if (length == -1 && config.timer_mode == TIMER_REMAINING)
+        config.timer_mode = TIMER_ELAPSED;
+#if 0
+    playlistwin_set_time(time, length, config.timer_mode);
+#endif
+    if (config.timer_mode == TIMER_REMAINING) {
+        if (length != -1) {
+            ui_skinned_number_set_number(mainwin_minus_num, 11);
+            t = length - time;
+            stime_prefix = '-';
+        }
+        else {
+            ui_skinned_number_set_number(mainwin_minus_num, 10);
+            t = time;
+            stime_prefix = ' ';
+        }
+    }
+    else {
+        ui_skinned_number_set_number(mainwin_minus_num, 10);
+        t = time;
+        stime_prefix = ' ';
+    }
+    t /= 1000;
+
+    /* Show the time in the format HH:MM when we have more than 100
+     * minutes. */
+    if (t >= 100 * 60)
+        t /= 60;
+    ui_skinned_number_set_number(mainwin_10min_num, t / 600);
+    ui_skinned_number_set_number(mainwin_min_num, (t / 60) % 10);
+    ui_skinned_number_set_number(mainwin_10sec_num, (t / 10) % 6);
+    ui_skinned_number_set_number(mainwin_sec_num, t % 10);
+
+    if (!UI_SKINNED_HORIZONTAL_SLIDER(mainwin_sposition)->pressed) {
+        gchar *time_str;
+
+        time_str = g_strdup_printf("%c%2.2d", stime_prefix, t / 60);
+        ui_skinned_textbox_set_text(mainwin_stime_min, time_str);
+        g_free(time_str);
+
+        time_str = g_strdup_printf("%2.2d", t % 60);
+        ui_skinned_textbox_set_text(mainwin_stime_sec, time_str);
+        g_free(time_str);
+    }
+
+    time /= 1000;
+    length /= 1000;
+    if (length > 0) {
+        if (time > length) {
+            ui_skinned_horizontal_slider_set_position(mainwin_position, 219);
+            ui_skinned_horizontal_slider_set_position(mainwin_sposition, 13);
+        }
+        /* update the slider position ONLY if there is not a seek in progress */
+        else if (seek_state == MAINWIN_SEEK_NIL)  {
+            ui_skinned_horizontal_slider_set_position(mainwin_position, (time * 219) / length);
+            ui_skinned_horizontal_slider_set_position(mainwin_sposition,
+                                                      ((time * 12) / length) + 1);
+        }
+    }
+    else {
+        ui_skinned_horizontal_slider_set_position(mainwin_position, 0);
+        ui_skinned_horizontal_slider_set_position(mainwin_sposition, 1);
+    }
+
+    return TRUE;
+}
+
+static gboolean
+mainwin_idle_func(gpointer data)
+{
+    GDK_THREADS_ENTER();
+
+    /* tristate buttons seek */
+    if ( seek_state != MAINWIN_SEEK_NIL )
+    {
+        GTimeVal now_time;
+        GTimeVal delta_time;
+        gulong now_dur;
+        g_get_current_time(&now_time);
+
+        delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+        delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+        now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+        if ( now_dur > TRISTATE_THRESHOLD )
+        {
+            gint np;
+            if (seek_state == MAINWIN_SEEK_REV)
+                np = seek_initial_pos - labs((gulong)(now_dur/100)); /* seek back */
+            else
+                np = seek_initial_pos + labs((gulong)(now_dur/100)); /* seek forward */
+
+            /* boundaries check */
+            if (np < 0 )
+                np = 0;
+            else if ( np > 219 )
+                np = 219;
+
+            ui_skinned_horizontal_slider_set_position( mainwin_position , np );
+            mainwin_position_motion_cb( mainwin_position, np );
+        }
+    }
+
+    GDK_THREADS_LEAVE();
+    return TRUE;
+}
+
+
+/* toggleactionentries actions */
+
+void
+action_anamode_peaks( GtkToggleAction * action )
+{
+    config.analyzer_peaks = gtk_toggle_action_get_active( action );
+}
+
+void
+action_autoscroll_songname( GtkToggleAction * action )
+{
+    mainwin_set_title_scroll(gtk_toggle_action_get_active(action));
+    playlistwin_set_sinfo_scroll(config.autoscroll); /* propagate scroll setting to playlistwin_sinfo */
+}
+
+void
+action_playback_noplaylistadvance( GtkToggleAction * action )
+{
+    aud_cfg->no_playlist_advance = gtk_toggle_action_get_active( action );
+}
+
+void
+action_playback_repeat( GtkToggleAction * action )
+{
+    aud_cfg->repeat = gtk_toggle_action_get_active( action );
+    UI_SKINNED_BUTTON(mainwin_repeat)->inside = aud_cfg->repeat;
+    gtk_widget_queue_draw(mainwin_repeat);
+}
+
+void
+action_playback_shuffle( GtkToggleAction * action )
+{
+    aud_cfg->shuffle = gtk_toggle_action_get_active( action );
+    aud_playlist_set_shuffle(aud_cfg->shuffle);
+    UI_SKINNED_BUTTON(mainwin_shuffle)->inside = aud_cfg->shuffle;
+    gtk_widget_queue_draw(mainwin_shuffle);
+}
+
+void
+action_stop_after_current_song( GtkToggleAction * action )
+{
+    aud_cfg->stopaftersong = gtk_toggle_action_get_active( action );
+}
+
+void
+action_view_always_on_top( GtkToggleAction * action )
+{
+    UI_SKINNED_MENUROW(mainwin_menurow)->always_selected = gtk_toggle_action_get_active( action );
+    config.always_on_top = UI_SKINNED_MENUROW(mainwin_menurow)->always_selected;
+    gtk_widget_queue_draw(mainwin_menurow);
+    hint_set_always(config.always_on_top);
+}
+
+void
+action_view_scaled( GtkToggleAction * action )
+{
+    UI_SKINNED_MENUROW(mainwin_menurow)->scale_selected = gtk_toggle_action_get_active( action );
+    gtk_widget_queue_draw(mainwin_menurow);
+    set_scaled(UI_SKINNED_MENUROW(mainwin_menurow)->scale_selected);
+    gdk_flush();
+}
+
+void
+action_view_easymove( GtkToggleAction * action )
+{
+    config.easy_move = gtk_toggle_action_get_active( action );
+}
+
+void
+action_view_on_all_workspaces( GtkToggleAction * action )
+{
+    config.sticky = gtk_toggle_action_get_active( action );
+    hint_set_sticky(config.sticky);
+}
+
+void
+action_roll_up_equalizer( GtkToggleAction * action )
+{
+    equalizerwin_set_shade_menu_cb(gtk_toggle_action_get_active(action));
+}
+
+void
+action_roll_up_player( GtkToggleAction * action )
+{
+    mainwin_set_shade_menu_cb(gtk_toggle_action_get_active(action));
+}
+
+void
+action_roll_up_playlist_editor( GtkToggleAction * action )
+{
+    playlistwin_set_shade(gtk_toggle_action_get_active(action));
+}
+
+void
+action_show_equalizer( GtkToggleAction * action )
+{
+    if (gtk_toggle_action_get_active(action))
+        equalizerwin_real_show();
+    else
+        equalizerwin_real_hide();
+}
+
+void
+action_show_playlist_editor( GtkToggleAction * action )
+{
+    if (gtk_toggle_action_get_active(action))
+        playlistwin_show();
+    else
+        playlistwin_hide();
+}
+
+void
+action_show_player( GtkToggleAction * action )
+{
+    mainwin_show(gtk_toggle_action_get_active(action));
+}
+
+
+/* radioactionentries actions (one callback for each radio group) */
+
+void
+action_anafoff( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_afalloff(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_anamode( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_analyzer_mode(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_anatype( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_analyzer_type(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_peafoff( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_pfalloff(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_refrate( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_refresh(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_scomode( GtkAction *action, GtkRadioAction *current )
+{
+    config.scope_mode = gtk_radio_action_get_current_value(current);
+}
+
+void
+action_vismode( GtkAction *action, GtkRadioAction *current )
+{
+    mainwin_vis_set_type_menu_cb(gtk_radio_action_get_current_value(current));
+}
+
+void
+action_vprmode( GtkAction *action, GtkRadioAction *current )
+{
+    config.voiceprint_mode = gtk_radio_action_get_current_value(current);
+}
+
+void
+action_wshmode( GtkAction *action, GtkRadioAction *current )
+{
+    config.vu_mode = gtk_radio_action_get_current_value(current);
+}
+
+void
+action_viewtime( GtkAction *action, GtkRadioAction *current )
+{
+    set_timer_mode_menu_cb(gtk_radio_action_get_current_value(current));
+}
+
+
+/* actionentries actions */
+
+void
+action_about_audacious( void )
+{
+#if 0
+    show_about_window();
+#endif
+}
+
+void
+action_play_file( void )
+{
+    run_filebrowser(PLAY_BUTTON);
+}
+
+void
+action_play_location( void )
+{
+    mainwin_show_add_url_window();
+}
+
+void
+action_ab_set( void )
+{
+    Playlist *playlist = aud_playlist_get_active();
+    if (aud_playlist_get_current_length(playlist) != -1)
+    {
+        if (ab_position_a == -1)
+        {
+            ab_position_a = audacious_drct_get_time();
+            ab_position_b = -1;
+            mainwin_lock_info_text("LOOP-POINT A POSITION SET.");
+        }
+        else if (ab_position_b == -1)
+        {
+            int time = audacious_drct_get_time();
+            if (time > ab_position_a)
+                ab_position_b = time;
+            mainwin_release_info_text();
+        }
+        else
+        {
+            ab_position_a = audacious_drct_get_time();
+            ab_position_b = -1;
+            mainwin_lock_info_text("LOOP-POINT A POSITION RESET.");
+        }
+    }
+}
+
+void
+action_ab_clear( void )
+{
+    Playlist *playlist = aud_playlist_get_active();
+    if (aud_playlist_get_current_length(playlist) != -1)
+    {
+        ab_position_a = ab_position_b = -1;
+        mainwin_release_info_text();
+    }
+}
+
+void
+action_current_track_info( void )
+{
+#if 0
+    ui_fileinfo_show_current(aud_playlist_get_active());
+#endif
+}
+
+void
+action_jump_to_file( void )
+{
+#if 0
+    ui_jump_to_track();
+#endif
+}
+
+void
+action_jump_to_playlist_start( void )
+{
+    Playlist *playlist = aud_playlist_get_active();
+    aud_playlist_set_position(playlist, 0);
+}
+
+void
+action_jump_to_time( void )
+{
+    mainwin_jump_to_time();
+}
+
+void
+action_playback_next( void )
+{
+    Playlist *playlist = aud_playlist_get_active();
+    aud_playlist_next(playlist);
+}
+
+void
+action_playback_previous( void )
+{
+    Playlist *playlist = aud_playlist_get_active();
+    aud_playlist_prev(playlist);
+}
+
+void
+action_playback_play( void )
+{
+    mainwin_play_pushed();
+}
+
+void
+action_playback_pause( void )
+{
+    audacious_drct_pause();
+}
+
+void
+action_playback_stop( void )
+{
+    mainwin_stop_pushed();
+}
+
+void
+action_preferences( void )
+{
+#if 0
+    show_prefs_window();
+#endif
+}
+
+void
+action_quit( void )
+{
+    mainwin_quit_cb();
+}
+
+void
+util_menu_main_show( gint x , gint y , guint button , guint time )
+{
+    /* convenience function that shows the main popup menu wherever requested */
+    ui_manager_popup_menu_show( GTK_MENU(mainwin_general_menu),
+                                x , y , button , time );
+    return;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_main.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,194 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_MAIN_H
+#define AUDACIOUS_UI_MAIN_H
+
+#include <gtk/gtk.h>
+
+#include "ui_vis.h"
+#include "ui_svis.h"
+
+/* yes, main window size is fixed */
+#define MAINWIN_WIDTH            (gint)275
+#define MAINWIN_HEIGHT           (gint)116
+#define MAINWIN_TITLEBAR_HEIGHT  (gint)14
+#define MAINWIN_SHADED_WIDTH     MAINWIN_WIDTH
+#define MAINWIN_SHADED_HEIGHT    MAINWIN_TITLEBAR_HEIGHT
+#define MAINWIN_SCALE_FACTOR     (config.scaled ? config.scale_factor : 1)
+
+#define MAINWIN_UPDATE_INTERVAL  100
+
+#define MAINWIN_DEFAULT_POS_X    20
+#define MAINWIN_DEFAULT_POS_Y    20
+
+#define MAINWIN_DEFAULT_FONT     "Sans Bold 9"
+
+
+typedef enum {
+    TIMER_ELAPSED,
+    TIMER_REMAINING
+} TimerMode;
+
+enum {
+    MAINWIN_GENERAL_ABOUT,
+    
+    MAINWIN_GENERAL_PLAYFILE,
+    MAINWIN_GENERAL_PLAYLOCATION,
+
+    MAINWIN_GENERAL_FILEINFO,
+    MAINWIN_GENERAL_PREFS,
+
+    MAINWIN_GENERAL_SHOWMWIN,
+    MAINWIN_GENERAL_SHOWPLWIN,
+
+    MAINWIN_GENERAL_FOCUSMWIN,
+    MAINWIN_GENERAL_FOCUSPLWIN,
+
+    MAINWIN_GENERAL_SHOWEQWIN,
+    MAINWIN_GENERAL_EXIT,
+
+    MAINWIN_GENERAL_PREV,
+    MAINWIN_GENERAL_PLAY,
+    MAINWIN_GENERAL_PAUSE,
+    MAINWIN_GENERAL_STOP,
+    MAINWIN_GENERAL_NEXT,
+    MAINWIN_GENERAL_STOPFADE,
+    MAINWIN_GENERAL_BACK5SEC,
+    MAINWIN_GENERAL_FWD5SEC,
+    MAINWIN_GENERAL_START,
+    MAINWIN_GENERAL_BACK10,
+    MAINWIN_GENERAL_FWD10,
+    MAINWIN_GENERAL_JTT,
+    MAINWIN_GENERAL_JTF,
+    MAINWIN_GENERAL_QUEUE,
+    MAINWIN_GENERAL_CQUEUE,
+    MAINWIN_GENERAL_VOLUP,
+    MAINWIN_GENERAL_VOLDOWN,
+    MAINWIN_GENERAL_SETAB,
+    MAINWIN_GENERAL_CLEARAB,
+
+    MAINWIN_GENERAL_NEXT_PL,
+    MAINWIN_GENERAL_PREV_PL,
+    MAINWIN_GENERAL_NEW_PL
+};
+
+extern GtkWidget *mainwin;
+extern GtkWidget *err;
+
+extern gboolean mainwin_moving;
+extern gboolean mainwin_focus;
+
+extern GtkWidget *mainwin_jtf;
+extern GtkWidget *mainwin_eq, *mainwin_pl;
+extern GtkWidget *mainwin_info;
+
+extern GtkWidget *mainwin_stime_min, *mainwin_stime_sec;
+
+extern GtkWidget *mainwin_vis;
+extern GtkWidget *mainwin_svis;
+
+extern GtkWidget *mainwin_playstatus;
+
+extern GtkWidget *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num;
+extern GtkWidget *mainwin_10sec_num, *mainwin_sec_num;
+
+extern GtkWidget *mainwin_position, *mainwin_sposition;
+
+void mainwin_create(void);
+void ui_main_set_initial_volume(void);
+
+void mainwin_lock_info_text(const gchar * text);
+void mainwin_release_info_text(void);
+void mainwin_play_pushed(void);
+void mainwin_stop_pushed(void);
+void mainwin_eject_pushed(void);
+
+void mainwin_rev_pushed(void);
+void mainwin_rev_release(void);
+void mainwin_fwd_pushed(void);
+void mainwin_fwd_release(void);
+
+void mainwin_adjust_volume_motion(gint v);
+void mainwin_adjust_volume_release(void);
+void mainwin_adjust_balance_motion(gint b);
+void mainwin_adjust_balance_release(void);
+void mainwin_set_volume_slider(gint percent);
+void mainwin_set_balance_slider(gint percent);
+
+void mainwin_vis_set_type(VisType mode);
+
+void mainwin_refresh_hints(void);
+void mainwin_set_info_text(void);
+void mainwin_set_song_info(gint rate, gint freq, gint nch);
+void mainwin_clear_song_info(void);
+void mainwin_set_stopaftersong(gboolean stop);
+void mainwin_set_noplaylistadvance(gboolean no_advance);
+
+void mainwin_set_always_on_top(gboolean always);
+void mainwin_set_volume_diff(gint diff);
+void mainwin_set_balance_diff(gint diff);
+
+void mainwin_show(gboolean);
+void mainwin_real_show(void);
+void mainwin_real_hide(void);
+void mainwin_move(gint x, gint y);
+void mainwin_shuffle_pushed(gboolean toggled);
+void mainwin_repeat_pushed(gboolean toggled);
+void mainwin_disable_seekbar(void);
+void mainwin_set_title(const gchar * text);
+void mainwin_show_add_url_window(void);
+void mainwin_minimize_cb(void);
+void mainwin_general_menu_callback(gpointer cb_data,
+                                   guint action,
+                                   GtkWidget * widget);
+
+gboolean mainwin_update_song_info(void);
+void mainwin_drag_data_received(GtkWidget * widget,
+                                GdkDragContext * context,
+                                gint x,
+                                gint y,
+                                GtkSelectionData * selection_data,
+                                guint info,
+                                guint time,
+                                gpointer user_data);
+
+void mainwin_setup_menus(void);
+gboolean change_timer_mode_cb(GtkWidget *widget, GdkEventButton *event);
+
+void mainwin_jump_to_file(void);
+void mainwin_jump_to_time(void);
+
+void mainwin_ewmh_activate(void);
+
+void mainwin_show_visibility_warning(void);
+
+/* FIXME: placed here for now */
+void playback_get_sample_params(gint * bitrate,
+                                gint * frequency,
+                                gint * numchannels);
+
+void ui_main_check_theme_engine(void);
+
+void util_menu_main_show( gint x , gint y , guint button , guint time );
+
+#endif /* AUDACIOUS_UI_MAIN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_main_evlisteners.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,336 @@
+/*
+ * Audacious
+ * Copyright (c) 2006-2007 Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+#if 0
+#include "ui_playlist_evlisteners.h"
+#endif
+#include <glib.h>
+#include <math.h>
+#if 0
+#include "hook.h"
+#include "playback.h"
+#include "playlist.h"
+#include "playlist_evmessages.h"
+#include "visualization.h"
+#endif
+#include <audacious/plugin.h>
+#if 0
+#include "ui_credits.h"
+#include "ui_equalizer.h"
+#include "ui_jumptotrack.h"
+#endif
+#include "ui_fileopener.h"
+#include "ui_main.h"
+#if 0
+#include "ui_playlist.h"
+#include "ui_preferences.h"
+#endif
+#include "ui_skinned_playstatus.h"
+#include "ui_skinned_textbox.h"
+#include "ui_skinned_window.h"
+#include "skins_cfg.h"
+
+static gint song_info_timeout_source = 0;
+static gint update_vis_timeout_source = 0;
+
+typedef struct {
+    gint bitrate;
+    gint samplerate;
+    gint channels;
+} PlaylistEventInfoChange;
+
+/* XXX: there has to be a better way than polling here! */
+/* also: where should this function go? should it stay here? --mf0102 */
+static gboolean
+update_vis_func(gpointer unused)
+{
+    if (!audacious_drct_get_playing())
+        return FALSE;
+#if 0
+    input_update_vis(audacious_drct_get_time());
+#endif
+    return TRUE;
+}
+
+static void
+ui_main_evlistener_title_change(gpointer hook_data, gpointer user_data)
+{
+    gchar *text = (gchar *) hook_data;
+
+    ui_skinned_textbox_set_text(mainwin_info, text);
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+static void
+ui_main_evlistener_hide_seekbar(gpointer hook_data, gpointer user_data)
+{
+    mainwin_disable_seekbar();
+}
+
+static void
+ui_main_evlistener_volume_change(gpointer hook_data, gpointer user_data)
+{
+    gint *h_vol = (gint *) hook_data;
+    gint vl, vr, b, v;
+
+    vl = CLAMP(h_vol[0], 0, 100);
+    vr = CLAMP(h_vol[1], 0, 100);
+    v = MAX(vl, vr);
+    if (vl > vr)
+        b = (gint) rint(((gdouble) vr / vl) * 100) - 100;
+    else if (vl < vr)
+        b = 100 - (gint) rint(((gdouble) vl / vr) * 100);
+    else
+        b = 0;
+
+    mainwin_set_volume_slider(v);
+    equalizerwin_set_volume_slider(v);
+    mainwin_set_balance_slider(b);
+    equalizerwin_set_balance_slider(b);
+}
+
+static void
+ui_main_evlistener_playback_begin(gpointer hook_data, gpointer user_data)
+{
+
+    PlaylistEntry *entry = (PlaylistEntry*)hook_data;
+    g_return_if_fail(entry != NULL);
+#if 0
+    equalizerwin_load_auto_preset(entry->filename);
+    input_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
+                 cfg.equalizer_bands);
+    output_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
+                  cfg.equalizer_bands);
+#endif
+    ui_vis_clear_data(mainwin_vis);
+    ui_svis_clear_data(mainwin_svis);
+    mainwin_disable_seekbar();
+    mainwin_update_song_info();
+
+    if (config.player_shaded) {
+        gtk_widget_show(mainwin_stime_min);
+        gtk_widget_show(mainwin_stime_sec);
+        gtk_widget_show(mainwin_sposition);
+    } else {
+        gtk_widget_show(mainwin_minus_num);
+        gtk_widget_show(mainwin_10min_num);
+        gtk_widget_show(mainwin_min_num);
+        gtk_widget_show(mainwin_10sec_num);
+        gtk_widget_show(mainwin_sec_num);
+        gtk_widget_show(mainwin_position);
+    }
+
+    song_info_timeout_source = 
+        g_timeout_add_seconds(1, (GSourceFunc) mainwin_update_song_info, NULL);
+
+    update_vis_timeout_source =
+        g_timeout_add(10, (GSourceFunc) update_vis_func, NULL);
+#if 0
+    vis_playback_start();
+#endif
+    ui_skinned_playstatus_set_status(mainwin_playstatus, STATUS_PLAY);
+}
+
+static void
+ui_main_evlistener_playback_stop(gpointer hook_data, gpointer user_data)
+{
+    if (song_info_timeout_source)
+        g_source_remove(song_info_timeout_source);
+#if 0
+    vis_playback_stop();
+    free_vis_data();
+#endif
+    ui_skinned_playstatus_set_buffering(mainwin_playstatus, FALSE);
+}
+
+static void
+ui_main_evlistener_playback_pause(gpointer hook_data, gpointer user_data)
+{
+    ui_skinned_playstatus_set_status(mainwin_playstatus, STATUS_PAUSE);
+}
+
+static void
+ui_main_evlistener_playback_unpause(gpointer hook_data, gpointer user_data)
+{
+    ui_skinned_playstatus_set_status(mainwin_playstatus, STATUS_PLAY);
+}
+
+static void
+ui_main_evlistener_playback_seek(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    free_vis_data();
+#endif
+}
+
+static void
+ui_main_evlistener_playback_play_file(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    if (cfg.random_skin_on_play)
+        skin_set_random_skin();
+#endif
+}
+
+static void
+ui_main_evlistener_playlist_end_reached(gpointer hook_data, gpointer user_data)
+{
+    mainwin_clear_song_info();
+#if 0
+    if (cfg.stopaftersong)
+        mainwin_set_stopaftersong(FALSE);
+#endif
+}
+
+static void
+ui_main_evlistener_playlist_info_change(gpointer hook_data, gpointer user_data)
+{
+    PlaylistEventInfoChange *msg = (PlaylistEventInfoChange *) hook_data;
+
+    mainwin_set_song_info(msg->bitrate, msg->samplerate, msg->channels);
+}
+
+static void
+ui_main_evlistener_mainwin_set_always_on_top(gpointer hook_data, gpointer user_data)
+{
+    gboolean *ontop = (gboolean*)hook_data;
+    mainwin_set_always_on_top(*ontop);
+}
+
+static void
+ui_main_evlistener_mainwin_show(gpointer hook_data, gpointer user_data)
+{
+    gboolean *show = (gboolean*)hook_data;
+    mainwin_show(*show);
+}
+
+static void
+ui_main_evlistener_equalizerwin_show(gpointer hook_data, gpointer user_data)
+{
+    gboolean *show = (gboolean*)hook_data;
+    equalizerwin_show(*show);
+}
+
+static void
+ui_main_evlistener_prefswin_show(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    gboolean *show = (gboolean*)hook_data;
+    if (*show == TRUE)
+        show_prefs_window();
+    else
+        hide_prefs_window();
+#endif
+}
+
+static void
+ui_main_evlistener_aboutwin_show(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    gboolean *show = (gboolean*)hook_data;
+    if (*show == TRUE)
+        show_about_window();
+    else
+        hide_about_window();
+#endif
+}
+
+
+static void
+ui_main_evlistener_ui_jump_to_track_show(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    gboolean *show = (gboolean*)hook_data;
+    if (*show == TRUE)
+        ui_jump_to_track();
+    else
+        ui_jump_to_track_hide();
+#endif
+}
+
+static void
+ui_main_evlistener_filebrowser_show(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    gboolean *play_button = (gboolean*)hook_data;
+    run_filebrowser(*play_button);
+#endif
+}
+
+static void
+ui_main_evlistener_filebrowser_hide(gpointer hook_data, gpointer user_data)
+{
+#if 0
+    hide_filebrowser();
+#endif
+}
+
+static void
+ui_main_evlistener_visualization_timeout(gpointer hook_data, gpointer user_data)
+{
+    if (config.player_shaded && config.player_visible)
+        ui_svis_timeout_func(mainwin_svis, hook_data);
+    else
+        ui_vis_timeout_func(mainwin_vis, hook_data);
+}
+
+static void
+ui_main_evlistener_config_save(gpointer hook_data, gpointer user_data)
+{
+    ConfigDb *db = (ConfigDb *) hook_data;
+
+    if (SKINNED_WINDOW(mainwin)->x != -1 &&
+        SKINNED_WINDOW(mainwin)->y != -1 )
+    {
+        aud_cfg_db_set_int(db, "skins", "player_x", SKINNED_WINDOW(mainwin)->x);
+        aud_cfg_db_set_int(db, "skins", "player_y", SKINNED_WINDOW(mainwin)->y);
+    }
+
+    aud_cfg_db_set_bool(db, "skins", "mainwin_use_bitmapfont",
+                    config.mainwin_use_bitmapfont);
+}
+
+void
+ui_main_evlistener_init(void)
+{
+    aud_hook_associate("title change", ui_main_evlistener_title_change, NULL);
+    aud_hook_associate("hide seekbar", ui_main_evlistener_hide_seekbar, NULL);
+    aud_hook_associate("volume set", ui_main_evlistener_volume_change, NULL);
+    aud_hook_associate("playback begin", ui_main_evlistener_playback_begin, NULL);
+    aud_hook_associate("playback stop", ui_main_evlistener_playback_stop, NULL);
+    aud_hook_associate("playback pause", ui_main_evlistener_playback_pause, NULL);
+    aud_hook_associate("playback unpause", ui_main_evlistener_playback_unpause, NULL);
+    aud_hook_associate("playback seek", ui_main_evlistener_playback_seek, NULL);
+    aud_hook_associate("playback play file", ui_main_evlistener_playback_play_file, NULL);
+    aud_hook_associate("playlist end reached", ui_main_evlistener_playlist_end_reached, NULL);
+    aud_hook_associate("playlist info change", ui_main_evlistener_playlist_info_change, NULL);
+    aud_hook_associate("mainwin set always on top", ui_main_evlistener_mainwin_set_always_on_top, NULL);
+    aud_hook_associate("mainwin show", ui_main_evlistener_mainwin_show, NULL);
+    aud_hook_associate("equalizerwin show", ui_main_evlistener_equalizerwin_show, NULL);
+#if 0
+    aud_hook_associate("prefswin show", ui_main_evlistener_prefswin_show, NULL);
+    aud_hook_associate("aboutwin show", ui_main_evlistener_aboutwin_show, NULL);
+    aud_hook_associate("ui jump to track show", ui_main_evlistener_ui_jump_to_track_show, NULL);
+    aud_hook_associate("filebrowser show", ui_main_evlistener_filebrowser_show, NULL);
+    aud_hook_associate("filebrowser hide", ui_main_evlistener_filebrowser_hide, NULL);
+#endif
+    aud_hook_associate("visualization timeout", ui_main_evlistener_visualization_timeout, NULL);
+    aud_hook_associate("config save", ui_main_evlistener_config_save, NULL);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_main_evlisteners.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,28 @@
+/*
+ * Audacious
+ * Copyright (c) 2006-2007 Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include <glib.h>
+
+#ifndef AUDACIOUS_UI_MAIN_EVLISTENERS_H
+#define AUDACIOUS_UI_MAIN_EVLISTENERS_H
+
+void ui_main_evlistener_init(void);
+
+#endif /* AUDACIOUS_UI_MAIN_EVLISTENERS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_manager.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,877 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "ui_manager.h"
+#include "actions-mainwin.h"
+#include "actions-playlist.h"
+#include "actions-equalizer.h"
+
+#if 0
+/* this header contains prototypes for plugin-available menu functions */
+#include "ui_plugin_menu.h"
+#endif
+
+/* TODO ui_main.h is only included because ui_manager.c needs the values of
+   TimerMode enum; move that enum elsewhere so we can get rid of this include */
+#include "ui_main.h"
+
+#include "icons-stock.h"
+#if 0
+#include "sync-menu.h"
+#endif
+#include "plugin.h"
+#include <audacious/ui_plugin_menu.h>
+
+static GtkUIManager *ui_manager = NULL;
+static gboolean menu_created = FALSE;
+
+
+/* toggle action entries */
+
+static GtkToggleActionEntry toggleaction_entries_others[] = {
+
+	{ "autoscroll songname", NULL , N_("Autoscroll Songname"), NULL,
+	  N_("Autoscroll Songname"), G_CALLBACK(action_autoscroll_songname) , FALSE },
+
+	{ "stop after current song", NULL , N_("Stop after Current Song"), "<Ctrl>M",
+	  N_("Stop after Current Song"), G_CALLBACK(action_stop_after_current_song) , FALSE },
+
+	{ "anamode peaks", NULL , N_("Peaks"), NULL,
+	  N_("Peaks"), G_CALLBACK(action_anamode_peaks) , FALSE },
+
+	{ "playback repeat", NULL , N_("Repeat"), "R",
+	  N_("Repeat"), G_CALLBACK(action_playback_repeat) , FALSE },
+
+	{ "playback shuffle", NULL , N_("Shuffle"), "S",
+	  N_("Shuffle"), G_CALLBACK(action_playback_shuffle) , FALSE },
+
+	{ "playback no playlist advance", NULL , N_("No Playlist Advance"), "<Ctrl>N",
+	  N_("No Playlist Advance"), G_CALLBACK(action_playback_noplaylistadvance) , FALSE },
+
+	{ "show player", NULL , N_("Show Player"), "<Alt>M",
+	  N_("Show Player"), G_CALLBACK(action_show_player) , FALSE },
+
+	{ "show playlist editor", NULL , N_("Show Playlist Editor"), "<Alt>E",
+	  N_("Show Playlist Editor"), G_CALLBACK(action_show_playlist_editor) , FALSE },
+
+	{ "show equalizer", NULL , N_("Show Equalizer"), "<Alt>G",
+	  N_("Show Equalizer"), G_CALLBACK(action_show_equalizer) , FALSE },
+
+	{ "view always on top", NULL , N_("Always on Top"), "<Ctrl>O",
+	  N_("Always on Top"), G_CALLBACK(action_view_always_on_top) , FALSE },
+
+	{ "view put on all workspaces", NULL , N_("Put on All Workspaces"), "<Ctrl>S",
+	  N_("Put on All Workspaces"), G_CALLBACK(action_view_on_all_workspaces) , FALSE },
+
+	{ "roll up player", NULL , N_("Roll up Player"), "<Ctrl>W",
+	  N_("Roll up Player"), G_CALLBACK(action_roll_up_player) , FALSE },
+
+	{ "roll up playlist editor", NULL , N_("Roll up Playlist Editor"), "<Shift><Ctrl>W",
+	  N_("Roll up Playlist Editor"), G_CALLBACK(action_roll_up_playlist_editor) , FALSE },
+
+	{ "roll up equalizer", NULL , N_("Roll up Equalizer"), "<Ctrl><Alt>W",
+	  N_("Roll up Equalizer"), G_CALLBACK(action_roll_up_equalizer) , FALSE },
+
+	{ "view scaled", NULL , N_("Scale"), "<Ctrl>D",
+	  N_("DoubleSize"), G_CALLBACK(action_view_scaled) , FALSE },
+
+	{ "view easy move", NULL , N_("Easy Move"), "<Ctrl>E",
+	  N_("Easy Move"), G_CALLBACK(action_view_easymove) , FALSE }
+};
+
+
+
+/* radio action entries */
+
+static GtkRadioActionEntry radioaction_entries_vismode[] = {
+	{ "vismode analyzer", NULL , N_("Analyzer"), NULL, N_("Analyzer"), VIS_ANALYZER },
+	{ "vismode scope", NULL , N_("Scope"), NULL, N_("Scope"), VIS_SCOPE },
+	{ "vismode voiceprint", NULL , N_("Voiceprint"), NULL, N_("Voiceprint"), VIS_VOICEPRINT },
+	{ "vismode off", NULL , N_("Off"), NULL, N_("Off"), VIS_OFF }
+};
+
+static GtkRadioActionEntry radioaction_entries_anamode[] = {
+	{ "anamode normal", NULL , N_("Normal"), NULL, N_("Normal"), ANALYZER_NORMAL },
+	{ "anamode fire", NULL , N_("Fire"), NULL, N_("Fire"), ANALYZER_FIRE },
+	{ "anamode vertical lines", NULL , N_("Vertical Lines"), NULL, N_("Vertical Lines"), ANALYZER_VLINES }
+};
+
+static GtkRadioActionEntry radioaction_entries_anatype[] = {
+	{ "anatype lines", NULL , N_("Lines"), NULL, N_("Lines"), ANALYZER_LINES },
+	{ "anatype bars", NULL , N_("Bars"), NULL, N_("Bars"), ANALYZER_BARS }
+};
+
+static GtkRadioActionEntry radioaction_entries_scomode[] = {
+	{ "scomode dot", NULL , N_("Dot Scope"), NULL, N_("Dot Scope"), SCOPE_DOT },
+	{ "scomode line", NULL , N_("Line Scope"), NULL, N_("Line Scope"), SCOPE_LINE },
+	{ "scomode solid", NULL , N_("Solid Scope"), NULL, N_("Solid Scope"), SCOPE_SOLID }
+};
+
+static GtkRadioActionEntry radioaction_entries_vprmode[] = {
+	{ "vprmode normal", NULL , N_("Normal"), NULL, N_("Normal"), VOICEPRINT_NORMAL },
+	{ "vprmode fire", NULL , N_("Fire"), NULL, N_("Fire"), VOICEPRINT_FIRE },
+	{ "vprmode ice", NULL , N_("Ice"), NULL, N_("Ice"), VOICEPRINT_ICE }
+};
+
+static GtkRadioActionEntry radioaction_entries_wshmode[] = {
+	{ "wshmode normal", NULL , N_("Normal"), NULL, N_("Normal"), VU_NORMAL },
+	{ "wshmode smooth", NULL , N_("Smooth"), NULL, N_("Smooth"), VU_SMOOTH }
+};
+
+static GtkRadioActionEntry radioaction_entries_refrate[] = {
+	{ "refrate full", NULL , N_("Full (~50 fps)"), NULL, N_("Full (~50 fps)"), REFRESH_FULL },
+	{ "refrate half", NULL , N_("Half (~25 fps)"), NULL, N_("Half (~25 fps)"), REFRESH_HALF },
+	{ "refrate quarter", NULL , N_("Quarter (~13 fps)"), NULL, N_("Quarter (~13 fps)"), REFRESH_QUARTER },
+	{ "refrate eighth", NULL , N_("Eighth (~6 fps)"), NULL, N_("Eighth (~6 fps)"), REFRESH_EIGTH }
+};
+
+static GtkRadioActionEntry radioaction_entries_anafoff[] = {
+	{ "anafoff slowest", NULL , N_("Slowest"), NULL, N_("Slowest"), FALLOFF_SLOWEST },
+	{ "anafoff slow", NULL , N_("Slow"), NULL, N_("Slow"), FALLOFF_SLOW },
+	{ "anafoff medium", NULL , N_("Medium"), NULL, N_("Medium"), FALLOFF_MEDIUM },
+	{ "anafoff fast", NULL , N_("Fast"), NULL, N_("Fast"), FALLOFF_FAST },
+	{ "anafoff fastest", NULL , N_("Fastest"), NULL, N_("Fastest"), FALLOFF_FASTEST }
+};
+
+static GtkRadioActionEntry radioaction_entries_peafoff[] = {
+	{ "peafoff slowest", NULL , N_("Slowest"), NULL, N_("Slowest"), FALLOFF_SLOWEST },
+	{ "peafoff slow", NULL , N_("Slow"), NULL, N_("Slow"), FALLOFF_SLOW },
+	{ "peafoff medium", NULL , N_("Medium"), NULL, N_("Medium"), FALLOFF_MEDIUM },
+	{ "peafoff fast", NULL , N_("Fast"), NULL, N_("Fast"), FALLOFF_FAST },
+	{ "peafoff fastest", NULL , N_("Fastest"), NULL, N_("Fastest"), FALLOFF_FASTEST }
+};
+
+static GtkRadioActionEntry radioaction_entries_viewtime[] = {
+	{ "view time elapsed", NULL , N_("Time Elapsed"), "<Ctrl>E", N_("Time Elapsed"), TIMER_ELAPSED },
+	{ "view time remaining", NULL , N_("Time Remaining"), "<Ctrl>R", N_("Time Remaining"), TIMER_REMAINING }
+};
+
+
+
+/* normal actions */
+
+static GtkActionEntry action_entries_playback[] = {
+
+	{ "playback", NULL, N_("Playback") },
+	
+	{ "playback play", GTK_STOCK_MEDIA_PLAY , N_("Play"), "X",
+	  N_("Play"), G_CALLBACK(action_playback_play) },
+
+	{ "playback pause", GTK_STOCK_MEDIA_PAUSE , N_("Pause"), "C",
+	  N_("Pause"), G_CALLBACK(action_playback_pause) },
+
+	{ "playback stop", GTK_STOCK_MEDIA_STOP , N_("Stop"), "V",
+	  N_("Stop"), G_CALLBACK(action_playback_stop) },
+
+	{ "playback previous", GTK_STOCK_MEDIA_PREVIOUS , N_("Previous"), "Z",
+	  N_("Previous"), G_CALLBACK(action_playback_previous) },
+
+	{ "playback next", GTK_STOCK_MEDIA_NEXT , N_("Next"), "B",
+	  N_("Next"), G_CALLBACK(action_playback_next) }
+};
+
+
+static GtkActionEntry action_entries_visualization[] = {
+	{ "visualization", NULL, N_("Visualization") },
+	{ "vismode", NULL, N_("Visualization Mode") },
+	{ "anamode", NULL, N_("Analyzer Mode") },
+	{ "scomode", NULL, N_("Scope Mode") },
+	{ "vprmode", NULL, N_("Voiceprint Mode") },
+	{ "wshmode", NULL, N_("WindowShade VU Mode") },
+	{ "refrate", NULL, N_("Refresh Rate") },
+	{ "anafoff", NULL, N_("Analyzer Falloff") },
+	{ "peafoff", NULL, N_("Peaks Falloff") }
+};
+
+static GtkActionEntry action_entries_playlist[] = {
+
+	{ "playlist", NULL, N_("Playlist") },
+
+	{ "playlist new", GTK_STOCK_NEW , N_("New Playlist"), "<Shift>N",
+	  N_("New Playlist"), G_CALLBACK(action_playlist_new) },
+
+	{ "playlist select next", GTK_STOCK_MEDIA_NEXT, N_("Select Next Playlist"), "<Shift>P",
+	  N_("Select Next Playlist"), G_CALLBACK(action_playlist_next) },
+
+	{ "playlist select previous", GTK_STOCK_MEDIA_PREVIOUS, N_("Select Previous Playlist"), "<Shift><Ctrl>P",
+	  N_("Select Previous Playlist"), G_CALLBACK(action_playlist_prev) },
+
+	{ "playlist delete", GTK_STOCK_DELETE , N_("Delete Playlist"), "<Shift>D",
+	  N_("Delete Playlist"), G_CALLBACK(action_playlist_delete) },
+
+        { "playlist load", GTK_STOCK_OPEN, N_("Load List"), "O",
+          N_("Loads a playlist file into the selected playlist."), G_CALLBACK(action_playlist_load_list) },
+
+        { "playlist save", GTK_STOCK_SAVE, N_("Save List"), "<Shift>S",
+          N_("Saves the selected playlist."), G_CALLBACK(action_playlist_save_list) },
+
+        { "playlist save default", GTK_STOCK_SAVE, N_("Save Default List"), "<Alt>S",
+          N_("Saves the selected playlist to the default location."),
+          G_CALLBACK(action_playlist_save_default_list) },
+
+        { "playlist refresh", GTK_STOCK_REFRESH, N_("Refresh List"), "F5",
+          N_("Refreshes metadata associated with a playlist entry."),
+          G_CALLBACK(action_playlist_refresh_list) },
+
+        { "playlist manager", AUD_STOCK_PLAYLIST , N_("List Manager"), "P",
+          N_("Opens the playlist manager."),
+          G_CALLBACK(action_open_list_manager) }
+};
+
+static GtkActionEntry action_entries_view[] = {
+
+	{ "view", NULL, N_("View") }
+};
+
+static GtkActionEntry action_entries_playlist_add[] = {
+        { "playlist add url", GTK_STOCK_NETWORK, N_("Add Internet Address..."), "<Ctrl>H",
+          N_("Adds a remote track to the playlist."),
+          G_CALLBACK(action_playlist_add_url) },
+
+        { "playlist add files", GTK_STOCK_ADD, N_("Add Files..."), "F",
+          N_("Adds files to the playlist."),
+          G_CALLBACK(action_playlist_add_files) },
+};
+
+static GtkActionEntry action_entries_playlist_select[] = {
+        { "playlist search and select", GTK_STOCK_FIND, N_("Search and Select"), "<Ctrl>F",
+          N_("Searches the playlist and selects playlist entries based on specific criteria."),
+          G_CALLBACK(action_playlist_search_and_select) },
+
+        { "playlist invert selection", NULL , N_("Invert Selection"), NULL,
+          N_("Inverts the selected and unselected entries."),
+          G_CALLBACK(action_playlist_invert_selection) },
+
+        { "playlist select all", NULL , N_("Select All"), "<Ctrl>A",
+          N_("Selects all of the playlist entries."),
+          G_CALLBACK(action_playlist_select_all) },
+
+        { "playlist select none", NULL , N_("Select None"), "<Shift><Ctrl>A",
+          N_("Deselects all of the playlist entries."),
+          G_CALLBACK(action_playlist_select_none) },
+};
+
+static GtkActionEntry action_entries_playlist_delete[] = {
+	{ "playlist remove all", GTK_STOCK_CLEAR, N_("Remove All"), NULL, 
+	  N_("Removes all entries from the playlist."),
+	  G_CALLBACK(action_playlist_remove_all) },
+
+	{ "playlist clear queue", GTK_STOCK_CLEAR, N_("Clear Queue"), "<Shift>Q",
+	  N_("Clears the queue associated with this playlist."),
+	  G_CALLBACK(action_playlist_clear_queue) },
+
+	{ "playlist remove unavailable", GTK_STOCK_DIALOG_ERROR , N_("Remove Unavailable Files"), NULL,
+	  N_("Removes unavailable files from the playlist."),
+	  G_CALLBACK(action_playlist_remove_unavailable) },
+
+	{ "playlist remove dups menu", NULL , N_("Remove Duplicates") },
+
+	{ "playlist remove dups by title", NULL , N_("By Title"), NULL,
+	  N_("Removes duplicate entries from the playlist by title."),
+	  G_CALLBACK(action_playlist_remove_dupes_by_title) },
+
+	{ "playlist remove dups by filename", NULL , N_("By Filename"), NULL, 
+	  N_("Removes duplicate entries from the playlist by filename."),
+	  G_CALLBACK(action_playlist_remove_dupes_by_filename) },
+
+	{ "playlist remove dups by full path", NULL , N_("By Path + Filename"), NULL, 
+	  N_("Removes duplicate entries from the playlist by their full path."),
+	  G_CALLBACK(action_playlist_remove_dupes_by_full_path) },
+
+	{ "playlist remove unselected", GTK_STOCK_REMOVE, N_("Remove Unselected"), NULL,
+	  N_("Remove unselected entries from the playlist."),
+	  G_CALLBACK(action_playlist_remove_unselected) },
+
+	{ "playlist remove selected", GTK_STOCK_REMOVE, N_("Remove Selected"), "Delete", 
+	  N_("Remove selected entries from the playlist."),
+	  G_CALLBACK(action_playlist_remove_selected) },
+};
+
+static GtkActionEntry action_entries_playlist_sort[] = {
+	{ "playlist randomize list", AUD_STOCK_RANDOMIZEPL , N_("Randomize List"), "<Ctrl><Shift>R",
+	  N_("Randomizes the playlist."),
+	  G_CALLBACK(action_playlist_randomize_list) },
+
+	{ "playlist reverse list", GTK_STOCK_GO_UP , N_("Reverse List"), NULL,
+	  N_("Reverses the playlist."),
+	  G_CALLBACK(action_playlist_reverse_list) },
+
+	{ "playlist sort menu", GTK_STOCK_GO_DOWN , N_("Sort List") },
+
+	{ "playlist sort by title", NULL , N_("By Title"), NULL,
+	  N_("Sorts the list by title."),
+	  G_CALLBACK(action_playlist_sort_by_title) },
+
+	{ "playlist sort by artist", NULL , N_("By Artist"), NULL,
+	  N_("Sorts the list by artist."),
+	  G_CALLBACK(action_playlist_sort_by_artist) },
+
+	{ "playlist sort by filename", NULL , N_("By Filename"), NULL,
+	  N_("Sorts the list by filename."),
+	  G_CALLBACK(action_playlist_sort_by_filename) },
+
+	{ "playlist sort by full path", NULL , N_("By Path + Filename"), NULL,
+	  N_("Sorts the list by full pathname."),
+	  G_CALLBACK(action_playlist_sort_by_full_path) },
+
+	{ "playlist sort by date", NULL , N_("By Date"), NULL,
+	  N_("Sorts the list by modification time."),
+	  G_CALLBACK(action_playlist_sort_by_date) },
+
+	{ "playlist sort by track number", NULL , N_("By Track Number"), NULL,
+	  N_("Sorts the list by track number."),
+	  G_CALLBACK(action_playlist_sort_by_track_number) },
+
+	{ "playlist sort by playlist entry", NULL , N_("By Playlist Entry"), NULL,
+	  N_("Sorts the list by playlist entry."),
+	  G_CALLBACK(action_playlist_sort_by_playlist_entry) },
+
+	{ "playlist sort selected menu", GTK_STOCK_GO_DOWN , N_("Sort Selected") },
+
+	{ "playlist sort selected by title", NULL , N_("By Title"), NULL,
+	  N_("Sorts the list by title."),
+	  G_CALLBACK(action_playlist_sort_selected_by_title) },
+
+	{ "playlist sort selected by artist", NULL, N_("By Artist"), NULL,
+	  N_("Sorts the list by artist."),
+	  G_CALLBACK(action_playlist_sort_selected_by_artist) },
+
+	{ "playlist sort selected by filename", NULL , N_("By Filename"), NULL,
+	  N_("Sorts the list by filename."),
+	  G_CALLBACK(action_playlist_sort_selected_by_filename) },
+
+	{ "playlist sort selected by full path", NULL , N_("By Path + Filename"), NULL,
+	  N_("Sorts the list by full pathname."),
+	  G_CALLBACK(action_playlist_sort_selected_by_full_path) },
+
+	{ "playlist sort selected by date", NULL , N_("By Date"), NULL,
+	  N_("Sorts the list by modification time."),
+	  G_CALLBACK(action_playlist_sort_selected_by_date) },
+
+	{ "playlist sort selected by track number", NULL , N_("By Track Number"), NULL,
+	  N_("Sorts the list by track number."),
+	  G_CALLBACK(action_playlist_sort_selected_by_track_number) },
+
+	{ "playlist sort selected by playlist entry", NULL, N_("By Playlist Entry"), NULL,
+	  N_("Sorts the list by playlist entry."),
+	  G_CALLBACK(action_playlist_sort_selected_by_playlist_entry) },
+};
+
+static GtkActionEntry action_entries_others[] = {
+
+	{ "dummy", NULL, "dummy" },
+
+        /* XXX Carbon support */
+        { "file", NULL, N_("File") },
+        { "help", NULL, N_("Help") },
+
+	{ "plugins-menu", AUD_STOCK_PLUGIN, N_("Plugin Services") },
+
+	{ "current track info", GTK_STOCK_INFO , N_("View Track Details"), "I",
+	  N_("View track details"), G_CALLBACK(action_current_track_info) },
+
+	{ "playlist track info", GTK_STOCK_INFO , N_("View Track Details"), "<Alt>I",
+	  N_("View track details"), G_CALLBACK(action_playlist_track_info) },
+
+	{ "about audacious", GTK_STOCK_DIALOG_INFO , N_("About Audacious"), NULL,
+	  N_("About Audacious"), G_CALLBACK(action_about_audacious) },
+
+	{ "play file", GTK_STOCK_OPEN , N_("Play File"), "L",
+	  N_("Load and play a file"), G_CALLBACK(action_play_file) },
+
+	{ "play location", GTK_STOCK_NETWORK , N_("Play Location"), "<Ctrl>L",
+	  N_("Play media from the selected location"), G_CALLBACK(action_play_location) },
+
+    { "plugins", NULL , N_("Plugin services") },
+
+	{ "preferences", GTK_STOCK_PREFERENCES , N_("Preferences"), "<Ctrl>P",
+	  N_("Open preferences window"), G_CALLBACK(action_preferences) },
+
+	{ "quit", GTK_STOCK_QUIT , N_("_Quit"), NULL,
+	  N_("Quit Audacious"), G_CALLBACK(action_quit) },
+
+	{ "ab set", NULL , N_("Set A-B"), "A",
+	  N_("Set A-B"), G_CALLBACK(action_ab_set) },
+
+	{ "ab clear", NULL , N_("Clear A-B"), "<Shift>A",
+	  N_("Clear A-B"), G_CALLBACK(action_ab_clear) },
+
+	{ "jump to playlist start", GTK_STOCK_GOTO_TOP , N_("Jump to Playlist Start"), "<Ctrl>Z",
+	  N_("Jump to Playlist Start"), G_CALLBACK(action_jump_to_playlist_start) },
+
+	{ "jump to file", GTK_STOCK_JUMP_TO , N_("Jump to File"), "J",
+	  N_("Jump to File"), G_CALLBACK(action_jump_to_file) },
+
+	{ "jump to time", GTK_STOCK_JUMP_TO , N_("Jump to Time"), "<Ctrl>J",
+	  N_("Jump to Time"), G_CALLBACK(action_jump_to_time) },
+
+	{ "queue toggle", AUD_STOCK_QUEUETOGGLE , N_("Queue Toggle"), "Q", 
+	  N_("Enables/disables the entry in the playlist's queue."),
+	  G_CALLBACK(action_queue_toggle) },
+};
+
+
+static GtkActionEntry action_entries_equalizer[] = {
+
+    { "equ preset load menu", NULL, N_("Load") },
+    { "equ preset import menu", NULL, N_("Import") },
+    { "equ preset save menu", NULL, N_("Save") },
+    { "equ preset delete menu", NULL, N_("Delete") },
+
+    { "equ load preset", NULL, N_("Preset"), NULL,
+      N_("Load preset"), G_CALLBACK(action_equ_load_preset) },
+
+    { "equ load auto preset", NULL, N_("Auto-load preset"), NULL,
+      N_("Load auto-load preset"), G_CALLBACK(action_equ_load_auto_preset) },
+
+    { "equ load default preset", NULL, N_("Default"), NULL,
+      N_("Load default preset into equalizer"), G_CALLBACK(action_equ_load_default_preset) },
+
+    { "equ zero preset", NULL, N_("Zero"), NULL,
+      N_("Set equalizer preset levels to zero"), G_CALLBACK(action_equ_zero_preset) },
+
+    { "equ load preset file", NULL, N_("From file"), NULL,
+      N_("Load preset from file"), G_CALLBACK(action_equ_load_preset_file) },
+
+    { "equ load preset eqf", NULL, N_("From WinAMP EQF file"), NULL,
+      N_("Load preset from WinAMP EQF file"), G_CALLBACK(action_equ_load_preset_eqf) },
+
+    { "equ import winamp presets", NULL, N_("WinAMP Presets"), NULL,
+      N_("Import WinAMP presets"), G_CALLBACK(action_equ_import_winamp_presets) },
+
+    { "equ save preset", NULL, N_("Preset"), NULL,
+      N_("Save preset"), G_CALLBACK(action_equ_save_preset) },
+
+    { "equ save auto preset", NULL, N_("Auto-load preset"), NULL,
+      N_("Save auto-load preset"), G_CALLBACK(action_equ_save_auto_preset) },
+
+    { "equ save default preset", NULL, N_("Default"), NULL,
+      N_("Save default preset"), G_CALLBACK(action_equ_save_default_preset) },
+
+    { "equ save preset file", NULL, N_("To file"), NULL,
+      N_("Save preset to file"), G_CALLBACK(action_equ_save_preset_file) },
+
+    { "equ save preset eqf", NULL, N_("To WinAMP EQF file"), NULL,
+      N_("Save preset to WinAMP EQF file"), G_CALLBACK(action_equ_save_preset_eqf) },
+
+    { "equ delete preset", NULL, N_("Preset"), NULL,
+      N_("Delete preset"), G_CALLBACK(action_equ_delete_preset) },
+
+    { "equ delete auto preset", NULL, N_("Auto-load preset"), NULL,
+      N_("Delete auto-load preset"), G_CALLBACK(action_equ_delete_auto_preset) }
+};
+
+
+
+/* ***************************** */
+
+
+static GtkActionGroup *
+ui_manager_new_action_group( const gchar * group_name )
+{
+  GtkActionGroup *group = gtk_action_group_new( group_name );
+  gtk_action_group_set_translation_domain( group , PACKAGE_NAME );
+  return group;
+}
+
+void
+ui_manager_init ( void )
+{
+  /* toggle actions */
+  toggleaction_group_others = ui_manager_new_action_group("toggleaction_others");
+  gtk_action_group_add_toggle_actions(
+    toggleaction_group_others , toggleaction_entries_others ,
+    G_N_ELEMENTS(toggleaction_entries_others) , NULL );
+
+  /* radio actions */
+  radioaction_group_anamode = ui_manager_new_action_group("radioaction_anamode");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_anamode , radioaction_entries_anamode ,
+    G_N_ELEMENTS(radioaction_entries_anamode) , 0 , G_CALLBACK(action_anamode) , NULL );
+
+  radioaction_group_anatype = ui_manager_new_action_group("radioaction_anatype");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_anatype , radioaction_entries_anatype ,
+    G_N_ELEMENTS(radioaction_entries_anatype) , 0 , G_CALLBACK(action_anatype) , NULL );
+
+  radioaction_group_scomode = ui_manager_new_action_group("radioaction_scomode");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_scomode , radioaction_entries_scomode ,
+    G_N_ELEMENTS(radioaction_entries_scomode) , 0 , G_CALLBACK(action_scomode) , NULL );
+
+  radioaction_group_vprmode = ui_manager_new_action_group("radioaction_vprmode");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_vprmode , radioaction_entries_vprmode ,
+    G_N_ELEMENTS(radioaction_entries_vprmode) , 0 , G_CALLBACK(action_vprmode) , NULL );
+
+  radioaction_group_wshmode = ui_manager_new_action_group("radioaction_wshmode");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_wshmode , radioaction_entries_wshmode ,
+    G_N_ELEMENTS(radioaction_entries_wshmode) , 0 , G_CALLBACK(action_wshmode) , NULL );
+
+  radioaction_group_refrate = ui_manager_new_action_group("radioaction_refrate");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_refrate , radioaction_entries_refrate ,
+    G_N_ELEMENTS(radioaction_entries_refrate) , 0 , G_CALLBACK(action_refrate) , NULL );
+
+  radioaction_group_anafoff = ui_manager_new_action_group("radioaction_anafoff");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_anafoff , radioaction_entries_anafoff ,
+    G_N_ELEMENTS(radioaction_entries_anafoff) , 0 , G_CALLBACK(action_anafoff) , NULL );
+
+  radioaction_group_peafoff = ui_manager_new_action_group("radioaction_peafoff");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_peafoff , radioaction_entries_peafoff ,
+    G_N_ELEMENTS(radioaction_entries_peafoff) , 0 , G_CALLBACK(action_peafoff) , NULL );
+
+  radioaction_group_vismode = ui_manager_new_action_group("radioaction_vismode");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_vismode , radioaction_entries_vismode ,
+    G_N_ELEMENTS(radioaction_entries_vismode) , 0 , G_CALLBACK(action_vismode) , NULL );
+
+  radioaction_group_viewtime = ui_manager_new_action_group("radioaction_viewtime");
+  gtk_action_group_add_radio_actions(
+    radioaction_group_viewtime , radioaction_entries_viewtime ,
+    G_N_ELEMENTS(radioaction_entries_viewtime) , 0 , G_CALLBACK(action_viewtime) , NULL );
+
+  /* normal actions */
+  action_group_playback = ui_manager_new_action_group("action_playback");
+    gtk_action_group_add_actions(
+    action_group_playback , action_entries_playback ,
+    G_N_ELEMENTS(action_entries_playback) , NULL );
+
+  action_group_playlist = ui_manager_new_action_group("action_playlist");
+    gtk_action_group_add_actions(
+    action_group_playlist , action_entries_playlist ,
+    G_N_ELEMENTS(action_entries_playlist) , NULL );
+
+  action_group_visualization = ui_manager_new_action_group("action_visualization");
+    gtk_action_group_add_actions(
+    action_group_visualization , action_entries_visualization ,
+    G_N_ELEMENTS(action_entries_visualization) , NULL );
+
+  action_group_view = ui_manager_new_action_group("action_view");
+    gtk_action_group_add_actions(
+    action_group_view , action_entries_view ,
+    G_N_ELEMENTS(action_entries_view) , NULL );
+
+  action_group_others = ui_manager_new_action_group("action_others");
+    gtk_action_group_add_actions(
+    action_group_others , action_entries_others ,
+    G_N_ELEMENTS(action_entries_others) , NULL );
+
+  action_group_playlist_add = ui_manager_new_action_group("action_playlist_add");
+  gtk_action_group_add_actions(
+    action_group_playlist_add, action_entries_playlist_add,
+    G_N_ELEMENTS(action_entries_playlist_add), NULL );
+
+  action_group_playlist_select = ui_manager_new_action_group("action_playlist_select");
+  gtk_action_group_add_actions(
+    action_group_playlist_select, action_entries_playlist_select,
+    G_N_ELEMENTS(action_entries_playlist_select), NULL );
+
+  action_group_playlist_delete = ui_manager_new_action_group("action_playlist_delete");
+  gtk_action_group_add_actions(
+    action_group_playlist_delete, action_entries_playlist_delete,
+    G_N_ELEMENTS(action_entries_playlist_delete), NULL );
+
+  action_group_playlist_sort = ui_manager_new_action_group("action_playlist_sort");
+  gtk_action_group_add_actions(
+    action_group_playlist_sort, action_entries_playlist_sort,
+    G_N_ELEMENTS(action_entries_playlist_sort), NULL );
+
+  action_group_equalizer = ui_manager_new_action_group("action_equalizer");
+  gtk_action_group_add_actions(
+    action_group_equalizer, action_entries_equalizer,
+    G_N_ELEMENTS(action_entries_equalizer), NULL);
+
+  /* ui */
+  ui_manager = gtk_ui_manager_new();
+  gtk_ui_manager_insert_action_group( ui_manager , toggleaction_group_others , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_anamode , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_anatype , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_scomode , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_vprmode , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_wshmode , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_refrate , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_anafoff , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_peafoff , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_vismode , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , radioaction_group_viewtime , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playback , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playlist , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_visualization , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_view , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_others , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playlist_add , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playlist_select , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playlist_delete , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_playlist_sort , 0 );
+  gtk_ui_manager_insert_action_group( ui_manager , action_group_equalizer , 0 );
+
+  return;
+}
+
+#ifdef GDK_WINDOWING_QUARTZ
+static GtkWidget *carbon_menubar;
+#endif
+
+static void
+ui_manager_create_menus_init_pmenu( gchar * path )
+{
+  GtkWidget *plugins_menu_item = gtk_ui_manager_get_widget( ui_manager , path );
+  if ( plugins_menu_item )
+  {
+    /* initially set count of items under plugins_menu_item to 0 */
+    g_object_set_data( G_OBJECT(plugins_menu_item) , "ic" , GINT_TO_POINTER(0) );
+    /* and since it's 0, hide the plugins_menu_item */
+    gtk_widget_hide( plugins_menu_item );
+  }
+  return;
+}
+
+
+void
+ui_manager_create_menus ( void )
+{
+  GError *gerr = NULL;
+
+  /* attach xml menu definitions */
+  gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/mainwin.ui" , &gerr );
+
+  if ( gerr != NULL )
+  {
+    g_critical( "Error creating UI<ui/mainwin.ui>: %s" , gerr->message );
+    g_error_free( gerr );
+    return;
+  }
+
+  /* create GtkMenu widgets using path from xml definitions */
+  mainwin_songname_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/songname-menu" );
+  mainwin_visualization_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/main-menu/visualization" );
+  mainwin_playback_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/main-menu/playback" );
+  mainwin_playlist_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/main-menu/playlist" );
+  mainwin_view_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/main-menu/view" );
+  mainwin_general_menu = ui_manager_get_popup_menu( ui_manager , "/mainwin-menus/main-menu" );
+
+  /* initialize plugins-menu for mainwin-menus */
+  ui_manager_create_menus_init_pmenu( "/mainwin-menus/main-menu/plugins-menu" );
+
+#ifdef GDK_WINDOWING_QUARTZ
+  gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/carbon-menubar.ui" , &gerr );
+
+  if ( gerr != NULL )
+  {
+    g_critical( "Error creating UI<ui/carbon-menubar.ui>: %s" , gerr->message );
+    g_error_free( gerr );
+    return;
+  }
+
+  carbon_menubar = ui_manager_get_popup_menu( ui_manager , "/carbon-menubar/main-menu" );
+  sync_menu_takeover_menu(GTK_MENU_SHELL(carbon_menubar));
+#endif
+
+  gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/playlist.ui" , &gerr );
+
+  if ( gerr != NULL )
+  {
+    g_critical( "Error creating UI<ui/playlist.ui>: %s" , gerr->message );
+    g_error_free( gerr );
+    return;
+  }
+
+  playlistwin_popup_menu = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/playlist-rightclick-menu");
+
+  playlistwin_pladd_menu  = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/add-menu");
+  playlistwin_pldel_menu  = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/del-menu");
+  playlistwin_plsel_menu  = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/select-menu");
+  playlistwin_plsort_menu = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/misc-menu");
+  playlistwin_pllist_menu = ui_manager_get_popup_menu(ui_manager, "/playlist-menus/playlist-menu");
+
+  /* initialize plugins-menu for playlist-menus */
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/playlist-menu/plugins-menu" );
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/add-menu/plugins-menu" );
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/del-menu/plugins-menu" );
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/select-menu/plugins-menu" );
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/misc-menu/plugins-menu" );
+  ui_manager_create_menus_init_pmenu( "/playlist-menus/playlist-rightclick-menu/plugins-menu" );
+
+  gtk_ui_manager_add_ui_from_file( ui_manager , DATA_DIR "/ui/equalizer.ui" , &gerr );
+
+  if ( gerr != NULL )
+  {
+    g_critical( "Error creating UI<ui/equalizer.ui>: %s" , gerr->message );
+    g_error_free( gerr );
+    return;
+  }
+
+  equalizerwin_presets_menu = ui_manager_get_popup_menu(ui_manager, "/equalizer-menus/preset-menu");
+
+  menu_created = TRUE;
+
+  return;
+}
+
+
+GtkAccelGroup *
+ui_manager_get_accel_group ( void )
+{
+  return gtk_ui_manager_get_accel_group( ui_manager );
+}
+
+
+GtkWidget *
+ui_manager_get_popup_menu ( GtkUIManager * self , const gchar * path )
+{
+  GtkWidget *menu_item = gtk_ui_manager_get_widget( self , path );
+
+  if (GTK_IS_MENU_ITEM(menu_item))
+    return gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
+  else
+    return NULL;
+}
+
+
+static void
+menu_popup_pos_func (GtkMenu * menu , gint * x , gint * y , gboolean * push_in , gint * point )
+{
+  GtkRequisition requisition;
+  gint screen_width;
+  gint screen_height;
+
+  gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
+
+  screen_width = gdk_screen_width();
+  screen_height = gdk_screen_height();
+
+  *x = CLAMP(point[0] - 2, 0, MAX(0, screen_width - requisition.width));
+  *y = CLAMP(point[1] - 2, 0, MAX(0, screen_height - requisition.height));
+
+  *push_in = FALSE;
+}
+
+
+void
+ui_manager_popup_menu_show ( GtkMenu * menu , gint x , gint y , guint button , guint time )
+{
+  gint pos[2];
+  pos[0] = x;
+  pos[1] = y;
+
+  gtk_menu_popup( menu , NULL , NULL ,
+    (GtkMenuPositionFunc) menu_popup_pos_func , pos , button , time );
+}
+
+
+
+/******************************/
+/* plugin-available functions */
+
+#define _MP_GWID(y) gtk_ui_manager_get_widget( ui_manager , y )
+
+static GtkWidget*
+audacious_menu_plugin_menuwid( menu_id )
+{
+  switch (menu_id)
+  {
+    case AUDACIOUS_MENU_MAIN:
+      return _MP_GWID("/mainwin-menus/main-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST:
+      return _MP_GWID("/playlist-menus/playlist-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST_RCLICK:
+      return _MP_GWID("/playlist-menus/playlist-rightclick-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST_ADD:
+      return _MP_GWID("/playlist-menus/add-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST_REMOVE:
+      return _MP_GWID("/playlist-menus/del-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST_SELECT:
+      return _MP_GWID("/playlist-menus/select-menu/plugins-menu");
+    case AUDACIOUS_MENU_PLAYLIST_MISC:
+      return _MP_GWID("/playlist-menus/misc-menu/plugins-menu");
+    default:
+      return NULL;
+  }
+}
+
+
+gint
+menu_plugin_item_add( gint menu_id , GtkWidget * item )
+{
+  if ( menu_created )
+  {
+    GtkWidget *plugins_menu = NULL;
+    GtkWidget *plugins_menu_item = audacious_menu_plugin_menuwid( menu_id );
+    if ( plugins_menu_item )
+    {
+      gint ic = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(plugins_menu_item),"ic"));
+      if ( ic == 0 ) /* no items under plugins_menu_item, create the submenu */
+      {
+        plugins_menu = gtk_menu_new();
+        gtk_menu_item_set_submenu( GTK_MENU_ITEM(plugins_menu_item), plugins_menu );
+      }
+      else /* items available under plugins_menu_item, pick the existing submenu */
+      {
+        plugins_menu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(plugins_menu_item) );
+        if ( !plugins_menu ) return -1;
+      }
+      gtk_menu_shell_append( GTK_MENU_SHELL(plugins_menu) , item );
+      gtk_widget_show( plugins_menu_item );
+      g_object_set_data( G_OBJECT(plugins_menu_item) , "ic" , GINT_TO_POINTER(++ic) );
+      return 0; /* success */
+    }
+  }
+  return -1; /* failure */
+}
+
+
+gint
+menu_plugin_item_remove( gint menu_id , GtkWidget * item )
+{
+  if ( menu_created )
+  {
+    GtkWidget *plugins_menu = NULL;
+    GtkWidget *plugins_menu_item = audacious_menu_plugin_menuwid( menu_id );
+    if ( plugins_menu_item )
+    {
+      gint ic = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(plugins_menu_item),"ic"));
+      if ( ic > 0 )
+      {
+        plugins_menu = gtk_menu_item_get_submenu( GTK_MENU_ITEM(plugins_menu_item) );
+        if ( plugins_menu )
+        {
+          /* remove the plugin-added entry */
+          gtk_container_remove( GTK_CONTAINER(plugins_menu) , item );
+          g_object_set_data( G_OBJECT(plugins_menu_item) , "ic" , GINT_TO_POINTER(--ic) );
+          if ( ic == 0 ) /* if the menu is empty now, destroy it */
+          {
+            gtk_menu_item_remove_submenu( GTK_MENU_ITEM(plugins_menu_item) );
+            gtk_widget_destroy( plugins_menu );
+            gtk_widget_hide( plugins_menu_item );
+          }
+          return 0; /* success */
+        }
+      }
+    }
+  }
+  return -1; /* failure */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_manager.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,77 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_MANAGER_H
+#define AUDACIOUS_UI_MANAGER_H
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GtkWidget *mainwin_songname_menu;
+GtkWidget *mainwin_general_menu;
+GtkWidget *mainwin_visualization_menu;
+GtkWidget *mainwin_playback_menu;
+GtkWidget *mainwin_playlist_menu;
+GtkWidget *mainwin_view_menu;
+
+GtkWidget *playlistwin_pladd_menu;
+GtkWidget *playlistwin_pldel_menu;
+GtkWidget *playlistwin_plsel_menu;
+GtkWidget *playlistwin_plsort_menu;
+GtkWidget *playlistwin_pllist_menu;
+GtkWidget *playlistwin_popup_menu;
+
+GtkWidget *equalizerwin_presets_menu;
+
+GtkActionGroup *toggleaction_group_others;
+GtkActionGroup *radioaction_group_anamode; /* Analyzer mode */
+GtkActionGroup *radioaction_group_anatype; /* Analyzer type */
+GtkActionGroup *radioaction_group_scomode; /* Scope mode */
+GtkActionGroup *radioaction_group_vprmode; /* Voiceprint mode */
+GtkActionGroup *radioaction_group_wshmode; /* WindowShade VU mode */
+GtkActionGroup *radioaction_group_refrate; /* Refresh rate */
+GtkActionGroup *radioaction_group_anafoff; /* Analyzer Falloff */
+GtkActionGroup *radioaction_group_peafoff; /* Peak Falloff */
+GtkActionGroup *radioaction_group_vismode; /* Visualization mode */
+GtkActionGroup *radioaction_group_viewtime; /* View time (remaining/elapsed) */
+GtkActionGroup *action_group_playback;
+GtkActionGroup *action_group_visualization;
+GtkActionGroup *action_group_view;
+GtkActionGroup *action_group_others;
+GtkActionGroup *action_group_playlist;
+GtkActionGroup *action_group_playlist_add;
+GtkActionGroup *action_group_playlist_select;
+GtkActionGroup *action_group_playlist_delete;
+GtkActionGroup *action_group_playlist_sort;
+GtkActionGroup *action_group_equalizer;
+
+
+void ui_manager_init ( void );
+void ui_manager_create_menus ( void );
+GtkAccelGroup * ui_manager_get_accel_group ( void );
+GtkWidget * ui_manager_get_popup_menu ( GtkUIManager * , const gchar * );
+void ui_manager_popup_menu_show( GtkMenu * , gint , gint , guint , guint );
+#define popup_menu_show(x1,x2,x3,x4,x5) ui_manager_popup_menu_show(x1,x2,x3,x4,x5)
+
+G_END_DECLS
+
+#endif /* AUDACIOUS_UI_MANAGER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,1950 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2006  Audacious development team.
+ *
+ *  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+/* #define AUD_DEBUG 1 */
+
+#include "ui_playlist.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "platform/smartinclude.h"
+
+#include <unistd.h>
+#include <errno.h>
+
+#include "actions-playlist.h"
+#include "dnd.h"
+#if 0
+#include "input.h"
+#include "main.h"
+#include "playback.h"
+#include "playlist.h"
+#include "playlist_container.h"
+#include "strings.h"
+#endif
+#include "ui_dock.h"
+#include "ui_equalizer.h"
+#if 0
+#include "ui_fileinfo.h"
+#include "ui_fileopener.h"
+#endif
+#include "ui_main.h"
+#include "ui_manager.h"
+#include "ui_playlist_evlisteners.h"
+#if 0
+#include "ui_playlist_manager.h"
+#endif
+#include "util.h"
+
+#include "ui_skinned_window.h"
+#include "ui_skinned_button.h"
+#include "ui_skinned_textbox.h"
+#include "ui_skinned_playlist_slider.h"
+#include "ui_skinned_playlist.h"
+
+#include "icons-stock.h"
+#include "images/audacious_playlist.xpm"
+
+GtkWidget *playlistwin;
+
+static GMutex *resize_mutex = NULL;
+
+static GtkWidget *playlistwin_list = NULL;
+static GtkWidget *playlistwin_shade, *playlistwin_close;
+
+static gboolean playlistwin_hint_flag = FALSE;
+
+static GtkWidget *playlistwin_slider;
+static GtkWidget *playlistwin_time_min, *playlistwin_time_sec;
+static GtkWidget *playlistwin_info, *playlistwin_sinfo;
+static GtkWidget *playlistwin_srew, *playlistwin_splay;
+static GtkWidget *playlistwin_spause, *playlistwin_sstop;
+static GtkWidget *playlistwin_sfwd, *playlistwin_seject;
+static GtkWidget *playlistwin_sscroll_up, *playlistwin_sscroll_down;
+
+static void playlistwin_select_search_cbt_cb(GtkWidget *called_cbt,
+                                             gpointer other_cbt);
+static gboolean playlistwin_select_search_kp_cb(GtkWidget *entry,
+                                                GdkEventKey *event,
+                                                gpointer searchdlg_win);
+
+static gboolean playlistwin_resizing = FALSE;
+static gint playlistwin_resize_x, playlistwin_resize_y;
+
+gboolean
+playlistwin_is_shaded(void)
+{
+    return config.playlist_shaded;
+}
+
+gint
+playlistwin_get_width(void)
+{
+    config.playlist_width /= PLAYLISTWIN_WIDTH_SNAP;
+    config.playlist_width *= PLAYLISTWIN_WIDTH_SNAP;
+    return config.playlist_width;
+}
+
+gint
+playlistwin_get_height_unshaded(void)
+{
+    config.playlist_height /= PLAYLISTWIN_HEIGHT_SNAP;
+    config.playlist_height *= PLAYLISTWIN_HEIGHT_SNAP;
+    return config.playlist_height;
+}
+
+gint
+playlistwin_get_height_shaded(void)
+{
+    return PLAYLISTWIN_SHADED_HEIGHT;
+}
+
+gint
+playlistwin_get_height(void)
+{
+    if (playlistwin_is_shaded())
+        return playlistwin_get_height_shaded();
+    else
+        return playlistwin_get_height_unshaded();
+}
+
+static void
+playlistwin_update_info(Playlist *playlist)
+{
+#if 0
+    gchar *text, *sel_text, *tot_text;
+    gulong selection, total;
+    gboolean selection_more, total_more;
+
+    aud_playlist_get_total_time(playlist, &total, &selection, &total_more, &selection_more);
+
+    if (selection > 0 || (selection == 0 && !selection_more)) {
+        if (selection > 3600)
+            sel_text =
+                g_strdup_printf("%lu:%-2.2lu:%-2.2lu%s", selection / 3600,
+                                (selection / 60) % 60, selection % 60,
+                                (selection_more ? "+" : ""));
+        else
+            sel_text =
+                g_strdup_printf("%lu:%-2.2lu%s", selection / 60,
+                                selection % 60, (selection_more ? "+" : ""));
+    }
+    else
+        sel_text = g_strdup("?");
+    if (total > 0 || (total == 0 && !total_more)) {
+        if (total > 3600)
+            tot_text =
+                g_strdup_printf("%lu:%-2.2lu:%-2.2lu%s", total / 3600,
+                                (total / 60) % 60, total % 60,
+                                total_more ? "+" : "");
+        else
+            tot_text =
+                g_strdup_printf("%lu:%-2.2lu%s", total / 60, total % 60,
+                                total_more ? "+" : "");
+    }
+    else
+        tot_text = g_strdup("?");
+    text = g_strconcat(sel_text, "/", tot_text, NULL);
+    ui_skinned_textbox_set_text(playlistwin_info, text ? text : "");
+    g_free(text);
+    g_free(tot_text);
+    g_free(sel_text);
+#endif
+}
+
+static void
+playlistwin_update_sinfo(Playlist *playlist)
+{
+#if 0
+    gchar *posstr, *timestr, *title, *info;
+    gint pos, time;
+
+    pos = aud_playlist_get_position(playlist);
+    title = aud_playlist_get_songtitle(playlist, pos);
+
+    if (!title) {
+        ui_skinned_textbox_set_text(playlistwin_sinfo, "");
+        return;
+    }
+
+    aud_convert_title_text(title);
+
+    time = aud_playlist_get_songtime(playlist, pos);
+
+    if (config.show_numbers_in_pl)
+        posstr = g_strdup_printf("%d. ", pos + 1);
+    else
+        posstr = g_strdup("");
+
+    if (time != -1) {
+        timestr = g_strdup_printf(" (%d:%-2.2d)", time / 60000,
+                                      (time / 1000) % 60);
+    }
+    else
+        timestr = g_strdup("");
+
+    info = g_strdup_printf("%s%s%s", posstr, title, timestr);
+
+    g_free(posstr);
+    g_free(title);
+    g_free(timestr);
+
+    ui_skinned_textbox_set_text(playlistwin_sinfo, info ? info : "");
+    g_free(info);
+#endif
+}
+
+gboolean
+playlistwin_item_visible(gint index)
+{
+    g_return_val_if_fail(UI_SKINNED_IS_PLAYLIST(playlistwin_list), FALSE);
+
+    if (index >= UI_SKINNED_PLAYLIST(playlistwin_list)->first &&
+        index < (UI_SKINNED_PLAYLIST(playlistwin_list)->first + UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible) ) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+gint
+playlistwin_list_get_visible_count(void)
+{
+    g_return_val_if_fail(UI_SKINNED_IS_PLAYLIST(playlistwin_list), -1);
+
+    return UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible;
+}
+
+gint
+playlistwin_list_get_first(void)
+{
+    g_return_val_if_fail(UI_SKINNED_IS_PLAYLIST(playlistwin_list), -1);
+
+    return UI_SKINNED_PLAYLIST(playlistwin_list)->first;
+}
+
+gint
+playlistwin_get_toprow(void)
+{
+    g_return_val_if_fail(UI_SKINNED_IS_PLAYLIST(playlistwin_list), -1);
+
+    return (UI_SKINNED_PLAYLIST(playlistwin_list)->first);
+}
+
+void
+playlistwin_set_toprow(gint toprow)
+{
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list))
+        UI_SKINNED_PLAYLIST(playlistwin_list)->first = toprow;
+#if 0
+    g_cond_signal(cond_scan);
+#endif
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+void
+playlistwin_update_list(Playlist *playlist)
+{
+    /* this can happen early on. just bail gracefully. */
+    g_return_if_fail(playlistwin_list);
+
+    playlistwin_update_info(playlist);
+    playlistwin_update_sinfo(playlist);
+    gtk_widget_queue_draw(playlistwin_list);
+    gtk_widget_queue_draw(playlistwin_slider);
+}
+
+static void
+playlistwin_set_geometry_hints(gboolean shaded)
+{
+    GdkGeometry geometry;
+    GdkWindowHints mask;
+
+    geometry.min_width = PLAYLISTWIN_MIN_WIDTH;
+    geometry.max_width = G_MAXUINT16;
+
+    geometry.width_inc = PLAYLISTWIN_WIDTH_SNAP;
+    geometry.height_inc = PLAYLISTWIN_HEIGHT_SNAP;
+
+    if (shaded) {
+        geometry.min_height = PLAYLISTWIN_SHADED_HEIGHT;
+        geometry.max_height = PLAYLISTWIN_SHADED_HEIGHT;
+        geometry.base_height = PLAYLISTWIN_SHADED_HEIGHT;
+    }
+    else {
+        geometry.min_height = PLAYLISTWIN_MIN_HEIGHT;
+        geometry.max_height = G_MAXUINT16;
+    }
+
+    mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_RESIZE_INC;
+
+    gtk_window_set_geometry_hints(GTK_WINDOW(playlistwin),
+                                  playlistwin, &geometry, mask);
+}
+
+void
+playlistwin_set_sinfo_font(gchar *font)
+{
+    gchar *tmp = NULL, *tmp2 = NULL;
+
+    g_return_if_fail(font);
+    AUDDBG("Attempt to set font \"%s\"\n", font);
+
+    tmp = g_strdup(font);
+    g_return_if_fail(tmp);
+
+    *strrchr(tmp, ' ') = '\0';
+    tmp2 = g_strdup_printf("%s 8", tmp);
+    g_return_if_fail(tmp2);
+
+    ui_skinned_textbox_set_xfont(playlistwin_sinfo, !config.mainwin_use_bitmapfont, tmp2);
+
+    g_free(tmp);
+    g_free(tmp2);
+}
+
+void
+playlistwin_set_sinfo_scroll(gboolean scroll)
+{
+    if(playlistwin_is_shaded())
+        ui_skinned_textbox_set_scroll(playlistwin_sinfo, config.autoscroll);
+    else
+        ui_skinned_textbox_set_scroll(playlistwin_sinfo, FALSE);
+}
+
+void
+playlistwin_set_shade(gboolean shaded)
+{
+    config.playlist_shaded = shaded;
+
+    if (shaded) {
+        playlistwin_set_sinfo_font(aud_cfg->playlist_font);
+        playlistwin_set_sinfo_scroll(config.autoscroll);
+        gtk_widget_show(playlistwin_sinfo);
+        ui_skinned_set_push_button_data(playlistwin_shade, 128, 45, 150, 42);
+        ui_skinned_set_push_button_data(playlistwin_close, 138, 45, -1, -1);
+    }
+    else {
+        gtk_widget_hide(playlistwin_sinfo);
+        playlistwin_set_sinfo_scroll(FALSE);
+        ui_skinned_set_push_button_data(playlistwin_shade, 157, 3, 62, 42);
+        ui_skinned_set_push_button_data(playlistwin_close, 167, 3, -1, -1);
+    }
+
+    dock_shade(get_dock_window_list(), GTK_WINDOW(playlistwin),
+               playlistwin_get_height());
+
+    playlistwin_set_geometry_hints(config.playlist_shaded);
+
+    gtk_window_resize(GTK_WINDOW(playlistwin),
+                      playlistwin_get_width(),
+                      playlistwin_get_height());
+}
+
+static void
+playlistwin_set_shade_menu(gboolean shaded)
+{
+    GtkAction *action = gtk_action_group_get_action(
+      toggleaction_group_others , "roll up playlist editor" );
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , shaded );
+
+    playlistwin_set_shade(shaded);
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+void
+playlistwin_shade_toggle(void)
+{
+    playlistwin_set_shade_menu(!config.playlist_shaded);
+}
+
+static gboolean
+playlistwin_release(GtkWidget * widget,
+                    GdkEventButton * event,
+                    gpointer callback_data)
+{
+    playlistwin_resizing = FALSE;
+    return FALSE;
+}
+
+void
+playlistwin_scroll(gint num)
+{
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list))
+        UI_SKINNED_PLAYLIST(playlistwin_list)->first += num;
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+void
+playlistwin_scroll_up_pushed(void)
+{
+    playlistwin_scroll(-3);
+}
+
+void
+playlistwin_scroll_down_pushed(void)
+{
+    playlistwin_scroll(3);
+}
+
+static void
+playlistwin_select_all(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_select_all(playlist, TRUE);
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list)) {
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected = 0;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_min = 0;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_max = aud_playlist_get_length(playlist) - 1;
+    }
+    playlistwin_update_list(playlist);
+}
+
+static void
+playlistwin_select_none(void)
+{
+    aud_playlist_select_all(aud_playlist_get_active(), FALSE);
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list)) {
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected = -1;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_min = -1;
+    }
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+static void
+playlistwin_select_search(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+    GtkWidget *searchdlg_win, *searchdlg_table;
+    GtkWidget *searchdlg_hbox, *searchdlg_logo, *searchdlg_helptext;
+    GtkWidget *searchdlg_entry_title, *searchdlg_label_title;
+    GtkWidget *searchdlg_entry_album, *searchdlg_label_album;
+    GtkWidget *searchdlg_entry_file_name, *searchdlg_label_file_name;
+    GtkWidget *searchdlg_entry_performer, *searchdlg_label_performer;
+    GtkWidget *searchdlg_checkbt_clearprevsel;
+    GtkWidget *searchdlg_checkbt_newplaylist;
+    GtkWidget *searchdlg_checkbt_autoenqueue;
+    gint result;
+
+    /* create dialog */
+    searchdlg_win = gtk_dialog_new_with_buttons(
+      _("Search entries in active playlist") , GTK_WINDOW(mainwin) ,
+      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT ,
+      GTK_STOCK_CANCEL , GTK_RESPONSE_REJECT , GTK_STOCK_OK , GTK_RESPONSE_ACCEPT , NULL );
+    gtk_window_set_position(GTK_WINDOW(searchdlg_win), GTK_WIN_POS_CENTER);
+
+    /* help text and logo */
+    searchdlg_hbox = gtk_hbox_new( FALSE , 4 );
+    searchdlg_logo = gtk_image_new_from_stock( GTK_STOCK_FIND , GTK_ICON_SIZE_DIALOG );
+    searchdlg_helptext = gtk_label_new( _("Select entries in playlist by filling one or more "
+      "fields. Fields use regular expressions syntax, case-insensitive. If you don't know how "
+      "regular expressions work, simply insert a literal portion of what you're searching for.") );
+    gtk_label_set_line_wrap( GTK_LABEL(searchdlg_helptext) , TRUE );
+    gtk_box_pack_start( GTK_BOX(searchdlg_hbox) , searchdlg_logo , FALSE , FALSE , 0 );
+    gtk_box_pack_start( GTK_BOX(searchdlg_hbox) , searchdlg_helptext , FALSE , FALSE , 0 );
+
+    /* title */
+    searchdlg_label_title = gtk_label_new( _("Title: ") );
+    searchdlg_entry_title = gtk_entry_new();
+    gtk_misc_set_alignment( GTK_MISC(searchdlg_label_title) , 0 , 0.5 );
+    g_signal_connect( G_OBJECT(searchdlg_entry_title) , "key-press-event" ,
+      G_CALLBACK(playlistwin_select_search_kp_cb) , searchdlg_win );
+
+    /* album */
+    searchdlg_label_album= gtk_label_new( _("Album: ") );
+    searchdlg_entry_album= gtk_entry_new();
+    gtk_misc_set_alignment( GTK_MISC(searchdlg_label_album) , 0 , 0.5 );
+    g_signal_connect( G_OBJECT(searchdlg_entry_album) , "key-press-event" ,
+      G_CALLBACK(playlistwin_select_search_kp_cb) , searchdlg_win );
+
+    /* artist */
+    searchdlg_label_performer = gtk_label_new( _("Artist: ") );
+    searchdlg_entry_performer = gtk_entry_new();
+    gtk_misc_set_alignment( GTK_MISC(searchdlg_label_performer) , 0 , 0.5 );
+    g_signal_connect( G_OBJECT(searchdlg_entry_performer) , "key-press-event" ,
+      G_CALLBACK(playlistwin_select_search_kp_cb) , searchdlg_win );
+
+    /* file name */
+    searchdlg_label_file_name = gtk_label_new( _("Filename: ") );
+    searchdlg_entry_file_name = gtk_entry_new();
+    gtk_misc_set_alignment( GTK_MISC(searchdlg_label_file_name) , 0 , 0.5 );
+    g_signal_connect( G_OBJECT(searchdlg_entry_file_name) , "key-press-event" ,
+      G_CALLBACK(playlistwin_select_search_kp_cb) , searchdlg_win );
+
+    /* some options that control behaviour */
+    searchdlg_checkbt_clearprevsel = gtk_check_button_new_with_label(
+      _("Clear previous selection before searching") );
+    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(searchdlg_checkbt_clearprevsel) , TRUE );
+    searchdlg_checkbt_autoenqueue = gtk_check_button_new_with_label(
+      _("Automatically toggle queue for matching entries") );
+    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(searchdlg_checkbt_autoenqueue) , FALSE );
+    searchdlg_checkbt_newplaylist = gtk_check_button_new_with_label(
+      _("Create a new playlist with matching entries") );
+    gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(searchdlg_checkbt_newplaylist) , FALSE );
+    g_signal_connect( G_OBJECT(searchdlg_checkbt_autoenqueue) , "clicked" ,
+      G_CALLBACK(playlistwin_select_search_cbt_cb) , searchdlg_checkbt_newplaylist );
+    g_signal_connect( G_OBJECT(searchdlg_checkbt_newplaylist) , "clicked" ,
+      G_CALLBACK(playlistwin_select_search_cbt_cb) , searchdlg_checkbt_autoenqueue );
+
+    /* place fields in searchdlg_table */
+    searchdlg_table = gtk_table_new( 8 , 2 , FALSE );
+    gtk_table_set_row_spacing( GTK_TABLE(searchdlg_table) , 0 , 8 );
+    gtk_table_set_row_spacing( GTK_TABLE(searchdlg_table) , 4 , 8 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_hbox ,
+      0 , 2 , 0 , 1 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_label_title ,
+      0 , 1 , 1 , 2 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_entry_title ,
+      1 , 2 , 1 , 2 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_label_album,
+      0 , 1 , 2 , 3 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_entry_album,
+      1 , 2 , 2 , 3 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_label_performer ,
+      0 , 1 , 3 , 4 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_entry_performer ,
+      1 , 2 , 3 , 4 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_label_file_name ,
+      0 , 1 , 4 , 5 , GTK_FILL , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_entry_file_name ,
+      1 , 2 , 4 , 5 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 2 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_checkbt_clearprevsel ,
+      0 , 2 , 5 , 6 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 1 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_checkbt_autoenqueue ,
+      0 , 2 , 6 , 7 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 1 );
+    gtk_table_attach( GTK_TABLE(searchdlg_table) , searchdlg_checkbt_newplaylist ,
+      0 , 2 , 7 , 8 , GTK_FILL | GTK_EXPAND , GTK_FILL | GTK_EXPAND , 0 , 1 );
+
+    gtk_container_set_border_width( GTK_CONTAINER(searchdlg_table) , 5 );
+    gtk_container_add( GTK_CONTAINER(GTK_DIALOG(searchdlg_win)->vbox) , searchdlg_table );
+    gtk_widget_show_all( searchdlg_win );
+    result = gtk_dialog_run( GTK_DIALOG(searchdlg_win) );
+    switch(result)
+    {
+      case GTK_RESPONSE_ACCEPT:
+      {
+         gint matched_entries_num = 0;
+         /* create a TitleInput tuple with user search data */
+         Tuple *tuple = aud_tuple_new();
+         gchar *searchdata = NULL;
+
+         searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_title) );
+         AUDDBG("title=\"%s\"\n", searchdata);
+         aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, searchdata);
+
+         searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_album) );
+         AUDDBG("album=\"%s\"\n", searchdata);
+         aud_tuple_associate_string(tuple, FIELD_ALBUM, NULL, searchdata);
+
+         searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_performer) );
+         AUDDBG("performer=\"%s\"\n", searchdata);
+         aud_tuple_associate_string(tuple, FIELD_ARTIST, NULL, searchdata);
+
+         searchdata = (gchar*)gtk_entry_get_text( GTK_ENTRY(searchdlg_entry_file_name) );
+         AUDDBG("filename=\"%s\"\n", searchdata);
+         aud_tuple_associate_string(tuple, FIELD_FILE_NAME, NULL, searchdata);
+
+         /* check if previous selection should be cleared before searching */
+         if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(searchdlg_checkbt_clearprevsel)) == TRUE )
+             playlistwin_select_none();
+         /* now send this tuple to the real search function */
+         matched_entries_num = aud_playlist_select_search( playlist , tuple , 0 );
+         /* we do not need the tuple and its data anymore */
+         mowgli_object_unref(tuple);
+         playlistwin_update_list(aud_playlist_get_active());
+         /* check if a new playlist should be created after searching */
+         if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(searchdlg_checkbt_newplaylist)) == TRUE )
+             aud_playlist_new_from_selected();
+         /* check if matched entries should be queued */
+         else if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(searchdlg_checkbt_autoenqueue)) == TRUE )
+             aud_playlist_queue(aud_playlist_get_active());
+         break;
+      }
+      default:
+         break;
+    }
+    /* done here :) */
+    gtk_widget_destroy( searchdlg_win );
+}
+
+static void
+playlistwin_inverse_selection(void)
+{
+    aud_playlist_select_invert_all(aud_playlist_get_active());
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list)) {
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected = -1;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->prev_min = -1;
+    }
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+static void
+playlistwin_resize(gint width, gint height)
+{
+    gint tx, ty;
+    gint dx, dy;
+
+    g_return_if_fail(width > 0 && height > 0);
+
+    tx = (width - PLAYLISTWIN_MIN_WIDTH) / PLAYLISTWIN_WIDTH_SNAP;
+    tx = (tx * PLAYLISTWIN_WIDTH_SNAP) + PLAYLISTWIN_MIN_WIDTH;
+    if (tx < PLAYLISTWIN_MIN_WIDTH)
+        tx = PLAYLISTWIN_MIN_WIDTH;
+
+    if (!config.playlist_shaded)
+    {
+        ty = (height - PLAYLISTWIN_MIN_HEIGHT) / PLAYLISTWIN_HEIGHT_SNAP;
+        ty = (ty * PLAYLISTWIN_HEIGHT_SNAP) + PLAYLISTWIN_MIN_HEIGHT;
+        if (ty < PLAYLISTWIN_MIN_HEIGHT)
+            ty = PLAYLISTWIN_MIN_HEIGHT;
+    }
+    else
+        ty = config.playlist_height;
+
+    if (tx == config.playlist_width && ty == config.playlist_height)
+        return;
+
+    /* difference between previous size and new size */
+    dx = tx - config.playlist_width;
+    dy = ty - config.playlist_height;
+
+    config.playlist_width = width = tx;
+    config.playlist_height = height = ty;
+
+    g_mutex_lock(resize_mutex);
+    ui_skinned_playlist_resize_relative(playlistwin_list, dx, dy);
+
+    ui_skinned_playlist_slider_move_relative(playlistwin_slider, dx);
+    ui_skinned_playlist_slider_resize_relative(playlistwin_slider, dy);
+
+    playlistwin_update_sinfo(aud_playlist_get_active());
+
+    ui_skinned_button_move_relative(playlistwin_shade, dx, 0);
+    ui_skinned_button_move_relative(playlistwin_close, dx, 0);
+    ui_skinned_textbox_move_relative(playlistwin_time_min, dx, dy);
+    ui_skinned_textbox_move_relative(playlistwin_time_sec, dx, dy);
+    ui_skinned_textbox_move_relative(playlistwin_info, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_srew, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_splay, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_spause, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_sstop, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_sfwd, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_seject, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_sscroll_up, dx, dy);
+    ui_skinned_button_move_relative(playlistwin_sscroll_down, dx, dy);
+
+    gtk_widget_set_size_request(playlistwin_sinfo, playlistwin_get_width() - 35,
+                                aud_active_skin->properties.textbox_bitmap_font_height);
+    GList *iter;
+    for (iter = GTK_FIXED (SKINNED_WINDOW(playlistwin)->fixed)->children; iter; iter = g_list_next (iter)) {
+         GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+         GtkWidget *child = child_data->widget;
+         g_signal_emit_by_name(child, "redraw");
+    }
+    g_mutex_unlock(resize_mutex);
+}
+
+static void
+playlistwin_motion(GtkWidget * widget,
+                   GdkEventMotion * event,
+                   gpointer callback_data)
+{
+    GdkEvent *gevent;
+
+    /*
+     * GDK2's resize is broken and doesn't really play nice, so we have
+     * to do all of this stuff by hand.
+     */
+    if (playlistwin_resizing == TRUE)
+    {
+        if (event->x + playlistwin_resize_x != playlistwin_get_width() ||
+            event->y + playlistwin_resize_y != playlistwin_get_height())
+        {
+            playlistwin_resize(event->x + playlistwin_resize_x,
+                               event->y + playlistwin_resize_y);
+            gdk_window_resize(playlistwin->window,
+                              config.playlist_width, playlistwin_get_height());
+            gdk_flush();
+        }
+    }
+    else if (dock_is_moving(GTK_WINDOW(playlistwin)))
+        dock_move_motion(GTK_WINDOW(playlistwin), event);
+
+    while ((gevent = gdk_event_get()) != NULL) gdk_event_free(gevent);
+}
+
+static void
+playlistwin_show_filebrowser(void)
+{
+#if 0
+    run_filebrowser(NO_PLAY_BUTTON);
+#endif
+}
+
+static void
+playlistwin_fileinfo(void)
+{
+#if 0
+    Playlist *playlist = aud_playlist_get_active();
+
+    /* Show the first selected file, or the current file if nothing is
+     * selected */
+    GList *list = aud_playlist_get_selected(playlist);
+    if (list) {
+        ui_fileinfo_show(playlist, GPOINTER_TO_INT(list->data));
+        g_list_free(list);
+    }
+    else
+
+  ui_fileinfo_show_current(playlist);
+#endif
+}
+
+static void
+show_playlist_save_error(GtkWindow *parent,
+                         const gchar *filename)
+{
+    GtkWidget *dialog;
+
+    g_return_if_fail(GTK_IS_WINDOW(parent));
+    g_return_if_fail(filename);
+
+    dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_ERROR,
+                                    GTK_BUTTONS_OK,
+                                    _("Error writing playlist \"%s\": %s"),
+                                    filename, strerror(errno));
+
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+}
+
+static gboolean
+show_playlist_overwrite_prompt(GtkWindow * parent,
+                               const gchar * filename)
+{
+    GtkWidget *dialog;
+    gint result;
+
+    g_return_val_if_fail(GTK_IS_WINDOW(parent), FALSE);
+    g_return_val_if_fail(filename != NULL, FALSE);
+
+    dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_QUESTION,
+                                    GTK_BUTTONS_YES_NO,
+                                    _("%s already exist. Continue?"),
+                                    filename);
+
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
+    result = gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+
+    return (result == GTK_RESPONSE_YES);
+}
+
+static void
+show_playlist_save_format_error(GtkWindow * parent,
+                                const gchar * filename)
+{
+    const gchar *markup =
+        N_("<b><big>Unable to save playlist.</big></b>\n\n"
+           "Unknown file type for '%s'.\n");
+
+    GtkWidget *dialog;
+
+    g_return_if_fail(GTK_IS_WINDOW(parent));
+    g_return_if_fail(filename != NULL);
+
+    dialog =
+        gtk_message_dialog_new_with_markup(GTK_WINDOW(parent),
+                                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                                           GTK_MESSAGE_ERROR,
+                                           GTK_BUTTONS_OK,
+                                           _(markup),
+                                           filename);
+
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+}
+
+static void
+playlistwin_save_playlist(const gchar * filename)
+{
+    PlaylistContainer *plc;
+    gchar *ext = strrchr(filename, '.') + 1;
+
+    plc = aud_playlist_container_find(ext);
+    if (plc == NULL) {
+        show_playlist_save_format_error(GTK_WINDOW(playlistwin), filename);
+        return;
+    }
+
+    aud_str_replace_in(&aud_cfg->playlist_path, g_path_get_dirname(filename));
+
+    if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+        if (!show_playlist_overwrite_prompt(GTK_WINDOW(playlistwin), filename))
+            return;
+
+    if (!aud_playlist_save(aud_playlist_get_active(), filename))
+        show_playlist_save_error(GTK_WINDOW(playlistwin), filename);
+}
+
+static void
+playlistwin_load_playlist(const gchar * filename)
+{
+    const gchar *title;
+    Playlist *playlist = aud_playlist_get_active();
+
+    g_return_if_fail(filename != NULL);
+
+    aud_str_replace_in(&aud_cfg->playlist_path, g_path_get_dirname(filename));
+
+    aud_playlist_clear(playlist);
+    mainwin_clear_song_info();
+
+    aud_playlist_load(playlist, filename);
+    title = aud_playlist_get_current_name(playlist);
+    if(!title || !title[0])
+        aud_playlist_set_current_name(playlist, filename);
+}
+
+static gchar *
+playlist_file_selection_load(const gchar * title,
+                        const gchar * default_filename)
+{
+    GtkWidget *dialog;
+    gchar *filename;
+
+    g_return_val_if_fail(title != NULL, NULL);
+
+    dialog = make_filebrowser(title, FALSE);
+    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), aud_cfg->playlist_path);
+    if (default_filename)
+        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), default_filename);
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
+
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+    else
+        filename = NULL;
+
+    gtk_widget_destroy(dialog);
+    return filename;
+}
+
+static void
+on_static_toggle(GtkToggleButton *button, gpointer data)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    playlist->attribute =
+        gtk_toggle_button_get_active(button) ?
+        playlist->attribute | PLAYLIST_STATIC :
+        playlist->attribute & ~PLAYLIST_STATIC;
+}
+
+static void
+on_relative_toggle(GtkToggleButton *button, gpointer data)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    playlist->attribute =
+        gtk_toggle_button_get_active(button) ?
+        playlist->attribute | PLAYLIST_USE_RELATIVE :
+        playlist->attribute & ~PLAYLIST_USE_RELATIVE;
+}
+
+static gchar *
+playlist_file_selection_save(const gchar * title,
+                        const gchar * default_filename)
+{
+    GtkWidget *dialog;
+    gchar *filename;
+    GtkWidget *hbox;
+    GtkWidget *toggle, *toggle2;
+
+    g_return_val_if_fail(title != NULL, NULL);
+
+    dialog = make_filebrowser(title, TRUE);
+    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), aud_cfg->playlist_path);
+    gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), default_filename);
+
+    hbox = gtk_hbox_new(FALSE, 5);
+
+    /* static playlist */
+    toggle = gtk_check_button_new_with_label(_("Save as Static Playlist"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle),
+                                 (aud_playlist_get_active()->attribute & PLAYLIST_STATIC) ? TRUE : FALSE);
+    g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(on_static_toggle), dialog);
+    gtk_box_pack_start(GTK_BOX(hbox), toggle, FALSE, FALSE, 0);
+
+    /* use relative path */
+    toggle2 = gtk_check_button_new_with_label(_("Use Relative Path"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle2),
+                                 (aud_playlist_get_active()->attribute & PLAYLIST_USE_RELATIVE) ? TRUE : FALSE);
+    g_signal_connect(G_OBJECT(toggle2), "toggled", G_CALLBACK(on_relative_toggle), dialog);
+    gtk_box_pack_start(GTK_BOX(hbox), toggle2, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(hbox);
+    gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), hbox);
+
+    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
+        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
+    else
+        filename = NULL;
+
+    gtk_widget_destroy(dialog);
+    return filename;
+}
+
+void
+playlistwin_select_playlist_to_load(const gchar * default_filename)
+{
+    gchar *filename =
+        playlist_file_selection_load(_("Load Playlist"), default_filename);
+
+    if (filename) {
+        playlistwin_load_playlist(filename);
+        g_free(filename);
+    }
+}
+
+static void
+playlistwin_select_playlist_to_save(const gchar * default_filename)
+{
+    gchar *dot = NULL, *basename = NULL;
+    gchar *filename =
+        playlist_file_selection_save(_("Save Playlist"), default_filename);
+
+    if (filename) {
+        /* Default extension */
+        basename = g_path_get_basename(filename);
+        dot = strrchr(basename, '.');
+        if( dot == NULL || dot == basename) {
+            gchar *oldname = filename;
+#ifdef HAVE_XSPF_PLAYLIST
+            filename = g_strconcat(oldname, ".xspf", NULL);
+#else
+            filename = g_strconcat(oldname, ".m3u", NULL);
+#endif
+            g_free(oldname);
+        }
+        g_free(basename);
+
+        playlistwin_save_playlist(filename);
+        g_free(filename);
+    }
+}
+
+#define REGION_L(x1,x2,y1,y2)                   \
+    (event->x >= (x1) && event->x < (x2) &&     \
+     event->y >= config.playlist_height - (y1) &&  \
+     event->y < config.playlist_height - (y2))
+
+#define REGION_R(x1,x2,y1,y2)                      \
+    (event->x >= playlistwin_get_width() - (x1) && \
+     event->x < playlistwin_get_width() - (x2) &&  \
+     event->y >= config.playlist_height - (y1) &&     \
+     event->y < config.playlist_height - (y2))
+
+static void
+playlistwin_scrolled(GtkWidget * widget,
+                     GdkEventScroll * event,
+                     gpointer callback_data)
+{
+    if (event->direction == GDK_SCROLL_DOWN)
+        playlistwin_scroll(config.scroll_pl_by);
+
+    if (event->direction == GDK_SCROLL_UP)
+        playlistwin_scroll(-config.scroll_pl_by);
+#if 0
+    g_cond_signal(cond_scan);
+#endif
+}
+
+static gboolean
+playlistwin_press(GtkWidget * widget,
+                  GdkEventButton * event,
+                  gpointer callback_data)
+{
+    gint xpos, ypos;
+    GtkRequisition req;
+
+    gtk_window_get_position(GTK_WINDOW(playlistwin), &xpos, &ypos);
+
+    if (event->button == 1 && !config.show_wm_decorations &&
+        ((!config.playlist_shaded &&
+          event->x > playlistwin_get_width() - 20 &&
+          event->y > config.playlist_height - 20) ||
+         (config.playlist_shaded &&
+          event->x >= playlistwin_get_width() - 31 &&
+          event->x < playlistwin_get_width() - 22))) {
+
+        if (event->type != GDK_2BUTTON_PRESS &&
+            event->type != GDK_3BUTTON_PRESS) {
+            playlistwin_resizing = TRUE;
+            playlistwin_resize_x = config.playlist_width - event->x;
+            playlistwin_resize_y = config.playlist_height - event->y;
+        }
+    }
+    else if (event->button == 1 && REGION_L(12, 37, 29, 11)) {
+        /* ADD button menu */
+        gtk_widget_size_request(playlistwin_pladd_menu, &req);
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_pladd_menu),
+                   xpos + 12,
+                   (ypos + playlistwin_get_height()) - 8 - req.height,
+                   event->button,
+                   event->time);
+    }
+    else if (event->button == 1 && REGION_L(41, 66, 29, 11)) {
+        /* SUB button menu */
+        gtk_widget_size_request(playlistwin_pldel_menu, &req);
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_pldel_menu),
+                   xpos + 40,
+                   (ypos + playlistwin_get_height()) - 8 - req.height,
+                   event->button,
+                   event->time);
+    }
+    else if (event->button == 1 && REGION_L(70, 95, 29, 11)) {
+        /* SEL button menu */
+        gtk_widget_size_request(playlistwin_plsel_menu, &req);
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_plsel_menu),
+                   xpos + 68,
+                   (ypos + playlistwin_get_height()) - 8 - req.height,
+                   event->button,
+                   event->time);
+    }
+    else if (event->button == 1 && REGION_L(99, 124, 29, 11)) {
+        /* MISC button menu */
+        gtk_widget_size_request(playlistwin_plsort_menu, &req);
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_plsort_menu),
+                   xpos + 100,
+                   (ypos + playlistwin_get_height()) - 8 - req.height,
+                   event->button,
+                   event->time);
+    }
+    else if (event->button == 1 && REGION_R(46, 23, 29, 11)) {
+        /* LIST button menu */
+        gtk_widget_size_request(playlistwin_pllist_menu, &req);
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_pllist_menu),
+                   xpos + playlistwin_get_width() - req.width - 12,
+                   (ypos + playlistwin_get_height()) - 8 - req.height,
+                   event->button,
+                   event->time);
+    }
+    else if (event->button == 1 && event->type == GDK_BUTTON_PRESS &&
+             (config.easy_move || event->y < 14))
+    {
+        return FALSE;
+    }
+    else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS
+             && event->y < 14) {
+        /* double click on title bar */
+        playlistwin_shade_toggle();
+        if (dock_is_moving(GTK_WINDOW(playlistwin)))
+            dock_move_release(GTK_WINDOW(playlistwin));
+        return TRUE;
+    }
+    else if (event->button == 3) {
+        /*
+         * Pop up the main menu a few pixels down to avoid
+         * anything to be selected initially.
+         */
+        ui_manager_popup_menu_show(GTK_MENU(mainwin_general_menu), event->x_root,
+                                event->y_root + 2, 3, event->time);
+    }
+
+    return TRUE;
+}
+
+static gboolean
+playlistwin_delete(GtkWidget * w, gpointer data)
+{
+    playlistwin_hide();
+    return TRUE;
+}
+
+static gboolean
+playlistwin_keypress_up_down_handler(UiSkinnedPlaylist * pl,
+                                     gboolean up, guint state)
+{
+    Playlist *playlist = aud_playlist_get_active();
+    if ((!(pl->prev_selected || pl->first) && up) ||
+       ((pl->prev_selected >= aud_playlist_get_length(playlist) - 1) && !up))
+         return FALSE;
+
+    if ((state & GDK_MOD1_MASK) && (state & GDK_SHIFT_MASK))
+        return FALSE;
+    if (!(state & GDK_MOD1_MASK))
+        aud_playlist_select_all(playlist, FALSE);
+
+    if (pl->prev_selected == -1 ||
+        (!playlistwin_item_visible(pl->prev_selected) &&
+         !(state & GDK_SHIFT_MASK && pl->prev_min != -1))) {
+        pl->prev_selected = pl->first;
+    }
+    else if (state & GDK_SHIFT_MASK) {
+        if (pl->prev_min == -1) {
+            pl->prev_max = pl->prev_selected;
+            pl->prev_min = pl->prev_selected;
+        }
+        pl->prev_max += (up ? -1 : 1);
+        pl->prev_max =
+            CLAMP(pl->prev_max, 0, aud_playlist_get_length(playlist) - 1);
+
+        pl->first = MIN(pl->first, pl->prev_max);
+        pl->first = MAX(pl->first, pl->prev_max -
+                           pl->num_visible + 1);
+        aud_playlist_select_range(playlist, pl->prev_min, pl->prev_max, TRUE);
+        return TRUE;
+    }
+    else if (state & GDK_MOD1_MASK) {
+        if (up)
+            ui_skinned_playlist_move_up(pl);
+        else
+            ui_skinned_playlist_move_down(pl);
+        if (pl->prev_min < pl->first)
+            pl->first = pl->prev_min;
+        else if (pl->prev_max >= (pl->first + pl->num_visible))
+            pl->first = pl->prev_max - pl->num_visible + 1;
+        return TRUE;
+    }
+    else if (up)
+        pl->prev_selected--;
+    else
+        pl->prev_selected++;
+
+    pl->prev_selected =
+        CLAMP(pl->prev_selected, 0, aud_playlist_get_length(playlist) - 1);
+
+    if (pl->prev_selected < pl->first)
+        pl->first--;
+    else if (pl->prev_selected >= (pl->first + pl->num_visible))
+        pl->first++;
+
+    aud_playlist_select_range(playlist, pl->prev_selected, pl->prev_selected, TRUE);
+    pl->prev_min = -1;
+
+    return TRUE;
+}
+
+/* FIXME: Handle the keys through menu */
+
+static gboolean
+playlistwin_keypress(GtkWidget * w, GdkEventKey * event, gpointer data)
+{
+    g_return_val_if_fail(UI_SKINNED_IS_PLAYLIST(playlistwin_list), FALSE);
+    Playlist *playlist = aud_playlist_get_active();
+
+    guint keyval;
+    gboolean refresh = FALSE;
+    guint cur_pos;
+
+    if (config.playlist_shaded)
+        return FALSE;
+
+    switch (keyval = event->keyval) {
+    case GDK_KP_Up:
+    case GDK_KP_Down:
+    case GDK_Up:
+    case GDK_Down:
+        refresh = playlistwin_keypress_up_down_handler(UI_SKINNED_PLAYLIST(playlistwin_list),
+                                                       keyval == GDK_Up
+                                                       || keyval == GDK_KP_Up,
+                                                       event->state);
+        break;
+    case GDK_Page_Up:
+        playlistwin_scroll(-UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible);
+        refresh = TRUE;
+        break;
+    case GDK_Page_Down:
+        playlistwin_scroll(UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible);
+        refresh = TRUE;
+        break;
+    case GDK_Home:
+        UI_SKINNED_PLAYLIST(playlistwin_list)->first = 0;
+        refresh = TRUE;
+        break;
+    case GDK_End:
+        UI_SKINNED_PLAYLIST(playlistwin_list)->first =
+            aud_playlist_get_length(playlist) - UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible;
+        refresh = TRUE;
+        break;
+    case GDK_Return:
+        if (UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected > -1
+            && playlistwin_item_visible(UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected)) {
+            aud_playlist_set_position(playlist, UI_SKINNED_PLAYLIST(playlistwin_list)->prev_selected);
+            if (!audacious_drct_get_playing())
+                audacious_drct_initiate();
+        }
+        refresh = TRUE;
+        break;
+    case GDK_3:
+        if (event->state & GDK_CONTROL_MASK)
+            playlistwin_fileinfo();
+        break;
+    case GDK_Delete:
+        if (event->state & GDK_CONTROL_MASK)
+            aud_playlist_delete(playlist, TRUE);
+        else
+            aud_playlist_delete(playlist, FALSE);
+        break;
+    case GDK_Insert:
+        if (event->state & GDK_MOD1_MASK)
+            mainwin_show_add_url_window();
+        else
+            playlistwin_show_filebrowser();
+        break;
+    case GDK_Left:
+    case GDK_KP_Left:
+    case GDK_KP_7:
+        if (aud_playlist_get_current_length(playlist) != -1)
+            audacious_drct_seek(CLAMP
+                              (audacious_drct_get_time() - 5000, 0,
+                              aud_playlist_get_current_length(playlist)));
+        break;
+    case GDK_Right:
+    case GDK_KP_Right:
+    case GDK_KP_9:
+        if (aud_playlist_get_current_length(playlist) != -1)
+            audacious_drct_seek(CLAMP
+                              (audacious_drct_get_time() + 5000, 0,
+                              aud_playlist_get_current_length(playlist)));
+        break;
+    case GDK_KP_4:
+        aud_playlist_prev(playlist);
+        break;
+    case GDK_KP_6:
+        aud_playlist_next(playlist);
+        break;
+
+    case GDK_Escape:
+        mainwin_minimize_cb();
+        break;
+    case GDK_Tab:
+        if (event->state & GDK_CONTROL_MASK) {
+            if (config.player_visible)
+                gtk_window_present(GTK_WINDOW(mainwin));
+            else if (config.equalizer_visible)
+                gtk_window_present(GTK_WINDOW(equalizerwin));
+        }
+        break;
+    case GDK_space:
+        cur_pos=aud_playlist_get_position(playlist);
+        UI_SKINNED_PLAYLIST(playlistwin_list)->first =
+            cur_pos - (UI_SKINNED_PLAYLIST(playlistwin_list)->num_visible >> 1);
+        refresh = TRUE;
+        break;
+    default:
+        return FALSE;
+    }
+#if 0
+    if (refresh) {
+        g_cond_signal(cond_scan);
+        playlistwin_update_list(aud_playlist_get_active());
+    }
+#endif
+    return TRUE;
+}
+
+void
+playlistwin_hide_timer(void)
+{
+    ui_skinned_textbox_set_text(playlistwin_time_min, "   ");
+    ui_skinned_textbox_set_text(playlistwin_time_sec, "  ");
+}
+
+void
+playlistwin_set_time(gint time, gint length, TimerMode mode)
+{
+    gchar *text, sign;
+
+    if (mode == TIMER_REMAINING && length != -1) {
+        time = length - time;
+        sign = '-';
+    }
+    else
+        sign = ' ';
+
+    time /= 1000;
+
+    if (time < 0)
+        time = 0;
+    if (time > 99 * 60)
+        time /= 60;
+
+    text = g_strdup_printf("%c%-2.2d", sign, time / 60);
+    ui_skinned_textbox_set_text(playlistwin_time_min, text);
+    g_free(text);
+
+    text = g_strdup_printf("%-2.2d", time % 60);
+    ui_skinned_textbox_set_text(playlistwin_time_sec, text);
+    g_free(text);
+}
+
+static void
+playlistwin_drag_motion(GtkWidget * widget,
+                        GdkDragContext * context,
+                        gint x, gint y,
+                        GtkSelectionData * selection_data,
+                        guint info, guint time, gpointer user_data)
+{
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list)) {
+        UI_SKINNED_PLAYLIST(playlistwin_list)->drag_motion = TRUE;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->drag_motion_x = x;
+        UI_SKINNED_PLAYLIST(playlistwin_list)->drag_motion_y = y;
+    }
+    playlistwin_update_list(aud_playlist_get_active());
+    playlistwin_hint_flag = TRUE;
+}
+
+static void
+playlistwin_drag_end(GtkWidget * widget,
+                     GdkDragContext * context, gpointer user_data)
+{
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list))
+        UI_SKINNED_PLAYLIST(playlistwin_list)->drag_motion = FALSE;
+    playlistwin_hint_flag = FALSE;
+    playlistwin_update_list(aud_playlist_get_active());
+}
+
+static void
+playlistwin_drag_data_received(GtkWidget * widget,
+                               GdkDragContext * context,
+                               gint x, gint y,
+                               GtkSelectionData *
+                               selection_data, guint info,
+                               guint time, gpointer user_data)
+{
+    gint pos;
+    Playlist *playlist = aud_playlist_get_active();
+
+    g_return_if_fail(selection_data);
+
+    if (!selection_data->data) {
+        g_message("Received no DND data!");
+        return;
+    }
+    if (UI_SKINNED_IS_PLAYLIST(playlistwin_list) &&
+        (x < playlistwin_get_width() - 20 || y < config.playlist_height - 38)) {
+        pos = y / UI_SKINNED_PLAYLIST(playlistwin_list)->fheight + UI_SKINNED_PLAYLIST(playlistwin_list)->first;
+
+        pos = MIN(pos, aud_playlist_get_length(playlist));
+        aud_playlist_ins_url(playlist, (gchar *) selection_data->data, pos);
+    }
+    else
+        aud_playlist_add_url(playlist, (gchar *) selection_data->data);
+}
+
+static void
+local_playlist_prev(void)
+{
+    aud_playlist_prev(aud_playlist_get_active());
+}
+
+static void
+local_playlist_next(void)
+{
+    aud_playlist_next(aud_playlist_get_active());
+}
+
+static void
+playlistwin_create_widgets(void)
+{
+    /* This function creates the custom widgets used by the playlist editor */
+
+    /* text box for displaying song title in shaded mode */
+    playlistwin_sinfo = ui_skinned_textbox_new(SKINNED_WINDOW(playlistwin)->fixed,
+                                               4, 4, playlistwin_get_width() - 35, TRUE, SKIN_TEXT);
+
+    playlistwin_set_sinfo_font(aud_cfg->playlist_font);
+
+    playlistwin_shade = ui_skinned_button_new();
+    /* shade/unshade window push button */
+    if (config.playlist_shaded)
+        ui_skinned_push_button_setup(playlistwin_shade, SKINNED_WINDOW(playlistwin)->fixed,
+                                     playlistwin_get_width() - 21, 3,
+                                     9, 9, 128, 45, 150, 42, SKIN_PLEDIT);
+    else
+        ui_skinned_push_button_setup(playlistwin_shade, SKINNED_WINDOW(playlistwin)->fixed,
+                                     playlistwin_get_width() - 21, 3,
+                                     9, 9, 157, 3, 62, 42, SKIN_PLEDIT);
+
+    g_signal_connect(playlistwin_shade, "clicked", playlistwin_shade_toggle, NULL );
+
+    /* close window push button */
+    playlistwin_close = ui_skinned_button_new();
+    ui_skinned_push_button_setup(playlistwin_close, SKINNED_WINDOW(playlistwin)->fixed,
+                                 playlistwin_get_width() - 11, 3, 9, 9,
+                                 config.playlist_shaded ? 138 : 167,
+                                 config.playlist_shaded ? 45 : 3, 52, 42, SKIN_PLEDIT);
+
+    g_signal_connect(playlistwin_close, "clicked", playlistwin_hide, NULL );
+
+    /* playlist list box */
+    playlistwin_list = ui_skinned_playlist_new(SKINNED_WINDOW(playlistwin)->fixed, 12, 20,
+                             playlistwin_get_width() - 31,
+                             config.playlist_height - 58);
+    ui_skinned_playlist_set_font(aud_cfg->playlist_font);
+
+    /* playlist list box slider */
+    playlistwin_slider = ui_skinned_playlist_slider_new(SKINNED_WINDOW(playlistwin)->fixed, playlistwin_get_width() - 15,
+                              20, config.playlist_height - 58);
+
+    /* track time (minute) */
+    playlistwin_time_min = ui_skinned_textbox_new(SKINNED_WINDOW(playlistwin)->fixed,
+                       playlistwin_get_width() - 82,
+                       config.playlist_height - 15, 15, FALSE, SKIN_TEXT);
+    g_signal_connect(playlistwin_time_min, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    /* track time (second) */
+    playlistwin_time_sec = ui_skinned_textbox_new(SKINNED_WINDOW(playlistwin)->fixed,
+                       playlistwin_get_width() - 64,
+                       config.playlist_height - 15, 10, FALSE, SKIN_TEXT);
+    g_signal_connect(playlistwin_time_sec, "button-press-event", G_CALLBACK(change_timer_mode_cb), NULL);
+
+    /* playlist information (current track length / total track length) */
+    playlistwin_info = ui_skinned_textbox_new(SKINNED_WINDOW(playlistwin)->fixed,
+                       playlistwin_get_width() - 143,
+                       config.playlist_height - 28, 90, FALSE, SKIN_TEXT);
+
+    /* mini play control buttons at right bottom corner */
+
+    /* rewind button */
+    playlistwin_srew = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_srew, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 144,
+                                  config.playlist_height - 16, 8, 7);
+    g_signal_connect(playlistwin_srew, "clicked", local_playlist_prev, NULL);
+
+    /* play button */
+    playlistwin_splay = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_splay, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 138,
+                                  config.playlist_height - 16, 10, 7);
+    g_signal_connect(playlistwin_splay, "clicked", mainwin_play_pushed, NULL);
+
+    /* pause button */
+    playlistwin_spause = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_spause, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 128,
+                                  config.playlist_height - 16, 10, 7);
+    g_signal_connect(playlistwin_spause, "clicked", audacious_drct_pause, NULL);
+
+    /* stop button */
+    playlistwin_sstop = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_sstop, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 118,
+                                  config.playlist_height - 16, 9, 7);
+    g_signal_connect(playlistwin_sstop, "clicked", mainwin_stop_pushed, NULL);
+
+    /* forward button */
+    playlistwin_sfwd = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_sfwd, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 109,
+                                  config.playlist_height - 16, 8, 7);
+    g_signal_connect(playlistwin_sfwd, "clicked", local_playlist_next, NULL);
+
+    /* eject button */
+    playlistwin_seject = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_seject, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 100,
+                                  config.playlist_height - 16, 9, 7);
+    g_signal_connect(playlistwin_seject, "clicked", mainwin_eject_pushed, NULL);
+
+    playlistwin_sscroll_up = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_sscroll_up, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 14,
+                                  config.playlist_height - 35, 8, 5);
+    g_signal_connect(playlistwin_sscroll_up, "clicked", playlistwin_scroll_up_pushed, NULL);
+
+    playlistwin_sscroll_down = ui_skinned_button_new();
+    ui_skinned_small_button_setup(playlistwin_sscroll_down, SKINNED_WINDOW(playlistwin)->fixed,
+                                  playlistwin_get_width() - 14,
+                                  config.playlist_height - 30, 8, 5);
+    g_signal_connect(playlistwin_sscroll_down, "clicked", playlistwin_scroll_down_pushed, NULL);
+
+    ui_playlist_evlistener_init();
+}
+
+static void
+selection_received(GtkWidget * widget,
+                   GtkSelectionData * selection_data, gpointer data)
+{
+    if (selection_data->type == GDK_SELECTION_TYPE_STRING &&
+        selection_data->length > 0)
+        aud_playlist_add_url(aud_playlist_get_active(), (gchar *) selection_data->data);
+}
+
+static void
+playlistwin_create_window(void)
+{
+    GdkPixbuf *icon;
+
+    playlistwin = ui_skinned_window_new("playlist");
+    gtk_window_set_title(GTK_WINDOW(playlistwin), _("Audacious Playlist Editor"));
+    gtk_window_set_role(GTK_WINDOW(playlistwin), "playlist");
+    gtk_window_set_default_size(GTK_WINDOW(playlistwin),
+                                playlistwin_get_width(),
+                                playlistwin_get_height());
+    gtk_window_set_resizable(GTK_WINDOW(playlistwin), TRUE);
+    playlistwin_set_geometry_hints(config.playlist_shaded);
+
+    gtk_window_set_transient_for(GTK_WINDOW(playlistwin),
+                                 GTK_WINDOW(mainwin));
+    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(playlistwin), TRUE);
+
+    icon = gdk_pixbuf_new_from_xpm_data((const gchar **) audacious_playlist_icon);
+    gtk_window_set_icon(GTK_WINDOW(playlistwin), icon);
+    g_object_unref(icon);
+
+    if (config.playlist_x != -1 && config.save_window_position)
+        gtk_window_move(GTK_WINDOW(playlistwin),
+                        config.playlist_x, config.playlist_y);
+
+    gtk_widget_add_events(playlistwin, GDK_POINTER_MOTION_MASK |
+                          GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK |
+                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                          GDK_SCROLL_MASK | GDK_VISIBILITY_NOTIFY_MASK);
+    gtk_widget_realize(playlistwin);
+
+    g_signal_connect(playlistwin, "delete_event",
+                     G_CALLBACK(playlistwin_delete), NULL);
+    g_signal_connect(playlistwin, "button_press_event",
+                     G_CALLBACK(playlistwin_press), NULL);
+    g_signal_connect(playlistwin, "button_release_event",
+                     G_CALLBACK(playlistwin_release), NULL);
+    g_signal_connect(playlistwin, "scroll_event",
+                     G_CALLBACK(playlistwin_scrolled), NULL);
+    g_signal_connect(playlistwin, "motion_notify_event",
+                     G_CALLBACK(playlistwin_motion), NULL);
+
+    aud_drag_dest_set(playlistwin);
+
+    /* DnD stuff */
+    g_signal_connect(playlistwin, "drag-leave",
+                     G_CALLBACK(playlistwin_drag_end), NULL);
+    g_signal_connect(playlistwin, "drag-data-delete",
+                     G_CALLBACK(playlistwin_drag_end), NULL);
+    g_signal_connect(playlistwin, "drag-end",
+                     G_CALLBACK(playlistwin_drag_end), NULL);
+    g_signal_connect(playlistwin, "drag-drop",
+                     G_CALLBACK(playlistwin_drag_end), NULL);
+    g_signal_connect(playlistwin, "drag-data-received",
+                     G_CALLBACK(playlistwin_drag_data_received), NULL);
+    g_signal_connect(playlistwin, "drag-motion",
+                     G_CALLBACK(playlistwin_drag_motion), NULL);
+
+    g_signal_connect(playlistwin, "key_press_event",
+                     G_CALLBACK(playlistwin_keypress), NULL);
+    g_signal_connect(playlistwin, "selection_received",
+                     G_CALLBACK(selection_received), NULL);
+}
+
+void
+playlistwin_create(void)
+{
+    resize_mutex = g_mutex_new();
+    playlistwin_create_window();
+
+    playlistwin_create_widgets();
+    playlistwin_update_info(aud_playlist_get_active());
+
+    gtk_window_add_accel_group(GTK_WINDOW(playlistwin), ui_manager_get_accel_group());
+}
+
+
+void
+playlistwin_show(void)
+{
+    gtk_window_move(GTK_WINDOW(playlistwin), config.playlist_x, config.playlist_y);
+    GtkAction *action = gtk_action_group_get_action(
+      toggleaction_group_others , "show playlist editor" );
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , TRUE );
+
+    config.playlist_visible = TRUE;
+    UI_SKINNED_BUTTON(mainwin_pl)->inside = TRUE;
+    gtk_widget_queue_draw(mainwin_pl);
+
+    playlistwin_set_toprow(0);
+    aud_playlist_check_pos_current(aud_playlist_get_active());
+
+    gtk_widget_show_all(playlistwin);
+    if (!config.playlist_shaded)
+        gtk_widget_hide(playlistwin_sinfo);
+    gtk_window_present(GTK_WINDOW(playlistwin));
+}
+
+void
+playlistwin_hide(void)
+{
+    GtkAction *action = gtk_action_group_get_action(
+      toggleaction_group_others , "show playlist editor" );
+    gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(action) , FALSE );
+
+    gtk_widget_hide(playlistwin);
+    config.playlist_visible = FALSE;
+    UI_SKINNED_BUTTON(mainwin_pl)->inside = FALSE;
+    gtk_widget_queue_draw(mainwin_pl);
+
+    if ( config.player_visible )
+    {
+      gtk_window_present(GTK_WINDOW(mainwin));
+      gtk_widget_grab_focus(mainwin);
+    }
+}
+
+void action_playlist_track_info(void)
+{
+    playlistwin_fileinfo();
+}
+
+void action_queue_toggle(void)
+{
+    aud_playlist_queue(aud_playlist_get_active());
+}
+
+void action_playlist_sort_by_playlist_entry(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_PLAYLIST);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_track_number(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_TRACK);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_title(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_TITLE);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_artist(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_ARTIST);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_full_path(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_PATH);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_date(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_DATE);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_by_filename(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort(playlist, PLAYLIST_SORT_FILENAME);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_playlist_entry(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_PLAYLIST);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_track_number(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_TRACK);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_title(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_TITLE);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_artist(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_ARTIST);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_full_path(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_PATH);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_date(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_DATE);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_sort_selected_by_filename(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_sort_selected(playlist, PLAYLIST_SORT_FILENAME);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_randomize_list(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_random(playlist);
+    playlistwin_update_list(playlist);
+}
+
+void action_playlist_reverse_list(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_reverse(playlist);
+    playlistwin_update_list(playlist);
+}
+
+void
+action_playlist_clear_queue(void)
+{
+    aud_playlist_clear_queue(aud_playlist_get_active());
+}
+
+void
+action_playlist_remove_unavailable(void)
+{
+    aud_playlist_remove_dead_files(aud_playlist_get_active());
+}
+
+void
+action_playlist_remove_dupes_by_title(void)
+{
+    aud_playlist_remove_duplicates(aud_playlist_get_active(), PLAYLIST_DUPS_TITLE);
+}
+
+void
+action_playlist_remove_dupes_by_filename(void)
+{
+    aud_playlist_remove_duplicates(aud_playlist_get_active(), PLAYLIST_DUPS_FILENAME);
+}
+
+void
+action_playlist_remove_dupes_by_full_path(void)
+{
+    aud_playlist_remove_duplicates(aud_playlist_get_active(), PLAYLIST_DUPS_PATH);
+}
+
+void
+action_playlist_remove_all(void)
+{
+    aud_playlist_clear(aud_playlist_get_active());
+
+    /* XXX -- should this really be coupled here? -nenolod */
+    mainwin_clear_song_info();
+}
+
+void
+action_playlist_remove_selected(void)
+{
+    aud_playlist_delete(aud_playlist_get_active(), FALSE);
+}
+
+void
+action_playlist_remove_unselected(void)
+{
+    aud_playlist_delete(aud_playlist_get_active(), TRUE);
+}
+
+void
+action_playlist_add_files(void)
+{
+#if 0
+    run_filebrowser(NO_PLAY_BUTTON);
+#endif
+}
+
+void
+action_playlist_add_url(void)
+{
+    mainwin_show_add_url_window();
+}
+
+void
+action_playlist_new( void )
+{
+  Playlist *new_pl = aud_playlist_new();
+  aud_playlist_add_playlist(new_pl);
+  aud_playlist_select_playlist(new_pl);
+}
+
+void
+action_playlist_prev( void )
+{
+    aud_playlist_select_prev();
+}
+
+void
+action_playlist_next( void )
+{
+    aud_playlist_select_next();
+}
+
+void
+action_playlist_delete( void )
+{
+    aud_playlist_remove_playlist( aud_playlist_get_active() );
+}
+
+void
+action_playlist_save_list(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    playlistwin_select_playlist_to_save(aud_playlist_get_current_name(playlist));
+}
+
+void
+action_playlist_save_default_list(void)
+{
+#if 0
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_save(playlist, aud_paths[BMP_PATH_PLAYLIST_FILE]);
+#endif
+}
+
+void
+action_playlist_load_list(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    playlistwin_select_playlist_to_load(aud_playlist_get_current_name(playlist));
+}
+
+void
+action_playlist_refresh_list(void)
+{
+    Playlist *playlist = aud_playlist_get_active();
+
+    aud_playlist_read_info_selection(playlist);
+    playlistwin_update_list(playlist);
+}
+
+void
+action_open_list_manager(void)
+{
+#if 0
+    playlist_manager_ui_show();
+#endif
+}
+
+void
+action_playlist_search_and_select(void)
+{
+    playlistwin_select_search();
+}
+
+void
+action_playlist_invert_selection(void)
+{
+    playlistwin_inverse_selection();
+}
+
+void
+action_playlist_select_none(void)
+{
+    playlistwin_select_none();
+}
+
+void
+action_playlist_select_all(void)
+{
+    playlistwin_select_all();
+}
+
+
+static void
+playlistwin_select_search_cbt_cb(GtkWidget *called_cbt, gpointer other_cbt)
+{
+    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(called_cbt)) == TRUE)
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(other_cbt), FALSE);
+    return;
+}
+
+static gboolean
+playlistwin_select_search_kp_cb(GtkWidget *entry, GdkEventKey *event,
+                                gpointer searchdlg_win)
+{
+    switch (event->keyval)
+    {
+        case GDK_Return:
+            if (gtk_im_context_filter_keypress (GTK_ENTRY (entry)->im_context, event)) {
+                GTK_ENTRY (entry)->need_im_reset = TRUE;
+                return TRUE;
+            } else {
+                gtk_dialog_response(GTK_DIALOG(searchdlg_win), GTK_RESPONSE_ACCEPT);
+                return TRUE;
+            }
+        default:
+            return FALSE;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,81 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_PLAYLIST_H
+#define AUDACIOUS_UI_PLAYLIST_H
+
+#include <glib.h>
+
+#include "ui_main.h"
+#include <audacious/plugin.h>
+#include "skins_cfg.h"
+
+#define PLAYLISTWIN_FRAME_TOP_HEIGHT    20
+#define PLAYLISTWIN_FRAME_BOTTOM_HEIGHT 38
+#define PLAYLISTWIN_FRAME_LEFT_WIDTH    12
+#define PLAYLISTWIN_FRAME_RIGHT_WIDTH   19
+
+#define PLAYLISTWIN_MIN_WIDTH           MAINWIN_WIDTH
+#define PLAYLISTWIN_MIN_HEIGHT          MAINWIN_HEIGHT
+#define PLAYLISTWIN_WIDTH_SNAP          25
+#define PLAYLISTWIN_HEIGHT_SNAP         29
+#define PLAYLISTWIN_SHADED_HEIGHT       MAINWIN_SHADED_HEIGHT
+#define PLAYLISTWIN_WIDTH               config.playlist_width
+#define PLAYLISTWIN_HEIGHT \
+    (config.playlist_shaded ? PLAYLISTWIN_SHADED_HEIGHT : config.playlist_height)
+
+#define PLAYLISTWIN_DEFAULT_WIDTH       275
+#define PLAYLISTWIN_DEFAULT_HEIGHT      232
+#define PLAYLISTWIN_DEFAULT_POS_X       295
+#define PLAYLISTWIN_DEFAULT_POS_Y       20
+
+#define PLAYLISTWIN_DEFAULT_FONT        "Sans Bold 8"
+
+gboolean playlistwin_is_shaded(void);
+gint playlistwin_get_width(void);
+gint playlistwin_get_height(void);
+void playlistwin_update_list(Playlist *playlist);
+gboolean playlistwin_item_visible(gint index);
+gint playlistwin_get_toprow(void);
+void playlistwin_set_toprow(gint top);
+void playlistwin_set_shade_menu_cb(gboolean shaded);
+void playlistwin_set_shade(gboolean shaded);
+void playlistwin_shade_toggle(void);
+void playlistwin_create(void);
+void playlistwin_hide_timer(void);
+void playlistwin_set_time(gint time, gint length, TimerMode mode);
+void playlistwin_show(void);
+void playlistwin_hide(void);
+void playlistwin_scroll(gint num);
+void playlistwin_scroll_up_pushed(void);
+void playlistwin_scroll_down_pushed(void);
+void playlistwin_select_playlist_to_load(const gchar * default_filename);
+void playlistwin_set_sinfo_font(gchar *font);
+void playlistwin_set_sinfo_scroll(gboolean scroll);
+gint playlistwin_list_get_visible_count(void);
+gint playlistwin_list_get_first(void);
+
+extern GtkWidget *playlistwin;
+
+extern gboolean playlistwin_focus;
+
+#endif /* AUDACIOUS_UI_PLAYLIST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist_evlisteners.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,57 @@
+/*
+ * Audacious
+ * Copyright (c) 2006-2007 Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_playlist_evlisteners.h"
+
+#include <glib.h>
+#if 0
+#include "hook.h"
+#include "playlist.h"
+#endif
+#include "ui_playlist.h"
+#if 0
+#include "ui_playlist_manager.h"
+#endif
+static void
+ui_playlist_evlistener_playlist_update(gpointer hook_data, gpointer user_data)
+{
+    Playlist *playlist = (Playlist *) hook_data;
+    if (playlist != NULL)
+        playlistwin_update_list(playlist);
+#if 0
+    playlist_manager_update();
+#endif
+}
+
+static void
+ui_playlist_evlistener_playlistwin_show(gpointer hook_data, gpointer user_data)
+{
+    gboolean *show = (gboolean*)hook_data;
+    if (*show == TRUE)
+        playlistwin_show();
+    else
+        playlistwin_hide();
+}
+
+void ui_playlist_evlistener_init(void)
+{
+    aud_hook_associate("playlist update", ui_playlist_evlistener_playlist_update, NULL);
+    aud_hook_associate("playlistwin show", ui_playlist_evlistener_playlistwin_show, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist_evlisteners.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,26 @@
+/*
+ * Audacious
+ * Copyright (c) 2006-2007 Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_PLAYLIST_EVLISTENERS_H
+#define AUDACIOUS_UI_PLAYLIST_EVLISTENERS_H
+
+void ui_playlist_evlistener_init(void);
+
+#endif /* AUDACIOUS_UI_PLAYLIST_EVLISTENERS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skin.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,2062 @@
+/*  Audacious
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+/*#define AUD_DEBUG*/
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+/* TODO: enforce default sizes! */
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "plugin.h"
+
+#include <audacious/plugin.h>
+#include "ui_skin.h"
+#include "util.h"
+#include "ui_main.h"
+#include "ui_equalizer.h"
+#include "ui_playlist.h"
+#if 0
+#include "ui_skinselector.h"
+#endif
+#include "debug.h"
+
+#include "platform/smartinclude.h"
+
+#include "ui_skinned_window.h"
+#include "ui_skinned_button.h"
+#include "ui_skinned_number.h"
+#include "ui_skinned_horizontal_slider.h"
+#include "ui_skinned_playstatus.h"
+
+#define EXTENSION_TARGETS 7
+
+static gchar *ext_targets[EXTENSION_TARGETS] =
+{ "bmp", "xpm", "png", "svg", "gif", "jpg", "jpeg" };
+
+struct _SkinPixmapIdMapping {
+    SkinPixmapId id;
+    const gchar *name;
+    const gchar *alt_name;
+    gint width, height;
+};
+
+struct _SkinMaskInfo {
+    gint width, height;
+    gchar *inistr;
+};
+/* I know, it's not nice to copy'n'paste stuff, but I wanted it to
+   be atleast parially working, before dealing with such things */
+
+typedef struct {
+    const gchar *to_match;
+    gchar *match;
+    gboolean found;
+} FindFileContext;
+
+typedef struct _SkinPixmapIdMapping SkinPixmapIdMapping;
+typedef struct _SkinMaskInfo SkinMaskInfo;
+
+
+Skin *aud_active_skin = NULL;
+
+static gint skin_current_num;
+
+static SkinMaskInfo skin_mask_info[] = {
+    {275, 116, "Normal"},
+    {275, 16,  "WindowShade"},
+    {275, 116, "Equalizer"},
+    {275, 16,  "EqualizerWS"}
+};
+
+static SkinPixmapIdMapping skin_pixmap_id_map[] = {
+    {SKIN_MAIN, "main", NULL, 0, 0},
+    {SKIN_CBUTTONS, "cbuttons", NULL, 0, 0},
+    {SKIN_SHUFREP, "shufrep", NULL, 0, 0},
+    {SKIN_TEXT, "text", NULL, 0, 0},
+    {SKIN_TITLEBAR, "titlebar", NULL, 0, 0},
+    {SKIN_VOLUME, "volume", NULL, 0, 0},
+    {SKIN_BALANCE, "balance", "volume", 0, 0},
+    {SKIN_MONOSTEREO, "monoster", NULL, 0, 0},
+    {SKIN_PLAYPAUSE, "playpaus", NULL, 0, 0},
+    {SKIN_NUMBERS, "nums_ex", "numbers", 0, 0},
+    {SKIN_POSBAR, "posbar", NULL, 0, 0},
+    {SKIN_EQMAIN, "eqmain", NULL, 0, 0},
+    {SKIN_PLEDIT, "pledit", NULL, 0, 0},
+    {SKIN_EQ_EX, "eq_ex", NULL, 0, 0}
+};
+
+static guint skin_pixmap_id_map_size = G_N_ELEMENTS(skin_pixmap_id_map);
+
+static const guchar skin_default_viscolor[24][3] = {
+    {9, 34, 53},
+    {10, 18, 26},
+    {0, 54, 108},
+    {0, 58, 116},
+    {0, 62, 124},
+    {0, 66, 132},
+    {0, 70, 140},
+    {0, 74, 148},
+    {0, 78, 156},
+    {0, 82, 164},
+    {0, 86, 172},
+    {0, 92, 184},
+    {0, 98, 196},
+    {0, 104, 208},
+    {0, 110, 220},
+    {0, 116, 232},
+    {0, 122, 244},
+    {0, 128, 255},
+    {0, 128, 255},
+    {0, 104, 208},
+    {0, 80, 160},
+    {0, 56, 112},
+    {0, 32, 64},
+    {200, 200, 200}
+};
+
+static gchar *original_gtk_theme = NULL;
+
+static GdkBitmap *skin_create_transparent_mask(const gchar *,
+                                               const gchar *,
+                                               const gchar *,
+                                               GdkWindow *,
+                                               gint, gint, gboolean);
+
+static void skin_set_default_vis_color(Skin * skin);
+
+void
+skin_lock(Skin * skin)
+{
+    g_mutex_lock(skin->lock);
+}
+
+void
+skin_unlock(Skin * skin)
+{
+    g_mutex_unlock(skin->lock);
+}
+
+gboolean
+aud_active_skin_reload(void) 
+{
+    AUDDBG("\n");
+    return aud_active_skin_load(aud_active_skin->path); 
+}
+
+gboolean
+aud_active_skin_load(const gchar * path)
+{
+    AUDDBG("%s\n", path);
+    g_return_val_if_fail(aud_active_skin != NULL, FALSE);
+
+    if (!skin_load(aud_active_skin, path)) {
+        AUDDBG("loading failed\n");
+        return FALSE;
+    }
+
+    ui_skinned_window_draw_all(mainwin);
+    ui_skinned_window_draw_all(equalizerwin);
+    ui_skinned_window_draw_all(playlistwin);
+
+    playlistwin_update_list(aud_playlist_get_active());
+
+    SkinPixmap *pixmap;
+    pixmap = &aud_active_skin->pixmaps[SKIN_POSBAR];
+    /* last 59 pixels of SKIN_POSBAR are knobs (normal and selected) */
+    gtk_widget_set_size_request(mainwin_position, pixmap->width - 59, pixmap->height);
+
+    return TRUE;
+}
+
+void
+skin_pixmap_free(SkinPixmap * p)
+{
+    g_return_if_fail(p != NULL);
+    g_return_if_fail(p->pixbuf != NULL);
+
+    g_object_unref(p->pixbuf);
+    p->pixbuf = NULL;
+}
+
+Skin *
+skin_new(void)
+{
+    Skin *skin;
+    skin = g_new0(Skin, 1);
+    skin->lock = g_mutex_new();
+    return skin;
+}
+
+/**
+ * Frees the data associated for skin.
+ *
+ * Does not free skin itself or lock variable so that the skin can immediately
+ * populated with new skin data if needed.
+ */
+void
+skin_free(Skin * skin)
+{
+    gint i;
+
+    g_return_if_fail(skin != NULL);
+
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
+        skin_pixmap_free(&skin->pixmaps[i]);
+
+    for (i = 0; i < SKIN_MASK_COUNT; i++) {
+        if (skin->masks[i])
+            g_object_unref(skin->masks[i]);
+        if (skin->scaled_masks[i])
+            g_object_unref(skin->scaled_masks[i]);
+
+        skin->masks[i] = NULL;
+        skin->scaled_masks[i] = NULL;
+    }
+
+    for (i = 0; i < SKIN_COLOR_COUNT; i++) {
+        if (skin->colors[i])
+            g_free(skin->colors[i]);
+
+        skin->colors[i] = NULL;
+    }
+
+    g_free(skin->path);
+    skin->path = NULL;
+
+    skin_set_default_vis_color(skin);
+}
+
+void
+skin_destroy(Skin * skin)
+{
+    g_return_if_fail(skin != NULL);
+    skin_free(skin);
+    g_mutex_free(skin->lock);
+    g_free(skin);
+}
+
+const SkinPixmapIdMapping *
+skin_pixmap_id_lookup(guint id)
+{
+    guint i;
+
+    for (i = 0; i < skin_pixmap_id_map_size; i++) {
+        if (id == skin_pixmap_id_map[i].id) {
+            return &skin_pixmap_id_map[i];
+        }
+    }
+
+    return NULL;
+}
+
+const gchar *
+skin_pixmap_id_to_name(SkinPixmapId id)
+{
+    guint i;
+
+    for (i = 0; i < skin_pixmap_id_map_size; i++) {
+        if (id == skin_pixmap_id_map[i].id)
+            return skin_pixmap_id_map[i].name;
+    }
+    return NULL;
+}
+
+static void
+skin_set_default_vis_color(Skin * skin)
+{
+    memcpy(skin->vis_color, skin_default_viscolor,
+           sizeof(skin_default_viscolor));
+}
+
+/*
+ * I have rewritten this to take an array of possible targets,
+ * once we find a matching target we now return, instead of loop
+ * recursively. This allows for us to support many possible format
+ * targets for our skinning engine than just the original winamp 
+ * formats.
+ *
+ *    -- nenolod, 16 January 2006
+ */
+gchar *
+skin_pixmap_locate(const gchar * dirname, gchar ** basenames)
+{
+    gchar *filename;
+    gint i;
+
+    for (i = 0; basenames[i]; i++)
+    if (!(filename = find_path_recursively(dirname, basenames[i]))) 
+        g_free(filename);
+    else
+        return filename;
+
+    /* can't find any targets -- sorry */
+    return NULL;
+}
+
+/**
+ * Creates possible file names for a pixmap.
+ *
+ * Basically this makes list of all possible file names that pixmap data
+ * can be found from by using the static ext_targets variable to get all
+ * possible extensions that pixmap file might have.
+ */
+static gchar **
+skin_pixmap_create_basenames(const SkinPixmapIdMapping * pixmap_id_mapping)
+{
+    gchar **basenames = g_malloc0(sizeof(gchar*) * (EXTENSION_TARGETS * 2 + 1));
+    gint i, y;
+
+    // Create list of all possible image formats that can be loaded
+    for (i = 0, y = 0; i < EXTENSION_TARGETS; i++, y++)
+    {
+        basenames[y] =
+            g_strdup_printf("%s.%s", pixmap_id_mapping->name, ext_targets[i]);
+
+        if (pixmap_id_mapping->alt_name)
+            basenames[++y] =
+                g_strdup_printf("%s.%s", pixmap_id_mapping->alt_name,
+                                ext_targets[i]);
+    }
+
+    return basenames;
+}
+
+/**
+ * Frees the data allocated by skin_pixmap_create_basenames
+ */
+static void
+skin_pixmap_free_basenames(gchar ** basenames)
+{
+    int i;
+    for (i = 0; basenames[i] != NULL; i++)
+    {
+        g_free(basenames[i]);
+        basenames[i] = NULL;
+    }
+    g_free(basenames);
+}
+
+/**
+ * Locates a pixmap file for skin.
+ */
+static gchar *
+skin_pixmap_locate_basenames(const Skin * skin,
+                             const SkinPixmapIdMapping * pixmap_id_mapping,
+                             const gchar * path_p)
+{
+    gchar *filename = NULL;
+    const gchar *path = path_p ? path_p : skin->path;
+    gchar **basenames = skin_pixmap_create_basenames(pixmap_id_mapping);
+
+    filename = skin_pixmap_locate(path, basenames);
+
+    skin_pixmap_free_basenames(basenames);
+
+    return filename;
+}
+
+
+static gboolean
+skin_load_pixmap_id(Skin * skin, SkinPixmapId id, const gchar * path_p)
+{
+    const SkinPixmapIdMapping *pixmap_id_mapping;
+    gchar *filename;
+    SkinPixmap *pm = NULL;
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+    g_return_val_if_fail(id < SKIN_PIXMAP_COUNT, FALSE);
+
+    pixmap_id_mapping = skin_pixmap_id_lookup(id);
+    g_return_val_if_fail(pixmap_id_mapping != NULL, FALSE);
+
+    filename = skin_pixmap_locate_basenames(skin, pixmap_id_mapping, path_p);
+
+    if (filename == NULL)
+        return FALSE;
+
+    AUDDBG("loaded %s\n", filename);
+
+    pm = &skin->pixmaps[id];
+    GdkPixbuf *pix = gdk_pixbuf_new_from_file(filename, NULL);
+    pm->pixbuf = audacious_create_colorized_pixbuf(pix, config.colorize_r, aud_cfg->colorize_g, aud_cfg->colorize_b);
+    g_object_unref(pix);
+    pm->width = gdk_pixbuf_get_width(pm->pixbuf);
+    pm->height = gdk_pixbuf_get_height(pm->pixbuf);
+    pm->current_width = pm->width;
+    pm->current_height = pm->height;
+
+    g_free(filename);
+
+    return TRUE;
+}
+
+void
+skin_mask_create(Skin * skin,
+                 const gchar * path,
+                 gint id,
+                 GdkWindow * window)
+{
+    skin->masks[id] =
+        skin_create_transparent_mask(path, "region.txt",
+                                     skin_mask_info[id].inistr, window,
+                                     skin_mask_info[id].width,
+                                     skin_mask_info[id].height, FALSE);
+
+    skin->scaled_masks[id] =
+        skin_create_transparent_mask(path, "region.txt",
+                                     skin_mask_info[id].inistr, window,
+                                     skin_mask_info[id].width * 2,
+                                     skin_mask_info[id].height * 2, TRUE);
+}
+
+static GdkBitmap *
+create_default_mask(GdkWindow * parent, gint w, gint h)
+{
+    GdkBitmap *ret;
+    GdkGC *gc;
+    GdkColor pattern;
+
+    ret = gdk_pixmap_new(parent, w, h, 1);
+    gc = gdk_gc_new(ret);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(gc, &pattern);
+    gdk_draw_rectangle(ret, gc, TRUE, 0, 0, w, h);
+    g_object_unref(gc);
+
+    return ret;
+}
+
+static void
+skin_query_color(GdkColormap * cm, GdkColor * c)
+{
+#ifdef GDK_WINDOWING_X11
+    XColor xc = { 0,0,0,0,0,0 };
+
+    xc.pixel = c->pixel;
+    XQueryColor(GDK_COLORMAP_XDISPLAY(cm), GDK_COLORMAP_XCOLORMAP(cm), &xc);
+    c->red = xc.red;
+    c->green = xc.green;
+    c->blue = xc.blue;
+#else
+    /* do nothing. see what breaks? */
+#endif
+}
+
+static glong
+skin_calc_luminance(GdkColor * c)
+{
+    return (0.212671 * c->red + 0.715160 * c->green + 0.072169 * c->blue);
+}
+
+static void
+skin_get_textcolors(GdkPixbuf * pix, GdkColor * bgc, GdkColor * fgc)
+{
+    /*
+     * Try to extract reasonable background and foreground colors
+     * from the font pixmap
+     */
+
+    GdkImage *gi;
+    GdkColormap *cm;
+    gint i;
+
+    g_return_if_fail(pix != NULL);
+
+    GdkPixmap *text = gdk_pixmap_new(NULL, gdk_pixbuf_get_width(pix), gdk_pixbuf_get_height(pix), gdk_rgb_get_visual()->depth);
+    gdk_draw_pixbuf(text, NULL, pix, 0, 0, 0, 0, gdk_pixbuf_get_width(pix), gdk_pixbuf_get_height(pix),
+                    GDK_RGB_DITHER_NONE, 0, 0);
+    /* Get the first line of text */
+    gi = gdk_drawable_get_image(text, 0, 0, 152, 6);
+    cm = gdk_colormap_get_system();
+
+    for (i = 0; i < 6; i++) {
+        GdkColor c;
+        gint x;
+        glong d, max_d;
+
+        /* Get a pixel from the middle of the space character */
+        bgc[i].pixel = gdk_image_get_pixel(gi, 151, i);
+        skin_query_color(cm, &bgc[i]);
+
+        max_d = 0;
+        for (x = 1; x < 150; x++) {
+            c.pixel = gdk_image_get_pixel(gi, x, i);
+            skin_query_color(cm, &c);
+
+            d = labs(skin_calc_luminance(&c) - skin_calc_luminance(&bgc[i]));
+            if (d > max_d) {
+                memcpy(&fgc[i], &c, sizeof(GdkColor));
+                max_d = d;
+            }
+        }
+    }
+    g_object_unref(gi);
+    g_object_unref(text);
+}
+
+gboolean
+init_skins(const gchar * path)
+{
+    aud_active_skin = skin_new();
+
+    skin_parse_hints(aud_active_skin, NULL);
+
+    /* create the windows if they haven't been created yet, needed for bootstrapping */
+    if (mainwin == NULL)
+    {
+        mainwin_create();
+        equalizerwin_create();
+        playlistwin_create();
+    }
+
+    if (!aud_active_skin_load(path)) {
+        if (path != NULL)
+            AUDDBG("Unable to load skin (%s), trying default...\n", path);
+        else
+            AUDDBG("Skin not defined: trying default...\n");
+
+        /* can't load configured skin, retry with default */
+        if (!aud_active_skin_load(BMP_DEFAULT_SKIN_PATH)) {
+            AUDDBG("Unable to load default skin (%s)! Giving up.\n",
+                      BMP_DEFAULT_SKIN_PATH);
+            return FALSE;
+        }
+    }
+#if 0
+    if (config.random_skin_on_play)
+        skinlist_update();
+#endif
+    return TRUE;
+}
+
+void cleanup_skins()
+{
+    skin_destroy(aud_active_skin);
+    aud_active_skin = NULL;
+}
+
+
+/*
+ * Opens and parses a skin's hints file.
+ * Hints files are somewhat like "scripts" in Winamp3/5.
+ * We'll probably add scripts to it next.
+ */
+void
+skin_parse_hints(Skin * skin, gchar *path_p)
+{
+    gchar *filename, *tmp;
+    INIFile *inifile;
+
+    path_p = path_p ? path_p : skin->path;
+
+    skin->properties.mainwin_othertext = FALSE;
+    skin->properties.mainwin_vis_x = 24;
+    skin->properties.mainwin_vis_y = 43;
+    skin->properties.mainwin_vis_width = 76;
+    skin->properties.mainwin_text_x = 112;
+    skin->properties.mainwin_text_y = 27;
+    skin->properties.mainwin_text_width = 153;
+    skin->properties.mainwin_infobar_x = 112;
+    skin->properties.mainwin_infobar_y = 43;
+    skin->properties.mainwin_number_0_x = 36;
+    skin->properties.mainwin_number_0_y = 26;
+    skin->properties.mainwin_number_1_x = 48;
+    skin->properties.mainwin_number_1_y = 26;
+    skin->properties.mainwin_number_2_x = 60;
+    skin->properties.mainwin_number_2_y = 26;
+    skin->properties.mainwin_number_3_x = 78;
+    skin->properties.mainwin_number_3_y = 26;
+    skin->properties.mainwin_number_4_x = 90;
+    skin->properties.mainwin_number_4_y = 26;
+    skin->properties.mainwin_playstatus_x = 24;
+    skin->properties.mainwin_playstatus_y = 28;
+    skin->properties.mainwin_menurow_visible = TRUE;
+    skin->properties.mainwin_volume_x = 107;
+    skin->properties.mainwin_volume_y = 57;
+    skin->properties.mainwin_balance_x = 177;
+    skin->properties.mainwin_balance_y = 57;
+    skin->properties.mainwin_position_x = 16;
+    skin->properties.mainwin_position_y = 72;
+    skin->properties.mainwin_othertext_is_status = FALSE;
+    skin->properties.mainwin_othertext_visible = skin->properties.mainwin_othertext;
+    skin->properties.mainwin_text_visible = TRUE;
+    skin->properties.mainwin_vis_visible = TRUE;
+    skin->properties.mainwin_previous_x = 16;
+    skin->properties.mainwin_previous_y = 88;
+    skin->properties.mainwin_play_x = 39;
+    skin->properties.mainwin_play_y = 88;
+    skin->properties.mainwin_pause_x = 62;
+    skin->properties.mainwin_pause_y = 88;
+    skin->properties.mainwin_stop_x = 85;
+    skin->properties.mainwin_stop_y = 88;
+    skin->properties.mainwin_next_x = 108;
+    skin->properties.mainwin_next_y = 88;
+    skin->properties.mainwin_eject_x = 136;
+    skin->properties.mainwin_eject_y = 89;
+    skin->properties.mainwin_width = 275;
+    skin_mask_info[0].width = skin->properties.mainwin_width;
+    skin->properties.mainwin_height = 116;
+    skin_mask_info[0].height = skin->properties.mainwin_height;
+    skin->properties.mainwin_about_x = 247;
+    skin->properties.mainwin_about_y = 83;
+    skin->properties.mainwin_shuffle_x = 164;
+    skin->properties.mainwin_shuffle_y = 89;
+    skin->properties.mainwin_repeat_x = 210;
+    skin->properties.mainwin_repeat_y = 89;
+    skin->properties.mainwin_eqbutton_x = 219;
+    skin->properties.mainwin_eqbutton_y = 58;
+    skin->properties.mainwin_plbutton_x = 242;
+    skin->properties.mainwin_plbutton_y = 58;
+    skin->properties.textbox_bitmap_font_width = 5;
+    skin->properties.textbox_bitmap_font_height = 6;
+    skin->properties.mainwin_minimize_x = 244;
+    skin->properties.mainwin_minimize_y = 3;
+    skin->properties.mainwin_shade_x = 254;
+    skin->properties.mainwin_shade_y = 3;
+    skin->properties.mainwin_close_x = 264;
+    skin->properties.mainwin_close_y = 3;
+
+    if (path_p == NULL)
+        return;
+
+    filename = find_file_recursively(path_p, "skin.hints");
+
+    if (filename == NULL)
+        return;
+
+    inifile = aud_open_ini_file(filename);
+    if (!inifile)
+        return;
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinOthertext");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_othertext = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVisX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_vis_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVisY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_vis_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVisWidth");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_vis_width = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinTextX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_text_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinTextY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_text_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinTextWidth");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_text_width = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinInfoBarX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_infobar_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinInfoBarY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_infobar_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber0X");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_0_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber0Y");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_0_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber1X");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_1_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber1Y");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_1_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber2X");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_2_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber2Y");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_2_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber3X");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_3_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber3Y");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_3_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber4X");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_4_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNumber4Y");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_number_4_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPlayStatusX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_playstatus_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPlayStatusY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_playstatus_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinMenurowVisible");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_menurow_visible = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVolumeX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_volume_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVolumeY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_volume_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinBalanceX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_balance_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinBalanceY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_balance_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPositionX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_position_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPositionY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_position_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinOthertextIsStatus");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_othertext_is_status = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinOthertextVisible");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_othertext_visible = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinTextVisible");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_text_visible = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinVisVisible");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_vis_visible = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPreviousX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_previous_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPreviousY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_previous_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPlayX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_play_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPlayY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_play_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPauseX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_pause_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPauseY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_pause_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinStopX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_stop_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinStopY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_stop_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNextX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_next_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinNextY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_next_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinEjectX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_eject_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinEjectY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_eject_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinWidth");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_width = atoi(tmp);
+        g_free(tmp);
+    }
+
+    skin_mask_info[0].width = skin->properties.mainwin_width;
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinHeight");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_height = atoi(tmp);
+        g_free(tmp);
+    }
+
+    skin_mask_info[0].height = skin->properties.mainwin_height;
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinAboutX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_about_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinAboutY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_about_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinShuffleX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_shuffle_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinShuffleY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_shuffle_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinRepeatX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_repeat_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinRepeatY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_repeat_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinEQButtonX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_eqbutton_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinEQButtonY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_eqbutton_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPLButtonX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_plbutton_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinPLButtonY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_plbutton_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "textboxBitmapFontWidth");
+
+    if (tmp != NULL)
+    {
+        skin->properties.textbox_bitmap_font_width = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "textboxBitmapFontHeight");
+
+    if (tmp != NULL)
+    {
+        skin->properties.textbox_bitmap_font_height = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinMinimizeX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_minimize_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinMinimizeY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_minimize_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinShadeX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_shade_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinShadeY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_shade_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinCloseX");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_close_x = atoi(tmp);
+        g_free(tmp);
+    }
+
+    tmp = aud_read_ini_string(inifile, "skin", "mainwinCloseY");
+
+    if (tmp != NULL)
+    {
+        skin->properties.mainwin_close_y = atoi(tmp);
+        g_free(tmp);
+    }
+
+    if (filename != NULL)
+        g_free(filename);
+
+    aud_close_ini_file(inifile);
+}
+
+static guint
+hex_chars_to_int(gchar hi, gchar lo)
+{
+    /*
+     * Converts a value in the range 0x00-0xFF
+     * to a integer in the range 0-65535
+     */
+    gchar str[3];
+
+    str[0] = hi;
+    str[1] = lo;
+    str[2] = 0;
+
+    return (CLAMP(strtol(str, NULL, 16), 0, 0xFF) << 8);
+}
+
+static GdkColor *
+skin_load_color(INIFile *inifile,
+                const gchar * section, const gchar * key,
+                gchar * default_hex)
+{
+    gchar *value;
+    GdkColor *color = NULL;
+
+    if (inifile || default_hex) {
+        if (inifile) {
+            value = aud_read_ini_string(inifile, section, key);
+            if (value == NULL) {
+                value = g_strdup(default_hex);
+            }
+        } else {
+            value = g_strdup(default_hex);
+        }
+        if (value) {
+            gchar *ptr = value;
+            gint len;
+
+            color = g_new0(GdkColor, 1);
+            g_strstrip(value);
+
+            if (value[0] == '#')
+                ptr++;
+            len = strlen(ptr);
+            /*
+             * The handling of incomplete values is done this way
+             * to maximize winamp compatibility
+             */
+            if (len >= 6) {
+                color->red = hex_chars_to_int(*ptr, *(ptr + 1));
+                ptr += 2;
+            }
+            if (len >= 4) {
+                color->green = hex_chars_to_int(*ptr, *(ptr + 1));
+                ptr += 2;
+            }
+            if (len >= 2)
+                color->blue = hex_chars_to_int(*ptr, *(ptr + 1));
+
+            g_free(value);
+        }
+    }
+    return color;
+}
+
+
+
+GdkBitmap *
+skin_create_transparent_mask(const gchar * path,
+                             const gchar * file,
+                             const gchar * section,
+                             GdkWindow * window,
+                             gint width,
+                             gint height, gboolean scale)
+{
+    GdkBitmap *mask = NULL;
+    GdkGC *gc = NULL;
+    GdkColor pattern;
+    GdkPoint *gpoints;
+
+    gchar *filename = NULL;
+    INIFile *inifile = NULL;
+    gboolean created_mask = FALSE;
+    GArray *num, *point;
+    guint i, j;
+    gint k;
+
+    if (path)
+        filename = find_file_recursively(path, file);
+
+    /* filename will be null if path wasn't set */
+    if (!filename)
+        return create_default_mask(window, width, height);
+
+    inifile = aud_open_ini_file(filename);
+
+    if ((num = aud_read_ini_array(inifile, section, "NumPoints")) == NULL) {
+        g_free(filename);
+        aud_close_ini_file(inifile);
+        return NULL;
+    }
+
+    if ((point = aud_read_ini_array(inifile, section, "PointList")) == NULL) {
+        g_array_free(num, TRUE);
+        g_free(filename);
+        aud_close_ini_file(inifile);
+        return NULL;
+    }
+
+    aud_close_ini_file(inifile);
+
+    mask = gdk_pixmap_new(window, width, height, 1);
+    gc = gdk_gc_new(mask);
+
+    pattern.pixel = 0;
+    gdk_gc_set_foreground(gc, &pattern);
+    gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(gc, &pattern);
+
+    j = 0;
+    for (i = 0; i < num->len; i++) {
+        if ((int)(point->len - j) >= (g_array_index(num, gint, i) * 2)) {
+            created_mask = TRUE;
+            gpoints = g_new(GdkPoint, g_array_index(num, gint, i));
+            for (k = 0; k < g_array_index(num, gint, i); k++) {
+                gpoints[k].x =
+                    g_array_index(point, gint, j + k * 2) * (scale ? config.scale_factor : 1 );
+                gpoints[k].y =
+                    g_array_index(point, gint,
+                                  j + k * 2 + 1) * (scale ? config.scale_factor : 1);
+            }
+            j += k * 2;
+            gdk_draw_polygon(mask, gc, TRUE, gpoints,
+                             g_array_index(num, gint, i));
+            g_free(gpoints);
+        }
+    }
+    g_array_free(num, TRUE);
+    g_array_free(point, TRUE);
+    g_free(filename);
+
+    if (!created_mask)
+        gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
+
+    g_object_unref(gc);
+
+    return mask;
+}
+
+void
+skin_load_viscolor(Skin * skin, const gchar * path, const gchar * basename)
+{
+#if 0
+    VFSFile *file;
+    gint i, c;
+    gchar line[256], *filename;
+    GArray *a;
+
+    g_return_if_fail(skin != NULL);
+    g_return_if_fail(path != NULL);
+    g_return_if_fail(basename != NULL);
+
+    skin_set_default_vis_color(skin);
+
+    filename = find_file_recursively(path, basename);
+    if (!filename)
+        return;
+
+    if (!(file = vfs_fopen(filename, "r"))) {
+        g_free(filename);
+        return;
+    }
+
+    g_free(filename);
+
+    for (i = 0; i < 24; i++) {
+        if (vfs_fgets(line, 255, file)) {
+            a = string_to_garray(line);
+            if (a->len > 2) {
+                for (c = 0; c < 3; c++)
+                    skin->vis_color[i][c] = g_array_index(a, gint, c);
+            }
+            g_array_free(a, TRUE);
+        }
+        else
+            break;
+    }
+
+    vfs_fclose(file);
+#endif
+}
+
+static void
+skin_numbers_generate_dash(Skin * skin)
+{
+    GdkPixbuf *pixbuf;
+    SkinPixmap *numbers;
+
+    g_return_if_fail(skin != NULL);
+
+    numbers = &skin->pixmaps[SKIN_NUMBERS];
+    if (!numbers->pixbuf || numbers->current_width < 99)
+        return;
+
+    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
+                            108, numbers->current_height);
+
+    skin_draw_pixbuf(NULL, skin, pixbuf, SKIN_NUMBERS, 0, 0, 0, 0, 99, numbers->current_height);
+    skin_draw_pixbuf(NULL, skin, pixbuf, SKIN_NUMBERS, 90, 0, 99, 0, 9, numbers->current_height);
+    skin_draw_pixbuf(NULL, skin, pixbuf, SKIN_NUMBERS, 20, 6, 101, 6, 5, 1);
+
+    g_object_unref(numbers->pixbuf);
+
+    numbers->pixbuf = pixbuf;
+    numbers->current_width = 108;
+    numbers->width = 108;
+}
+
+static gboolean
+skin_load_pixmaps(Skin * skin, const gchar * path)
+{
+    GdkPixbuf *text_pb;
+    guint i;
+    gchar *filename;
+    INIFile *inifile;
+    
+    if(!skin) return FALSE;
+    if(!path) return FALSE;
+
+    AUDDBG("Loading pixmaps in %s\n", path);
+
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
+        if (!skin_load_pixmap_id(skin, i, path) && !config.allow_broken_skins)
+            return FALSE;
+
+    text_pb = skin->pixmaps[SKIN_TEXT].pixbuf;
+
+    if (text_pb)
+        skin_get_textcolors(text_pb, skin->textbg, skin->textfg);
+
+    if (skin->pixmaps[SKIN_NUMBERS].pixbuf &&
+        skin->pixmaps[SKIN_NUMBERS].width < 108 )
+        skin_numbers_generate_dash(skin);
+
+    filename = find_file_recursively(path, "pledit.txt");
+    inifile = aud_open_ini_file(filename);
+
+    skin->colors[SKIN_PLEDIT_NORMAL] =
+        skin_load_color(inifile, "Text", "Normal", "#2499ff");
+    skin->colors[SKIN_PLEDIT_CURRENT] =
+        skin_load_color(inifile, "Text", "Current", "#ffeeff");
+    skin->colors[SKIN_PLEDIT_NORMALBG] =
+        skin_load_color(inifile, "Text", "NormalBG", "#0a120a");
+    skin->colors[SKIN_PLEDIT_SELECTEDBG] =
+        skin_load_color(inifile, "Text", "SelectedBG", "#0a124a");
+
+    if (inifile)
+        aud_close_ini_file(inifile);
+
+    if (filename)
+        g_free(filename);
+
+    skin_mask_create(skin, path, SKIN_MASK_MAIN, mainwin->window);
+    skin_mask_create(skin, path, SKIN_MASK_MAIN_SHADE, mainwin->window);
+
+    skin_mask_create(skin, path, SKIN_MASK_EQ, equalizerwin->window);
+    skin_mask_create(skin, path, SKIN_MASK_EQ_SHADE, equalizerwin->window);
+#if 0
+    skin_load_viscolor(skin, path, "viscolor.txt");
+#endif
+    return TRUE;
+}
+
+static void
+skin_set_gtk_theme(GtkSettings * settings, Skin * skin)
+{
+    if (original_gtk_theme == NULL)
+         g_object_get(settings, "gtk-theme-name", &original_gtk_theme, NULL);
+
+    /* the way GTK does things can be very broken. --nenolod */
+
+    gchar *tmp = g_strdup_printf("%s/.themes/aud-%s", g_get_home_dir(),
+                                 basename(skin->path));
+
+    gchar *troot = g_strdup_printf("%s/.themes", g_get_home_dir());
+    g_mkdir_with_parents(troot, 0755);
+    g_free(troot);
+
+    symlink(skin->path, tmp);
+    gtk_settings_set_string_property(settings, "gtk-theme-name",
+                                     basename(tmp), "audacious");
+    g_free(tmp);
+}
+
+/**
+ * Checks if all pixmap files exist that skin needs.
+ */
+static gboolean
+skin_check_pixmaps(const Skin * skin, const gchar * skin_path)
+{
+    guint i;
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
+    {
+        gchar *filename = skin_pixmap_locate_basenames(skin,
+                                                       skin_pixmap_id_lookup(i),
+                                                       skin_path);
+        if (!filename)
+            return FALSE;
+        g_free(filename);
+    }
+    return TRUE;
+}
+
+static gboolean
+skin_load_nolock(Skin * skin, const gchar * path, gboolean force)
+{
+    GtkSettings *settings;
+    gchar *gtkrcpath;
+    gchar *newpath, *skin_path;
+    int archive = 0;
+
+    AUDDBG("Attempt to load skin \"%s\"\n", path);
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+    g_return_val_if_fail(path != NULL, FALSE);
+    REQUIRE_LOCK(skin->lock);
+
+    if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR))
+        return FALSE;
+
+    if(force) AUDDBG("reloading forced!\n");
+    if (!force && skin->path && !strcmp(skin->path, path)) {
+        AUDDBG("skin %s already loaded\n", path);
+        return FALSE;
+    }
+
+    if (file_is_archive(path)) {
+        AUDDBG("Attempt to load archive\n");
+        if (!(skin_path = archive_decompress(path))) {
+            AUDDBG("Unable to extract skin archive (%s)\n", path);
+            return FALSE;
+        }
+        archive = 1;
+    } else {
+        skin_path = g_strdup(path);
+    }
+
+    // Check if skin path has all necessary files.
+    if (!config.allow_broken_skins && !skin_check_pixmaps(skin, skin_path)) {
+        if(archive) del_directory(skin_path);
+        g_free(skin_path);
+        AUDDBG("Skin path (%s) doesn't have all wanted pixmaps\n", skin_path);
+        return FALSE;
+    }
+
+    // skin_free() frees skin->path and variable path can actually be skin->path
+    // and we want to get the path before possibly freeing it.
+    newpath = g_strdup(path);
+    skin_free(skin);
+    skin->path = newpath;
+
+    memset(&(skin->properties), 0, sizeof(SkinProperties)); /* do it only if all tests above passed! --asphyx */
+
+    skin_current_num++;
+
+    /* Parse the hints for this skin. */
+    skin_parse_hints(skin, skin_path);
+
+    if (!skin_load_pixmaps(skin, skin_path)) {
+        if(archive) del_directory(skin_path);
+        g_free(skin_path);
+        AUDDBG("Skin loading failed\n");
+        return FALSE;
+    }
+
+    /* restore gtk theme if changed by previous skin */
+    settings = gtk_settings_get_default();
+
+    if (original_gtk_theme != NULL) {
+        gtk_settings_set_string_property(settings, "gtk-theme-name",
+                                              original_gtk_theme, "audacious");
+        g_free(original_gtk_theme);
+        original_gtk_theme = NULL;
+    }
+
+#ifndef _WIN32
+    if (!config.disable_inline_gtk && !archive) {
+        gtkrcpath = find_path_recursively(skin->path, "gtkrc");
+        if (gtkrcpath != NULL)
+            skin_set_gtk_theme(settings, skin);
+        g_free(gtkrcpath);
+    }
+#endif
+
+    if(archive) del_directory(skin_path);
+    g_free(skin_path);
+
+    gtk_widget_shape_combine_mask(mainwin, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + config.player_shaded), 0, 0);
+    gtk_widget_shape_combine_mask(equalizerwin, skin_get_mask(aud_active_skin, SKIN_MASK_EQ + config.equalizer_shaded), 0, 0);
+
+    return TRUE;
+}
+
+void
+skin_install_skin(const gchar * path)
+{
+#if 0
+    gchar *command;
+
+    g_return_if_fail(path != NULL);
+
+    command = g_strdup_printf("cp %s %s",
+                              path, aud_paths[BMP_PATH_USER_SKIN_DIR]);
+    if (system(command)) {
+        AUDDBG("Unable to install skin (%s) into user directory (%s)\n",
+                  path, aud_paths[BMP_PATH_USER_SKIN_DIR]);
+    }
+    g_free(command);
+#endif
+}
+
+static SkinPixmap *
+skin_get_pixmap(Skin * skin, SkinPixmapId map_id)
+{
+    g_return_val_if_fail(skin != NULL, NULL);
+    g_return_val_if_fail(map_id < SKIN_PIXMAP_COUNT, NULL);
+
+    return &skin->pixmaps[map_id];
+}
+
+gboolean
+skin_load(Skin * skin, const gchar * path)
+{
+    gboolean ret;
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+
+    if (!path)
+        return FALSE;
+
+    skin_lock(skin);
+    ret = skin_load_nolock(skin, path, FALSE);
+    skin_unlock(skin);
+
+    if(!ret) {
+        AUDDBG("loading failed\n");
+        return FALSE; /* don't try to update anything if loading failed --asphyx */
+    }
+
+    SkinPixmap *pixmap = NULL;
+    pixmap = skin_get_pixmap(skin, SKIN_NUMBERS);
+    if (pixmap) {
+        ui_skinned_number_set_size(mainwin_minus_num, 9, pixmap->height);
+        ui_skinned_number_set_size(mainwin_10min_num, 9, pixmap->height);
+        ui_skinned_number_set_size(mainwin_min_num, 9, pixmap->height);
+        ui_skinned_number_set_size(mainwin_10sec_num, 9, pixmap->height);
+        ui_skinned_number_set_size(mainwin_sec_num, 9, pixmap->height);
+    }
+
+    pixmap = skin_get_pixmap(skin, SKIN_MAIN);
+    if (pixmap && skin->properties.mainwin_height > pixmap->height)
+        skin->properties.mainwin_height = pixmap->height;
+
+    pixmap = skin_get_pixmap(skin, SKIN_PLAYPAUSE);
+    if (pixmap)
+        ui_skinned_playstatus_set_size(mainwin_playstatus, 11, pixmap->height);
+
+    pixmap = skin_get_pixmap(skin, SKIN_EQMAIN);
+    if (pixmap->height >= 313)
+        gtk_widget_show(equalizerwin_graph);
+
+    return TRUE;
+}
+
+gboolean
+skin_reload_forced(void) 
+{
+   gboolean error;
+   AUDDBG("\n");
+
+   skin_lock(aud_active_skin);
+   error = skin_load_nolock(aud_active_skin, aud_active_skin->path, TRUE);
+   skin_unlock(aud_active_skin);
+
+   return error;
+}
+
+void
+skin_reload(Skin * skin)
+{
+    AUDDBG("\n");
+    g_return_if_fail(skin != NULL);
+    skin_load_nolock(skin, skin->path, TRUE);
+}
+
+GdkBitmap *
+skin_get_mask(Skin * skin, SkinMaskId mi)
+{
+    GdkBitmap **masks;
+
+    g_return_val_if_fail(skin != NULL, NULL);
+    g_return_val_if_fail(mi < SKIN_PIXMAP_COUNT, NULL);
+
+    masks = config.scaled ? skin->scaled_masks : skin->masks;
+    return masks[mi];
+}
+
+GdkColor *
+skin_get_color(Skin * skin, SkinColorId color_id)
+{
+    GdkColor *ret = NULL;
+
+    g_return_val_if_fail(skin != NULL, NULL);
+
+    switch (color_id) {
+    case SKIN_TEXTBG:
+        if (skin->pixmaps[SKIN_TEXT].pixbuf)
+            ret = skin->textbg;
+        else
+            ret = skin->def_textbg;
+        break;
+    case SKIN_TEXTFG:
+        if (skin->pixmaps[SKIN_TEXT].pixbuf)
+            ret = skin->textfg;
+        else
+            ret = skin->def_textfg;
+        break;
+    default:
+        if (color_id < SKIN_COLOR_COUNT)
+            ret = skin->colors[color_id];
+        break;
+    }
+    return ret;
+}
+
+void
+skin_get_viscolor(Skin * skin, guchar vis_color[24][3])
+{
+    gint i;
+
+    g_return_if_fail(skin != NULL);
+
+    for (i = 0; i < 24; i++) {
+        vis_color[i][0] = skin->vis_color[i][0];
+        vis_color[i][1] = skin->vis_color[i][1];
+        vis_color[i][2] = skin->vis_color[i][2];
+    }
+}
+
+gint
+skin_get_id(void)
+{
+    return skin_current_num;
+}
+
+void
+skin_draw_pixbuf(GtkWidget *widget, Skin * skin, GdkPixbuf * pix,
+                 SkinPixmapId pixmap_id,
+                 gint xsrc, gint ysrc, gint xdest, gint ydest,
+                 gint width, gint height)
+{
+    SkinPixmap *pixmap;
+
+    g_return_if_fail(skin != NULL);
+
+    pixmap = skin_get_pixmap(skin, pixmap_id);
+    g_return_if_fail(pixmap != NULL);
+    g_return_if_fail(pixmap->pixbuf != NULL);
+
+    /* perhaps we should use transparency or resize widget? */
+    if (xsrc+width > pixmap->width || ysrc+height > pixmap->height) {
+        if (widget) {
+            /* it's better to hide widget using SKIN_PLAYPAUSE/SKIN_POSBAR than display mess */
+            if ((pixmap_id == SKIN_PLAYPAUSE && pixmap->width != 42) || pixmap_id == SKIN_POSBAR) {
+                gtk_widget_hide(widget);
+                return;
+            }
+            gint x, y;
+            x = -1;
+            y = -1;
+
+            if (gtk_widget_get_parent(widget) == SKINNED_WINDOW(mainwin)->fixed) {
+                GList *iter;
+                for (iter = GTK_FIXED (SKINNED_WINDOW(mainwin)->fixed)->children; iter; iter = g_list_next (iter)) {
+                     GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+                     if (child_data->widget == widget) {
+                         x = child_data->x;
+                         y = child_data->y;
+                         break;
+                     }
+                }
+
+                if (x != -1 && y != -1) {
+                    /* Some skins include SKIN_VOLUME and/or SKIN_BALANCE
+                       without knobs */
+                    if (pixmap_id == SKIN_VOLUME || pixmap_id == SKIN_BALANCE) {
+                        if (ysrc+height > 421 && xsrc+width <= pixmap->width)
+                            return;
+                    }
+                    /* let's copy what's under widget */
+                    gdk_pixbuf_copy_area(skin_get_pixmap(aud_active_skin, SKIN_MAIN)->pixbuf,
+                                         x, y, width, height, pix, xdest, ydest);
+
+                    /* XMMS skins seems to have SKIN_MONOSTEREO with size 58x20 instead of 58x24 */
+                    if (pixmap_id == SKIN_MONOSTEREO)
+                        height = pixmap->height/2;
+                }
+            } else if (gtk_widget_get_parent(widget) == SKINNED_WINDOW(equalizerwin)->fixed) {
+                   if (!(pixmap_id == SKIN_EQMAIN && ysrc == 314)) /* equalizer preamp on equalizer graph */
+                         gtk_widget_hide(widget);
+            } else if (gtk_widget_get_parent(widget) == SKINNED_WINDOW(playlistwin)->fixed) {
+                   /* I haven't seen any skin with substandard playlist */
+                   gtk_widget_hide(widget);
+            }
+        } else
+            return;
+    }
+
+    width = MIN(width, pixmap->width - xsrc);
+    height = MIN(height, pixmap->height - ysrc);
+    gdk_pixbuf_copy_area(pixmap->pixbuf, xsrc, ysrc, width, height,
+                         pix, xdest, ydest);
+}
+
+void
+skin_get_eq_spline_colors(Skin * skin, guint32 colors[19])
+{
+    gint i;
+    GdkPixbuf *pixbuf;
+    SkinPixmap *eqmainpm;
+    guchar* pixels,*p;
+    guint rowstride, n_channels;
+    g_return_if_fail(skin != NULL);
+
+    eqmainpm = &skin->pixmaps[SKIN_EQMAIN];
+    if (eqmainpm->pixbuf &&
+            eqmainpm->current_width >= 116 && eqmainpm->current_height >= 313)
+        pixbuf = eqmainpm->pixbuf;
+    else
+        return;
+
+    if (!GDK_IS_PIXBUF(pixbuf))
+        return;
+
+    pixels = gdk_pixbuf_get_pixels (pixbuf);
+    rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+    n_channels = gdk_pixbuf_get_n_channels (pixbuf);
+    for (i = 0; i < 19; i++)
+    {
+        p = pixels + rowstride * (i + 294) + 115 * n_channels;
+        colors[i] = (p[0] << 16) | (p[1] << 8) | p[2]; 
+        /* should we really treat the Alpha channel? */
+        /*if (n_channels == 4)
+            colors[i] = (colors[i] << 8) | p[3];*/
+    }
+}
+
+
+static void
+skin_draw_playlistwin_frame_top(Skin * skin, GdkPixbuf * pix,
+                                gint width, gint height, gboolean focus)
+{
+    /* The title bar skin consists of 2 sets of 4 images, 1 set
+     * for focused state and the other for unfocused. The 4 images
+     * are: 
+     *
+     * a. right corner (25,20)
+     * b. left corner  (25,20)
+     * c. tiler        (25,20)
+     * d. title        (100,20)
+     * 
+     * min allowed width = 100+25+25 = 150
+     */
+
+    gint i, y, c;
+
+    /* get y offset of the pixmap set to use */
+    if (focus)
+        y = 0;
+    else
+        y = 21;
+
+    /* left corner */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 0, y, 0, 0, 25, 20);
+
+    /* titlebar title */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 26, y,
+                     (width - 100) / 2, 0, 100, 20);
+
+    /* titlebar right corner  */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 153, y,
+                     width - 25, 0, 25, 20);
+
+    /* tile draw the remaining frame */
+
+    /* compute tile count */
+    c = (width - (100 + 25 + 25)) / 25;
+
+    for (i = 0; i < c / 2; i++) {
+        /* left of title */
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 127, y,
+                         25 + i * 25, 0, 25, 20);
+
+        /* right of title */
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 127, y,
+                         (width + 100) / 2 + i * 25, 0, 25, 20);
+    }
+
+    if (c & 1) {
+        /* Odd tile count, so one remaining to draw. Here we split
+         * it into two and draw half on either side of the title */
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 127, y,
+                         ((c / 2) * 25) + 25, 0, 12, 20);
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 127, y,
+                         (width / 2) + ((c / 2) * 25) + 50, 0, 13, 20);
+    }
+}
+
+static void
+skin_draw_playlistwin_frame_bottom(Skin * skin, GdkPixbuf * pix,
+                                   gint width, gint height, gboolean focus)
+{
+    /* The bottom frame skin consists of 1 set of 4 images. The 4
+     * images are:
+     *
+     * a. left corner with menu buttons (125,38)
+     * b. visualization window (75,38)
+     * c. right corner with play buttons (150,38)
+     * d. frame tile (25,38)
+     * 
+     * (min allowed width = 125+150+25=300
+     */
+
+    gint i, c;
+
+    /* bottom left corner (menu buttons) */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 0, 72,
+                     0, height - 38, 125, 38);
+
+    c = (width - 275) / 25;
+
+    /* draw visualization window, if width allows */
+    if (c >= 3) {
+        c -= 3;
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 205, 0,
+                         width - (150 + 75), height - 38, 75, 38);
+    }
+
+    /* Bottom right corner (playbuttons etc) */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT,
+                     126, 72, width - 150, height - 38, 150, 38);
+
+    /* Tile draw the remaining undrawn portions */
+    for (i = 0; i < c; i++)
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 179, 0,
+                         125 + i * 25, height - 38, 25, 38);
+}
+
+static void
+skin_draw_playlistwin_frame_sides(Skin * skin, GdkPixbuf * pix,
+                                  gint width, gint height, gboolean focus)
+{
+    /* The side frames consist of 2 tile images. 1 for the left, 1 for
+     * the right. 
+     * a. left  (12,29)
+     * b. right (19,29)
+     */
+
+    gint i;
+
+    /* frame sides */
+    for (i = 0; i < (height - (20 + 38)) / 29; i++) {
+        /* left */
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 0, 42,
+                         0, 20 + i * 29, 12, 29);
+
+        /* right */
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 32, 42,
+                         width - 19, 20 + i * 29, 19, 29);
+    }
+}
+
+
+void
+skin_draw_playlistwin_frame(Skin * skin, GdkPixbuf * pix,
+                            gint width, gint height, gboolean focus)
+{
+    skin_draw_playlistwin_frame_top(skin, pix, width, height, focus);
+    skin_draw_playlistwin_frame_bottom(skin, pix, width, height, focus);
+    skin_draw_playlistwin_frame_sides(skin, pix, width, height, focus);
+}
+
+
+void
+skin_draw_playlistwin_shaded(Skin * skin, GdkPixbuf * pix,
+                             gint width, gboolean focus)
+{
+    /* The shade mode titlebar skin consists of 4 images:
+     * a) left corner               offset (72,42) size (25,14)
+     * b) right corner, focused     offset (99,57) size (50,14)
+     * c) right corner, unfocused   offset (99,42) size (50,14)
+     * d) bar tile                  offset (72,57) size (25,14)
+     */
+
+    gint i;
+
+    /* left corner */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 72, 42, 0, 0, 25, 14);
+
+    /* bar tile */
+    for (i = 0; i < (width - 75) / 25; i++)
+        skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 72, 57,
+                         (i * 25) + 25, 0, 25, 14);
+
+    /* right corner */
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_PLEDIT, 99, focus ? 42 : 57,
+                     width - 50, 0, 50, 14);
+}
+
+
+void
+skin_draw_mainwin_titlebar(Skin * skin, GdkPixbuf * pix,
+                           gboolean shaded, gboolean focus)
+{
+    /* The titlebar skin consists of 2 sets of 2 images, one for for
+     * shaded and the other for unshaded mode, giving a total of 4.
+     * The images are exactly 275x14 pixels, aligned and arranged
+     * vertically on each other in the pixmap in the following order:
+     * 
+     * a) unshaded, focused      offset (27, 0)
+     * b) unshaded, unfocused    offset (27, 15)
+     * c) shaded, focused        offset (27, 29)
+     * d) shaded, unfocused      offset (27, 42)
+     */
+
+    gint y_offset;
+
+    if (shaded) {
+        if (focus)
+            y_offset = 29;
+        else
+            y_offset = 42;
+    }
+    else {
+        if (focus)
+            y_offset = 0;
+        else
+            y_offset = 15;
+    }
+
+    skin_draw_pixbuf(NULL, skin, pix, SKIN_TITLEBAR, 27, y_offset,
+                     0, 0, aud_active_skin->properties.mainwin_width, MAINWIN_TITLEBAR_HEIGHT);
+}
+
+
+void
+skin_set_random_skin(void)
+{
+#if 0
+    SkinNode *node;
+    guint32 randval;
+
+    /* Get a random value to select the skin to use */
+    randval = g_random_int_range(0, g_list_length(skinlist));
+    node = g_list_nth(skinlist, randval)->data;
+    aud_active_skin_load(node->path);
+#endif
+}
+
+
+void ui_skinned_widget_draw(GtkWidget *widget, GdkPixbuf *obj, gint width, gint height, gboolean scale) {
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(obj != NULL);
+
+    if (scale) {
+        GdkPixbuf *image = gdk_pixbuf_scale_simple(obj, width * config.scale_factor, height* aud_cfg->scale_factor, GDK_INTERP_NEAREST);
+        gdk_draw_pixbuf(widget->window, NULL, image, 0, 0, 0, 0, width * config.scale_factor , height * aud_cfg->scale_factor, GDK_RGB_DITHER_NONE, 0, 0);
+        g_object_unref(image);
+    } else {
+        gdk_draw_pixbuf(widget->window, NULL, obj, 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NONE, 0, 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skin.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,244 @@
+/*  Audacious
+ *  Copyright (C) 2005-2007  Audacious development team.
+ *
+ *  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef SKIN_H
+#define SKIN_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#define BMP_DEFAULT_SKIN_PATH \
+  DATA_DIR G_DIR_SEPARATOR_S "Skins" G_DIR_SEPARATOR_S "Default"
+
+typedef enum {
+    SKIN_MAIN = 0,
+    SKIN_CBUTTONS,
+    SKIN_TITLEBAR,
+    SKIN_SHUFREP,
+    SKIN_TEXT,
+    SKIN_VOLUME,
+    SKIN_BALANCE,
+    SKIN_MONOSTEREO,
+    SKIN_PLAYPAUSE,
+    SKIN_NUMBERS,
+    SKIN_POSBAR,
+    SKIN_PLEDIT,
+    SKIN_EQMAIN,
+    SKIN_EQ_EX,
+    SKIN_PIXMAP_COUNT
+} SkinPixmapId;
+
+typedef enum {
+    SKIN_MASK_MAIN = 0,
+    SKIN_MASK_MAIN_SHADE,
+    SKIN_MASK_EQ,
+    SKIN_MASK_EQ_SHADE,
+    SKIN_MASK_COUNT
+} SkinMaskId;
+
+typedef enum {
+    SKIN_PLEDIT_NORMAL = 0,
+    SKIN_PLEDIT_CURRENT,
+    SKIN_PLEDIT_NORMALBG,
+    SKIN_PLEDIT_SELECTEDBG,
+    SKIN_TEXTBG,
+    SKIN_TEXTFG,
+    SKIN_COLOR_COUNT
+} SkinColorId;
+
+typedef struct _SkinProperties {
+	/* this enables the othertext engine, not it's visibility -nenolod */
+	gboolean mainwin_othertext;
+
+	/* Vis properties */
+	gint mainwin_vis_x;
+	gint mainwin_vis_y;
+	gint mainwin_vis_width;
+	gboolean mainwin_vis_visible;
+
+	/* Text properties */
+	gint mainwin_text_x;
+	gint mainwin_text_y;
+	gint mainwin_text_width;
+	gboolean mainwin_text_visible;
+
+	/* Infobar properties */
+	gint mainwin_infobar_x;
+	gint mainwin_infobar_y;
+	gboolean mainwin_othertext_visible;
+
+	gint mainwin_number_0_x;
+	gint mainwin_number_0_y;
+
+	gint mainwin_number_1_x;
+	gint mainwin_number_1_y;
+
+	gint mainwin_number_2_x;
+	gint mainwin_number_2_y;
+
+	gint mainwin_number_3_x;
+	gint mainwin_number_3_y;
+
+	gint mainwin_number_4_x;
+	gint mainwin_number_4_y;
+
+	gint mainwin_playstatus_x;
+	gint mainwin_playstatus_y;
+
+	gint mainwin_volume_x;
+	gint mainwin_volume_y;	
+
+	gint mainwin_balance_x;
+	gint mainwin_balance_y;	
+
+	gint mainwin_position_x;
+	gint mainwin_position_y;
+
+	gint mainwin_previous_x;
+	gint mainwin_previous_y;
+
+	gint mainwin_play_x;
+	gint mainwin_play_y;
+
+	gint mainwin_pause_x;
+	gint mainwin_pause_y;
+
+	gint mainwin_stop_x;
+	gint mainwin_stop_y;
+
+	gint mainwin_next_x;
+	gint mainwin_next_y;
+
+	gint mainwin_eject_x;
+	gint mainwin_eject_y;
+
+	gint mainwin_eqbutton_x;
+	gint mainwin_eqbutton_y;
+
+	gint mainwin_plbutton_x;
+	gint mainwin_plbutton_y;
+
+	gint mainwin_shuffle_x;
+	gint mainwin_shuffle_y;
+
+	gint mainwin_repeat_x;
+	gint mainwin_repeat_y;
+
+	gint mainwin_about_x;
+	gint mainwin_about_y;
+
+	gint mainwin_minimize_x;
+	gint mainwin_minimize_y;
+
+	gint mainwin_shade_x;
+	gint mainwin_shade_y;
+
+	gint mainwin_close_x;
+	gint mainwin_close_y;
+
+	gint mainwin_width;
+	gint mainwin_height;
+
+	gboolean mainwin_menurow_visible;
+	gboolean mainwin_othertext_is_status;
+
+	gint textbox_bitmap_font_width;
+	gint textbox_bitmap_font_height;
+} SkinProperties;
+
+#define SKIN_PIXMAP(x)  ((SkinPixmap *)(x))
+typedef struct _SkinPixmap {
+    GdkPixbuf *pixbuf;
+
+    /* The real size of the pixmap */
+    gint width, height;
+
+    /* The size of the pixmap from the current skin,
+       which might be smaller */
+    gint current_width, current_height;
+} SkinPixmap;
+
+
+#define SKIN(x)  ((Skin *)(x))
+typedef struct _Skin {
+    GMutex *lock;
+    gchar *path;
+    gchar *def_path;
+    SkinPixmap pixmaps[SKIN_PIXMAP_COUNT];
+    GdkColor textbg[6], def_textbg[6];
+    GdkColor textfg[6], def_textfg[6];
+    GdkColor *colors[SKIN_COLOR_COUNT];
+    guchar vis_color[24][3];
+    GdkBitmap *masks[SKIN_MASK_COUNT];
+    GdkBitmap *scaled_masks[SKIN_MASK_COUNT];
+    SkinProperties properties;
+} Skin;
+
+extern Skin *aud_active_skin;
+
+gboolean init_skins(const gchar * path);
+void cleanup_skins(void);
+
+gboolean aud_active_skin_load(const gchar * path);
+gboolean aud_active_skin_reload(void);
+
+Skin *skin_new(void);
+gboolean skin_load(Skin * skin, const gchar * path);
+gboolean skin_reload_forced(void);
+void skin_reload(Skin * skin);
+void skin_free(Skin * skin);
+
+GdkBitmap *skin_get_mask(Skin * skin, SkinMaskId mi);
+GdkColor *skin_get_color(Skin * skin, SkinColorId color_id);
+
+void skin_get_viscolor(Skin * skin, guchar vis_color[24][3]);
+gint skin_get_id(void);
+void skin_draw_pixbuf(GtkWidget *widget, Skin * skin, GdkPixbuf * pix,
+                 SkinPixmapId pixmap_id,
+                 gint xsrc, gint ysrc, gint xdest, gint ydest,
+                 gint width, gint height);
+
+void skin_get_eq_spline_colors(Skin * skin, guint32 colors[19]);
+void skin_install_skin(const gchar * path);
+
+void skin_draw_playlistwin_shaded(Skin * skin, GdkPixbuf * pix,
+                                  gint width, gboolean focus);
+void skin_draw_playlistwin_frame(Skin * skin, GdkPixbuf * pix,
+                                 gint width, gint height, gboolean focus);
+
+void skin_draw_mainwin_titlebar(Skin * skin, GdkPixbuf * pix,
+                                gboolean shaded, gboolean focus);
+
+
+void skin_parse_hints(Skin * skin, gchar *path_p);
+
+
+void skin_set_random_skin(void);
+
+
+void ui_skinned_widget_draw(GtkWidget *widget, GdkPixbuf *obj, gint width, gint height, gboolean scale);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_button.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,536 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skinned_button.h"
+#include "skins_cfg.h"
+#include <math.h>
+
+#define UI_SKINNED_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ui_skinned_button_get_type(), UiSkinnedButtonPrivate))
+typedef struct _UiSkinnedButtonPrivate UiSkinnedButtonPrivate;
+
+enum {
+    PRESSED,
+    RELEASED,
+    CLICKED,
+    DOUBLED,
+    REDRAW,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedButtonPrivate {
+    //Skinned part
+    GdkGC            *gc;
+    gint             w;
+    gint             h;
+    SkinPixmapId     skin_index1;
+    SkinPixmapId     skin_index2;
+    gboolean         scaled;
+    gint             move_x, move_y;
+
+    gint             nx, ny, px, py;
+    //Toogle button needs also those
+    gint             pnx, pny, ppx, ppy;
+};
+
+
+static GtkWidgetClass *parent_class = NULL;
+static void ui_skinned_button_class_init(UiSkinnedButtonClass *klass);
+static void ui_skinned_button_init(UiSkinnedButton *button);
+static void ui_skinned_button_destroy(GtkObject *object);
+static void ui_skinned_button_realize(GtkWidget *widget);
+static void ui_skinned_button_unrealize(GtkWidget *widget);
+static void ui_skinned_button_map(GtkWidget *widget);
+static void ui_skinned_button_unmap(GtkWidget *widget);
+static void ui_skinned_button_size_request(GtkWidget *widget, GtkRequisition *requisition);
+static gint ui_skinned_button_expose(GtkWidget *widget,GdkEventExpose *event);
+
+static void ui_skinned_button_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
+static void ui_skinned_button_update_state(UiSkinnedButton *button);
+
+static guint button_signals[LAST_SIGNAL] = { 0 };
+static gint ui_skinned_button_button_press(GtkWidget *widget, GdkEventButton *event);
+static gint ui_skinned_button_button_release(GtkWidget *widget, GdkEventButton *event);
+static void button_pressed(UiSkinnedButton *button);
+static void button_released(UiSkinnedButton *button);
+static void ui_skinned_button_pressed(UiSkinnedButton *button);
+static void ui_skinned_button_released(UiSkinnedButton *button);
+static void ui_skinned_button_clicked(UiSkinnedButton *button);
+static void ui_skinned_button_set_pressed (UiSkinnedButton *button, gboolean pressed);
+
+static void ui_skinned_button_toggle_scaled(UiSkinnedButton *button);
+
+static gint ui_skinned_button_enter_notify(GtkWidget *widget, GdkEventCrossing *event);
+static gint ui_skinned_button_leave_notify(GtkWidget *widget, GdkEventCrossing *event);
+static void ui_skinned_button_redraw(UiSkinnedButton *button);
+
+GType ui_skinned_button_get_type() {
+    static GType button_type = 0;
+    if (!button_type) {
+        static const GTypeInfo button_info = {
+            sizeof (UiSkinnedButtonClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_button_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedButton),
+            0,
+            (GInstanceInitFunc) ui_skinned_button_init,
+        };
+        button_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedButton", &button_info, 0);
+    }
+
+    return button_type;
+}
+
+static void ui_skinned_button_class_init (UiSkinnedButtonClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_button_destroy;
+
+    widget_class->realize = ui_skinned_button_realize;
+    widget_class->unrealize = ui_skinned_button_unrealize;
+    widget_class->map = ui_skinned_button_map;
+    widget_class->unmap = ui_skinned_button_unmap;
+    widget_class->expose_event = ui_skinned_button_expose;
+    widget_class->size_request = ui_skinned_button_size_request;
+    widget_class->size_allocate = ui_skinned_button_size_allocate;
+    widget_class->button_press_event = ui_skinned_button_button_press;
+    widget_class->button_release_event = ui_skinned_button_button_release;
+    widget_class->enter_notify_event = ui_skinned_button_enter_notify;
+    widget_class->leave_notify_event = ui_skinned_button_leave_notify;
+
+    klass->pressed = button_pressed;
+    klass->released = button_released;
+    klass->clicked = NULL;
+    klass->scaled = ui_skinned_button_toggle_scaled;
+    klass->redraw = ui_skinned_button_redraw;
+
+    button_signals[PRESSED] = 
+        g_signal_new ("pressed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (UiSkinnedButtonClass, pressed), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    button_signals[RELEASED] = 
+        g_signal_new ("released", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (UiSkinnedButtonClass, released), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    button_signals[CLICKED] = 
+        g_signal_new ("clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedButtonClass, clicked), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    button_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedButtonClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    button_signals[REDRAW] = 
+        g_signal_new ("redraw", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedButtonClass, redraw), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedButtonPrivate));
+}
+
+static void ui_skinned_button_init (UiSkinnedButton *button) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    button->inside = FALSE;
+    button->type = TYPE_NOT_SET;
+    priv->move_x = 0;
+    priv->move_y = 0;
+    button->event_window = NULL;
+}
+
+static void ui_skinned_button_destroy (GtkObject *object) {
+    UiSkinnedButton *button;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_BUTTON (object));
+
+    button = UI_SKINNED_BUTTON(object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_button_realize (GtkWidget *widget) {
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_BUTTON(widget));
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+
+    if (button->type == TYPE_SMALL || button->type == TYPE_NOT_SET) {
+        widget->window = gtk_widget_get_parent_window (widget);
+        g_object_ref (widget->window);
+        attributes.wclass = GDK_INPUT_ONLY;
+        attributes_mask = GDK_WA_X | GDK_WA_Y;
+        button->event_window = gdk_window_new (widget->window, &attributes, attributes_mask);
+        GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
+        gdk_window_set_user_data(button->event_window, widget);
+    } else {
+        attributes.visual = gtk_widget_get_visual(widget);
+        attributes.colormap = gtk_widget_get_colormap(widget);
+        attributes.wclass = GDK_INPUT_OUTPUT;
+        attributes.event_mask |= GDK_EXPOSURE_MASK;
+        attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+        widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+        GTK_WIDGET_UNSET_FLAGS (widget, GTK_NO_WINDOW);
+        gdk_window_set_user_data(widget->window, widget);
+    }
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+}
+
+static void ui_skinned_button_unrealize (GtkWidget *widget) {
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+
+    if ( button->event_window != NULL )
+    {
+      gdk_window_set_user_data( button->event_window , NULL );
+      gdk_window_destroy( button->event_window );
+      button->event_window = NULL;
+    }
+
+    if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+        (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void ui_skinned_button_map (GtkWidget *widget)
+{
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+
+    if (button->event_window != NULL)
+      gdk_window_show (button->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->map)
+      (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+}
+
+static void ui_skinned_button_unmap (GtkWidget *widget)
+{
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+
+    if (button->event_window != NULL)
+      gdk_window_hide (button->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->unmap)
+      (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static void ui_skinned_button_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE(widget);
+    requisition->width = priv->w*(priv->scaled ? config.scale_factor : 1);
+    requisition->height = priv->h*(priv->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_button_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    widget->allocation = *allocation;
+    widget->allocation.x = ceil(widget->allocation.x*(priv->scaled ? config.scale_factor : 1));
+    widget->allocation.y = ceil(widget->allocation.y*(priv->scaled ? config.scale_factor : 1));
+
+    if (GTK_WIDGET_REALIZED (widget))
+    {
+        if ( button->event_window != NULL )
+            gdk_window_move_resize(button->event_window, ceil(allocation->x*(priv->scaled ? config.scale_factor : 1)), ceil(allocation->y*(priv->scaled ? config.scale_factor : 1)), allocation->width, allocation->height);
+        else
+            gdk_window_move_resize(widget->window, ceil(allocation->x*(priv->scaled ? config.scale_factor : 1)), ceil(allocation->y*(priv->scaled ? config.scale_factor : 1)), allocation->width, allocation->height);
+    }
+
+    if (button->x + priv->move_x == ceil(widget->allocation.x/(priv->scaled ? config.scale_factor : 1)))
+        priv->move_x = 0;
+    if (button->y + priv->move_y == ceil(widget->allocation.y/(priv->scaled ? config.scale_factor : 1)))
+        priv->move_y = 0;
+
+    button->x = ceil(widget->allocation.x/(priv->scaled ? config.scale_factor : 1));
+    button->y = ceil(widget->allocation.y/(priv->scaled ? config.scale_factor : 1));
+}
+
+static gboolean ui_skinned_button_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_BUTTON (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedButton *button = UI_SKINNED_BUTTON (widget);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    g_return_val_if_fail (priv->w > 0 && priv->h > 0, FALSE);
+
+    //TYPE_SMALL doesn't have its own face
+    if (button->type == TYPE_SMALL || button->type == TYPE_NOT_SET)
+        return FALSE;
+
+    /* paranoia */
+    if (button->event_window != NULL)
+        return FALSE;
+
+    GdkPixbuf *obj;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, priv->w, priv->h);
+
+    switch (button->type) {
+        case TYPE_PUSH:
+            skin_draw_pixbuf(widget, aud_active_skin, obj,
+                             button->pressed ? priv->skin_index2 : priv->skin_index1,
+                             button->pressed ? priv->px : priv->nx,
+                             button->pressed ? priv->py : priv->ny,
+                             0, 0, priv->w, priv->h);
+            break;
+        case TYPE_TOGGLE:
+            if (button->inside)
+                skin_draw_pixbuf(widget, aud_active_skin, obj,
+                                 button->pressed ? priv->skin_index2 : priv->skin_index1,
+                                 button->pressed ? priv->ppx : priv->pnx,
+                                 button->pressed ? priv->ppy : priv->pny,
+                                 0, 0, priv->w, priv->h);
+            else
+                skin_draw_pixbuf(widget, aud_active_skin, obj,
+                                 button->pressed ? priv->skin_index2 : priv->skin_index1,
+                                 button->pressed ? priv->px : priv->nx,
+                                 button->pressed ? priv->py : priv->ny,
+                                 0, 0, priv->w, priv->h);
+            break;
+        default:
+            break;
+    }
+
+    ui_skinned_widget_draw(widget, obj, priv->w, priv->h, priv->scaled);
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+GtkWidget* ui_skinned_button_new () {
+    UiSkinnedButton *button = g_object_new (ui_skinned_button_get_type (), NULL);
+
+    return GTK_WIDGET(button);
+}
+
+void ui_skinned_push_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h, gint nx, gint ny, gint px, gint py, SkinPixmapId si) {
+
+    UiSkinnedButton *sbutton = UI_SKINNED_BUTTON(button);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE(sbutton);
+    priv->w = w;
+    priv->h = h;
+    sbutton->x = x;
+    sbutton->y = y;
+    priv->nx = nx;
+    priv->ny = ny;
+    priv->px = px;
+    priv->py = py;
+    sbutton->type = TYPE_PUSH;
+    priv->skin_index1 = si;
+    priv->skin_index2 = si;
+    priv->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(button), sbutton->x, sbutton->y);
+}
+
+void ui_skinned_toggle_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h, gint nx, gint ny, gint px, gint py, gint pnx, gint pny, gint ppx, gint ppy, SkinPixmapId si) {
+
+    UiSkinnedButton *sbutton = UI_SKINNED_BUTTON(button);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE(sbutton);
+    priv->w = w;
+    priv->h = h;
+    sbutton->x = x;
+    sbutton->y = y;
+    priv->nx = nx;
+    priv->ny = ny;
+    priv->px = px;
+    priv->py = py;
+    priv->pnx = pnx;
+    priv->pny = pny;
+    priv->ppx = ppx;
+    priv->ppy = ppy;
+    sbutton->type = TYPE_TOGGLE;
+    priv->skin_index1 = si;
+    priv->skin_index2 = si;
+    priv->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(button), sbutton->x, sbutton->y);
+}
+
+void ui_skinned_small_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h) {
+
+    UiSkinnedButton *sbutton = UI_SKINNED_BUTTON(button);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE(sbutton);
+    priv->w = w;
+    priv->h = h;
+    sbutton->x = x;
+    sbutton->y = y;
+    sbutton->type = TYPE_SMALL;
+    priv->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(button), sbutton->x, sbutton->y);
+}
+
+static void button_pressed(UiSkinnedButton *button) {
+    button->button_down = TRUE;
+    ui_skinned_button_update_state(button);
+}
+
+static void button_released(UiSkinnedButton *button) {
+    button->button_down = FALSE;
+    if(button->hover) ui_skinned_button_clicked(button);
+    ui_skinned_button_update_state(button);
+}
+
+static void ui_skinned_button_update_state(UiSkinnedButton *button) {
+    ui_skinned_button_set_pressed(button, button->button_down); 
+}
+
+static void ui_skinned_button_set_pressed (UiSkinnedButton *button, gboolean pressed) {
+    if (pressed != button->pressed) {
+        button->pressed = pressed;
+        gtk_widget_queue_draw(GTK_WIDGET(button));
+    }
+}
+
+static gboolean ui_skinned_button_button_press(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedButton *button;
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        button = UI_SKINNED_BUTTON(widget);
+
+        if (event->button == 1)
+            ui_skinned_button_pressed (button);
+        else if (event->button == 3) {
+            event->x = event->x + button->x;
+            event->y = event->y + button->y;
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_button_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedButton *button;
+    if (event->button == 1) {
+            button = UI_SKINNED_BUTTON(widget);
+            ui_skinned_button_released(button);
+    }
+    return TRUE;
+}
+
+static void ui_skinned_button_pressed(UiSkinnedButton *button) {
+    g_return_if_fail(UI_SKINNED_IS_BUTTON(button));
+    g_signal_emit(button, button_signals[PRESSED], 0);
+}
+
+static void ui_skinned_button_released(UiSkinnedButton *button) {
+    g_return_if_fail(UI_SKINNED_IS_BUTTON(button));
+    g_signal_emit(button, button_signals[RELEASED], 0);
+}
+
+static void ui_skinned_button_clicked(UiSkinnedButton *button) {
+    g_return_if_fail(UI_SKINNED_IS_BUTTON(button));
+    button->inside = !button->inside;
+    g_signal_emit(button, button_signals[CLICKED], 0);
+}
+
+static gboolean ui_skinned_button_enter_notify(GtkWidget *widget, GdkEventCrossing *event) {
+    UiSkinnedButton *button;
+
+    button = UI_SKINNED_BUTTON(widget);
+    button->hover = TRUE;
+    if(button->button_down) ui_skinned_button_set_pressed(button, TRUE);
+
+    return FALSE;
+}
+
+static gboolean ui_skinned_button_leave_notify(GtkWidget *widget, GdkEventCrossing *event) {
+    UiSkinnedButton *button;
+
+    button = UI_SKINNED_BUTTON (widget);
+    button->hover = FALSE;
+    if(button->button_down) ui_skinned_button_set_pressed(button, FALSE);
+
+    return FALSE;
+}
+
+static void ui_skinned_button_toggle_scaled(UiSkinnedButton *button) {
+    GtkWidget *widget = GTK_WIDGET (button);
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    priv->scaled = !priv->scaled;
+
+    gtk_widget_set_size_request(widget, priv->w*(priv->scaled ? config.scale_factor : 1), priv->h*(priv->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(widget);
+}
+
+static void ui_skinned_button_redraw(UiSkinnedButton *button) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    if (priv->move_x || priv->move_y)
+        gtk_fixed_move(GTK_FIXED(gtk_widget_get_parent(GTK_WIDGET(button))), GTK_WIDGET(button),
+                       button->x+priv->move_x, button->y+priv->move_y);
+
+    gtk_widget_queue_draw(GTK_WIDGET(button));
+}
+
+
+void ui_skinned_set_push_button_data(GtkWidget *button, gint nx, gint ny, gint px, gint py) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE(button);
+    if (nx > -1) priv->nx = nx;
+    if (ny > -1) priv->ny = ny;
+    if (px > -1) priv->px = px;
+    if (py > -1) priv->py = py;
+    gtk_widget_queue_draw(button);
+}
+
+void ui_skinned_button_set_skin_index(GtkWidget *button, SkinPixmapId si) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    priv->skin_index1 = priv->skin_index2 = si;
+}
+
+void ui_skinned_button_set_skin_index1(GtkWidget *button, SkinPixmapId si) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    priv->skin_index1 = si;
+}
+
+void ui_skinned_button_set_skin_index2(GtkWidget *button, SkinPixmapId si) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    priv->skin_index2 = si;
+}
+
+void ui_skinned_button_move_relative(GtkWidget *button, gint x, gint y) {
+    UiSkinnedButtonPrivate *priv = UI_SKINNED_BUTTON_GET_PRIVATE (button);
+    priv->move_x += x;
+    priv->move_y += y;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_button.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,74 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_BUTTON_H
+#define AUDACIOUS_UI_SKINNED_BUTTON_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#define UI_SKINNED_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ui_skinned_button_get_type(), UiSkinnedButton))
+#define UI_SKINNED_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  ui_skinned_button_get_type(), UiSkinnedButtonClass))
+#define UI_SKINNED_IS_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ui_skinned_button_get_type()))
+
+typedef struct _UiSkinnedButton		UiSkinnedButton;
+typedef struct _UiSkinnedButtonClass	UiSkinnedButtonClass;
+
+enum {
+    TYPE_NOT_SET,
+    TYPE_PUSH,
+    TYPE_TOGGLE,
+    TYPE_SMALL
+};
+
+struct _UiSkinnedButton {
+    GtkWidget widget;
+
+    GdkWindow *event_window;
+    gboolean button_down;
+    gboolean pressed;
+    gboolean hover;
+    gboolean inside;
+    gint type;
+    gint x, y;
+};
+
+struct _UiSkinnedButtonClass {
+    GtkWidgetClass          parent_class;
+    void (* pressed)       (UiSkinnedButton *button);
+    void (* released)      (UiSkinnedButton *button);
+    void (* clicked)       (UiSkinnedButton *button);
+    void (* right_clicked) (UiSkinnedButton *button);
+    void (* scaled)        (UiSkinnedButton *button);
+    void (* redraw)        (UiSkinnedButton *button);
+};
+
+GType ui_skinned_button_get_type(void) G_GNUC_CONST;
+GtkWidget* ui_skinned_button_new();
+void ui_skinned_push_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h, gint nx, gint ny, gint px, gint py, SkinPixmapId si);
+void ui_skinned_set_push_button_data(GtkWidget *button, gint nx, gint ny, gint px, gint py);
+void ui_skinned_toggle_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h, gint nx, gint ny, gint px, gint py, gint pnx, gint pny, gint ppx, gint ppy, SkinPixmapId si);
+void ui_skinned_small_button_setup(GtkWidget *button, GtkWidget *fixed, gint x, gint y, gint w, gint h);
+void ui_skinned_button_set_skin_index(GtkWidget *button, SkinPixmapId si);
+void ui_skinned_button_set_skin_index1(GtkWidget *button, SkinPixmapId si);
+void ui_skinned_button_set_skin_index2(GtkWidget *button, SkinPixmapId si);
+void ui_skinned_button_move_relative(GtkWidget *button, gint x, gint y);
+
+#endif /* AUDACIOUS_UI_SKINNED_BUTTON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_equalizer_graph.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,307 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
+#include "ui_skin.h"
+#include "ui_skinned_equalizer_graph.h"
+#include "skins_cfg.h"
+#include <audacious/plugin.h>
+
+#define UI_TYPE_SKINNED_EQUALIZER_GRAPH           (ui_skinned_equalizer_graph_get_type())
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_skinned_equalizer_graph_class_init         (UiSkinnedEqualizerGraphClass *klass);
+static void ui_skinned_equalizer_graph_init               (UiSkinnedEqualizerGraph *equalizer_graph);
+static void ui_skinned_equalizer_graph_destroy            (GtkObject *object);
+static void ui_skinned_equalizer_graph_realize            (GtkWidget *widget);
+static void ui_skinned_equalizer_graph_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_equalizer_graph_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_equalizer_graph_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_skinned_equalizer_graph_toggle_scaled  (UiSkinnedEqualizerGraph *equalizer_graph);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint equalizer_graph_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_equalizer_graph_get_type() {
+    static GType equalizer_graph_type = 0;
+    if (!equalizer_graph_type) {
+        static const GTypeInfo equalizer_graph_info = {
+            sizeof (UiSkinnedEqualizerGraphClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_equalizer_graph_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedEqualizerGraph),
+            0,
+            (GInstanceInitFunc) ui_skinned_equalizer_graph_init,
+        };
+        equalizer_graph_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedEqualizerGraph", &equalizer_graph_info, 0);
+    }
+
+    return equalizer_graph_type;
+}
+
+static void ui_skinned_equalizer_graph_class_init(UiSkinnedEqualizerGraphClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_equalizer_graph_destroy;
+
+    widget_class->realize = ui_skinned_equalizer_graph_realize;
+    widget_class->expose_event = ui_skinned_equalizer_graph_expose;
+    widget_class->size_request = ui_skinned_equalizer_graph_size_request;
+    widget_class->size_allocate = ui_skinned_equalizer_graph_size_allocate;
+
+    klass->scaled = ui_skinned_equalizer_graph_toggle_scaled;
+
+    equalizer_graph_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedEqualizerGraphClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_skinned_equalizer_graph_init(UiSkinnedEqualizerGraph *equalizer_graph) {
+    equalizer_graph->width = 113;
+    equalizer_graph->height = 19;
+}
+
+GtkWidget* ui_skinned_equalizer_graph_new(GtkWidget *fixed, gint x, gint y) {
+    UiSkinnedEqualizerGraph *equalizer_graph = g_object_new (ui_skinned_equalizer_graph_get_type (), NULL);
+
+    equalizer_graph->x = x;
+    equalizer_graph->y = y;
+    equalizer_graph->skin_index = SKIN_EQMAIN;
+    equalizer_graph->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(equalizer_graph), equalizer_graph->x, equalizer_graph->y);
+
+    return GTK_WIDGET(equalizer_graph);
+}
+
+static void ui_skinned_equalizer_graph_destroy(GtkObject *object) {
+    UiSkinnedEqualizerGraph *equalizer_graph;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_EQUALIZER_GRAPH (object));
+
+    equalizer_graph = UI_SKINNED_EQUALIZER_GRAPH (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_equalizer_graph_realize(GtkWidget *widget) {
+    UiSkinnedEqualizerGraph *equalizer_graph;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_EQUALIZER_GRAPH(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    equalizer_graph = UI_SKINNED_EQUALIZER_GRAPH(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_equalizer_graph_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedEqualizerGraph *equalizer_graph = UI_SKINNED_EQUALIZER_GRAPH(widget);
+
+    requisition->width = equalizer_graph->width*(equalizer_graph->scaled ? config.scale_factor : 1);
+    requisition->height = equalizer_graph->height*(equalizer_graph->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_equalizer_graph_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedEqualizerGraph *equalizer_graph = UI_SKINNED_EQUALIZER_GRAPH (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (equalizer_graph->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (equalizer_graph->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    equalizer_graph->x = widget->allocation.x/(equalizer_graph->scaled ? config.scale_factor : 1);
+    equalizer_graph->y = widget->allocation.y/(equalizer_graph->scaled ? config.scale_factor : 1);
+}
+
+void
+init_spline(gfloat * x, gfloat * y, gint n, gfloat * y2)
+{
+    gint i, k;
+    gfloat p, qn, sig, un, *u;
+
+    u = (gfloat *) g_malloc(n * sizeof(gfloat));
+
+    y2[0] = u[0] = 0.0;
+
+    for (i = 1; i < n - 1; i++) {
+        sig = ((gfloat) x[i] - x[i - 1]) / ((gfloat) x[i + 1] - x[i - 1]);
+        p = sig * y2[i - 1] + 2.0;
+        y2[i] = (sig - 1.0) / p;
+        u[i] =
+            (((gfloat) y[i + 1] - y[i]) / (x[i + 1] - x[i])) -
+            (((gfloat) y[i] - y[i - 1]) / (x[i] - x[i - 1]));
+        u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
+    }
+    qn = un = 0.0;
+
+    y2[n - 1] = (un - qn * u[n - 2]) / (qn * y2[n - 2] + 1.0);
+    for (k = n - 2; k >= 0; k--)
+        y2[k] = y2[k] * y2[k + 1] + u[k];
+    g_free(u);
+}
+
+gfloat
+eval_spline(gfloat xa[], gfloat ya[], gfloat y2a[], gint n, gfloat x)
+{
+    gint klo, khi, k;
+    gfloat h, b, a;
+
+    klo = 0;
+    khi = n - 1;
+    while (khi - klo > 1) {
+        k = (khi + klo) >> 1;
+        if (xa[k] > x)
+            khi = k;
+        else
+            klo = k;
+    }
+    h = xa[khi] - xa[klo];
+    a = (xa[khi] - x) / h;
+    b = (x - xa[klo]) / h;
+    return (a * ya[klo] + b * ya[khi] +
+            ((a * a * a - a) * y2a[klo] +
+             (b * b * b - b) * y2a[khi]) * (h * h) / 6.0);
+}
+
+static gboolean ui_skinned_equalizer_graph_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_GRAPH (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedEqualizerGraph *equalizer_graph = UI_SKINNED_EQUALIZER_GRAPH (widget);
+    g_return_val_if_fail (equalizer_graph->width > 0 && equalizer_graph->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, equalizer_graph->width, equalizer_graph->height);
+
+    guint32 cols[19], rowstride;
+    gint i, y, ymin, ymax, py = 0;
+    gfloat x[] = { 0, 11, 23, 35, 47, 59, 71, 83, 97, 109 }, yf[10];
+    guchar* pixels, *p;
+    gint n_channels;
+    /*
+     * This avoids the init_spline() function to be inlined.
+     * Inlining the function caused troubles when compiling with
+     * `-O' (at least on FreeBSD).
+     */
+    void (*__init_spline) (gfloat *, gfloat *, gint, gfloat *) = init_spline;
+
+    skin_draw_pixbuf(widget, aud_active_skin, obj, equalizer_graph->skin_index, 0, 294, 0, 0,
+                     equalizer_graph->width, equalizer_graph->height);
+    skin_draw_pixbuf(widget, aud_active_skin, obj, equalizer_graph->skin_index, 0, 314,
+                     0, 9 + ((aud_cfg->equalizer_preamp * 9) / 20),
+                     equalizer_graph->width, 1);
+
+    skin_get_eq_spline_colors(aud_active_skin, cols);
+
+    __init_spline(x, aud_cfg->equalizer_bands, 10, yf);
+    for (i = 0; i < 109; i++) {
+        y = 9 -
+            (gint) ((eval_spline(x, aud_cfg->equalizer_bands, yf, 10, i) *
+                     9.0) / EQUALIZER_MAX_GAIN);
+        if (y < 0)
+            y = 0;
+        if (y > 18)
+            y = 18;
+        if (!i)
+            py = y;
+        if (y < py) {
+            ymin = y;
+            ymax = py;
+        }
+        else {
+            ymin = py;
+            ymax = y;
+        }
+        py = y;
+
+        pixels = gdk_pixbuf_get_pixels(obj);
+        rowstride = gdk_pixbuf_get_rowstride(obj);
+        n_channels = gdk_pixbuf_get_n_channels(obj); 
+
+        for (y = ymin; y <= ymax; y++) 
+        {
+            p = pixels + (y * rowstride) + (( i + 2) * n_channels); 
+            p[0] = (cols[y] & 0xff0000) >> 16;
+            p[1] = (cols[y] & 0x00ff00) >> 8;
+            p[2] = (cols[y] & 0x0000ff);
+            /* do we really need to treat the alpha channel? */
+            /*if (n_channels == 4)
+                  p[3] = cols[y] >> 24;*/
+        }
+    }
+
+    ui_skinned_widget_draw(widget, obj, equalizer_graph->width, equalizer_graph->height, equalizer_graph->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void ui_skinned_equalizer_graph_toggle_scaled(UiSkinnedEqualizerGraph *equalizer_graph) {
+    GtkWidget *widget = GTK_WIDGET (equalizer_graph);
+
+    equalizer_graph->scaled = !equalizer_graph->scaled;
+    gtk_widget_set_size_request(widget, equalizer_graph->width*(equalizer_graph->scaled ? config.scale_factor : 1),
+                                        equalizer_graph->height*(equalizer_graph->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(equalizer_graph));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_equalizer_graph.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,65 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_EQUALIZER_GRAPH_H
+#define AUDACIOUS_UI_SKINNED_EQUALIZER_GRAPH_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EQUALIZER_MAX_GAIN 12.0
+
+#define UI_SKINNED_EQUALIZER_GRAPH(obj)          GTK_CHECK_CAST (obj, ui_skinned_equalizer_graph_get_type (), UiSkinnedEqualizerGraph)
+#define UI_SKINNED_EQUALIZER_GRAPH_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_equalizer_graph_get_type (), UiSkinnedEqualizerGraphClass)
+#define UI_SKINNED_IS_EQUALIZER_GRAPH(obj)       GTK_CHECK_TYPE (obj, ui_skinned_equalizer_graph_get_type ())
+
+typedef struct _UiSkinnedEqualizerGraph        UiSkinnedEqualizerGraph;
+typedef struct _UiSkinnedEqualizerGraphClass   UiSkinnedEqualizerGraphClass;
+
+struct _UiSkinnedEqualizerGraph {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    SkinPixmapId     skin_index;
+    gboolean         scaled;
+};
+
+struct _UiSkinnedEqualizerGraphClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)        (UiSkinnedEqualizerGraph *eq_graph);
+};
+
+GtkWidget* ui_skinned_equalizer_graph_new(GtkWidget *fixed, gint x, gint y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_EQUALIZER_GRAPH_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_equalizer_slider.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,400 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
+#include "ui_skin.h"
+#if 0
+#include "ui_equalizer.h"
+#endif
+#include "ui_main.h"
+#include "skins_cfg.h"
+#include "ui_skinned_equalizer_slider.h"
+#include <glib/gi18n.h>
+
+#define UI_TYPE_SKINNED_EQUALIZER_SLIDER           (ui_skinned_equalizer_slider_get_type())
+#define UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UI_TYPE_SKINNED_EQUALIZER_SLIDER, UiSkinnedEqualizerSliderPrivate))
+typedef struct _UiSkinnedEqualizerSliderPrivate UiSkinnedEqualizerSliderPrivate;
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedEqualizerSliderPrivate {
+    SkinPixmapId     skin_index;
+    gboolean         scaled;
+    gint             position;
+    gint             width, height;
+    gboolean         pressed;
+    gint             drag_y;
+    gfloat           value; /* store gain as is to prevent truncation --asphyx */
+};
+
+static void ui_skinned_equalizer_slider_class_init         (UiSkinnedEqualizerSliderClass *klass);
+static void ui_skinned_equalizer_slider_init               (UiSkinnedEqualizerSlider *equalizer_slider);
+static void ui_skinned_equalizer_slider_destroy            (GtkObject *object);
+static void ui_skinned_equalizer_slider_realize            (GtkWidget *widget);
+static void ui_skinned_equalizer_slider_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_equalizer_slider_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_equalizer_slider_expose         (GtkWidget *widget, GdkEventExpose *event);
+static gboolean ui_skinned_equalizer_slider_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_equalizer_slider_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_equalizer_slider_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static gboolean ui_skinned_equalizer_slider_scroll         (GtkWidget *widget, GdkEventScroll *event);
+static void ui_skinned_equalizer_slider_toggle_scaled      (UiSkinnedEqualizerSlider *equalizer_slider);
+void ui_skinned_equalizer_slider_set_mainwin_text          (UiSkinnedEqualizerSlider * es);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint equalizer_slider_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_equalizer_slider_get_type() {
+    static GType equalizer_slider_type = 0;
+    if (!equalizer_slider_type) {
+        static const GTypeInfo equalizer_slider_info = {
+            sizeof (UiSkinnedEqualizerSliderClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_equalizer_slider_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedEqualizerSlider),
+            0,
+            (GInstanceInitFunc) ui_skinned_equalizer_slider_init,
+        };
+        equalizer_slider_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedEqualizerSlider", &equalizer_slider_info, 0);
+    }
+
+    return equalizer_slider_type;
+}
+
+static void ui_skinned_equalizer_slider_class_init(UiSkinnedEqualizerSliderClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_equalizer_slider_destroy;
+
+    widget_class->realize = ui_skinned_equalizer_slider_realize;
+    widget_class->expose_event = ui_skinned_equalizer_slider_expose;
+    widget_class->size_request = ui_skinned_equalizer_slider_size_request;
+    widget_class->size_allocate = ui_skinned_equalizer_slider_size_allocate;
+    widget_class->button_press_event = ui_skinned_equalizer_slider_button_press;
+    widget_class->button_release_event = ui_skinned_equalizer_slider_button_release;
+    widget_class->motion_notify_event = ui_skinned_equalizer_slider_motion_notify;
+    widget_class->scroll_event = ui_skinned_equalizer_slider_scroll;
+
+    klass->scaled = ui_skinned_equalizer_slider_toggle_scaled;
+
+    equalizer_slider_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedEqualizerSliderClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedEqualizerSliderPrivate));
+}
+
+static void ui_skinned_equalizer_slider_init(UiSkinnedEqualizerSlider *equalizer_slider) {
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(equalizer_slider);
+    priv->pressed = FALSE;
+}
+
+GtkWidget* ui_skinned_equalizer_slider_new(GtkWidget *fixed, gint x, gint y) {
+    UiSkinnedEqualizerSlider *es = g_object_new (ui_skinned_equalizer_slider_get_type (), NULL);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(es);
+
+    es->x = x;
+    es->y = y;
+    priv->width = 14;
+    priv->height = 63;
+    priv->skin_index = SKIN_EQMAIN;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(es), es->x, es->y);
+
+    return GTK_WIDGET(es);
+}
+
+static void ui_skinned_equalizer_slider_destroy(GtkObject *object) {
+    UiSkinnedEqualizerSlider *equalizer_slider;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (object));
+
+    equalizer_slider = UI_SKINNED_EQUALIZER_SLIDER (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_equalizer_slider_realize(GtkWidget *widget) {
+    UiSkinnedEqualizerSlider *equalizer_slider;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    equalizer_slider = UI_SKINNED_EQUALIZER_SLIDER(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                             GDK_POINTER_MOTION_MASK | GDK_SCROLL_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_equalizer_slider_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+
+    requisition->width = priv->width*(priv->scaled ? config.scale_factor : 1);
+    requisition->height = priv->height*(priv->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_equalizer_slider_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedEqualizerSlider *equalizer_slider = UI_SKINNED_EQUALIZER_SLIDER (widget);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(equalizer_slider);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (priv->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (priv->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    equalizer_slider->x = widget->allocation.x/(priv->scaled ? config.scale_factor : 1);
+    equalizer_slider->y = widget->allocation.y/(priv->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_skinned_equalizer_slider_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedEqualizerSlider *es = UI_SKINNED_EQUALIZER_SLIDER (widget);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(es);
+    g_return_val_if_fail (priv->width > 0 && priv->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, priv->width, priv->height);
+
+    gint frame;
+    frame = 27 - ((priv->position * 27) / 50);
+    if (frame < 14)
+        skin_draw_pixbuf(widget, aud_active_skin, obj, priv->skin_index, (frame * 15) + 13, 164, 0, 0, priv->width, priv->height);
+    else
+        skin_draw_pixbuf(widget, aud_active_skin, obj, priv->skin_index, ((frame - 14) * 15) + 13, 229, 0, 0, priv->width, priv->height);
+
+    if (priv->pressed)
+        skin_draw_pixbuf(widget, aud_active_skin, obj, priv->skin_index, 0, 176, 1, priv->position, 11, 11);
+    else
+        skin_draw_pixbuf(widget, aud_active_skin, obj, priv->skin_index, 0, 164, 1, priv->position, 11, 11);
+
+    ui_skinned_widget_draw(widget, obj, priv->width, priv->height, priv->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static gboolean ui_skinned_equalizer_slider_button_press(GtkWidget *widget, GdkEventButton *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedEqualizerSlider *es = UI_SKINNED_EQUALIZER_SLIDER (widget);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(es);
+
+    gint y;
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        if (event->button == 1) {
+            priv->pressed = TRUE;
+            y = event->y/(priv->scaled ? config.scale_factor : 1);
+
+            if (y >= priv->position && y < priv->position + 11)
+                priv->drag_y = y - priv->position;
+            else {
+                priv->position = y - 5;
+                priv->drag_y = 5;
+                if (priv->position < 0)
+                    priv->position = 0;
+                if (priv->position > 50)
+                    priv->position = 50;
+                if (priv->position >= 24 && priv->position <= 26)
+                    priv->position = 25;
+
+                priv->value = ((gfloat) (25 - priv->position) * EQUALIZER_MAX_GAIN / 25.0 );
+#if 0
+                equalizerwin_eq_changed();
+#endif
+            }
+
+            ui_skinned_equalizer_slider_set_mainwin_text(es);
+            gtk_widget_queue_draw(widget);
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_equalizer_slider_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+
+    if (event->button == 1) {
+        priv->pressed = FALSE;
+        mainwin_release_info_text();
+        gtk_widget_queue_draw(widget);
+    }
+    return TRUE;
+}
+
+static gboolean ui_skinned_equalizer_slider_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    UiSkinnedEqualizerSlider *es = UI_SKINNED_EQUALIZER_SLIDER(widget);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+
+    if (priv->pressed) {
+        gint y;
+
+        y = event->y/(priv->scaled ? config.scale_factor : 1);
+        priv->position = y - priv->drag_y;
+
+        if (priv->position < 0)
+            priv->position = 0;
+        if (priv->position > 50)
+            priv->position = 50;
+        if (priv->position >= 24 && priv->position <= 26)
+            priv->position = 25;
+
+        priv->value = ((gfloat) (25 - priv->position) * EQUALIZER_MAX_GAIN / 25.0 );
+        ui_skinned_equalizer_slider_set_mainwin_text(es);
+#if 0
+        equalizerwin_eq_changed();
+#endif
+        gtk_widget_queue_draw(widget);
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_equalizer_slider_scroll(GtkWidget *widget, GdkEventScroll *event) {
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+
+    if (event->direction == GDK_SCROLL_UP) {
+        priv->position -= 2;
+
+        if (priv->position < 0)
+            priv->position = 0;
+    }
+    else {
+        priv->position += 2;
+
+        if (priv->position > 50)
+            priv->position = 50;
+    }
+
+    priv->value = ((gfloat) (25 - priv->position) * EQUALIZER_MAX_GAIN / 25.0 );
+#if 0
+    equalizerwin_eq_changed();
+#endif
+    gtk_widget_queue_draw(widget);
+    return TRUE;
+}
+
+static void ui_skinned_equalizer_slider_toggle_scaled(UiSkinnedEqualizerSlider *equalizer_slider) {
+    GtkWidget *widget = GTK_WIDGET (equalizer_slider);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(equalizer_slider);
+
+    priv->scaled = !priv->scaled;
+
+    gtk_widget_set_size_request(widget, priv->width*(priv->scaled ? config.scale_factor : 1),
+    priv->height*(priv->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(equalizer_slider));
+}
+
+void ui_skinned_equalizer_slider_set_position(GtkWidget *widget, gfloat pos) {
+    g_return_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget));
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+
+    if (priv->pressed)
+        return;
+
+    priv->value = (pos > EQUALIZER_MAX_GAIN) ? EQUALIZER_MAX_GAIN : ((pos < -EQUALIZER_MAX_GAIN) ? -EQUALIZER_MAX_GAIN : pos);
+    priv->position = 25 - (gint) ((pos * 25.0) / EQUALIZER_MAX_GAIN);
+
+    if (priv->position < 0)
+        priv->position = 0;
+
+    if (priv->position > 50)
+        priv->position = 50;
+
+    if (priv->position >= 24 && priv->position <= 26)
+        priv->position = 25;
+
+    gtk_widget_queue_draw(widget);
+}
+
+gfloat ui_skinned_equalizer_slider_get_position(GtkWidget *widget) {
+    g_return_val_if_fail (UI_SKINNED_IS_EQUALIZER_SLIDER (widget), -1);
+    UiSkinnedEqualizerSliderPrivate *priv = UI_SKINNED_EQUALIZER_SLIDER_GET_PRIVATE(widget);
+    return priv->value;
+}
+
+void ui_skinned_equalizer_slider_set_mainwin_text(UiSkinnedEqualizerSlider * es) {
+    gint band = 0;
+    const gchar *bandname[11] = { N_("PREAMP"), N_("60HZ"), N_("170HZ"),
+        N_("310HZ"), N_("600HZ"), N_("1KHZ"),
+        N_("3KHZ"), N_("6KHZ"), N_("12KHZ"),
+        N_("14KHZ"), N_("16KHZ")
+    };
+    gchar *tmp;
+
+    if (es->x > 21)
+        band = ((es->x - 78) / 18) + 1;
+
+    tmp =
+        g_strdup_printf("EQ: %s: %+.1f DB", _(bandname[band]),
+                        ui_skinned_equalizer_slider_get_position(GTK_WIDGET(es)));
+    mainwin_lock_info_text(tmp);
+    g_free(tmp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_equalizer_slider.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,62 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_EQUALIZER_SLIDER_H
+#define AUDACIOUS_UI_SKINNED_EQUALIZER_SLIDER_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EQUALIZER_MAX_GAIN 12.0
+
+#define UI_SKINNED_EQUALIZER_SLIDER(obj)          GTK_CHECK_CAST (obj, ui_skinned_equalizer_slider_get_type (), UiSkinnedEqualizerSlider)
+#define UI_SKINNED_EQUALIZER_SLIDER_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_equalizer_slider_get_type (),  UiSkinnedEqualizerSliderClass)
+#define UI_SKINNED_IS_EQUALIZER_SLIDER(obj)       GTK_CHECK_TYPE (obj, ui_skinned_equalizer_slider_get_type ())
+
+typedef struct _UiSkinnedEqualizerSlider        UiSkinnedEqualizerSlider;
+typedef struct _UiSkinnedEqualizerSliderClass   UiSkinnedEqualizerSliderClass;
+
+struct _UiSkinnedEqualizerSlider {
+    GtkWidget   widget;
+    gint        x, y;
+};
+
+struct _UiSkinnedEqualizerSliderClass {
+    GtkWidgetClass    parent_class;
+    void (* scaled)  (UiSkinnedEqualizerSlider *equalizer_slider);
+};
+
+GtkWidget* ui_skinned_equalizer_slider_new(GtkWidget *fixed, gint x, gint y);
+GtkType ui_skinned_equalizer_slider_get_type(void);
+void ui_skinned_equalizer_slider_set_position(GtkWidget *widget, gfloat pos);
+gfloat ui_skinned_equalizer_slider_get_position(GtkWidget *widget);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_EQUALIZER_SLIDER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_horizontal_slider.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,386 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skinned_horizontal_slider.h"
+#include "skins_cfg.h"
+#include <math.h>
+
+#define UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ui_skinned_horizontal_slider_get_type(), UiSkinnedHorizontalSliderPrivate))
+typedef struct _UiSkinnedHorizontalSliderPrivate UiSkinnedHorizontalSliderPrivate;
+
+enum {
+    MOTION,
+    RELEASE,
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedHorizontalSliderPrivate {
+    SkinPixmapId     skin_index;
+    gboolean         scaled;
+    gint             frame, frame_offset, frame_height, min, max;
+    gint             knob_width, knob_height;
+    gint             position;
+    gint             width, height;
+    gint             (*frame_cb) (gint);
+};
+
+static void ui_skinned_horizontal_slider_class_init         (UiSkinnedHorizontalSliderClass *klass);
+static void ui_skinned_horizontal_slider_init               (UiSkinnedHorizontalSlider *horizontal_slider);
+static void ui_skinned_horizontal_slider_destroy            (GtkObject *object);
+static void ui_skinned_horizontal_slider_realize            (GtkWidget *widget);
+static void ui_skinned_horizontal_slider_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_horizontal_slider_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_horizontal_slider_expose         (GtkWidget *widget, GdkEventExpose *event);
+static gboolean ui_skinned_horizontal_slider_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_horizontal_slider_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_horizontal_slider_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static void ui_skinned_horizontal_slider_toggle_scaled  (UiSkinnedHorizontalSlider *horizontal_slider);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint horizontal_slider_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_horizontal_slider_get_type() {
+    static GType horizontal_slider_type = 0;
+    if (!horizontal_slider_type) {
+        static const GTypeInfo horizontal_slider_info = {
+            sizeof (UiSkinnedHorizontalSliderClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_horizontal_slider_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedHorizontalSlider),
+            0,
+            (GInstanceInitFunc) ui_skinned_horizontal_slider_init,
+        };
+        horizontal_slider_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedHorizontalSlider", &horizontal_slider_info, 0);
+    }
+
+    return horizontal_slider_type;
+}
+
+static void ui_skinned_horizontal_slider_class_init(UiSkinnedHorizontalSliderClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_horizontal_slider_destroy;
+
+    widget_class->realize = ui_skinned_horizontal_slider_realize;
+    widget_class->expose_event = ui_skinned_horizontal_slider_expose;
+    widget_class->size_request = ui_skinned_horizontal_slider_size_request;
+    widget_class->size_allocate = ui_skinned_horizontal_slider_size_allocate;
+    widget_class->button_press_event = ui_skinned_horizontal_slider_button_press;
+    widget_class->button_release_event = ui_skinned_horizontal_slider_button_release;
+    widget_class->motion_notify_event = ui_skinned_horizontal_slider_motion_notify;
+
+    klass->motion = NULL;
+    klass->release = NULL;
+    klass->scaled = ui_skinned_horizontal_slider_toggle_scaled;
+
+    horizontal_slider_signals[MOTION] = 
+        g_signal_new ("motion", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedHorizontalSliderClass, motion), NULL, NULL,
+                      gtk_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+    horizontal_slider_signals[RELEASE] = 
+        g_signal_new ("release", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedHorizontalSliderClass, release), NULL, NULL,
+                      gtk_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+    horizontal_slider_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedHorizontalSliderClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedHorizontalSliderPrivate));
+}
+
+static void ui_skinned_horizontal_slider_init(UiSkinnedHorizontalSlider *horizontal_slider) {
+    horizontal_slider->pressed = FALSE;
+}
+
+GtkWidget* ui_skinned_horizontal_slider_new(GtkWidget *fixed, gint x, gint y, gint w, gint h, gint knx, gint kny,
+                                            gint kpx, gint kpy, gint kw, gint kh, gint fh,
+                                            gint fo, gint min, gint max, gint(*fcb) (gint), SkinPixmapId si) {
+
+    UiSkinnedHorizontalSlider *hs = g_object_new (ui_skinned_horizontal_slider_get_type (), NULL);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(hs);
+
+    hs->x = x;
+    hs->y = y;
+    priv->width = w;
+    priv->height = h;
+    hs->knob_nx = knx;
+    hs->knob_ny = kny;
+    hs->knob_px = kpx;
+    hs->knob_py = kpy;
+    priv->knob_width = kw;
+    priv->knob_height = kh;
+    priv->frame_height = fh;
+    priv->frame_offset = fo;
+    priv->min = min;
+    priv->position = min;
+    priv->max = max;
+    priv->frame_cb = fcb;
+    if (priv->frame_cb)
+        priv->frame = priv->frame_cb(0);
+    priv->skin_index = si;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(hs), hs->x, hs->y);
+
+    return GTK_WIDGET(hs);
+}
+
+static void ui_skinned_horizontal_slider_destroy(GtkObject *object) {
+    UiSkinnedHorizontalSlider *horizontal_slider;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (object));
+
+    horizontal_slider = UI_SKINNED_HORIZONTAL_SLIDER (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_horizontal_slider_realize(GtkWidget *widget) {
+    UiSkinnedHorizontalSlider *horizontal_slider;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    horizontal_slider = UI_SKINNED_HORIZONTAL_SLIDER(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
+                             GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_horizontal_slider_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(widget);
+
+    requisition->width = priv->width*(priv->scaled ? config.scale_factor : 1);
+    requisition->height = priv->height*(priv->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_horizontal_slider_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedHorizontalSlider *horizontal_slider = UI_SKINNED_HORIZONTAL_SLIDER (widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(horizontal_slider);
+
+    widget->allocation = *allocation;
+    widget->allocation.x = ceil(widget->allocation.x*(priv->scaled ? config.scale_factor : 1));
+    widget->allocation.y = ceil(widget->allocation.y*(priv->scaled ? config.scale_factor : 1));
+
+    if (priv->knob_height == priv->height)
+        priv->knob_height = ceil(allocation->height/(priv->scaled ? config.scale_factor : 1));
+    priv->width = ceil(allocation->width/(priv->scaled ? config.scale_factor : 1));
+    priv->height = ceil(allocation->height/(priv->scaled ? config.scale_factor : 1));
+
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    horizontal_slider->x = ceil(widget->allocation.x/(priv->scaled ? config.scale_factor : 1));
+    horizontal_slider->y = ceil(widget->allocation.y/(priv->scaled ? config.scale_factor : 1));
+}
+
+static gboolean ui_skinned_horizontal_slider_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedHorizontalSlider *hs = UI_SKINNED_HORIZONTAL_SLIDER (widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(hs);
+    g_return_val_if_fail (priv->width > 0 && priv->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+
+    if (priv->position > priv->max) priv->position = priv->max;
+    else if (priv->position < priv->min) priv->position = priv->min;
+
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, priv->width, priv->height);
+
+    skin_draw_pixbuf(widget, aud_active_skin, obj,
+                     priv->skin_index, priv->frame_offset,
+                     priv->frame * priv->frame_height,
+                     0, 0, priv->width, priv->height);
+    if (hs->pressed)
+        skin_draw_pixbuf(widget, aud_active_skin, obj,
+                         priv->skin_index, hs->knob_px,
+                         hs->knob_py, priv->position,
+                         ((priv->height - priv->knob_height) / 2),
+                         priv->knob_width, priv->knob_height);
+    else
+        skin_draw_pixbuf(widget, aud_active_skin, obj,
+                         priv->skin_index, hs->knob_nx,
+                         hs->knob_ny, priv->position,
+                         ((priv->height - priv->knob_height) / 2),
+                         priv->knob_width, priv->knob_height);
+
+    ui_skinned_widget_draw(widget, obj, priv->width, priv->height, priv->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static gboolean ui_skinned_horizontal_slider_button_press(GtkWidget *widget, GdkEventButton *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedHorizontalSlider *hs = UI_SKINNED_HORIZONTAL_SLIDER (widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(hs);
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        if (event->button == 1) {
+            gint x;
+
+            x = event->x - (priv->knob_width / (priv->scaled ? 1 : config.scale_factor));
+            hs->pressed = TRUE;
+
+            priv->position = x/(priv->scaled ? config.scale_factor : 1);
+            if (priv->position < priv->min)
+                priv->position = priv->min;
+            if (priv->position > priv->max)
+                priv->position = priv->max;
+            if (priv->frame_cb)
+                priv->frame = priv->frame_cb(priv->position);
+
+            g_signal_emit_by_name(widget, "motion", priv->position);
+            gtk_widget_queue_draw(widget);
+        } else if (event->button == 3) {
+            if (hs->pressed) {
+                hs->pressed = FALSE;
+                g_signal_emit_by_name(widget, "release", priv->position);
+                gtk_widget_queue_draw(widget);
+            }
+            event->x = event->x + hs->x;
+            event->y = event->y + hs->y;
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
+static gboolean ui_skinned_horizontal_slider_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedHorizontalSlider *hs = UI_SKINNED_HORIZONTAL_SLIDER(widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(widget);
+
+    if (hs->pressed) {
+        hs->pressed = FALSE;
+        g_signal_emit_by_name(widget, "release", priv->position);
+        gtk_widget_queue_draw(widget);
+    }
+    return TRUE;
+}
+
+static gboolean ui_skinned_horizontal_slider_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    UiSkinnedHorizontalSlider *hs = UI_SKINNED_HORIZONTAL_SLIDER(widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(widget);
+
+    if (hs->pressed) {
+        gint x;
+
+        x = event->x - (priv->knob_width / (priv->scaled ? 1 : config.scale_factor));
+        priv->position = x/(priv->scaled ? config.scale_factor : 1);
+
+        if (priv->position < priv->min)
+            priv->position = priv->min;
+
+        if (priv->position > priv->max)
+            priv->position = priv->max;
+
+        if (priv->frame_cb)
+            priv->frame = priv->frame_cb(priv->position);
+
+        g_signal_emit_by_name(widget, "motion", priv->position);
+        gtk_widget_queue_draw(widget);
+    }
+
+    return TRUE;
+}
+
+static void ui_skinned_horizontal_slider_toggle_scaled(UiSkinnedHorizontalSlider *horizontal_slider) {
+    GtkWidget *widget = GTK_WIDGET (horizontal_slider);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(horizontal_slider);
+
+    priv->scaled = !priv->scaled;
+
+    gtk_widget_set_size_request(widget,
+        priv->width*(priv->scaled ? config.scale_factor : 1),
+        priv->height*(priv->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(horizontal_slider));
+}
+
+void ui_skinned_horizontal_slider_set_position(GtkWidget *widget, gint pos) {
+    g_return_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (widget));
+    UiSkinnedHorizontalSlider *hs = UI_SKINNED_HORIZONTAL_SLIDER(widget);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(widget);
+
+    if (pos == priv->position || hs->pressed)
+        return;
+
+    priv->position = pos;
+
+    if (priv->frame_cb)
+        priv->frame = priv->frame_cb(priv->position);
+
+    gtk_widget_queue_draw(widget);
+}
+
+gint ui_skinned_horizontal_slider_get_position(GtkWidget *widget) {
+    g_return_val_if_fail (UI_SKINNED_IS_HORIZONTAL_SLIDER (widget), -1);
+    UiSkinnedHorizontalSliderPrivate *priv = UI_SKINNED_HORIZONTAL_SLIDER_GET_PRIVATE(widget);
+    return priv->position;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_horizontal_slider.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,69 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_HORIZONTAL_SLIDER_H
+#define AUDACIOUS_UI_SKINNED_HORIZONTAL_SLIDER_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_HORIZONTAL_SLIDER(obj)          GTK_CHECK_CAST (obj, ui_skinned_horizontal_slider_get_type (), UiSkinnedHorizontalSlider)
+#define UI_SKINNED_HORIZONTAL_SLIDER_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_horizontal_slider_get_type (), UiSkinnedHorizontalSliderClass)
+#define UI_SKINNED_IS_HORIZONTAL_SLIDER(obj)       GTK_CHECK_TYPE (obj, ui_skinned_horizontal_slider_get_type ())
+
+typedef struct _UiSkinnedHorizontalSlider        UiSkinnedHorizontalSlider;
+typedef struct _UiSkinnedHorizontalSliderClass   UiSkinnedHorizontalSliderClass;
+
+struct _UiSkinnedHorizontalSlider {
+    GtkWidget   widget;
+    gboolean    pressed;
+    gint        x, y;
+    gint        knob_nx, knob_ny, knob_px, knob_py;
+};
+
+struct _UiSkinnedHorizontalSliderClass {
+    GtkWidgetClass    parent_class;
+    void (* motion)   (UiSkinnedHorizontalSlider *horizontal_slider);
+    void (* release)  (UiSkinnedHorizontalSlider *horizontal_slider);
+    void (* scaled)  (UiSkinnedHorizontalSlider *horizontal_slider);
+    void (* redraw)   (UiSkinnedHorizontalSlider *horizontal_slider);
+};
+GtkWidget* ui_skinned_horizontal_slider_new(GtkWidget *fixed, gint x, gint y, gint w, gint h, gint knx, gint kny,
+                                            gint kpx, gint kpy, gint kw, gint kh, gint fh,
+                                            gint fo, gint min, gint max, gint(*fcb) (gint), SkinPixmapId si);
+GtkType ui_skinned_horizontal_slider_get_type(void);
+void ui_skinned_horizontal_slider_set_position(GtkWidget *widget, gint pos);
+gint ui_skinned_horizontal_slider_get_position(GtkWidget *widget);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_HORIZONTAL_SLIDER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_menurow.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,332 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include <audacious/plugin.h>
+#include "plugin.h"
+#include "ui_skinned_menurow.h"
+
+enum {
+    DOUBLED,
+    CHANGE,
+    RELEASE,
+    LAST_SIGNAL
+};
+
+static void ui_skinned_menurow_class_init         (UiSkinnedMenurowClass *klass);
+static void ui_skinned_menurow_init               (UiSkinnedMenurow *menurow);
+static void ui_skinned_menurow_destroy            (GtkObject *object);
+static void ui_skinned_menurow_realize            (GtkWidget *widget);
+static void ui_skinned_menurow_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_menurow_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_menurow_expose         (GtkWidget *widget, GdkEventExpose *event);
+static MenuRowItem menurow_find_selected          (UiSkinnedMenurow * mr, gint x, gint y);
+static gboolean ui_skinned_menurow_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_menurow_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_menurow_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static void ui_skinned_menurow_toggle_scaled  (UiSkinnedMenurow *menurow);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint menurow_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_menurow_get_type() {
+    static GType menurow_type = 0;
+    if (!menurow_type) {
+        static const GTypeInfo menurow_info = {
+            sizeof (UiSkinnedMenurowClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_menurow_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedMenurow),
+            0,
+            (GInstanceInitFunc) ui_skinned_menurow_init,
+        };
+        menurow_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedMenurow", &menurow_info, 0);
+    }
+
+    return menurow_type;
+}
+
+static void ui_skinned_menurow_class_init(UiSkinnedMenurowClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_menurow_destroy;
+
+    widget_class->realize = ui_skinned_menurow_realize;
+    widget_class->expose_event = ui_skinned_menurow_expose;
+    widget_class->size_request = ui_skinned_menurow_size_request;
+    widget_class->size_allocate = ui_skinned_menurow_size_allocate;
+    widget_class->button_press_event = ui_skinned_menurow_button_press;
+    widget_class->button_release_event = ui_skinned_menurow_button_release;
+    widget_class->motion_notify_event = ui_skinned_menurow_motion_notify;
+
+    klass->scaled = ui_skinned_menurow_toggle_scaled;
+    klass->change = NULL;
+    klass->release = NULL;
+
+    menurow_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedMenurowClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+
+    menurow_signals[CHANGE] = 
+        g_signal_new ("change", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedMenurowClass, change), NULL, NULL,
+                      gtk_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
+
+    menurow_signals[RELEASE] = 
+        g_signal_new ("release", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedMenurowClass, release), NULL, NULL,
+                      g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
+
+}
+
+static void ui_skinned_menurow_init(UiSkinnedMenurow *menurow) {
+    menurow->scale_selected = config.scaled;
+    menurow->always_selected = config.always_on_top;
+}
+
+GtkWidget* ui_skinned_menurow_new(GtkWidget *fixed, gint x, gint y, gint nx, gint ny, gint sx, gint sy, SkinPixmapId si) {
+    UiSkinnedMenurow *menurow = g_object_new (ui_skinned_menurow_get_type (), NULL);
+
+    menurow->x = x;
+    menurow->y = y;
+    menurow->width = 8;
+    menurow->height = 43;
+    menurow->nx = nx;
+    menurow->ny = ny;
+    menurow->sx = sx;
+    menurow->sy = sy;
+    menurow->selected = MENUROW_NONE;
+
+    menurow->skin_index = si;
+
+    menurow->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(menurow), menurow->x, menurow->y);
+
+    return GTK_WIDGET(menurow);
+}
+
+static void ui_skinned_menurow_destroy(GtkObject *object) {
+    UiSkinnedMenurow *menurow;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_MENUROW (object));
+
+    menurow = UI_SKINNED_MENUROW (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_menurow_realize(GtkWidget *widget) {
+    UiSkinnedMenurow *menurow;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_MENUROW(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    menurow = UI_SKINNED_MENUROW(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
+                             GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_menurow_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW(widget);
+
+    requisition->width = menurow->width*(menurow->scaled ? config.scale_factor : 1);
+    requisition->height = menurow->height*(menurow->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_menurow_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (menurow->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (menurow->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    menurow->x = widget->allocation.x/(menurow->scaled ? config.scale_factor : 1);
+    menurow->y = widget->allocation.y/(menurow->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_skinned_menurow_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_MENUROW (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW (widget);
+    g_return_val_if_fail (menurow->width > 0 && menurow->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, menurow->width, menurow->height);
+
+    if (menurow->selected == MENUROW_NONE) {
+        if (config.always_show_cb || menurow->pushed)
+            skin_draw_pixbuf(widget, aud_active_skin, obj, menurow->skin_index,
+                             menurow->nx, menurow->ny, 0, 0, 8, 43);
+        else
+            skin_draw_pixbuf(widget, aud_active_skin, obj, menurow->skin_index,
+                             menurow->nx + 8, menurow->ny, 0, 0, 8, 43);
+    }
+    else {
+        skin_draw_pixbuf(widget, aud_active_skin, obj, menurow->skin_index,
+                         menurow->sx + ((menurow->selected - 1) * 8),
+                         menurow->sy, 0, 0, 8, 43);
+    }
+    if (config.always_show_cb || menurow->pushed) {
+        if (menurow->always_selected)
+            skin_draw_pixbuf(widget, aud_active_skin, obj, menurow->skin_index,
+                             menurow->sx + 8, menurow->sy + 10, 0, 10, 8, 8);
+        if (menurow->scale_selected)
+            skin_draw_pixbuf(widget, aud_active_skin, obj, menurow->skin_index,
+                             menurow->sx + 24, menurow->sy + 26, 0, 26, 8, 8);
+    }
+
+    ui_skinned_widget_draw(widget, obj, menurow->width, menurow->height, menurow->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static MenuRowItem menurow_find_selected(UiSkinnedMenurow * mr, gint x, gint y) {
+    MenuRowItem ret = MENUROW_NONE;
+
+    x = x/(mr->scaled ? config.scale_factor : 1);
+    y = y/(mr->scaled ? config.scale_factor : 1);
+    if (x > 0 && x < 8) {
+        if (y >= 0 && y <= 10)
+            ret = MENUROW_OPTIONS;
+        if (y >= 10 && y <= 17)
+            ret = MENUROW_ALWAYS;
+        if (y >= 18 && y <= 25)
+            ret = MENUROW_FILEINFOBOX;
+        if (y >= 26 && y <= 33)
+            ret = MENUROW_SCALE;
+        if (y >= 34 && y <= 42)
+            ret = MENUROW_VISUALIZATION;
+    }
+    return ret;
+}
+
+static gboolean ui_skinned_menurow_button_press(GtkWidget *widget, GdkEventButton *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_MENUROW (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW (widget);
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        if (event->button == 1) {
+
+        menurow->pushed = TRUE;
+        menurow->selected = menurow_find_selected(menurow, event->x, event->y);
+
+        gtk_widget_queue_draw(widget);
+        g_signal_emit_by_name(widget, "change", menurow->selected);
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_menurow_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW(widget);
+    if (menurow->pushed) {
+        menurow->pushed = FALSE;
+
+        if (menurow->selected == MENUROW_ALWAYS)
+            menurow->always_selected = !menurow->always_selected;
+
+        if (menurow->selected == MENUROW_SCALE)
+            menurow->scale_selected = !menurow->scale_selected;
+
+        if ((int)(menurow->selected) != -1)
+            g_signal_emit_by_name(widget, "release", menurow->selected, event);
+
+        menurow->selected = MENUROW_NONE;
+        gtk_widget_queue_draw(widget);
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_menurow_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_MENUROW (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    UiSkinnedMenurow *menurow = UI_SKINNED_MENUROW(widget);
+
+    if (menurow->pushed) {
+        menurow->selected = menurow_find_selected(menurow, event->x, event->y);
+
+        gtk_widget_queue_draw(widget);
+        g_signal_emit_by_name(widget, "change", menurow->selected);
+    }
+
+    return TRUE;
+}
+
+static void ui_skinned_menurow_toggle_scaled(UiSkinnedMenurow *menurow) {
+    GtkWidget *widget = GTK_WIDGET (menurow);
+
+    menurow->scaled = !menurow->scaled;
+    gtk_widget_set_size_request(widget, menurow->width* (menurow->scaled ? config.scale_factor : 1),
+    menurow->height * (menurow->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(menurow));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_menurow.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,77 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_MENUROW_H
+#define AUDACIOUS_UI_SKINNED_MENUROW_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_MENUROW(obj)          GTK_CHECK_CAST (obj, ui_skinned_menurow_get_type (), UiSkinnedMenurow)
+#define UI_SKINNED_MENUROW_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_menurow_get_type (), UiSkinnedMenurowClass)
+#define UI_SKINNED_IS_MENUROW(obj)       GTK_CHECK_TYPE (obj, ui_skinned_menurow_get_type ())
+
+typedef struct _UiSkinnedMenurow        UiSkinnedMenurow;
+typedef struct _UiSkinnedMenurowClass   UiSkinnedMenurowClass;
+
+typedef enum {
+    MENUROW_NONE, MENUROW_OPTIONS, MENUROW_ALWAYS, MENUROW_FILEINFOBOX,
+    MENUROW_SCALE, MENUROW_VISUALIZATION
+} MenuRowItem;
+
+struct _UiSkinnedMenurow {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gboolean         scaled;
+    gint             nx, ny;
+    gint             sx, sy;
+    MenuRowItem      selected;
+    gboolean         always_selected;
+    gboolean         scale_selected;
+    gboolean         pushed;
+    SkinPixmapId     skin_index;
+};
+
+struct _UiSkinnedMenurowClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)         (UiSkinnedMenurow *menurow);
+    void (* change)         (UiSkinnedMenurow *menurow);
+    void (* release)        (UiSkinnedMenurow *menurow);
+};
+
+GtkWidget* ui_skinned_menurow_new (GtkWidget *fixed, gint x, gint y, gint nx, gint ny, gint sx, gint sy, SkinPixmapId si);
+GtkType ui_skinned_menurow_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_MENUROW_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_monostereo.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,222 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skin.h"
+#include "ui_skinned_monostereo.h"
+#include "skins_cfg.h"
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_skinned_monostereo_class_init         (UiSkinnedMonoStereoClass *klass);
+static void ui_skinned_monostereo_init               (UiSkinnedMonoStereo *monostereo);
+static void ui_skinned_monostereo_destroy            (GtkObject *object);
+static void ui_skinned_monostereo_realize            (GtkWidget *widget);
+static void ui_skinned_monostereo_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_monostereo_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_monostereo_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_skinned_monostereo_toggle_scaled      (UiSkinnedMonoStereo *monostereo);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint monostereo_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_monostereo_get_type() {
+    static GType monostereo_type = 0;
+    if (!monostereo_type) {
+        static const GTypeInfo monostereo_info = {
+            sizeof (UiSkinnedMonoStereoClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_monostereo_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedMonoStereo),
+            0,
+            (GInstanceInitFunc) ui_skinned_monostereo_init,
+        };
+        monostereo_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedMonoStereo", &monostereo_info, 0);
+    }
+
+    return monostereo_type;
+}
+
+static void ui_skinned_monostereo_class_init(UiSkinnedMonoStereoClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_monostereo_destroy;
+
+    widget_class->realize = ui_skinned_monostereo_realize;
+    widget_class->expose_event = ui_skinned_monostereo_expose;
+    widget_class->size_request = ui_skinned_monostereo_size_request;
+    widget_class->size_allocate = ui_skinned_monostereo_size_allocate;
+
+    klass->scaled = ui_skinned_monostereo_toggle_scaled;
+
+    monostereo_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedMonoStereoClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_skinned_monostereo_init(UiSkinnedMonoStereo *monostereo) {
+    monostereo->width = 56;
+    monostereo->height = 12;
+}
+
+GtkWidget* ui_skinned_monostereo_new(GtkWidget *fixed, gint x, gint y, SkinPixmapId si) {
+    UiSkinnedMonoStereo *monostereo = g_object_new (ui_skinned_monostereo_get_type (), NULL);
+
+    monostereo->x = x;
+    monostereo->y = y;
+    monostereo->skin_index = si;
+    monostereo->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(monostereo), monostereo->x, monostereo->y);
+
+    return GTK_WIDGET(monostereo);
+}
+
+static void ui_skinned_monostereo_destroy(GtkObject *object) {
+    UiSkinnedMonoStereo *monostereo;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_MONOSTEREO (object));
+
+    monostereo = UI_SKINNED_MONOSTEREO (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_monostereo_realize(GtkWidget *widget) {
+    UiSkinnedMonoStereo *monostereo;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_MONOSTEREO(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    monostereo = UI_SKINNED_MONOSTEREO(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_monostereo_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedMonoStereo *monostereo = UI_SKINNED_MONOSTEREO(widget);
+
+    requisition->width = monostereo->width*(monostereo->scaled ? config.scale_factor : 1);
+    requisition->height = monostereo->height*(monostereo->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_monostereo_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedMonoStereo *monostereo = UI_SKINNED_MONOSTEREO (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (monostereo->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (monostereo->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    monostereo->x = widget->allocation.x/(monostereo->scaled ? config.scale_factor : 1);
+    monostereo->y = widget->allocation.y/(monostereo->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_skinned_monostereo_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_MONOSTEREO (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedMonoStereo *monostereo = UI_SKINNED_MONOSTEREO (widget);
+    g_return_val_if_fail (monostereo->width > 0 && monostereo->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, monostereo->width, monostereo->height);
+
+    switch (monostereo->num_channels) {
+    case 1:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 29, 0, 0, 0, 27, 12);
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 0, 12, 27, 0, 29, 12);
+        break;
+    case 2:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 29, 12, 0, 0, 27, 12);
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 0, 0, 27, 0, 29, 12);
+        break;
+    default:
+    case 0:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 29, 12, 0, 0, 27, 12);
+        skin_draw_pixbuf(widget, aud_active_skin, obj, monostereo->skin_index, 0, 12, 27, 0, 29, 12);
+        break;
+    }
+
+    ui_skinned_widget_draw(widget, obj, monostereo->width, monostereo->height, monostereo->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void ui_skinned_monostereo_toggle_scaled(UiSkinnedMonoStereo *monostereo) {
+    GtkWidget *widget = GTK_WIDGET (monostereo);
+
+    monostereo->scaled = !monostereo->scaled;
+    gtk_widget_set_size_request(widget, monostereo->width*(monostereo->scaled ? config.scale_factor : 1), monostereo->height*(monostereo->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(monostereo));
+}
+
+void ui_skinned_monostereo_set_num_channels(GtkWidget *widget, gint nch) {
+    g_return_if_fail (UI_SKINNED_IS_MONOSTEREO (widget));
+    UiSkinnedMonoStereo *monostereo = UI_SKINNED_MONOSTEREO (widget);
+
+    monostereo->num_channels = nch;
+    gtk_widget_queue_draw(widget);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_monostereo.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,66 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_MONOSTEREO_H
+#define AUDACIOUS_UI_SKINNED_MONOSTEREO_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_MONOSTEREO(obj)          GTK_CHECK_CAST (obj, ui_skinned_monostereo_get_type (), UiSkinnedMonoStereo)
+#define UI_SKINNED_MONOSTEREO_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_monostereo_get_type (), UiSkinnedMonoStereoClass)
+#define UI_SKINNED_IS_MONOSTEREO(obj)       GTK_CHECK_TYPE (obj, ui_skinned_monostereo_get_type ())
+
+typedef struct _UiSkinnedMonoStereo        UiSkinnedMonoStereo;
+typedef struct _UiSkinnedMonoStereoClass   UiSkinnedMonoStereoClass;
+
+struct _UiSkinnedMonoStereo {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gint             num_channels;
+    SkinPixmapId     skin_index;
+    gboolean         scaled;
+};
+
+struct _UiSkinnedMonoStereoClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)        (UiSkinnedMonoStereo *menurow);
+};
+
+GtkWidget* ui_skinned_monostereo_new (GtkWidget *fixed, gint x, gint y, SkinPixmapId si);
+GtkType ui_skinned_monostereo_get_type(void);
+void ui_skinned_monostereo_set_num_channels(GtkWidget *widget, gint nch);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_MONOSTEREO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_number.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,234 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skinned_number.h"
+#include "skins_cfg.h"
+#include "strings.h"
+#include <string.h>
+#include <ctype.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkmarshal.h>
+
+#define UI_TYPE_SKINNED_NUMBER           (ui_skinned_number_get_type())
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_skinned_number_class_init         (UiSkinnedNumberClass *klass);
+static void ui_skinned_number_init               (UiSkinnedNumber *number);
+static void ui_skinned_number_destroy            (GtkObject *object);
+static void ui_skinned_number_realize            (GtkWidget *widget);
+static void ui_skinned_number_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_number_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_number_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_skinned_number_toggle_scaled  (UiSkinnedNumber *number);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint number_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_number_get_type() {
+    static GType number_type = 0;
+    if (!number_type) {
+        static const GTypeInfo number_info = {
+            sizeof (UiSkinnedNumberClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_number_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedNumber),
+            0,
+            (GInstanceInitFunc) ui_skinned_number_init,
+        };
+        number_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedNumber", &number_info, 0);
+    }
+
+    return number_type;
+}
+
+static void ui_skinned_number_class_init(UiSkinnedNumberClass *klass) {
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_number_destroy;
+
+    widget_class->realize = ui_skinned_number_realize;
+    widget_class->expose_event = ui_skinned_number_expose;
+    widget_class->size_request = ui_skinned_number_size_request;
+    widget_class->size_allocate = ui_skinned_number_size_allocate;
+
+    klass->scaled = ui_skinned_number_toggle_scaled;
+
+    number_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedNumberClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_skinned_number_init(UiSkinnedNumber *number) {
+    number->width = 9;
+    number->height = 13;
+}
+
+GtkWidget* ui_skinned_number_new(GtkWidget *fixed, gint x, gint y, SkinPixmapId si) {
+    UiSkinnedNumber *number = g_object_new (ui_skinned_number_get_type (), NULL);
+
+    number->x = x;
+    number->y = y;
+    number->num = 0;
+    number->skin_index = si;
+
+    number->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(number), number->x, number->y);
+
+    return GTK_WIDGET(number);
+}
+
+static void ui_skinned_number_destroy(GtkObject *object) {
+    UiSkinnedNumber *number;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_NUMBER (object));
+
+    number = UI_SKINNED_NUMBER (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_number_realize(GtkWidget *widget) {
+    UiSkinnedNumber *number;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_NUMBER(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    number = UI_SKINNED_NUMBER(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_number_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedNumber *number = UI_SKINNED_NUMBER(widget);
+
+    requisition->width = number->width * ( number->scaled ? config.scale_factor : 1 );
+    requisition->height = number->height*( number->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_number_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedNumber *number = UI_SKINNED_NUMBER (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (number->scaled ? config.scale_factor: 1 );
+    widget->allocation.y *= (number->scaled ? config.scale_factor: 1 );
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    number->x = widget->allocation.x/(number->scaled ? config.scale_factor : 1);
+    number->y = widget->allocation.y/(number->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_skinned_number_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_NUMBER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedNumber *number = UI_SKINNED_NUMBER (widget);
+    g_return_val_if_fail (number->width > 0 && number->height > 0, FALSE);
+
+    GdkPixbuf *obj;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, number->width, number->height);
+
+    if (number->num > 11 || number->num < 0)
+        number->num = 10;
+
+    skin_draw_pixbuf(widget, aud_active_skin, obj,
+                     number->skin_index, number->num * 9, 0,
+                     0, 0, number->width, number->height);
+
+    ui_skinned_widget_draw(widget, obj, number->width, number->height, number->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void ui_skinned_number_toggle_scaled(UiSkinnedNumber *number) {
+    GtkWidget *widget = GTK_WIDGET (number);
+    number->scaled = !number->scaled;
+
+    gtk_widget_set_size_request(widget, number->width * ( number->scaled ? config.scale_factor : 1),
+        number->height * ( number->scaled ? config.scale_factor : 1) );
+
+    gtk_widget_queue_draw(GTK_WIDGET(number));
+}
+
+void ui_skinned_number_set_number(GtkWidget *widget, gint num) {
+    g_return_if_fail(UI_SKINNED_IS_NUMBER(widget));
+    UiSkinnedNumber *number = UI_SKINNED_NUMBER (widget);
+
+    if (number->num == num)
+         return;
+
+    number->num = num;
+    gtk_widget_queue_draw(GTK_WIDGET(number));
+}
+
+void ui_skinned_number_set_size(GtkWidget *widget, gint width, gint height) {
+    g_return_if_fail(UI_SKINNED_IS_NUMBER(widget));
+    UiSkinnedNumber *number = UI_SKINNED_NUMBER (widget);
+
+    number->width = width;
+    number->height = height;
+
+    gtk_widget_set_size_request(widget, width*(number->scaled ? config.scale_factor : 1 ),
+    height*(number->scaled ? config.scale_factor : 1 ));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_number.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,61 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_NUMBER_H
+#define AUDACIOUS_UI_SKINNED_NUMBER_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_NUMBER(obj)          GTK_CHECK_CAST (obj, ui_skinned_number_get_type (), UiSkinnedNumber)
+#define UI_SKINNED_NUMBER_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_number_get_type (), UiSkinnedNumberClass)
+#define UI_SKINNED_IS_NUMBER(obj)       GTK_CHECK_TYPE (obj, ui_skinned_number_get_type ())
+
+typedef struct _UiSkinnedNumber        UiSkinnedNumber;
+typedef struct _UiSkinnedNumberClass   UiSkinnedNumberClass;
+
+struct _UiSkinnedNumber {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gint             num;
+    gboolean         scaled;
+    SkinPixmapId     skin_index;
+};
+
+struct _UiSkinnedNumberClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)        (UiSkinnedNumber *textbox);
+};
+
+GtkWidget* ui_skinned_number_new (GtkWidget *fixed, gint x, gint y, SkinPixmapId si);
+GtkType ui_skinned_number_get_type(void);
+void ui_skinned_number_set_number(GtkWidget *widget, gint num);
+void ui_skinned_number_set_size(GtkWidget *widget, gint width, gint height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_NUMBER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playlist.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,1109 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ * Copyright (c) 2008 William Pitcock
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ *
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+/*
+ *  A note about Pango and some funky spacey fonts: Weirdly baselined
+ *  fonts, or fonts with weird ascents or descents _will_ display a
+ *  little bit weird in the playlist widget, but the display engine
+ *  won't make it look too bad, just a little deranged.  I honestly
+ *  don't think it's worth fixing (around...), it doesn't have to be
+ *  perfectly fitting, just the general look has to be ok, which it
+ *  IMHO is.
+ *
+ *  A second note: The numbers aren't perfectly aligned, but in the
+ *  end it looks better when using a single Pango layout for each
+ *  number.
+ */
+
+#include "ui_skinned_playlist.h"
+
+#include "debug.h"
+#if 0
+#include "ui_fileinfopopup.h"
+#endif
+#include "ui_playlist.h"
+#include "ui_manager.h"
+#include "ui_skin.h"
+#include "util.h"
+#include "skins_cfg.h"
+#include <audacious/plugin.h>
+
+static PangoFontDescription *playlist_list_font = NULL;
+static gint ascent, descent, width_delta_digit_one;
+static gboolean has_slant;
+static guint padding;
+
+/* FIXME: the following globals should not be needed. */
+static gint width_approx_letters;
+static gint width_colon, width_colon_third;
+static gint width_approx_digits, width_approx_digits_half;
+
+#define UI_SKINNED_PLAYLIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ui_skinned_playlist_get_type(), UiSkinnedPlaylistPrivate))
+typedef struct _UiSkinnedPlaylistPrivate UiSkinnedPlaylistPrivate;
+
+enum {
+    REDRAW,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedPlaylistPrivate {
+    SkinPixmapId     skin_index;
+    gint             width, height;
+    gint             resize_width, resize_height;
+    gint             drag_pos;
+    gboolean         dragging, auto_drag_down, auto_drag_up;
+    gint             auto_drag_up_tag, auto_drag_down_tag;
+};
+
+static void ui_skinned_playlist_class_init         (UiSkinnedPlaylistClass *klass);
+static void ui_skinned_playlist_init               (UiSkinnedPlaylist *playlist);
+static void ui_skinned_playlist_destroy            (GtkObject *object);
+static void ui_skinned_playlist_realize            (GtkWidget *widget);
+static void ui_skinned_playlist_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_playlist_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_playlist_expose         (GtkWidget *widget, GdkEventExpose *event);
+static gboolean ui_skinned_playlist_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_playlist_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_playlist_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static gboolean ui_skinned_playlist_leave_notify   (GtkWidget *widget, GdkEventCrossing *event);
+static void ui_skinned_playlist_redraw             (UiSkinnedPlaylist *playlist);
+static gboolean ui_skinned_playlist_popup_show     (gpointer data);
+static void ui_skinned_playlist_popup_hide         (GtkWidget *widget);
+static void ui_skinned_playlist_popup_timer_start  (GtkWidget *widget);
+static void ui_skinned_playlist_popup_timer_stop   (GtkWidget *widget);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint playlist_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_playlist_get_type() {
+    static GType playlist_type = 0;
+    if (!playlist_type) {
+        static const GTypeInfo playlist_info = {
+            sizeof (UiSkinnedPlaylistClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_playlist_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedPlaylist),
+            0,
+            (GInstanceInitFunc) ui_skinned_playlist_init,
+        };
+        playlist_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedPlaylist", &playlist_info, 0);
+    }
+
+    return playlist_type;
+}
+
+static void ui_skinned_playlist_class_init(UiSkinnedPlaylistClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_playlist_destroy;
+
+    widget_class->realize = ui_skinned_playlist_realize;
+    widget_class->expose_event = ui_skinned_playlist_expose;
+    widget_class->size_request = ui_skinned_playlist_size_request;
+    widget_class->size_allocate = ui_skinned_playlist_size_allocate;
+    widget_class->button_press_event = ui_skinned_playlist_button_press;
+    widget_class->button_release_event = ui_skinned_playlist_button_release;
+    widget_class->motion_notify_event = ui_skinned_playlist_motion_notify;
+    widget_class->leave_notify_event = ui_skinned_playlist_leave_notify;
+
+    klass->redraw = ui_skinned_playlist_redraw;
+
+    playlist_signals[REDRAW] = 
+        g_signal_new ("redraw", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedPlaylistClass, redraw), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedPlaylistPrivate));
+}
+
+static void ui_skinned_playlist_init(UiSkinnedPlaylist *playlist) {
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(playlist);
+    playlist->pressed = FALSE;
+    priv->resize_width = 0;
+    priv->resize_height = 0;
+    playlist->prev_selected = -1;
+    playlist->prev_min = -1;
+    playlist->prev_max = -1;
+
+    g_object_set_data(G_OBJECT(playlist), "timer_id", GINT_TO_POINTER(0));
+    g_object_set_data(G_OBJECT(playlist), "timer_active", GINT_TO_POINTER(0));
+#if 0
+    GtkWidget *popup = fileinfopopup_create();
+    g_object_set_data(G_OBJECT(playlist), "popup", popup);
+    g_object_set_data(G_OBJECT(playlist), "popup_active", GINT_TO_POINTER(0));
+    g_object_set_data(G_OBJECT(playlist), "popup_position", GINT_TO_POINTER(-1));
+#endif
+}
+
+GtkWidget* ui_skinned_playlist_new(GtkWidget *fixed, gint x, gint y, gint w, gint h) {
+
+    UiSkinnedPlaylist *hs = g_object_new (ui_skinned_playlist_get_type (), NULL);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(hs);
+
+    hs->x = x;
+    hs->y = y;
+    priv->width = w;
+    priv->height = h;
+    priv->skin_index = SKIN_PLEDIT;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(hs), hs->x, hs->y);
+    gtk_widget_set_double_buffered(GTK_WIDGET(hs), TRUE);
+
+    return GTK_WIDGET(hs);
+}
+
+static void ui_skinned_playlist_destroy(GtkObject *object) {
+    UiSkinnedPlaylist *playlist;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYLIST (object));
+
+    playlist = UI_SKINNED_PLAYLIST (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_playlist_realize(GtkWidget *widget) {
+    UiSkinnedPlaylist *playlist;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYLIST(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    playlist = UI_SKINNED_PLAYLIST(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
+                             GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_playlist_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(widget);
+
+    requisition->width = priv->width;
+    requisition->height = priv->height;
+}
+
+static void ui_skinned_playlist_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedPlaylist *playlist = UI_SKINNED_PLAYLIST (widget);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(playlist);
+
+    widget->allocation = *allocation;
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    playlist->x = widget->allocation.x;
+    playlist->y = widget->allocation.y;
+
+    if (priv->height != widget->allocation.height || priv->width != widget->allocation.width) {
+        priv->width = priv->width + priv->resize_width;
+        priv->height = priv->height + priv->resize_height;
+        priv->resize_width = 0;
+        priv->resize_height = 0;
+        gtk_widget_queue_draw(widget);
+    }
+}
+
+static gboolean ui_skinned_playlist_auto_drag_down_func(gpointer data) {
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST(data);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(data);
+
+    if (priv->auto_drag_down) {
+        ui_skinned_playlist_move_down(pl);
+        pl->first++;
+        playlistwin_update_list(aud_playlist_get_active());
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static gboolean ui_skinned_playlist_auto_drag_up_func(gpointer data) {
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST(data);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(data);
+
+    if (priv->auto_drag_up) {
+        ui_skinned_playlist_move_up(pl);
+        pl->first--;
+        playlistwin_update_list(aud_playlist_get_active());
+        return TRUE;
+
+    }
+    return FALSE;
+}
+
+void ui_skinned_playlist_move_up(UiSkinnedPlaylist * pl) {
+    GList *list;
+    Playlist *playlist = aud_playlist_get_active();
+
+    if (!playlist)
+        return;
+
+    PLAYLIST_LOCK(playlist);
+    if ((list = playlist->entries) == NULL) {
+        PLAYLIST_UNLOCK(playlist);
+        return;
+    }
+    if (PLAYLIST_ENTRY(list->data)->selected) {
+        /* We are at the top */
+        PLAYLIST_UNLOCK(playlist);
+        return;
+    }
+    while (list) {
+        if (PLAYLIST_ENTRY(list->data)->selected)
+            glist_moveup(list);
+        list = g_list_next(list);
+    }
+    PLAYLIST_INCR_SERIAL(playlist);
+    PLAYLIST_UNLOCK(playlist);
+    if (pl->prev_selected != -1)
+        pl->prev_selected--;
+    if (pl->prev_min != -1)
+        pl->prev_min--;
+    if (pl->prev_max != -1)
+        pl->prev_max--;
+}
+
+void ui_skinned_playlist_move_down(UiSkinnedPlaylist * pl) {
+    GList *list;
+    Playlist *playlist = aud_playlist_get_active();
+
+    if (!playlist)
+        return;
+
+    PLAYLIST_LOCK(playlist);
+
+    if (!(list = g_list_last(playlist->entries))) {
+        PLAYLIST_UNLOCK(playlist);
+        return;
+    }
+
+    if (PLAYLIST_ENTRY(list->data)->selected) {
+        /* We are at the bottom */
+        PLAYLIST_UNLOCK(playlist);
+        return;
+    }
+
+    while (list) {
+        if (PLAYLIST_ENTRY(list->data)->selected)
+            glist_movedown(list);
+        list = g_list_previous(list);
+    }
+
+    PLAYLIST_INCR_SERIAL(playlist);
+    PLAYLIST_UNLOCK(playlist);
+
+    if (pl->prev_selected != -1)
+        pl->prev_selected++;
+    if (pl->prev_min != -1)
+        pl->prev_min++;
+    if (pl->prev_max != -1)
+        pl->prev_max++;
+}
+
+static void
+playlist_list_draw_string(cairo_t *cr, UiSkinnedPlaylist *pl,
+                          PangoFontDescription * font,
+                          gint line,
+                          gint width,
+                          const gchar * text,
+                          guint ppos)
+{
+    guint plist_length_int;
+    Playlist *playlist = aud_playlist_get_active();
+    PangoLayout *layout;
+
+    REQUIRE_LOCK(playlist->mutex);
+
+    cairo_new_path(cr);
+
+    if (config.show_numbers_in_pl) {
+        gchar *pos_string = g_strdup_printf(config.show_separator_in_pl == TRUE ? "%d" : "%d.", ppos);
+        plist_length_int =
+            gint_count_digits(aud_playlist_get_length(playlist)) + !config.show_separator_in_pl + 1; /* cf.show_separator_in_pl will be 0 if false */
+
+        padding = plist_length_int;
+        padding = ((padding + 1) * width_approx_digits);
+
+        layout = gtk_widget_create_pango_layout(playlistwin, pos_string);
+        pango_layout_set_font_description(layout, playlist_list_font);
+        pango_layout_set_width(layout, plist_length_int * 100);
+
+        pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
+
+        cairo_move_to(cr, (width_approx_digits *
+                         (-1 + plist_length_int - strlen(pos_string))) +
+                        (width_approx_digits / 4), (line - 1) * pl->fheight +
+                        ascent + abs(descent));
+        pango_cairo_show_layout(cr, layout);
+
+        g_free(pos_string);
+        g_object_unref(layout);
+
+        if (!config.show_separator_in_pl)
+            padding -= (width_approx_digits * 1.5);
+    } else {
+        padding = 3;
+    }
+
+    width -= padding;
+
+    layout = gtk_widget_create_pango_layout(playlistwin, text);
+
+    pango_layout_set_font_description(layout, playlist_list_font);
+    pango_layout_set_width(layout, width * PANGO_SCALE);
+    pango_layout_set_single_paragraph_mode(layout, TRUE);
+    pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+
+    cairo_move_to(cr, padding + (width_approx_letters / 4),
+                    (line - 1) * pl->fheight +
+                    ascent + abs(descent));
+    pango_cairo_show_layout(cr, layout);
+
+    g_object_unref(layout);
+}
+
+static gboolean ui_skinned_playlist_expose(GtkWidget *widget, GdkEventExpose *event) {
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST (widget);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(pl);
+    g_return_val_if_fail (priv->width > 0 && priv->height > 0, FALSE);
+
+    Playlist *playlist = aud_playlist_get_active();
+    PlaylistEntry *entry;
+    GList *list;
+    PangoLayout *layout;
+    gchar *title;
+    gint width, height;
+    gint i, max_first;
+    guint padding, padding_dwidth, padding_plength;
+    guint max_time_len = 0;
+    gfloat queue_tailpadding = 0;
+    gint tpadding; 
+    gsize tpadding_dwidth = 0;
+    gint x, y;
+    guint tail_width;
+    guint tail_len;
+    gboolean in_selection = FALSE;
+
+    gchar tail[100];
+    gchar queuepos[255];
+    gchar length[40];
+
+    gchar **frags;
+    gchar *frag0;
+
+    gint plw_w, plw_h;
+
+    cairo_t *cr;
+    gint yc;
+    gint pos;
+    gdouble rounding_offset;
+
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_PLAYLIST (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    cr = gdk_cairo_create(widget->window);
+
+    width = priv->width;
+    height = priv->height;
+
+    plw_w = playlistwin_get_width();
+    plw_h = playlistwin_get_height();
+
+    gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_NORMALBG));
+
+    cairo_rectangle(cr, 0, 0, width, height);
+    cairo_paint(cr);
+
+    if (!playlist_list_font) {
+        g_critical("Couldn't open playlist font");
+        return FALSE;
+    }
+
+    pl->fheight = (ascent + abs(descent));
+    pl->num_visible = height / pl->fheight;
+
+    rounding_offset = pl->fheight / 3;
+
+    max_first = aud_playlist_get_length(playlist) - pl->num_visible;
+    max_first = MAX(max_first, 0);
+
+    pl->first = CLAMP(pl->first, 0, max_first);
+
+    PLAYLIST_LOCK(playlist);
+    list = playlist->entries;
+    list = g_list_nth(list, pl->first);
+
+    /* It sucks having to run the iteration twice but this is the only
+       way you can reliably get the maximum width so we can get our
+       playlist nice and aligned... -- plasmaroo */
+
+    for (i = pl->first;
+         list && i < pl->first + pl->num_visible;
+         list = g_list_next(list), i++) {
+        entry = list->data;
+
+        if (entry->length != -1)
+        {
+            g_snprintf(length, sizeof(length), "%d:%-2.2d",
+                       entry->length / 60000, (entry->length / 1000) % 60);
+            tpadding_dwidth = MAX(tpadding_dwidth, strlen(length));
+        }
+    }
+
+    /* Reset */
+    list = playlist->entries;
+    list = g_list_nth(list, pl->first);
+
+    for (i = pl->first;
+         list && i < pl->first + pl->num_visible;
+         list = g_list_next(list), i++) {
+        entry = list->data;
+
+        if (entry->selected && !in_selection) {
+            yc = ((i - pl->first) * pl->fheight);
+
+            cairo_new_path(cr);
+
+            cairo_move_to(cr, 0, yc + (rounding_offset * 2));
+            cairo_curve_to(cr, 0, yc + rounding_offset, 0, yc + 0.5, 0 + rounding_offset, yc + 0.5);
+
+            cairo_line_to(cr, 0 + width - (rounding_offset * 2), yc + 0.5);
+            cairo_curve_to(cr, 0 + width - rounding_offset, yc + 0.5,
+                        0 + width, yc + 0.5, 0 + width, yc + rounding_offset);
+
+            in_selection = TRUE;
+        }
+
+        if ((!entry->selected ||
+            (i == pl->first + pl->num_visible - 1) || !g_list_next(list))
+            && in_selection) {
+
+            if (!entry->selected)
+                yc = (((i - 1) - pl->first) * pl->fheight);
+            else /* last visible item */
+                yc = ((i - pl->first) * pl->fheight);
+
+            cairo_line_to(cr, 0 + width, yc + pl->fheight - (rounding_offset * 2));
+            cairo_curve_to (cr, 0 + width, yc + pl->fheight - rounding_offset,
+                        0 + width, yc + pl->fheight - 0.5,
+                        0 + width-rounding_offset, yc + pl->fheight - 0.5);
+
+            cairo_line_to (cr, 0 + (rounding_offset * 2), yc + pl->fheight - 0.5);
+            cairo_curve_to (cr, 0 + rounding_offset, yc + pl->fheight - 0.5,
+                        0, yc + pl->fheight - 0.5,
+                        0, yc + pl->fheight - rounding_offset);
+
+            cairo_close_path (cr);
+
+            gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_SELECTEDBG));
+
+            cairo_fill(cr);
+
+            in_selection = FALSE;
+        }
+    }
+
+    list = playlist->entries;
+    list = g_list_nth(list, pl->first);
+
+    /* now draw the text */
+    for (i = pl->first;
+         list && i < pl->first + pl->num_visible;
+         list = g_list_next(list), i++) {
+        entry = list->data;
+
+        /* FIXME: entry->title should NEVER be NULL, and there should
+           NEVER be a need to do a UTF-8 conversion. Playlist title
+           strings should be kept properly. */
+
+        if (!entry->title) {
+            gchar *realfn = g_filename_from_uri(entry->filename, NULL, NULL);
+            gchar *basename = g_path_get_basename(realfn ? realfn : entry->filename);
+            title = aud_filename_to_utf8(basename);
+            g_free(basename); g_free(realfn);
+        }
+        else
+            title = aud_str_to_utf8(entry->title);
+
+        title = aud_convert_title_text(title);
+
+        pos = aud_playlist_get_queue_position(playlist, entry);
+
+        tail[0] = 0;
+        queuepos[0] = 0;
+        length[0] = 0;
+
+        if (pos != -1)
+            g_snprintf(queuepos, sizeof(queuepos), "%d", pos + 1);
+
+        if (entry->length != -1)
+        {
+            g_snprintf(length, sizeof(length), "%d:%-2.2d",
+                       entry->length / 60000, (entry->length / 1000) % 60);
+        }
+
+        strncat(tail, length, sizeof(tail) - 1);
+        tail_len = strlen(tail);
+
+        max_time_len = MAX(max_time_len, tail_len);
+
+        if (pos != -1 && tpadding_dwidth <= 0)
+            tail_width = width - (width_approx_digits * (strlen(queuepos) + 2.25));
+        else if (pos != -1)
+            tail_width = width - (width_approx_digits * (tpadding_dwidth + strlen(queuepos) + 4));
+        else if (tpadding_dwidth > 0)
+            tail_width = width - (width_approx_digits * (tpadding_dwidth + 2.5));
+        else
+            tail_width = width;
+
+        if (i == aud_playlist_get_position_nolock(playlist))
+            gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_CURRENT));
+        else
+            gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_NORMAL));
+
+        playlist_list_draw_string(cr, pl, playlist_list_font,
+                                  i - pl->first, tail_width, title,
+                                  i + 1);
+
+        x = width - width_approx_digits * 2;
+        y = ((i - pl->first) - 1) * pl->fheight + ascent;
+
+        frags = NULL;
+        frag0 = NULL;
+
+        if ((strlen(tail) > 0) && (tail != NULL)) {
+            frags = g_strsplit(tail, ":", 0);
+            frag0 = g_strconcat(frags[0], ":", NULL);
+
+            layout = gtk_widget_create_pango_layout(playlistwin, frags[1]);
+            pango_layout_set_font_description(layout, playlist_list_font);
+            pango_layout_set_width(layout, tail_len * 100);
+            pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
+
+            cairo_new_path(cr);
+            cairo_move_to(cr, x - (0.5 * width_approx_digits), y + abs(descent));
+            pango_cairo_show_layout(cr, layout);
+            g_object_unref(layout);
+
+            layout = gtk_widget_create_pango_layout(playlistwin, frag0);
+            pango_layout_set_font_description(layout, playlist_list_font);
+            pango_layout_set_width(layout, tail_len * 100);
+            pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
+
+            cairo_move_to(cr, x - (0.75 * width_approx_digits), y + abs(descent));
+            pango_cairo_show_layout(cr, layout);
+            g_object_unref(layout);
+
+            g_free(frag0);
+            g_strfreev(frags);
+        }
+
+        if (pos != -1) {
+            if (tpadding_dwidth > 0)
+                queue_tailpadding = tpadding_dwidth + 1;
+            else
+                queue_tailpadding = -0.75;
+
+            cairo_rectangle(cr,
+                            x -
+                            (((queue_tailpadding +
+                               strlen(queuepos)) *
+                              width_approx_digits) +
+                             (width_approx_digits / 4)),
+                            y + abs(descent),
+                            (strlen(queuepos)) *
+                            width_approx_digits +
+                            (width_approx_digits / 2),
+                            pl->fheight - 2);
+
+            layout =
+                gtk_widget_create_pango_layout(playlistwin, queuepos);
+            pango_layout_set_font_description(layout, playlist_list_font);
+            pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
+
+            cairo_move_to(cr,
+                            x -
+                            ((queue_tailpadding +
+                              strlen(queuepos)) * width_approx_digits) +
+                            (width_approx_digits / 4),
+                            y + abs(descent));
+            pango_cairo_show_layout(cr, layout);
+
+            g_object_unref(layout);
+        }
+
+        cairo_stroke(cr);
+
+        g_free(title);
+    }
+
+
+    /*
+     * Drop target hovering over the playlist, so draw some hint where the
+     * drop will occur.
+     *
+     * This is (currently? unfixably?) broken when dragging files from Qt/KDE apps,
+     * probably due to DnD signaling problems (actually i have no clue).
+     *
+     */
+
+    if (pl->drag_motion) {
+        guint pos, plength, lpadding;
+
+        if (config.show_numbers_in_pl) {
+            lpadding = gint_count_digits(aud_playlist_get_length(playlist)) + 1;
+            lpadding = ((lpadding + 1) * width_approx_digits);
+        }
+        else {
+            lpadding = 3;
+        };
+
+        /* We already hold the mutex and have the playlist locked, so call
+           the non-locking function. */
+        plength = aud_playlist_get_length(playlist);
+
+        x = pl->drag_motion_x;
+        y = pl->drag_motion_y;
+
+        if ((x > pl->x) && !(x > priv->width)) {
+
+            if ((y > pl->y)
+                && !(y > (priv->height + pl->y))) {
+
+                pos = (y / pl->fheight) +
+                    pl->first;
+
+                if (pos > (plength)) {
+                    pos = plength;
+                }
+
+                gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_CURRENT));
+
+                cairo_new_path(cr);
+
+                cairo_move_to(cr, 0, ((pos - pl->first) * pl->fheight));
+                cairo_rel_line_to(cr, priv->width - 1, 0);
+
+                cairo_set_line_width(cr, 1);
+                cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+                cairo_stroke(cr);
+            }
+
+        }
+
+        /* When dropping on the borders of the playlist, outside the text area,
+         * files get appended at the end of the list. Show that too.
+         */
+
+        if ((y < pl->y) || (y > priv->height + pl->y)) {
+            if ((y >= 0) || (y <= (priv->height + pl->y))) {
+                pos = plength;
+
+                gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_CURRENT));
+
+                cairo_new_path(cr);
+
+                cairo_move_to(cr, 0, ((pos - pl->first) * pl->fheight));
+                cairo_rel_line_to(cr, priv->width - 1, 0);
+
+                cairo_set_line_width(cr, 1);
+                cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+                cairo_stroke(cr);
+            }
+        }
+    }
+
+    gdk_cairo_set_source_color(cr, skin_get_color(aud_active_skin, SKIN_PLEDIT_NORMAL));
+    cairo_set_line_width(cr, 1);
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+
+    if (config.show_numbers_in_pl)
+    {
+        padding_plength = aud_playlist_get_length(playlist);
+
+        if (padding_plength == 0) {
+            padding_dwidth = 0;
+        }
+        else {
+            padding_dwidth = gint_count_digits(aud_playlist_get_length(playlist));
+        }
+
+        padding =
+            (padding_dwidth *
+             width_approx_digits) + width_approx_digits;
+
+
+        /* For italic or oblique fonts we add another half of the
+         * approximate width */
+        if (has_slant)
+            padding += width_approx_digits_half;
+
+        if (config.show_separator_in_pl) {
+            cairo_new_path(cr);
+
+            cairo_move_to(cr, padding, 0);
+            cairo_rel_line_to(cr, 0, priv->height - 1);
+
+            cairo_stroke(cr);
+        }
+    }
+
+    if (tpadding_dwidth != 0)
+    {
+        tpadding = (tpadding_dwidth * width_approx_digits) + (width_approx_digits * 1.5);
+
+        if (has_slant)
+            tpadding += width_approx_digits_half;
+
+        if (config.show_separator_in_pl) {
+            cairo_new_path(cr);
+
+            cairo_move_to(cr, priv->width - tpadding, 0);
+            cairo_rel_line_to(cr, 0, priv->height - 1);
+
+            cairo_stroke(cr);
+        }
+    }
+
+    PLAYLIST_UNLOCK(playlist);
+
+    cairo_destroy(cr);
+
+    return FALSE;
+}
+
+gint ui_skinned_playlist_get_position(GtkWidget *widget, gint x, gint y) {
+    gint iy, length;
+    gint ret;
+    Playlist *playlist = aud_playlist_get_active();
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST (widget);
+
+    if (!pl->fheight)
+        return -1;
+
+    if ((length = aud_playlist_get_length(playlist)) == 0)
+        return -1;
+    iy = y;
+
+    ret = (iy / pl->fheight) + pl->first;
+
+    if (ret > length - 1)
+        ret = -1;
+
+    return ret;
+}
+
+static gboolean ui_skinned_playlist_button_press(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST (widget);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(widget);
+
+    gint nr;
+    Playlist *playlist = aud_playlist_get_active();
+
+    nr = ui_skinned_playlist_get_position(widget, event->x, event->y);
+    if (nr == -1)
+        return FALSE;
+
+    if (event->button == 3) {
+        ui_manager_popup_menu_show(GTK_MENU(playlistwin_popup_menu),
+                                   event->x_root, event->y_root + 5,
+                                   event->button, event->time);
+        GList* selection = aud_playlist_get_selected(playlist);
+        if (g_list_find(selection, GINT_TO_POINTER(nr)) == NULL) {
+            aud_playlist_select_all(playlist, FALSE);
+            aud_playlist_select_range(playlist, nr, nr, TRUE);
+        }
+    } else if (event->button == 1) {
+        if (!(event->state & GDK_CONTROL_MASK))
+            aud_playlist_select_all(playlist, FALSE);
+
+        if ((event->state & GDK_MOD1_MASK))
+            aud_playlist_queue_position(playlist, nr);
+
+        if (event->state & GDK_SHIFT_MASK && pl->prev_selected != -1) {
+            aud_playlist_select_range(playlist, pl->prev_selected, nr, TRUE);
+            pl->prev_min = pl->prev_selected;
+            pl->prev_max = nr;
+            priv->drag_pos = nr - pl->first;
+        }
+        else {
+            if (aud_playlist_select_invert(playlist, nr)) {
+                if (event->state & GDK_CONTROL_MASK) {
+                    if (pl->prev_min == -1) {
+                        pl->prev_min = pl->prev_selected;
+                        pl->prev_max = pl->prev_selected;
+                    }
+                    if (nr < pl->prev_min)
+                        pl->prev_min = nr;
+                    else if (nr > pl->prev_max)
+                        pl->prev_max = nr;
+                }
+                else
+                    pl->prev_min = -1;
+                pl->prev_selected = nr;
+                priv->drag_pos = nr - pl->first;
+            }
+        }
+        if (event->type == GDK_2BUTTON_PRESS) {
+            /*
+             * Ungrab the pointer to prevent us from
+             * hanging on to it during the sometimes slow
+             * audacious_drct_initiate().
+             */
+            gdk_pointer_ungrab(GDK_CURRENT_TIME);
+            aud_playlist_set_position(playlist, nr);
+            if (!audacious_drct_get_playing())
+                audacious_drct_initiate();
+        }
+
+        priv->dragging = TRUE;
+    }
+    playlistwin_update_list(playlist);
+    ui_skinned_playlist_popup_hide(widget);
+    ui_skinned_playlist_popup_timer_stop(widget);
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_playlist_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(widget);
+
+    priv->dragging = FALSE;
+    priv->auto_drag_down = FALSE;
+    priv->auto_drag_up = FALSE;
+    gtk_widget_queue_draw(widget);
+
+    ui_skinned_playlist_popup_hide(widget);
+    ui_skinned_playlist_popup_timer_stop(widget);
+    return TRUE;
+}
+
+static gboolean ui_skinned_playlist_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    UiSkinnedPlaylist *pl = UI_SKINNED_PLAYLIST(widget);
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(widget);
+
+    gint nr, y, off, i;
+    if (priv->dragging) {
+        y = event->y;
+        nr = (y / pl->fheight);
+        if (nr < 0) {
+            nr = 0;
+            if (!priv->auto_drag_up) {
+                priv->auto_drag_up = TRUE;
+                priv->auto_drag_up_tag =
+                    g_timeout_add(100, ui_skinned_playlist_auto_drag_up_func, pl);
+            }
+        }
+        else if (priv->auto_drag_up)
+            priv->auto_drag_up = FALSE;
+
+        if (nr >= pl->num_visible) {
+            nr = pl->num_visible - 1;
+            if (!priv->auto_drag_down) {
+                priv->auto_drag_down = TRUE;
+                priv->auto_drag_down_tag =
+                    g_timeout_add(100, ui_skinned_playlist_auto_drag_down_func, pl);
+            }
+        }
+        else if (priv->auto_drag_down)
+            priv->auto_drag_down = FALSE;
+
+        off = nr - priv->drag_pos;
+        if (off) {
+            for (i = 0; i < abs(off); i++) {
+                if (off < 0)
+                    ui_skinned_playlist_move_up(pl);
+                else
+                    ui_skinned_playlist_move_down(pl);
+
+            }
+            playlistwin_update_list(aud_playlist_get_active());
+        }
+        priv->drag_pos = nr;
+    } else if (aud_cfg->show_filepopup_for_tuple) {
+        gint pos = ui_skinned_playlist_get_position(widget, event->x, event->y);
+        gint cur_pos = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_position"));
+        if (pos != cur_pos) {
+            g_object_set_data(G_OBJECT(widget), "popup_position", GINT_TO_POINTER(pos));
+            ui_skinned_playlist_popup_hide(widget);
+            ui_skinned_playlist_popup_timer_stop(widget);
+            if (pos != -1)
+                ui_skinned_playlist_popup_timer_start(widget);
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_playlist_leave_notify(GtkWidget *widget, GdkEventCrossing *event) {
+    ui_skinned_playlist_popup_hide(widget);
+    ui_skinned_playlist_popup_timer_stop(widget);
+
+    return FALSE;
+}
+
+static void ui_skinned_playlist_redraw(UiSkinnedPlaylist *playlist) {
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(playlist);
+
+    if (priv->resize_height || priv->resize_width)
+        gtk_widget_set_size_request(GTK_WIDGET(playlist), priv->width+priv->resize_width, priv->height+priv->resize_height);
+
+    gtk_widget_queue_draw(GTK_WIDGET(playlist));
+}
+
+void ui_skinned_playlist_set_font(const gchar * font) {
+    /* Welcome to bad hack central 2k3 */
+    gchar *font_lower;
+    gint width_temp;
+    gint width_temp_0;
+
+    playlist_list_font = pango_font_description_from_string(font);
+
+    text_get_extents(font,
+                     "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ",
+                     &width_approx_letters, NULL, &ascent, &descent);
+
+    width_approx_letters = (width_approx_letters / 53);
+
+    /* Experimental: We don't weigh the 1 into total because it's width is almost always
+     * very different from the rest
+     */
+    text_get_extents(font, "023456789", &width_approx_digits, NULL, NULL,
+                     NULL);
+    width_approx_digits = (width_approx_digits / 9);
+
+    /* Precache some often used calculations */
+    width_approx_digits_half = width_approx_digits / 2;
+
+    /* FIXME: We assume that any other number is broader than the "1" */
+    text_get_extents(font, "1", &width_temp, NULL, NULL, NULL);
+    text_get_extents(font, "2", &width_temp_0, NULL, NULL, NULL);
+
+    if (abs(width_temp_0 - width_temp) < 2) {
+        width_delta_digit_one = 0;
+    }
+    else {
+        width_delta_digit_one = ((width_temp_0 - width_temp) / 2) + 2;
+    }
+
+    text_get_extents(font, ":", &width_colon, NULL, NULL, NULL);
+    width_colon_third = width_colon / 4;
+
+    font_lower = g_utf8_strdown(font, strlen(font));
+    /* This doesn't take any i18n into account, but i think there is none with TTF fonts
+     * FIXME: This can probably be retrieved trough Pango too
+     */
+    has_slant = g_strstr_len(font_lower, strlen(font_lower), "oblique")
+        || g_strstr_len(font_lower, strlen(font_lower), "italic");
+
+    g_free(font_lower);
+}
+
+void ui_skinned_playlist_resize_relative(GtkWidget *widget, gint w, gint h) {
+    UiSkinnedPlaylistPrivate *priv = UI_SKINNED_PLAYLIST_GET_PRIVATE(widget);
+    priv->resize_width += w;
+    priv->resize_height += h;
+}
+
+static gboolean ui_skinned_playlist_popup_show(gpointer data) {
+#if 0
+    GtkWidget *widget = data;
+    gint pos = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_position"));
+
+    if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_active")) == 1 && pos != -1) {
+        Tuple *tuple;
+        Playlist *pl_active = aud_playlist_get_active();
+        GtkWidget *popup = g_object_get_data(G_OBJECT(widget), "popup");
+
+        tuple = playlist_get_tuple(pl_active, pos);
+        if ((tuple == NULL) || (tuple_get_int(tuple, FIELD_LENGTH, NULL) < 1)) {
+           gchar *title = aud_playlist_get_songtitle(pl_active, pos);
+           fileinfopopup_show_from_title(popup, title);
+           g_free(title);
+        } else {
+           fileinfopopup_show_from_tuple(popup , tuple);
+        }
+        g_object_set_data(G_OBJECT(widget), "popup_active" , GINT_TO_POINTER(1));
+    }
+
+    ui_skinned_playlist_popup_timer_stop(widget);
+    return FALSE;
+#endif
+}
+
+static void ui_skinned_playlist_popup_hide(GtkWidget *widget) {
+#if 0
+    if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "popup_active")) == 1) {
+        GtkWidget *popup = g_object_get_data(G_OBJECT(widget), "popup");
+        g_object_set_data(G_OBJECT(widget), "popup_active", GINT_TO_POINTER(0));
+        fileinfopopup_hide(popup, NULL);
+    }
+#endif
+}
+
+static void ui_skinned_playlist_popup_timer_start(GtkWidget *widget) {
+    gint timer_id = g_timeout_add(aud_cfg->filepopup_delay*100, ui_skinned_playlist_popup_show, widget);
+    g_object_set_data(G_OBJECT(widget), "timer_id", GINT_TO_POINTER(timer_id));
+    g_object_set_data(G_OBJECT(widget), "timer_active", GINT_TO_POINTER(1));
+}
+
+static void ui_skinned_playlist_popup_timer_stop(GtkWidget *widget) {
+    if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_active")) == 1)
+        g_source_remove(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "timer_id")));
+
+    g_object_set_data(G_OBJECT(widget), "timer_id", GINT_TO_POINTER(0));
+    g_object_set_data(G_OBJECT(widget), "timer_active", GINT_TO_POINTER(0));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playlist.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,77 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_PLAYLIST_H
+#define AUDACIOUS_UI_SKINNED_PLAYLIST_H
+
+#include <gtk/gtk.h>
+
+#include <cairo.h>
+#include <pango/pangocairo.h>
+
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_PLAYLIST(obj)          GTK_CHECK_CAST (obj, ui_skinned_playlist_get_type (), UiSkinnedPlaylist)
+#define UI_SKINNED_PLAYLIST_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_playlist_get_type (), UiSkinnedPlaylistClass)
+#define UI_SKINNED_IS_PLAYLIST(obj)       GTK_CHECK_TYPE (obj, ui_skinned_playlist_get_type ())
+
+typedef struct _UiSkinnedPlaylist        UiSkinnedPlaylist;
+typedef struct _UiSkinnedPlaylistClass   UiSkinnedPlaylistClass;
+
+struct _UiSkinnedPlaylist {
+    GtkWidget   widget;
+    gboolean    pressed;
+    gint        x, y;
+    gint        first;
+    gint        num_visible;
+    gint        prev_selected, prev_min, prev_max;
+    gboolean    drag_motion;
+    gint        drag_motion_x, drag_motion_y;
+    gint        fheight;
+};
+
+struct _UiSkinnedPlaylistClass {
+    GtkWidgetClass    parent_class;
+    void (* redraw)   (UiSkinnedPlaylist *playlist);
+};
+
+GtkWidget* ui_skinned_playlist_new(GtkWidget *fixed, gint x, gint y, gint w, gint h);
+GtkType ui_skinned_playlist_get_type(void);
+void ui_skinned_playlist_resize_relative(GtkWidget *widget, gint w, gint h);
+void ui_skinned_playlist_set_font(const gchar * font);
+void ui_skinned_playlist_move_up(UiSkinnedPlaylist *pl);
+void ui_skinned_playlist_move_down(UiSkinnedPlaylist *pl);
+gint ui_skinned_playlist_get_position(GtkWidget *widget, gint x, gint y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_PLAYLIST_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playlist_slider.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,337 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skin.h"
+#include "ui_skinned_playlist_slider.h"
+#include "ui_playlist.h"
+
+#define UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ui_skinned_playlist_slider_get_type(), UiSkinnedPlaylistSliderPrivate))
+typedef struct _UiSkinnedPlaylistSliderPrivate UiSkinnedPlaylistSliderPrivate;
+
+enum {
+    REDRAW,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedPlaylistSliderPrivate {
+    SkinPixmapId     skin_index;
+    gint             width, height;
+
+    gint             resize_height;
+    gint             move_x;
+    gint             prev_y;
+    gint             drag_y;
+};
+
+static void ui_skinned_playlist_slider_class_init         (UiSkinnedPlaylistSliderClass *klass);
+static void ui_skinned_playlist_slider_init               (UiSkinnedPlaylistSlider *playlist_slider);
+static void ui_skinned_playlist_slider_destroy            (GtkObject *object);
+static void ui_skinned_playlist_slider_realize            (GtkWidget *widget);
+static void ui_skinned_playlist_slider_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_playlist_slider_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_playlist_slider_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_skinned_playlist_slider_set_position       (GtkWidget *widget, gint y);
+static gboolean ui_skinned_playlist_slider_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_playlist_slider_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_playlist_slider_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static void ui_skinned_playlist_slider_redraw             (UiSkinnedPlaylistSlider *playlist_slider);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint playlist_slider_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_playlist_slider_get_type() {
+    static GType playlist_slider_type = 0;
+    if (!playlist_slider_type) {
+        static const GTypeInfo playlist_slider_info = {
+            sizeof (UiSkinnedPlaylistSliderClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_playlist_slider_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedPlaylistSlider),
+            0,
+            (GInstanceInitFunc) ui_skinned_playlist_slider_init,
+        };
+        playlist_slider_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedPlaylistSlider", &playlist_slider_info, 0);
+    }
+
+    return playlist_slider_type;
+}
+
+static void ui_skinned_playlist_slider_class_init(UiSkinnedPlaylistSliderClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_playlist_slider_destroy;
+
+    widget_class->realize = ui_skinned_playlist_slider_realize;
+    widget_class->expose_event = ui_skinned_playlist_slider_expose;
+    widget_class->size_request = ui_skinned_playlist_slider_size_request;
+    widget_class->size_allocate = ui_skinned_playlist_slider_size_allocate;
+    widget_class->button_press_event = ui_skinned_playlist_slider_button_press;
+    widget_class->button_release_event = ui_skinned_playlist_slider_button_release;
+    widget_class->motion_notify_event = ui_skinned_playlist_slider_motion_notify;
+
+    klass->redraw = ui_skinned_playlist_slider_redraw;
+
+    playlist_slider_signals[REDRAW] = 
+        g_signal_new ("redraw", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedPlaylistSliderClass, redraw), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedPlaylistSliderPrivate));
+}
+
+static void ui_skinned_playlist_slider_init(UiSkinnedPlaylistSlider *playlist_slider) {
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(playlist_slider);
+    playlist_slider->pressed = FALSE;
+    priv->resize_height = 0;
+    priv->move_x = 0;
+    priv->drag_y = 0;
+    priv->prev_y = 0;
+}
+
+GtkWidget* ui_skinned_playlist_slider_new(GtkWidget *fixed, gint x, gint y, gint h) {
+
+    UiSkinnedPlaylistSlider *hs = g_object_new (ui_skinned_playlist_slider_get_type (), NULL);
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(hs);
+
+    hs->x = x;
+    hs->y = y;
+    priv->width = 8;
+    priv->height = h;
+    priv->skin_index = SKIN_PLEDIT;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(hs), hs->x, hs->y);
+
+    return GTK_WIDGET(hs);
+}
+
+static void ui_skinned_playlist_slider_destroy(GtkObject *object) {
+    UiSkinnedPlaylistSlider *playlist_slider;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYLIST_SLIDER (object));
+
+    playlist_slider = UI_SKINNED_PLAYLIST_SLIDER (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_playlist_slider_realize(GtkWidget *widget) {
+    UiSkinnedPlaylistSlider *playlist_slider;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYLIST_SLIDER(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    playlist_slider = UI_SKINNED_PLAYLIST_SLIDER(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
+                             GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_playlist_slider_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+
+    requisition->width = priv->width;
+    requisition->height = priv->height;
+}
+
+static void ui_skinned_playlist_slider_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedPlaylistSlider *playlist_slider = UI_SKINNED_PLAYLIST_SLIDER (widget);
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(playlist_slider);
+
+    widget->allocation = *allocation;
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    if (playlist_slider->x + priv->move_x == widget->allocation.x)
+        priv->move_x = 0;
+    playlist_slider->x = widget->allocation.x;
+    playlist_slider->y = widget->allocation.y;
+
+    if (priv->height != widget->allocation.height) {
+        priv->height = priv->height + priv->resize_height;
+        priv->resize_height = 0;
+        gtk_widget_queue_draw(widget);
+    }
+}
+
+static gboolean ui_skinned_playlist_slider_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_PLAYLIST_SLIDER (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedPlaylistSlider *ps = UI_SKINNED_PLAYLIST_SLIDER (widget);
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(ps);
+    g_return_val_if_fail (priv->width > 0 && priv->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, priv->width, priv->height);
+
+    gint num_visible;
+    num_visible = playlistwin_list_get_visible_count();
+
+
+    Playlist *playlist = aud_playlist_get_active();
+
+    gint y;
+    if (aud_playlist_get_length(playlist) > num_visible)
+        y = (playlistwin_list_get_first() * (priv->height - 19)) /
+            (aud_playlist_get_length(playlist) - num_visible);
+    else
+        y = 0;
+
+    if (y < 0) y=0;
+    if (y > priv->height - 19) y = priv->height - 19;
+
+    priv->prev_y = y;
+
+    /* FIXME: uses aud_active_skin->pixmaps directly and may need calibration */
+    /* drawing background */
+    gint c;
+    for (c = 0; c < priv->height / 29; c++) {
+         gdk_pixbuf_copy_area(aud_active_skin->pixmaps[SKIN_PLEDIT].pixbuf,
+                              36, 42, priv->width, 29, obj, 0, c*29);
+    }
+
+    /* drawing knob */
+    skin_draw_pixbuf(widget, aud_active_skin, obj, priv->skin_index, ps->pressed ? 61 : 52, 53, 0, y, priv->width, 18);
+
+    ui_skinned_widget_draw(widget, obj, priv->width, priv->height, FALSE);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void ui_skinned_playlist_slider_set_position(GtkWidget *widget, gint y) {
+    gint pos;
+    Playlist *playlist = aud_playlist_get_active();
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+
+    y = CLAMP(y, 0, priv->height - 19);
+
+    pos = (y * (aud_playlist_get_length(playlist) - playlistwin_list_get_visible_count())) / (priv->height - 19);
+    playlistwin_set_toprow(pos);
+
+    gtk_widget_queue_draw(widget);
+}
+
+static gboolean ui_skinned_playlist_slider_button_press(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedPlaylistSlider *ps = UI_SKINNED_PLAYLIST_SLIDER (widget);
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+
+    if (event->button != 1 && event->button != 2)
+        return TRUE;
+
+    gint y = event->y;
+    if (event->type == GDK_BUTTON_PRESS) {
+        ps->pressed = TRUE;
+        if ((y >= priv->prev_y && y < priv->prev_y + 18)) {
+            priv->drag_y = y - priv->prev_y;
+        } else if (event->button == 2) {
+            ui_skinned_playlist_slider_set_position(widget, y);
+            priv->drag_y = 0;
+        } else {
+            gint n = playlistwin_list_get_visible_count() / 2;
+            if (y < priv->prev_y)
+                n *= -1;
+            playlistwin_scroll(n);
+        }
+        gtk_widget_queue_draw(widget);
+    }
+    return TRUE;
+}
+
+static gboolean ui_skinned_playlist_slider_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedPlaylistSlider *ps = UI_SKINNED_PLAYLIST_SLIDER(widget);
+
+    if (event->button == 1 || event->button == 2) {
+        ps->pressed = FALSE;
+        gtk_widget_queue_draw(widget);
+    }
+    return TRUE;
+}
+
+static gboolean ui_skinned_playlist_slider_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    UiSkinnedPlaylistSlider *ps = UI_SKINNED_PLAYLIST_SLIDER(widget);
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+
+    if (ps->pressed) {
+        gint y = event->y - priv->drag_y;
+        ui_skinned_playlist_slider_set_position(widget, y);
+    }
+    return TRUE;
+}
+
+static void ui_skinned_playlist_slider_redraw(UiSkinnedPlaylistSlider *playlist_slider) {
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(playlist_slider);
+
+    if (priv->resize_height)
+        gtk_widget_set_size_request(GTK_WIDGET(playlist_slider), priv->width, priv->height+priv->resize_height);
+    if (priv->move_x)
+        gtk_fixed_move(GTK_FIXED(gtk_widget_get_parent(GTK_WIDGET(playlist_slider))), GTK_WIDGET(playlist_slider),
+                       playlist_slider->x+priv->move_x, playlist_slider->y);
+
+    gtk_widget_queue_draw(GTK_WIDGET(playlist_slider));
+}
+
+void ui_skinned_playlist_slider_move_relative(GtkWidget *widget, gint x) {
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+    priv->move_x += x;
+}
+
+void ui_skinned_playlist_slider_resize_relative(GtkWidget *widget, gint h) {
+    UiSkinnedPlaylistSliderPrivate *priv = UI_SKINNED_PLAYLIST_SLIDER_GET_PRIVATE(widget);
+    priv->resize_height += h;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playlist_slider.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,63 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_PLAYLIST_SLIDER_H
+#define AUDACIOUS_UI_SKINNED_PLAYLIST_SLIDER_H
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_PLAYLIST_SLIDER(obj)          GTK_CHECK_CAST (obj, ui_skinned_playlist_slider_get_type (), UiSkinnedPlaylistSlider)
+#define UI_SKINNED_PLAYLIST_SLIDER_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_playlist_slider_get_type (), UiSkinnedPlaylistSliderClass)
+#define UI_SKINNED_IS_PLAYLIST_SLIDER(obj)       GTK_CHECK_TYPE (obj, ui_skinned_playlist_slider_get_type ())
+
+typedef struct _UiSkinnedPlaylistSlider        UiSkinnedPlaylistSlider;
+typedef struct _UiSkinnedPlaylistSliderClass   UiSkinnedPlaylistSliderClass;
+
+struct _UiSkinnedPlaylistSlider {
+    GtkWidget   widget;
+    gboolean    pressed;
+    gint        x, y;
+};
+
+struct _UiSkinnedPlaylistSliderClass {
+    GtkWidgetClass    parent_class;
+    void (* redraw)   (UiSkinnedPlaylistSlider *playlist_slider);
+};
+
+GtkWidget* ui_skinned_playlist_slider_new(GtkWidget *fixed, gint x, gint y, gint h);
+GtkType ui_skinned_playlist_slider_get_type(void);
+void ui_skinned_playlist_slider_move_relative(GtkWidget *widget, gint x);
+void ui_skinned_playlist_slider_resize_relative(GtkWidget *widget, gint h);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_PLAYLIST_SLIDER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playstatus.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,246 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skin.h"
+#include "ui_skinned_playstatus.h"
+#include "skins_cfg.h"
+
+#define UI_TYPE_SKINNED_PLAYSTATUS           (ui_skinned_playstatus_get_type())
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_skinned_playstatus_class_init         (UiSkinnedPlaystatusClass *klass);
+static void ui_skinned_playstatus_init               (UiSkinnedPlaystatus *playstatus);
+static void ui_skinned_playstatus_destroy            (GtkObject *object);
+static void ui_skinned_playstatus_realize            (GtkWidget *widget);
+static void ui_skinned_playstatus_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_playstatus_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_playstatus_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_skinned_playstatus_toggle_scaled      (UiSkinnedPlaystatus *playstatus);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint playstatus_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_playstatus_get_type() {
+    static GType playstatus_type = 0;
+    if (!playstatus_type) {
+        static const GTypeInfo playstatus_info = {
+            sizeof (UiSkinnedPlaystatusClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_playstatus_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedPlaystatus),
+            0,
+            (GInstanceInitFunc) ui_skinned_playstatus_init,
+        };
+        playstatus_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedPlaystatus", &playstatus_info, 0);
+    }
+
+    return playstatus_type;
+}
+
+static void ui_skinned_playstatus_class_init(UiSkinnedPlaystatusClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_playstatus_destroy;
+
+    widget_class->realize = ui_skinned_playstatus_realize;
+    widget_class->expose_event = ui_skinned_playstatus_expose;
+    widget_class->size_request = ui_skinned_playstatus_size_request;
+    widget_class->size_allocate = ui_skinned_playstatus_size_allocate;
+
+    klass->scaled = ui_skinned_playstatus_toggle_scaled;
+
+    playstatus_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedPlaystatusClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_skinned_playstatus_init(UiSkinnedPlaystatus *playstatus) {
+    playstatus->width = 11;
+    playstatus->height = 9;
+}
+
+GtkWidget* ui_skinned_playstatus_new(GtkWidget *fixed, gint x, gint y) {
+    UiSkinnedPlaystatus *playstatus = g_object_new (ui_skinned_playstatus_get_type (), NULL);
+
+    playstatus->x = x;
+    playstatus->y = y;
+
+    playstatus->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(playstatus), playstatus->x, playstatus->y);
+
+    return GTK_WIDGET(playstatus);
+}
+
+static void ui_skinned_playstatus_destroy(GtkObject *object) {
+    UiSkinnedPlaystatus *playstatus;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYSTATUS (object));
+
+    playstatus = UI_SKINNED_PLAYSTATUS (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_playstatus_realize(GtkWidget *widget) {
+    UiSkinnedPlaystatus *playstatus;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_PLAYSTATUS(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    playstatus = UI_SKINNED_PLAYSTATUS(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_playstatus_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS(widget);
+
+    requisition->width = playstatus->width*(playstatus->scaled ? config.scale_factor : 1);
+    requisition->height = playstatus->height*(playstatus->scaled ? config.scale_factor : 1);
+}
+
+static void ui_skinned_playstatus_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (playstatus->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (playstatus->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    playstatus->x = widget->allocation.x/(playstatus->scaled ? config.scale_factor : 1);
+    playstatus->y = widget->allocation.y/(playstatus->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_skinned_playstatus_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_PLAYSTATUS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS (widget);
+    g_return_val_if_fail (playstatus->width > 0 && playstatus->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, playstatus->width, playstatus->height);
+
+    if (playstatus->status == STATUS_STOP && playstatus->buffering == TRUE)
+        playstatus->buffering = FALSE;
+    if (playstatus->status == STATUS_PLAY && playstatus->buffering == TRUE)
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 39, 0, 0, 0, 3, playstatus->height);
+    else if (playstatus->status == STATUS_PLAY)
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 36, 0, 0, 0, 3, playstatus->height);
+    else
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 27, 0, 0, 0, 2, playstatus->height);
+    switch (playstatus->status) {
+    case STATUS_STOP:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 18, 0, 2, 0, 9, playstatus->height);
+        break;
+    case STATUS_PAUSE:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 9, 0, 2, 0, 9, playstatus->height);
+        break;
+    case STATUS_PLAY:
+        skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_PLAYPAUSE, 1, 0, 3, 0, 8, playstatus->height);
+        break;
+    }
+
+    ui_skinned_widget_draw(widget, obj, playstatus->width, playstatus->height, playstatus->scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void ui_skinned_playstatus_toggle_scaled(UiSkinnedPlaystatus *playstatus) {
+    GtkWidget *widget = GTK_WIDGET (playstatus);
+
+    playstatus->scaled = !playstatus->scaled;
+    gtk_widget_set_size_request(widget, playstatus->width*(playstatus->scaled ? config.scale_factor : 1), playstatus->height*(playstatus->scaled ? config.scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(playstatus));
+}
+
+void ui_skinned_playstatus_set_status(GtkWidget *widget, PStatus status) {
+    g_return_if_fail (UI_SKINNED_IS_PLAYSTATUS (widget));
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS (widget);
+
+    playstatus->status = status;
+    gtk_widget_queue_draw(widget);
+}
+
+void ui_skinned_playstatus_set_buffering(GtkWidget *widget, gboolean status) {
+    g_return_if_fail (UI_SKINNED_IS_PLAYSTATUS (widget));
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS (widget);
+
+    playstatus->buffering = status;
+    gtk_widget_queue_draw(widget);
+}
+
+void ui_skinned_playstatus_set_size(GtkWidget *widget, gint width, gint height) {
+    g_return_if_fail (UI_SKINNED_IS_PLAYSTATUS (widget));
+    UiSkinnedPlaystatus *playstatus = UI_SKINNED_PLAYSTATUS (widget);
+
+    playstatus->width = width;
+    playstatus->height = height;
+
+    gtk_widget_set_size_request(widget, width*(playstatus->scaled ? config.scale_factor : 1), height*(playstatus->scaled ? config.scale_factor : 1));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playstatus.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,71 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_PLAYSTATUS_H
+#define AUDACIOUS_UI_SKINNED_PLAYSTATUS_H
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_PLAYSTATUS(obj)          GTK_CHECK_CAST (obj, ui_skinned_playstatus_get_type (), UiSkinnedPlaystatus)
+#define UI_SKINNED_PLAYSTATUS_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_playstatus_get_type (), UiSkinnedPlaystatusClass)
+#define UI_SKINNED_IS_PLAYSTATUS(obj)       GTK_CHECK_TYPE (obj, ui_skinned_playstatus_get_type ())
+
+typedef struct _UiSkinnedPlaystatus        UiSkinnedPlaystatus;
+typedef struct _UiSkinnedPlaystatusClass   UiSkinnedPlaystatusClass;
+
+typedef enum {
+    STATUS_STOP, STATUS_PAUSE, STATUS_PLAY
+} PStatus;
+
+struct _UiSkinnedPlaystatus {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gboolean         scaled;
+    PStatus          status;
+    gboolean         buffering;
+};
+
+struct _UiSkinnedPlaystatusClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)        (UiSkinnedPlaystatus *menurow);
+};
+
+GtkWidget* ui_skinned_playstatus_new (GtkWidget *fixed, gint x, gint y);
+GtkType ui_skinned_playstatus_get_type(void);
+void ui_skinned_playstatus_set_status(GtkWidget *widget, PStatus status);
+void ui_skinned_playstatus_set_buffering(GtkWidget *widget, gboolean status);
+void ui_skinned_playstatus_set_size(GtkWidget *widget, gint width, gint height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_PLAYSTATUS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_textbox.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,870 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skinned_textbox.h"
+#include "skins_cfg.h"
+#include "plugin.h"
+#include <string.h>
+
+#include "util.h"
+
+#define UI_SKINNED_TEXTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ui_skinned_textbox_get_type(), UiSkinnedTextboxPrivate))
+typedef struct _UiSkinnedTextboxPrivate UiSkinnedTextboxPrivate;
+
+#define TEXTBOX_SCROLL_SMOOTH_TIMEOUT  30
+#define TEXTBOX_SCROLL_WAIT            80
+
+enum {
+    CLICKED,
+    DOUBLE_CLICKED,
+    RIGHT_CLICKED,
+    DOUBLED,
+    REDRAW,
+    LAST_SIGNAL
+};
+
+struct _UiSkinnedTextboxPrivate {
+    SkinPixmapId     skin_index;
+    gboolean         scaled;
+    gboolean         scroll_back;
+    gint             nominal_y, nominal_height;
+    gint             scroll_timeout;
+    gint             font_ascent, font_descent;
+    PangoFontDescription *font;
+    gchar            *fontname;
+    gchar            *pixbuf_text;
+    gint             skin_id;
+    gint             drag_x, drag_off, offset;
+    gboolean         is_scrollable, is_dragging;
+    gint             pixbuf_width;
+    GdkPixbuf        *pixbuf;
+    gboolean         scroll_allowed, scroll_enabled;
+    gint             scroll_dummy;
+    gint             move_x, move_y;
+};
+
+static void ui_skinned_textbox_class_init         (UiSkinnedTextboxClass *klass);
+static void ui_skinned_textbox_init               (UiSkinnedTextbox *textbox);
+static void ui_skinned_textbox_destroy            (GtkObject *object);
+static void ui_skinned_textbox_realize            (GtkWidget *widget);
+static void ui_skinned_textbox_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_skinned_textbox_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_skinned_textbox_expose         (GtkWidget *widget, GdkEventExpose *event);
+static gboolean ui_skinned_textbox_button_press   (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_textbox_button_release (GtkWidget *widget, GdkEventButton *event);
+static gboolean ui_skinned_textbox_motion_notify  (GtkWidget *widget, GdkEventMotion *event);
+static void ui_skinned_textbox_toggle_scaled      (UiSkinnedTextbox *textbox);
+static void ui_skinned_textbox_redraw             (UiSkinnedTextbox *textbox);
+static gboolean ui_skinned_textbox_should_scroll  (UiSkinnedTextbox *textbox);
+static void textbox_generate_xfont_pixmap         (UiSkinnedTextbox *textbox, const gchar *pixmaptext);
+static gboolean textbox_scroll                    (gpointer data);
+static void textbox_generate_pixmap               (UiSkinnedTextbox *textbox);
+static void textbox_handle_special_char           (gchar *c, gint * x, gint * y);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint textbox_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_skinned_textbox_get_type() {
+    static GType textbox_type = 0;
+    if (!textbox_type) {
+        static const GTypeInfo textbox_info = {
+            sizeof (UiSkinnedTextboxClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_skinned_textbox_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSkinnedTextbox),
+            0,
+            (GInstanceInitFunc) ui_skinned_textbox_init,
+        };
+        textbox_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSkinnedTextbox", &textbox_info, 0);
+    }
+
+    return textbox_type;
+}
+
+static void ui_skinned_textbox_class_init(UiSkinnedTextboxClass *klass) {
+    GObjectClass *gobject_class;
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    gobject_class = G_OBJECT_CLASS(klass);
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_skinned_textbox_destroy;
+
+    widget_class->realize = ui_skinned_textbox_realize;
+    widget_class->expose_event = ui_skinned_textbox_expose;
+    widget_class->size_request = ui_skinned_textbox_size_request;
+    widget_class->size_allocate = ui_skinned_textbox_size_allocate;
+    widget_class->button_press_event = ui_skinned_textbox_button_press;
+    widget_class->button_release_event = ui_skinned_textbox_button_release;
+    widget_class->motion_notify_event = ui_skinned_textbox_motion_notify;
+
+    klass->clicked = NULL;
+    klass->double_clicked = NULL;
+    klass->right_clicked = NULL;
+    klass->scaled = ui_skinned_textbox_toggle_scaled;
+    klass->redraw = ui_skinned_textbox_redraw;
+
+    textbox_signals[CLICKED] = 
+        g_signal_new ("clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedTextboxClass, clicked), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    textbox_signals[DOUBLE_CLICKED] = 
+        g_signal_new ("double-clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedTextboxClass, double_clicked), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    textbox_signals[RIGHT_CLICKED] = 
+        g_signal_new ("right-clicked", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedTextboxClass, right_clicked), NULL, NULL,
+                      gtk_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+    textbox_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedTextboxClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    textbox_signals[REDRAW] = 
+        g_signal_new ("redraw", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSkinnedTextboxClass, redraw), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    g_type_class_add_private (gobject_class, sizeof (UiSkinnedTextboxPrivate));
+}
+
+static void ui_skinned_textbox_init(UiSkinnedTextbox *textbox) {
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+    priv->move_x = 0;
+    priv->move_y = 0;
+}
+
+GtkWidget* ui_skinned_textbox_new(GtkWidget *fixed, gint x, gint y, gint w, gboolean allow_scroll, SkinPixmapId si) {
+    UiSkinnedTextbox *textbox = g_object_new (ui_skinned_textbox_get_type (), NULL);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    textbox->height = aud_active_skin->properties.textbox_bitmap_font_height;
+    textbox->x = x;
+    textbox->y = y;
+    textbox->text = g_strdup("");
+    textbox->width = w;
+    priv->scroll_allowed = allow_scroll;
+    priv->scroll_enabled = TRUE;
+    priv->skin_index = si;
+    priv->nominal_y = y;
+    priv->nominal_height = textbox->height;
+    priv->scroll_timeout = 0;
+    priv->scroll_dummy = 0;
+
+    priv->scaled = FALSE;
+
+    gtk_fixed_put(GTK_FIXED(fixed), GTK_WIDGET(textbox), textbox->x, textbox->y);
+
+    return GTK_WIDGET(textbox);
+}
+
+static void ui_skinned_textbox_destroy(GtkObject *object) {
+    UiSkinnedTextbox *textbox;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_SKINNED_IS_TEXTBOX (object));
+
+    textbox = UI_SKINNED_TEXTBOX (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_skinned_textbox_realize(GtkWidget *widget) {
+    UiSkinnedTextbox *textbox;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_SKINNED_IS_TEXTBOX(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    textbox = UI_SKINNED_TEXTBOX(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | 
+                             GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK |
+                             GDK_POINTER_MOTION_HINT_MASK;
+    attributes.visual = gtk_widget_get_visual(widget);
+    attributes.colormap = gtk_widget_get_colormap(widget);
+
+    attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+    widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+
+    gdk_window_set_user_data(widget->window, widget);
+}
+
+static void ui_skinned_textbox_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX(widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    requisition->width = textbox->width*(priv->scaled ? config.scale_factor : 1);
+    requisition->height = textbox->height*(priv->scaled ?  config.scale_factor : 1 );
+}
+
+static void ui_skinned_textbox_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX (widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (priv->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (priv->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+        gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+
+    if (textbox->x + priv->move_x - widget->allocation.x/(priv->scaled ? config.scale_factor : 1) <3);
+        priv->move_x = 0;
+    if (textbox->y + priv->move_y - widget->allocation.y/(priv->scaled ? config.scale_factor : 1) <3);
+        priv->move_y = 0;
+    textbox->x = widget->allocation.x/(priv->scaled ? config.scale_factor : 1);
+    textbox->y = widget->allocation.y/(priv->scaled ? config.scale_factor : 1);
+
+    if (textbox->width - (guint) (widget->allocation.width / (priv->scaled ? config.scale_factor : 1)) > 2) {
+            textbox->width = (guint) (widget->allocation.width / (priv->scaled ? config.scale_factor : 1));
+            if (priv->pixbuf_text) g_free(priv->pixbuf_text);
+            priv->pixbuf_text = NULL;
+            priv->offset = 0;
+            gtk_widget_set_size_request(widget, textbox->width, textbox->height);
+            gtk_widget_queue_draw(GTK_WIDGET(textbox));
+    }
+}
+
+static gboolean ui_skinned_textbox_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_TEXTBOX (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX (widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+    g_return_val_if_fail (textbox->width > 0 && textbox->height > 0, FALSE);
+
+    GdkPixbuf *obj = NULL;
+    gint cw;
+
+    if (textbox->text && (!priv->pixbuf_text || strcmp(textbox->text, priv->pixbuf_text)))
+        textbox_generate_pixmap(textbox);
+
+    if (priv->pixbuf) {
+        if (skin_get_id() != priv->skin_id) {
+            priv->skin_id = skin_get_id();
+            textbox_generate_pixmap(textbox);
+        }
+        obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, textbox->width, textbox->height);
+
+        if (config.twoway_scroll) { // twoway scroll
+            cw = priv->pixbuf_width - priv->offset;
+            if (cw > textbox->width)
+                cw = textbox->width;
+            gdk_pixbuf_copy_area(priv->pixbuf, priv->offset, 0, cw, textbox->height, obj, 0, 0);
+            if (cw < textbox->width)
+                gdk_pixbuf_copy_area(priv->pixbuf, 0, 0, textbox->width - cw, textbox->height,
+                                     obj, textbox->width - cw, textbox->height);
+        } else { // oneway scroll
+            int cw1, cw2;
+
+            if (priv->offset >= priv->pixbuf_width)
+                priv->offset = 0;
+
+            if (priv->pixbuf_width - priv->offset > textbox->width) { // case1
+                cw1 = textbox->width;
+                gdk_pixbuf_copy_area(priv->pixbuf, priv->offset, 0, cw1, textbox->height,
+                                     obj, 0, 0);
+            } else { // case 2
+                cw1 = priv->pixbuf_width - priv->offset;
+                gdk_pixbuf_copy_area(priv->pixbuf, priv->offset, 0, cw1, textbox->height, obj, 0, 0);
+                cw2 = textbox->width - cw1;
+                gdk_pixbuf_copy_area(priv->pixbuf, 0, 0, cw2, textbox->height, obj, cw1, 0);
+            }
+        }
+
+        ui_skinned_widget_draw(widget, obj, textbox->width, textbox->height, priv->scaled);
+
+        g_object_unref(obj);
+    }
+
+    return FALSE;
+}
+
+static gboolean ui_skinned_textbox_button_press(GtkWidget *widget, GdkEventButton *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_TEXTBOX (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX (widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (event->type == GDK_BUTTON_PRESS) {
+        textbox = UI_SKINNED_TEXTBOX(widget);
+        if (event->button == 3 && !g_signal_has_handler_pending(widget, textbox_signals[RIGHT_CLICKED], 0, TRUE))
+            return FALSE;
+        else if (event->button == 1) {
+            if (priv->scroll_allowed) {
+                if ((priv->pixbuf_width > textbox->width) && priv->is_scrollable) {
+                    priv->is_dragging = TRUE;
+                    priv->drag_off = priv->offset;
+                    priv->drag_x = event->x;
+                }
+            } else
+                g_signal_emit(widget, textbox_signals[CLICKED], 0);
+
+        } else if (event->button == 3) {
+            g_signal_emit(widget, textbox_signals[RIGHT_CLICKED], 0, event);
+        } else
+            priv->is_dragging = FALSE;
+    } else if (event->type == GDK_2BUTTON_PRESS) {
+        if (event->button == 1) {
+           if (g_signal_has_handler_pending(widget, textbox_signals[DOUBLE_CLICKED], 0, TRUE))
+               g_signal_emit(widget, textbox_signals[DOUBLE_CLICKED], 0);
+           else
+               return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_textbox_button_release(GtkWidget *widget, GdkEventButton *event) {
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(widget);
+
+    if (event->button == 1) {
+        priv->is_dragging = FALSE;
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_textbox_motion_notify(GtkWidget *widget, GdkEventMotion *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_SKINNED_IS_TEXTBOX (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX(widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(widget);
+
+    if (priv->is_dragging) {
+        if (priv->scroll_allowed &&
+            priv->pixbuf_width > textbox->width) {
+            priv->offset = priv->drag_off - (event->x - priv->drag_x);
+
+            while (priv->offset < 0)
+                priv->offset = 0;
+
+            while (priv->offset > (priv->pixbuf_width - textbox->width))
+                priv->offset = (priv->pixbuf_width - textbox->width);
+
+            gtk_widget_queue_draw(widget);
+        }
+    }
+
+  return TRUE;
+}
+
+static void ui_skinned_textbox_toggle_scaled(UiSkinnedTextbox *textbox) {
+    GtkWidget *widget = GTK_WIDGET (textbox);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    priv->scaled = !priv->scaled;
+
+    gtk_widget_set_size_request(widget, textbox->width*(priv->scaled ? config.scale_factor : 1 ), 
+    textbox->height*(priv->scaled ? config.scale_factor : 1 ));
+
+    gtk_widget_queue_draw(GTK_WIDGET(textbox));
+}
+
+static void ui_skinned_textbox_redraw(UiSkinnedTextbox *textbox) {
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (priv->move_x || priv->move_y)
+        gtk_fixed_move(GTK_FIXED(gtk_widget_get_parent(GTK_WIDGET(textbox))), GTK_WIDGET(textbox),
+                       textbox->x+priv->move_x, textbox->y+priv->move_y);
+
+    gtk_widget_queue_draw(GTK_WIDGET(textbox));
+}
+
+static gboolean ui_skinned_textbox_should_scroll(UiSkinnedTextbox *textbox) {
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (!priv->scroll_allowed)
+        return FALSE;
+
+    if (priv->font) {
+        gint width;
+        text_get_extents(priv->fontname, textbox->text, &width, NULL, NULL, NULL);
+
+        if (width <= textbox->width)
+            return FALSE;
+        else
+            return TRUE;
+    }
+
+    if (g_utf8_strlen(textbox->text, -1) * aud_active_skin->properties.textbox_bitmap_font_width > textbox->width)
+        return TRUE;
+
+    return FALSE;
+}
+
+void ui_skinned_textbox_set_xfont(GtkWidget *widget, gboolean use_xfont, const gchar * fontname) {
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX (widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    gint ascent, descent;
+
+    g_return_if_fail(textbox != NULL);
+
+    if (priv->font) {
+        pango_font_description_free(priv->font);
+        priv->font = NULL;
+    }
+
+    textbox->y = priv->nominal_y;
+    textbox->height = priv->nominal_height;
+
+    /* Make sure the pixmap is regenerated */
+    if (priv->pixbuf_text) {
+        g_free(priv->pixbuf_text);
+        priv->pixbuf_text = NULL;
+    }
+
+    if (!use_xfont || strlen(fontname) == 0)
+        return;
+
+    priv->font = pango_font_description_from_string(fontname);
+    priv->fontname = g_strdup(fontname);
+
+    text_get_extents(fontname,
+                     "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ",
+                     NULL, NULL, &ascent, &descent);
+    priv->font_ascent = ascent;
+    priv->font_descent = descent;
+
+
+    if (priv->font == NULL)
+        return;
+
+    textbox->height = priv->font_ascent;
+    if (textbox->height > priv->nominal_height)
+        textbox->y -= (textbox->height - priv->nominal_height) / 2;
+    else
+        textbox->height = priv->nominal_height;
+}
+
+void ui_skinned_textbox_set_text(GtkWidget *widget, const gchar *text) {
+    g_return_if_fail(text != NULL);
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX (widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (!strcmp(textbox->text, text))
+         return;
+    if (textbox->text)
+        g_free(textbox->text);
+
+    textbox->text = aud_str_to_utf8(text);
+    priv->scroll_back = FALSE;
+    gtk_widget_queue_draw(GTK_WIDGET(textbox));
+}
+
+static void textbox_generate_xfont_pixmap(UiSkinnedTextbox *textbox, const gchar *pixmaptext) {
+    /* FIXME: should operate directly on priv->pixbuf, it shouldn't use pixmap */
+    gint length, i;
+    GdkGC *gc, *maskgc;
+    GdkColor *c, pattern;
+    GdkBitmap *mask;
+    PangoLayout *layout;
+    gint width;
+    GdkPixmap *pixmap;
+
+    g_return_if_fail(textbox != NULL);
+    g_return_if_fail(pixmaptext != NULL);
+    g_return_if_fail(textbox->height > 0);
+
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    length = g_utf8_strlen(pixmaptext, -1);
+
+    text_get_extents(priv->fontname, pixmaptext, &width, NULL, NULL, NULL);
+
+    priv->pixbuf_width = MAX(width, textbox->width);
+    pixmap = gdk_pixmap_new(mainwin->window, priv->pixbuf_width,
+                                   textbox->height,
+                                   gdk_rgb_get_visual()->depth);
+    gc = gdk_gc_new(pixmap);
+    c = skin_get_color(aud_active_skin, SKIN_TEXTBG);
+    for (i = 0; i < textbox->height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / textbox->height]);
+        gdk_draw_line(pixmap, gc, 0, i, priv->pixbuf_width, i);
+    }
+
+    mask = gdk_pixmap_new(mainwin->window, priv->pixbuf_width, textbox->height, 1);
+    maskgc = gdk_gc_new(mask);
+    pattern.pixel = 0;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_draw_rectangle(mask, maskgc, TRUE, 0, 0, priv->pixbuf_width, textbox->height);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_gc_set_foreground(gc, skin_get_color(aud_active_skin, SKIN_TEXTFG));
+
+    layout = gtk_widget_create_pango_layout(mainwin, pixmaptext);
+    pango_layout_set_font_description(layout, priv->font);
+
+    gdk_draw_layout(pixmap, gc, 0, (priv->font_descent / 2), layout);
+    g_object_unref(layout);
+
+    g_object_unref(maskgc);
+
+    gdk_gc_set_clip_mask(gc, mask);
+    c = skin_get_color(aud_active_skin, SKIN_TEXTFG);
+    for (i = 0; i < textbox->height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / textbox->height]);
+        gdk_draw_line(pixmap, gc, 0, i, priv->pixbuf_width, i);
+    }
+    priv->pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, priv->pixbuf_width, textbox->height);
+    g_object_unref(mask);
+    g_object_unref(gc);
+}
+
+static gboolean textbox_scroll(gpointer data) {
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX(data);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (!priv->is_dragging) {
+        if (priv->scroll_dummy < TEXTBOX_SCROLL_WAIT)
+            priv->scroll_dummy++;
+        else {
+            if(config.twoway_scroll) {
+                if (priv->scroll_back)
+                    priv->offset -= 1;
+                else
+                    priv->offset += 1;
+
+                if (priv->offset >= (priv->pixbuf_width - textbox->width)) {
+                    priv->scroll_back = TRUE;
+                    priv->scroll_dummy = 0;
+                    priv->offset = priv->pixbuf_width - textbox->width;
+                }
+                if (priv->offset <= 0) {
+                    priv->scroll_back = FALSE;
+                    priv->scroll_dummy = 0;
+                    priv->offset = 0;
+                }
+            }
+            else { // oneway scroll
+                priv->scroll_back = FALSE;
+                priv->offset += 1;
+            }
+            gtk_widget_queue_draw(GTK_WIDGET(textbox));
+        }
+    }
+    return TRUE;
+}
+
+static void textbox_generate_pixmap(UiSkinnedTextbox *textbox) {
+    gint length, i, x, y, wl;
+    gchar *pixmaptext;
+    gchar *tmp, *stxt;
+
+    g_return_if_fail(textbox != NULL);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    if (priv->pixbuf) {
+        g_object_unref(priv->pixbuf);
+        priv->pixbuf = NULL;
+    }
+
+    /*
+     * Don't reset the offset if only text after the last '(' has
+     * changed.  This is a hack to avoid visual noice on vbr files
+     * where we guess the length.
+     */
+    if (!(priv->pixbuf_text && strrchr(textbox->text, '(') &&
+          !strncmp(priv->pixbuf_text, textbox->text,
+                   strrchr(textbox->text, '(') - textbox->text)))
+        priv->offset = 0;
+
+    g_free(priv->pixbuf_text);
+    priv->pixbuf_text = g_strdup(textbox->text);
+
+    /*
+     * wl is the number of (partial) letters visible. Only makes
+     * sense when using skinned font.
+     */
+    wl = textbox->width / 5;
+    if (wl * 5 != textbox->width)
+        wl++;
+
+    length = g_utf8_strlen(textbox->text, -1);
+
+    priv->is_scrollable = FALSE;
+
+    priv->is_scrollable = ui_skinned_textbox_should_scroll(textbox);
+
+    if (priv->is_scrollable) {
+        if(!config.twoway_scroll) {
+            pixmaptext = g_strdup_printf("%s *** ", priv->pixbuf_text);
+            length += 5;
+        } else
+            pixmaptext = g_strdup(priv->pixbuf_text);
+    } else
+    if (!priv->font && length <= wl) {
+        gint pad = wl - length;
+        gchar *padchars = g_strnfill(pad, ' ');
+
+        pixmaptext = g_strconcat(priv->pixbuf_text, padchars, NULL);
+        g_free(padchars);
+        length += pad;
+    } else
+        pixmaptext = g_strdup(priv->pixbuf_text);
+
+    if (priv->is_scrollable) {
+        if (priv->scroll_enabled && !priv->scroll_timeout) {
+            gint tag;
+            tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+            priv->scroll_timeout = g_timeout_add(tag, textbox_scroll, textbox);
+        }
+    } else {
+        if (priv->scroll_timeout) {
+            g_source_remove(priv->scroll_timeout);
+            priv->scroll_timeout = 0;
+        }
+        priv->offset = 0;
+    }
+
+    if (priv->font) {
+        textbox_generate_xfont_pixmap(textbox, pixmaptext);
+        g_free(pixmaptext);
+        return;
+    }
+
+    priv->pixbuf_width = length * aud_active_skin->properties.textbox_bitmap_font_width;
+    priv->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
+                                  priv->pixbuf_width, aud_active_skin->properties.textbox_bitmap_font_height);
+
+    for (tmp = stxt = g_utf8_strup(pixmaptext, -1), i = 0;
+         tmp != NULL && i < length; i++, tmp = g_utf8_next_char(tmp)) {
+        gchar c = *tmp;
+        x = y = -1;
+        if (c >= 'A' && c <= 'Z') {
+            x = aud_active_skin->properties.textbox_bitmap_font_width * (c - 'A');
+            y = 0;
+        }
+        else if (c >= '0' && c <= '9') {
+            x = aud_active_skin->properties.textbox_bitmap_font_width * (c - '0');
+            y = aud_active_skin->properties.textbox_bitmap_font_height;
+        }
+        else
+            textbox_handle_special_char(tmp, &x, &y);
+
+        skin_draw_pixbuf(GTK_WIDGET(textbox), aud_active_skin,
+                         priv->pixbuf, priv->skin_index,
+                         x, y, i * aud_active_skin->properties.textbox_bitmap_font_width, 0,
+                         aud_active_skin->properties.textbox_bitmap_font_width, 
+                         aud_active_skin->properties.textbox_bitmap_font_height);
+    }
+    g_free(stxt);
+    g_free(pixmaptext);
+}
+
+void ui_skinned_textbox_set_scroll(GtkWidget *widget, gboolean scroll) {
+    g_return_if_fail(widget != NULL);
+    UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX(widget);
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
+
+    priv->scroll_enabled = scroll;
+    if (priv->scroll_enabled && priv->is_scrollable && priv->scroll_allowed) {
+        gint tag;
+        tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+        if (priv->scroll_timeout) {
+            g_source_remove(priv->scroll_timeout);
+            priv->scroll_timeout = 0;
+        }
+        priv->scroll_timeout = g_timeout_add(tag, textbox_scroll, textbox);
+
+    } else {
+
+        if (priv->scroll_timeout) {
+            g_source_remove(priv->scroll_timeout);
+            priv->scroll_timeout = 0;
+        }
+
+        priv->offset = 0;
+        gtk_widget_queue_draw(GTK_WIDGET(textbox));
+    }
+}
+
+static void textbox_handle_special_char(gchar *c, gint * x, gint * y) {
+    gint tx, ty;
+
+    switch (*c) {
+    case '"':
+        tx = 26;
+        ty = 0;
+        break;
+    case '\r':
+        tx = 10;
+        ty = 1;
+        break;
+    case ':':
+    case ';':
+        tx = 12;
+        ty = 1;
+        break;
+    case '(':
+        tx = 13;
+        ty = 1;
+        break;
+    case ')':
+        tx = 14;
+        ty = 1;
+        break;
+    case '-':
+        tx = 15;
+        ty = 1;
+        break;
+    case '`':
+    case '\'':
+        tx = 16;
+        ty = 1;
+        break;
+    case '!':
+        tx = 17;
+        ty = 1;
+        break;
+    case '_':
+        tx = 18;
+        ty = 1;
+        break;
+    case '+':
+        tx = 19;
+        ty = 1;
+        break;
+    case '\\':
+        tx = 20;
+        ty = 1;
+        break;
+    case '/':
+        tx = 21;
+        ty = 1;
+        break;
+    case '[':
+        tx = 22;
+        ty = 1;
+        break;
+    case ']':
+        tx = 23;
+        ty = 1;
+        break;
+    case '^':
+        tx = 24;
+        ty = 1;
+        break;
+    case '&':
+        tx = 25;
+        ty = 1;
+        break;
+    case '%':
+        tx = 26;
+        ty = 1;
+        break;
+    case '.':
+    case ',':
+        tx = 27;
+        ty = 1;
+        break;
+    case '=':
+        tx = 28;
+        ty = 1;
+        break;
+    case '$':
+        tx = 29;
+        ty = 1;
+        break;
+    case '#':
+        tx = 30;
+        ty = 1;
+        break;
+    case '?':
+        tx = 3;
+        ty = 2;
+        break;
+    case '*':
+        tx = 4;
+        ty = 2;
+        break;
+    default:
+        tx = 29;
+        ty = 0;
+        break;
+    }
+
+    const gchar *change[] = {"Ä„", "A", "Ę", "E", "Ć", "C", "Å", "L", "Ó", "O", "Åš", "S", "Å»", "Z", "Ź", "Z",
+                             "Ń", "N", "Ü", "U", NULL};
+    int i;
+    for (i = 0; change[i]; i+=2) {
+         if (!strncmp(c, change[i], strlen(change[i]))) {
+             tx = (*change[i+1] - 'A');
+             break;
+         }
+    }
+
+    /* those are commonly included into skins */
+    if (!strncmp(c, "Ã…", strlen("Ã…"))) {
+        tx = 0;
+        ty = 2;
+    } else if (!strncmp(c, "Ö", strlen("Ö"))) {
+        tx = 1;
+        ty = 2;
+    } else if (!strncmp(c, "Ä", strlen("Ä"))) {
+        tx = 2;
+        ty = 2;
+    }
+
+    *x = tx * aud_active_skin->properties.textbox_bitmap_font_width;
+    *y = ty * aud_active_skin->properties.textbox_bitmap_font_height;
+}
+
+void ui_skinned_textbox_move_relative(GtkWidget *widget, gint x, gint y) {
+    UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(widget);
+    priv->move_x += x;
+    priv->move_y += y;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_textbox.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,71 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007 Tomasz Moń
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef AUDACIOUS_UI_SKINNED_TEXTBOX_H
+#define AUDACIOUS_UI_SKINNED_TEXTBOX_H
+
+#include <gtk/gtk.h>
+#include "ui_skin.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SKINNED_TEXTBOX(obj)          GTK_CHECK_CAST (obj, ui_skinned_textbox_get_type (), UiSkinnedTextbox)
+#define UI_SKINNED_TEXTBOX_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_textbox_get_type (), UiSkinnedTextboxClass)
+#define UI_SKINNED_IS_TEXTBOX(obj)       GTK_CHECK_TYPE (obj, ui_skinned_textbox_get_type ())
+
+typedef struct _UiSkinnedTextbox        UiSkinnedTextbox;
+typedef struct _UiSkinnedTextboxClass   UiSkinnedTextboxClass;
+
+struct _UiSkinnedTextbox {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gchar            *text;
+};
+
+struct _UiSkinnedTextboxClass {
+    GtkWidgetClass          parent_class;
+    void (* clicked)        (UiSkinnedTextbox *textbox);
+    void (* double_clicked) (UiSkinnedTextbox *textbox);
+    void (* right_clicked)  (UiSkinnedTextbox *textbox);
+    void (* scaled)         (UiSkinnedTextbox *textbox);
+    void (* redraw)         (UiSkinnedTextbox *textbox);
+};
+
+GtkWidget* ui_skinned_textbox_new (GtkWidget *fixed, gint x, gint y, gint w, gboolean allow_scroll, SkinPixmapId si);
+GtkType ui_skinned_textbox_get_type(void);
+void ui_skinned_textbox_set_xfont(GtkWidget *widget, gboolean use_xfont, const gchar * fontname);
+void ui_skinned_textbox_set_text(GtkWidget *widget, const gchar *text);
+void ui_skinned_textbox_set_scroll(GtkWidget *widget, gboolean scroll);
+void ui_skinned_textbox_move_relative(GtkWidget *widget, gint x, gint y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* AUDACIOUS_UI_SKINNED_TEXTBOX_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_window.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,286 @@
+/*
+ * Audacious: A cross-platform multimedia player
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "platform/smartinclude.h"
+#include "ui_skin.h"
+#include "skins_cfg.h"
+
+#include <gtk/gtkmain.h>
+#include <glib-object.h>
+#include <glib/gmacros.h>
+#include <gtk/gtkmarshal.h>
+#include <gtk/gtkwindow.h>
+#include <string.h>
+
+#include <audacious/plugin.h>
+#include "ui_dock.h"
+#include "ui_skinned_window.h"
+
+static void ui_skinned_window_class_init(SkinnedWindowClass *klass);
+static void ui_skinned_window_init(GtkWidget *widget);
+static GtkWindowClass *parent = NULL;
+
+GType
+ui_skinned_window_get_type(void)
+{
+  static GType window_type = 0;
+
+  if (!window_type)
+    {
+      static const GTypeInfo window_info =
+      {
+        sizeof (SkinnedWindowClass),
+        NULL,           /* base_init */
+        NULL,           /* base_finalize */
+        (GClassInitFunc) ui_skinned_window_class_init,
+        NULL,           /* class_finalize */
+        NULL,           /* class_data */
+        sizeof (SkinnedWindow),
+        0,              /* n_preallocs */
+        (GInstanceInitFunc) ui_skinned_window_init
+      };
+
+      window_type =
+        g_type_register_static (GTK_TYPE_WINDOW, "SkinnedWindow",
+                                &window_info, 0);
+    }
+
+  return window_type;
+}
+
+static void
+ui_skinned_window_map(GtkWidget *widget)
+{
+    (* GTK_WIDGET_CLASS (parent)->map) (widget);
+
+    SkinnedWindow *window = SKINNED_WINDOW(widget);
+    if (window->type == WINDOW_MAIN)
+        gtk_widget_shape_combine_mask(widget, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + config.player_shaded), 0, 0);
+    else if (window->type == WINDOW_EQ)
+        gtk_widget_shape_combine_mask(widget, skin_get_mask(aud_active_skin, SKIN_MASK_EQ + config.equalizer_shaded), 0, 0);
+
+    gtk_window_set_keep_above(GTK_WINDOW(widget), config.always_on_top);
+}
+
+static gboolean
+ui_skinned_window_motion_notify_event(GtkWidget *widget,
+                                      GdkEventMotion *event)
+{
+    if (dock_is_moving(GTK_WINDOW(widget)))
+        dock_move_motion(GTK_WINDOW(widget), event);
+
+    return FALSE;
+}
+
+static gboolean ui_skinned_window_focus_in(GtkWidget *widget, GdkEventFocus *focus) {
+    gboolean val = GTK_WIDGET_CLASS (parent)->focus_in_event (widget, focus);
+    gtk_widget_queue_draw(widget);
+    return val;
+}
+
+static gboolean ui_skinned_window_focus_out(GtkWidget *widget, GdkEventFocus *focus) {
+    gboolean val = GTK_WIDGET_CLASS (parent)->focus_out_event (widget, focus);
+    gtk_widget_queue_draw(widget);
+    return val;
+}
+
+static gboolean ui_skinned_window_button_press(GtkWidget *widget, GdkEventButton *event) {
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS &&
+        (config.easy_move || config.equalizer_shaded || (event->y / config.scale_factor) < 14)) {
+         dock_move_press(get_dock_window_list(), GTK_WINDOW(widget),
+                         event, SKINNED_WINDOW(widget)->type == WINDOW_MAIN ? TRUE : FALSE);
+    }
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_window_button_release(GtkWidget *widget, GdkEventButton *event) {
+    if (dock_is_moving(GTK_WINDOW(widget)))
+       dock_move_release(GTK_WINDOW(widget));
+
+    return TRUE;
+}
+
+static gboolean ui_skinned_window_expose(GtkWidget *widget, GdkEventExpose *event) {
+    SkinnedWindow *window = SKINNED_WINDOW(widget);
+
+    GdkPixbuf *obj = NULL;
+
+    gint width = 0, height = 0;
+    switch (window->type) {
+        case WINDOW_MAIN:
+            width = aud_active_skin->properties.mainwin_width;
+            height = aud_active_skin->properties.mainwin_height;
+            break;
+        case WINDOW_EQ:
+            width = 275 * (config.scaled ? config.scale_factor : 1);
+            height = 116 * (config.scaled ? config.scale_factor : 1) ;
+            break;
+        case WINDOW_PLAYLIST:
+            width = playlistwin_get_width();
+            height = config.playlist_height;
+            break;
+        default:
+            return FALSE;
+    }
+    obj = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+
+    gboolean focus = gtk_window_has_toplevel_focus(GTK_WINDOW(widget));
+
+    switch (window->type) {
+        case WINDOW_MAIN:
+            skin_draw_pixbuf(widget, aud_active_skin, obj,SKIN_MAIN, 0, 0, 0, 0, width, height);
+            skin_draw_mainwin_titlebar(aud_active_skin, obj, config.player_shaded, focus || !config.dim_titlebar);
+            break;
+        case WINDOW_EQ:
+            skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_EQMAIN, 0, 0, 0, 0, width, height);
+            if (focus || !config.dim_titlebar) {
+                if (!config.equalizer_shaded)
+                    skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_EQMAIN, 0, 134, 0, 0, width, 14);
+                else
+                    skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_EQ_EX, 0, 0, 0, 0, width, 14);
+            } else {
+                if (!config.equalizer_shaded)
+                    skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_EQMAIN, 0, 149, 0, 0, width, 14);
+                else
+                    skin_draw_pixbuf(widget, aud_active_skin, obj, SKIN_EQ_EX, 0, 15, 0, 0, width, 14);
+            }
+            break;
+        case WINDOW_PLAYLIST:
+            focus |= !config.dim_titlebar;
+            if (config.playlist_shaded) {
+                skin_draw_playlistwin_shaded(aud_active_skin, obj, width, focus);
+            } else {
+                skin_draw_playlistwin_frame(aud_active_skin, obj, width, config.playlist_height, focus);
+            }
+            break;
+    }
+
+    ui_skinned_widget_draw(GTK_WIDGET(window), obj, width, height,
+                           window->type != WINDOW_PLAYLIST && config.scaled);
+
+    g_object_unref(obj);
+
+    return FALSE;
+}
+
+static void
+ui_skinned_window_class_init(SkinnedWindowClass *klass)
+{
+    GtkWidgetClass *widget_class;
+
+    widget_class = (GtkWidgetClass*) klass;
+
+    parent = gtk_type_class(gtk_window_get_type());
+
+    widget_class->motion_notify_event = ui_skinned_window_motion_notify_event;
+    widget_class->expose_event = ui_skinned_window_expose;
+    widget_class->focus_in_event = ui_skinned_window_focus_in;
+    widget_class->focus_out_event = ui_skinned_window_focus_out;
+    widget_class->button_press_event = ui_skinned_window_button_press;
+    widget_class->button_release_event = ui_skinned_window_button_release;
+    widget_class->map = ui_skinned_window_map;
+}
+
+void
+ui_skinned_window_hide(SkinnedWindow *window)
+{
+    g_return_if_fail(SKINNED_CHECK_WINDOW(window));
+
+    gtk_window_get_position(GTK_WINDOW(window), &window->x, &window->y);
+    gtk_widget_hide(GTK_WIDGET(window));
+}
+
+void
+ui_skinned_window_show(SkinnedWindow *window)
+{
+    g_return_if_fail(SKINNED_CHECK_WINDOW(window));
+
+    gtk_window_move(GTK_WINDOW(window), window->x, window->y);
+    gtk_widget_show_all(GTK_WIDGET(window));
+}
+
+static void
+ui_skinned_window_init(GtkWidget *widget)
+{
+    SkinnedWindow *window;
+    window = SKINNED_WINDOW(widget);
+    window->x = -1;
+    window->y = -1;
+}
+
+GtkWidget *
+ui_skinned_window_new(const gchar *wmclass_name)
+{
+    GtkWidget *widget = g_object_new(ui_skinned_window_get_type(), NULL);
+    GtkWindow *window = GTK_WINDOW(widget);
+
+    window->type = SKINNED_WINDOW_TYPE;
+
+    if (wmclass_name)
+        gtk_window_set_wmclass(GTK_WINDOW(widget), wmclass_name, "Audacious");
+
+    gtk_widget_add_events(GTK_WIDGET(widget),
+                          GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK |
+                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                          GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK |
+                          GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK);
+    gtk_widget_realize(GTK_WIDGET(widget));
+
+    set_dock_window_list(dock_window_set_decorated(get_dock_window_list(),
+                                                   GTK_WINDOW(widget),
+                                                   config.show_wm_decorations));
+    gtk_widget_set_app_paintable(GTK_WIDGET(widget), TRUE);
+    gdk_window_set_back_pixmap(widget->window, NULL, FALSE);
+    gtk_widget_shape_combine_mask(widget, NULL, 0, 0);
+
+    if (!strcmp(wmclass_name, "player"))
+        SKINNED_WINDOW(widget)->type = WINDOW_MAIN;
+    if (!strcmp(wmclass_name, "equalizer"))
+        SKINNED_WINDOW(widget)->type = WINDOW_EQ;
+    if (!strcmp(wmclass_name, "playlist"))
+        SKINNED_WINDOW(widget)->type = WINDOW_PLAYLIST;
+
+    /* GtkFixed hasn't got its GdkWindow, this means that it can be used to
+       display widgets while the logo below will be displayed anyway;
+       however fixed positions are not that great, cause the button sizes may (will)
+       vary depending on the gtk style used, so it's not possible to center
+       them unless a fixed width and heigth is forced (and this may bring to cutted
+       text if someone, i.e., uses a big font for gtk widgets);
+       other types of container most likely have their GdkWindow, this simply
+       means that the logo must be drawn on the container widget, instead of the
+       window; otherwise, it won't be displayed correctly */
+    SKINNED_WINDOW(widget)->fixed = gtk_fixed_new();
+    gtk_container_add(GTK_CONTAINER(widget), GTK_WIDGET(SKINNED_WINDOW(widget)->fixed));
+    return widget;
+}
+
+void ui_skinned_window_draw_all(GtkWidget *widget) {
+    if (SKINNED_WINDOW(widget)->type == WINDOW_MAIN)
+        mainwin_refresh_hints();
+
+    gtk_widget_queue_draw(widget);
+    GList *iter;
+    for (iter = GTK_FIXED (SKINNED_WINDOW(widget)->fixed)->children; iter; iter = g_list_next (iter)) {
+         GtkFixedChild *child_data = (GtkFixedChild *) iter->data;
+         GtkWidget *child = child_data->widget;
+         gtk_widget_queue_draw(child);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_window.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,64 @@
+/*
+ * Audacious: A cross-platform multimedia player
+ * Copyright (c) 2007 William Pitcock <nenolod -at- sacredspiral.co.uk>
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef UI_SKINNED_WINDOW_H
+#define UI_SKINNED_WINDOW_H
+
+#define SKINNED_WINDOW(obj)          GTK_CHECK_CAST (obj, ui_skinned_window_get_type (), SkinnedWindow)
+#define SKINNED_WINDOW_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_skinned_window_get_type (), SkinnedWindowClass)
+#define SKINNED_CHECK_WINDOW(obj)    GTK_CHECK_TYPE (obj, ui_skinned_window_get_type ())
+#define SKINNED_TYPE_WINDOW          (ui_skinned_window_get_type())
+
+#ifdef GDK_WINDOWING_QUARTZ
+# define SKINNED_WINDOW_TYPE		GTK_WINDOW_POPUP
+#else
+# define SKINNED_WINDOW_TYPE		GTK_WINDOW_TOPLEVEL
+#endif
+
+enum {
+    WINDOW_MAIN,
+    WINDOW_EQ,
+    WINDOW_PLAYLIST
+};
+
+typedef struct _SkinnedWindow SkinnedWindow;
+typedef struct _SkinnedWindowClass SkinnedWindowClass;
+
+struct _SkinnedWindow
+{
+  GtkWindow window;
+
+  GtkWidget *canvas;
+  gint x,y;
+
+  gint type;
+  GtkWidget *fixed;
+};
+
+struct _SkinnedWindowClass
+{
+  GtkWindowClass        parent_class;
+};
+
+extern GType ui_skinned_window_get_type(void);
+extern GtkWidget *ui_skinned_window_new(const gchar *wmclass_name);
+extern void ui_skinned_window_draw_all(GtkWidget *widget);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_svis.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,546 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007  Audacious development team.
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skin.h"
+#include "ui_svis.h"
+#include "ui_vis.h"
+#include "util.h"
+#include "skins_cfg.h"
+#include <audacious/plugin.h>
+#include <string.h>
+#include <ctype.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtkmarshal.h>
+#include <gtk/gtkimage.h>
+
+#define UI_TYPE_SVIS           (ui_svis_get_type())
+
+static gint svis_redraw_delays[] = { 1, 2, 4, 8 };
+
+/* FIXME: Are the svis_scope_colors correct? */
+static guint8 svis_scope_colors[] = { 20, 19, 18, 19, 20 };
+static guint8 svis_vu_normal_colors[] = { 17, 17, 17, 12, 12, 12, 2, 2 };
+
+#define DRAW_DS_PIXEL(ptr,value) \
+	*(ptr) = (value); \
+	*((ptr) + 1) = (value); \
+	*((ptr) + 76) = (value); \
+	*((ptr) + 77) = (value);
+
+#define SVIS_HEIGHT 5
+#define SVIS_WIDTH 38
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_svis_class_init         (UiSVisClass *klass);
+static void ui_svis_init               (UiSVis *svis);
+static void ui_svis_destroy            (GtkObject *object);
+static void ui_svis_realize            (GtkWidget *widget);
+static void ui_svis_unrealize          (GtkWidget *widget);
+static void ui_svis_map                (GtkWidget *widget);
+static void ui_svis_unmap              (GtkWidget *widget);
+static void ui_svis_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_svis_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_svis_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_svis_toggle_scaled      (UiSVis *svis);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint vis_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_svis_get_type() {
+    static GType vis_type = 0;
+    if (!vis_type) {
+        static const GTypeInfo vis_info = {
+            sizeof (UiSVisClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_svis_class_init,
+            NULL,
+            NULL,
+            sizeof (UiSVis),
+            0,
+            (GInstanceInitFunc) ui_svis_init,
+        };
+        vis_type = g_type_register_static (GTK_TYPE_WIDGET, "UiSVis", &vis_info, 0);
+    }
+
+    return vis_type;
+}
+
+static void ui_svis_class_init(UiSVisClass *klass) {
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_svis_destroy;
+
+    widget_class->realize = ui_svis_realize;
+    widget_class->unrealize = ui_svis_unrealize;
+    widget_class->map = ui_svis_map;
+    widget_class->unmap = ui_svis_unmap;
+    widget_class->expose_event = ui_svis_expose;
+    widget_class->size_request = ui_svis_size_request;
+    widget_class->size_allocate = ui_svis_size_allocate;
+
+    klass->scaled = ui_svis_toggle_scaled;
+
+    vis_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiSVisClass, scaled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_svis_init(UiSVis *svis) {
+
+}
+
+GtkWidget* ui_svis_new(GtkWidget *fixed, gint x, gint y) {
+    UiSVis *svis = g_object_new (ui_svis_get_type (), NULL);
+
+    svis->x = x;
+    svis->y = y;
+
+    svis->width = SVIS_WIDTH;
+    svis->height = SVIS_HEIGHT;
+
+    svis->fixed = fixed;
+    svis->scaled = FALSE;
+
+    svis->visible_window = TRUE;
+    svis->event_window = NULL;
+
+    gtk_fixed_put(GTK_FIXED(svis->fixed), GTK_WIDGET(svis), svis->x, svis->y);
+
+    return GTK_WIDGET(svis);
+}
+
+static void ui_svis_destroy(GtkObject *object) {
+    UiSVis *svis;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_IS_SVIS (object));
+
+    svis = UI_SVIS (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_svis_realize(GtkWidget *widget) {
+    UiSVis *svis;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_IS_SVIS(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    svis = UI_SVIS(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+
+    if (svis->visible_window)
+    {
+      attributes.visual = gtk_widget_get_visual(widget);
+      attributes.colormap = gtk_widget_get_colormap(widget);
+      attributes.wclass = GDK_INPUT_OUTPUT;
+      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+      widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+      GTK_WIDGET_UNSET_FLAGS(widget, GTK_NO_WINDOW);
+      gdk_window_set_user_data(widget->window, widget);
+    }
+    else
+    {
+      widget->window = gtk_widget_get_parent_window (widget);
+      g_object_ref (widget->window);
+
+      attributes.wclass = GDK_INPUT_ONLY;
+      attributes_mask = GDK_WA_X | GDK_WA_Y;
+      svis->event_window = gdk_window_new (widget->window, &attributes, attributes_mask);
+      GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
+      gdk_window_set_user_data(svis->event_window, widget);
+    }
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+}
+
+static void ui_svis_unrealize(GtkWidget *widget) {
+    UiSVis *svis;
+    svis = UI_SVIS(widget);
+
+    if ( svis->event_window != NULL )
+    {
+      gdk_window_set_user_data( svis->event_window , NULL );
+      gdk_window_destroy( svis->event_window );
+      svis->event_window = NULL;
+    }
+
+    if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+        (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void ui_svis_map(GtkWidget *widget)
+{
+    UiSVis *svis;
+    svis = UI_SVIS(widget);
+
+    if (svis->event_window != NULL)
+      gdk_window_show (svis->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->map)
+      (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+}
+
+static void ui_svis_unmap (GtkWidget *widget)
+{
+    UiSVis *svis;
+    svis = UI_SVIS(widget);
+
+    if (svis->event_window != NULL)
+      gdk_window_hide (svis->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->unmap)
+      (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static void ui_svis_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiSVis *svis = UI_SVIS(widget);
+
+    requisition->width = svis->width * (svis->scaled ? config.scale_factor : 1);
+    requisition->height = svis->height*(svis->scaled ? config.scale_factor : 1);
+}
+
+static void ui_svis_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiSVis *svis = UI_SVIS (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (svis->scaled ? config.scale_factor : 1 );
+    widget->allocation.y *= (svis->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+    {
+        if (svis->event_window != NULL)
+            gdk_window_move_resize(svis->event_window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+        else
+            gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+    }
+
+    svis->x = widget->allocation.x/(svis->scaled ? config.scale_factor : 1);
+    svis->y = widget->allocation.y/(svis->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_svis_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_IS_SVIS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiSVis *svis = UI_SVIS (widget);
+
+    gint x, y, h;
+    guchar svis_color[24][3];
+    guchar rgb_data[SVIS_WIDTH * 2 * SVIS_HEIGHT * 2], *ptr, c;
+    guint32 colors[24];
+    GdkRgbCmap *cmap;
+
+    if (!GTK_WIDGET_VISIBLE(widget))
+        return FALSE;
+
+    if (!svis->visible_window)
+        return FALSE;
+
+    skin_get_viscolor(aud_active_skin, svis_color);
+    for (y = 0; y < 24; y++) {
+        colors[y] =
+            svis_color[y][0] << 16 | svis_color[y][1] << 8 | svis_color[y][2];
+    }
+    cmap = gdk_rgb_cmap_new(colors, 24);
+
+    if (!config.scaled) {
+      memset(rgb_data, 0, SVIS_WIDTH * SVIS_HEIGHT);
+      if (config.vis_type == VIS_ANALYZER  && !audacious_drct_get_paused() && audacious_drct_get_playing()){
+	for(y=0; y < SVIS_HEIGHT; y++){
+	  if (config.analyzer_type == ANALYZER_BARS){
+	    for(x=0;x< SVIS_WIDTH; x++){
+	      if(svis->data[x] > y << 1)
+		{
+		  rgb_data[x*3+ (SVIS_HEIGHT - y) * SVIS_WIDTH] = 23;
+		  rgb_data[x*3+1 + (SVIS_HEIGHT - y) * SVIS_WIDTH] = 23;
+		  
+		}
+	    }
+	  }
+	  else{
+	    for(x=0;x< SVIS_WIDTH; x++){
+	      if(svis->data[x] > y << 1)
+		{
+		  rgb_data[x + (SVIS_HEIGHT - y) * SVIS_WIDTH] = 23;
+		}
+	    }
+	  }
+	}
+      }
+	else if (config.vis_type == VIS_VOICEPRINT){
+	  switch (config.vu_mode) {
+	  case VU_NORMAL:
+	    for (y = 0; y < 2; y++) {
+	      ptr = rgb_data + ((y * 3) * 38);
+	      h = (svis->data[y] * 7) / 37;
+	      for (x = 0; x < h; x++, ptr += 5) {
+		c = svis_vu_normal_colors[x];
+		*(ptr) = c;
+		*(ptr + 1) = c;
+		*(ptr + 2) = c;
+		*(ptr + 38) = c;
+		*(ptr + 39) = c;
+		*(ptr + 40) = c;
+	      }
+	    }
+	    break;
+	  case VU_SMOOTH:
+	    for (y = 0; y < 2; y++) {
+	      ptr = rgb_data + ((y * 3) * SVIS_WIDTH);
+	      for (x = 0; x < svis->data[y]; x++, ptr++) {
+		c = 17 - ((x * 15) / 37);
+		*(ptr) = c;
+		*(ptr + 38) = c;
+	      }
+	    }
+	    break;
+	  }	  
+	}
+        else if (config.vis_type == VIS_SCOPE) {
+            for (x = 0; x < 38; x++) {
+                h = svis->data[x << 1] / 3;
+                ptr = rgb_data + ((4 - h) * 38) + x;
+                *ptr = svis_scope_colors[h];
+            }
+        }
+
+    }
+    else {            /*svis scaling, this needs some work, since a lot of stuff is hardcoded --majeru*/
+
+      memset(rgb_data, 0, SVIS_WIDTH * config.scale_factor * SVIS_HEIGHT * aud_cfg->scale_factor);
+      if (config.vis_type == VIS_ANALYZER && !audacious_drct_get_paused() && audacious_drct_get_playing()){
+	  for(y=0; y < SVIS_HEIGHT; y++){
+            if (config.analyzer_type == ANALYZER_BARS){
+              for(x=0;x< SVIS_WIDTH; x++){
+                if(svis->data[x] > y << 1)
+                {
+                  ptr = rgb_data + x * 6 + (SVIS_HEIGHT * 2 - y * 2) * SVIS_WIDTH *2;
+                  DRAW_DS_PIXEL(ptr, 23);
+                  DRAW_DS_PIXEL(ptr + 2, 23);
+                }
+              }
+            }
+            else{
+              for(x=0;x< SVIS_WIDTH; x++){
+                if(svis->data[x] > y << 1)
+                {
+                  ptr = rgb_data + x * 2 + (SVIS_HEIGHT * 2 - y * 2) * SVIS_WIDTH * 2;
+                  DRAW_DS_PIXEL(ptr, 23);
+                }
+              }
+            }
+	  }
+        }
+	else if (config.vis_type == VIS_VOICEPRINT){
+	  switch (config.vu_mode) {
+	  case VU_NORMAL:
+	    for (y = 0; y < 2; y++) {
+	      ptr = rgb_data + ((y * 3) * 152);
+	      h = (svis->data[y] * 8) / 37;
+	      for (x = 0; x < h; x++, ptr += 10) {
+		c = svis_vu_normal_colors[x];
+		DRAW_DS_PIXEL(ptr, c);
+		DRAW_DS_PIXEL(ptr + 2, c);
+		DRAW_DS_PIXEL(ptr + 4, c);
+		DRAW_DS_PIXEL(ptr + 152, c);
+		DRAW_DS_PIXEL(ptr + 154, c);
+		DRAW_DS_PIXEL(ptr + 156, c);
+	      }
+	    }
+	    break;
+	  case VU_SMOOTH:
+	    for (y = 0; y < 2; y++) {
+	      ptr = rgb_data + ((y * 3) * 152);
+	      for (x = 0; x < svis->data[y]; x++, ptr += 2) {
+		c = 17 - ((x * 15) / 37);
+		DRAW_DS_PIXEL(ptr, c);
+		DRAW_DS_PIXEL(ptr + 152, c);
+	      }
+	    }
+	    break;
+	  }  
+	}
+        else if (config.vis_type == VIS_SCOPE) {
+            for (x = 0; x < 38; x++) {
+                h = svis->data[x << 1] / 3;
+                ptr = rgb_data + ((4 - h) * 152) + (x << 1);
+                *ptr = svis_scope_colors[h];
+                *(ptr + 1) = svis_scope_colors[h];
+                *(ptr + 76) = svis_scope_colors[h];
+                *(ptr + 77) = svis_scope_colors[h];
+            }
+        }
+
+
+    }
+
+    GdkPixmap *obj = NULL;
+    GdkGC *gc;
+    obj = gdk_pixmap_new(NULL, svis->width* ( svis->scaled ? config.scale_factor : 1), 
+        svis->height*(svis->scaled ? config.scale_factor : 1), gdk_rgb_get_visual()->depth);
+    gc = gdk_gc_new(obj);
+
+    if (!svis->scaled) {
+        gdk_draw_indexed_image(obj, gc, 0, 0, svis->width, svis->height,
+                               GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+                               38, cmap);
+    } else {
+        gdk_draw_indexed_image(obj, gc,
+                               0 << 1, 0 << 1,
+                               svis->width << 1, svis->height << 1,
+                               GDK_RGB_DITHER_NONE, (guchar *) rgb_data,
+                               76, cmap);
+    }
+
+    gdk_rgb_cmap_free(cmap);
+    gdk_draw_drawable (widget->window, gc, obj, 0, 0, 0, 0,
+                       svis->width*(svis->scaled ? config.scale_factor : 1), 
+                       svis->height*(svis->scaled ? config.scale_factor : 1));
+    g_object_unref(obj);
+    g_object_unref(gc);
+
+    return FALSE;
+}
+
+static void ui_svis_toggle_scaled(UiSVis *svis) {
+    GtkWidget *widget = GTK_WIDGET (svis);
+    svis->scaled = !svis->scaled;
+
+    gtk_widget_set_size_request(widget, svis->width* config.scale_factor, svis->height * aud_cfg->scale_factor);
+
+    gtk_widget_queue_draw(widget);
+}
+
+void ui_svis_set_visible(GtkWidget *widget, gboolean window_is_visible)
+{
+    UiSVis *svis;
+    gboolean widget_is_visible;
+
+    g_return_if_fail(UI_IS_SVIS(widget));
+
+    svis = UI_SVIS (widget);
+    widget_is_visible = GTK_WIDGET_VISIBLE(widget);
+
+    svis->visible_window = window_is_visible;
+
+    if (GTK_WIDGET_REALIZED (widget))
+    {
+        if ( widget_is_visible )
+            gtk_widget_hide(widget);
+
+        gtk_widget_unrealize(widget);
+        gtk_widget_realize(widget);
+
+        if ( widget_is_visible )
+            gtk_widget_show(widget);
+    }
+
+    if (widget_is_visible)
+        gtk_widget_queue_resize(widget);
+}
+
+void ui_svis_clear_data(GtkWidget *widget) {
+    gint i;
+
+    UiSVis *svis = UI_SVIS (widget);
+
+    for (i = 0; i < 75; i++) {
+        svis->data[i] = (config.vis_type == VIS_SCOPE) ? 6 : 0;
+    }
+}
+
+void ui_svis_timeout_func(GtkWidget *widget, guchar * data) {
+    UiSVis *svis = UI_SVIS (widget);
+    static GTimer *timer = NULL;
+    gulong micros = 9999999;
+    gboolean falloff = FALSE;
+    gint i;
+
+    if (!timer) {
+        timer = g_timer_new();
+        g_timer_start(timer);
+    }
+    else {
+        g_timer_elapsed(timer, &micros);
+        if (micros > 14000)
+            g_timer_reset(timer);
+
+    }
+
+    if (config.vis_type == VIS_VOICEPRINT) {
+        if (micros > 14000)
+            falloff = TRUE;
+
+        for (i = 0; i < 2; i++) {
+            if (falloff || data) {
+                if (data && data[i] > svis->data[i])
+                    svis->data[i] = data[i];
+                else if (falloff) {
+                    if (svis->data[i] >= 2)
+                        svis->data[i] -= 2;
+                    else
+                        svis->data[i] = 0;
+                }
+            }
+
+        }
+    }
+    else if (data) {
+        for (i = 0; i < 75; i++)
+            svis->data[i] = data[i];
+    }
+
+    if (micros > 14000) {
+        if (!svis->refresh_delay) {
+            gtk_widget_queue_draw(widget);
+            svis->refresh_delay = svis_redraw_delays[config.vis_refresh];
+        }
+        svis->refresh_delay--;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_svis.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,64 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007  Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef UISVIS_H
+#define UISVIS_H
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_SVIS(obj)          GTK_CHECK_CAST (obj, ui_svis_get_type (), UiSVis)
+#define UI_SVIS_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_svis_get_type (), UiSVisClass)
+#define UI_IS_SVIS(obj)       GTK_CHECK_TYPE (obj, ui_svis_get_type ())
+
+typedef struct _UiSVis        UiSVis;
+typedef struct _UiSVisClass   UiSVisClass;
+
+struct _UiSVis {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gint             data[75];
+    gint             refresh_delay;
+    gboolean         scaled;
+    GtkWidget        *fixed;
+    gboolean         visible_window;
+    GdkWindow        *event_window;
+};
+
+struct _UiSVisClass {
+    GtkWidgetClass          parent_class;
+    void (* scaled)        (UiSVis *vis);
+};
+
+GtkWidget* ui_svis_new (GtkWidget *fixed, gint x, gint y);
+GtkType ui_svis_get_type(void);
+void ui_svis_clear_data(GtkWidget *widget);
+void ui_svis_timeout_func(GtkWidget *widget, guchar * data);
+void ui_svis_set_visible(GtkWidget *widget, gboolean window_is_visible);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_vis.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,720 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007  Audacious development team.
+ *
+ * Based on:
+ * BMP - Cross-platform multimedia player
+ * Copyright (C) 2003-2004  BMP development team.
+ * XMMS:
+ * Copyright (C) 1998-2003  XMMS development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#include "ui_skin.h"
+#include "ui_vis.h"
+#include "util.h"
+#include "skins_cfg.h"
+#include <audacious/plugin.h>
+
+static const gfloat vis_afalloff_speeds[] = { 0.34, 0.5, 1.0, 1.3, 1.6 };
+static const gfloat vis_pfalloff_speeds[] = { 1.2, 1.3, 1.4, 1.5, 1.6 };
+static const gint vis_redraw_delays[] = { 1, 2, 4, 8 };
+static const guint8 vis_scope_colors[] =
+    { 21, 21, 20, 20, 19, 19, 18, 19, 19, 20, 20, 21, 21 };
+static guchar voiceprint_data[76*16];
+
+enum {
+    DOUBLED,
+    LAST_SIGNAL
+};
+
+static void ui_vis_class_init         (UiVisClass *klass);
+static void ui_vis_init               (UiVis *vis);
+static void ui_vis_destroy            (GtkObject *object);
+static void ui_vis_realize            (GtkWidget *widget);
+static void ui_vis_unrealize          (GtkWidget *widget);
+static void ui_vis_map                (GtkWidget *widget);
+static void ui_vis_unmap              (GtkWidget *widget);
+static void ui_vis_size_request       (GtkWidget *widget, GtkRequisition *requisition);
+static void ui_vis_size_allocate      (GtkWidget *widget, GtkAllocation *allocation);
+static gboolean ui_vis_expose         (GtkWidget *widget, GdkEventExpose *event);
+static void ui_vis_toggle_scaled      (UiVis *vis);
+
+static GtkWidgetClass *parent_class = NULL;
+static guint vis_signals[LAST_SIGNAL] = { 0 };
+
+GType ui_vis_get_type() {
+    static GType vis_type = 0;
+    if (!vis_type) {
+        static const GTypeInfo vis_info = {
+            sizeof (UiVisClass),
+            NULL,
+            NULL,
+            (GClassInitFunc) ui_vis_class_init,
+            NULL,
+            NULL,
+            sizeof (UiVis),
+            0,
+            (GInstanceInitFunc) ui_vis_init,
+        };
+        vis_type = g_type_register_static (GTK_TYPE_WIDGET, "UiVis", &vis_info, 0);
+    }
+
+    return vis_type;
+}
+
+static void ui_vis_class_init(UiVisClass *klass) {
+    GtkObjectClass *object_class;
+    GtkWidgetClass *widget_class;
+
+    object_class = (GtkObjectClass*) klass;
+    widget_class = (GtkWidgetClass*) klass;
+    parent_class = gtk_type_class (gtk_widget_get_type ());
+
+    object_class->destroy = ui_vis_destroy;
+
+    widget_class->realize = ui_vis_realize;
+    widget_class->unrealize = ui_vis_unrealize;
+    widget_class->map = ui_vis_map;
+    widget_class->unmap = ui_vis_unmap;
+    widget_class->expose_event = ui_vis_expose;
+    widget_class->size_request = ui_vis_size_request;
+    widget_class->size_allocate = ui_vis_size_allocate;
+
+    klass->doubled = ui_vis_toggle_scaled;
+
+    vis_signals[DOUBLED] = 
+        g_signal_new ("toggle-scaled", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                      G_STRUCT_OFFSET (UiVisClass, doubled), NULL, NULL,
+                      gtk_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+static void ui_vis_init(UiVis *vis) {
+    memset(voiceprint_data, 0, 16*76);
+}
+
+GtkWidget* ui_vis_new(GtkWidget *fixed, gint x, gint y, gint width) {
+    UiVis *vis = g_object_new (ui_vis_get_type (), NULL);
+
+    vis->x = x;
+    vis->y = y;
+
+    vis->width = width;
+    vis->height = 16;
+
+    vis->fixed = fixed;
+    vis->scaled = FALSE;
+
+    vis->visible_window = TRUE;
+    vis->event_window = NULL;
+
+    gtk_fixed_put(GTK_FIXED(vis->fixed), GTK_WIDGET(vis), vis->x, vis->y);
+
+    return GTK_WIDGET(vis);
+}
+
+static void ui_vis_destroy(GtkObject *object) {
+    UiVis *vis;
+
+    g_return_if_fail (object != NULL);
+    g_return_if_fail (UI_IS_VIS (object));
+
+    vis = UI_VIS (object);
+
+    if (GTK_OBJECT_CLASS (parent_class)->destroy)
+        (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void ui_vis_realize(GtkWidget *widget) {
+    UiVis *vis;
+    GdkWindowAttr attributes;
+    gint attributes_mask;
+
+    g_return_if_fail (widget != NULL);
+    g_return_if_fail (UI_IS_VIS(widget));
+
+    GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
+    vis = UI_VIS(widget);
+
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.event_mask = gtk_widget_get_events(widget);
+    attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
+
+    if (vis->visible_window)
+    {
+      attributes.visual = gtk_widget_get_visual(widget);
+      attributes.colormap = gtk_widget_get_colormap(widget);
+      attributes.wclass = GDK_INPUT_OUTPUT;
+      attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+      widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
+      GTK_WIDGET_UNSET_FLAGS(widget, GTK_NO_WINDOW);
+      gdk_window_set_user_data(widget->window, widget);
+    }
+    else
+    {
+      widget->window = gtk_widget_get_parent_window (widget);
+      g_object_ref (widget->window);
+
+      attributes.wclass = GDK_INPUT_ONLY;
+      attributes_mask = GDK_WA_X | GDK_WA_Y;
+      vis->event_window = gdk_window_new (widget->window, &attributes, attributes_mask);
+      GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW);
+      gdk_window_set_user_data(vis->event_window, widget);
+    }
+
+    widget->style = gtk_style_attach(widget->style, widget->window);
+}
+
+static void ui_vis_unrealize(GtkWidget *widget) {
+    UiVis *vis;
+    vis = UI_VIS(widget);
+
+    if ( vis->event_window != NULL )
+    {
+      gdk_window_set_user_data( vis->event_window , NULL );
+      gdk_window_destroy( vis->event_window );
+      vis->event_window = NULL;
+    }
+
+    if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+        (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void ui_vis_map(GtkWidget *widget)
+{
+    UiVis *vis;
+    vis = UI_VIS(widget);
+
+    if (vis->event_window != NULL)
+      gdk_window_show (vis->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->map)
+      (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+}
+
+static void ui_vis_unmap (GtkWidget *widget)
+{
+    UiVis *vis;
+    vis = UI_VIS(widget);
+
+    if (vis->event_window != NULL)
+      gdk_window_hide (vis->event_window);
+
+    if (GTK_WIDGET_CLASS (parent_class)->unmap)
+      (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
+}
+
+static void ui_vis_size_request(GtkWidget *widget, GtkRequisition *requisition) {
+    UiVis *vis = UI_VIS(widget);
+
+    requisition->width = vis->width*(vis->scaled ? config.scale_factor : 1);
+    requisition->height = vis->height*(vis->scaled ? config.scale_factor : 1);
+}
+
+static void ui_vis_size_allocate(GtkWidget *widget, GtkAllocation *allocation) {
+    UiVis *vis = UI_VIS (widget);
+
+    widget->allocation = *allocation;
+    widget->allocation.x *= (vis->scaled ? config.scale_factor : 1);
+    widget->allocation.y *= (vis->scaled ? config.scale_factor : 1);
+    if (GTK_WIDGET_REALIZED (widget))
+    {
+        if (vis->event_window != NULL)
+            gdk_window_move_resize(vis->event_window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+        else
+            gdk_window_move_resize(widget->window, widget->allocation.x, widget->allocation.y, allocation->width, allocation->height);
+    }
+
+    vis->x = widget->allocation.x/(vis->scaled ? config.scale_factor : 1);
+    vis->y = widget->allocation.y/(vis->scaled ? config.scale_factor : 1);
+}
+
+static gboolean ui_vis_expose(GtkWidget *widget, GdkEventExpose *event) {
+    g_return_val_if_fail (widget != NULL, FALSE);
+    g_return_val_if_fail (UI_IS_VIS (widget), FALSE);
+    g_return_val_if_fail (event != NULL, FALSE);
+
+    UiVis *vis = UI_VIS (widget);
+
+    gint x, y, n, h = 0, h2;
+    gfloat delta;
+    guchar skin_col[2][3];
+    guchar vis_color[24][3];
+    guchar vis_voice_color[256][3], voice_c[3];
+    guchar rgb_data[76 * 16 * 3 * 2 * 2], *ptr, c;
+    guint32 colors[24];
+    GdkColor *fgc, *bgc;
+    GdkRgbCmap *cmap;
+
+    if (!GTK_WIDGET_VISIBLE(widget))
+        return FALSE;
+
+    if (!vis->visible_window)
+        return FALSE;
+
+    skin_get_viscolor(aud_active_skin, vis_color);
+    for (y = 0; y < 24; y++) {
+        colors[y] =
+            vis_color[y][0] << 16 | vis_color[y][1] << 8 | vis_color[y][2];
+    }
+    cmap = gdk_rgb_cmap_new(colors, 24);
+
+    if (!vis->scaled) {
+      if(config.vis_type == VIS_VOICEPRINT /*&& aud_cfg->voiceprint_mode != VOICEPRINT_NORMAL*/){
+	memset(rgb_data, 0, 76 * 16 * 3);
+      }
+      else{
+	memset(rgb_data, 0, 76 * 16);
+	for (y = 1; y < 16; y += 2) {
+	  ptr = rgb_data + (y * 76);
+	  for (x = 0; x < 76; x += 2, ptr += 2)
+	    *ptr = 1;
+      }
+      }
+    }
+    else{
+      if(config.vis_type == VIS_VOICEPRINT /*&& aud_cfg->voiceprint_mode != VOICEPRINT_NORMAL*/){
+	memset(rgb_data, 0, 3 * 4 * 16 * 76);
+      }
+      else{
+	memset(rgb_data, 0, (guint)(76 * config.scale_factor) * 32);
+	for (y = 1; y < 16; y += 2) {
+	  ptr = rgb_data + (y * (guint)(76 * 4 * config.scale_factor));
+	  for (x = 0; x < 76; x += 2, ptr += 4) {
+	    *ptr = 1;
+	    *(ptr + 1) = 1;
+	    *(ptr + (guint)(76 * config.scale_factor)) = 1;
+	    *(ptr + (guint)(76 * config.scale_factor)+1) = 1;
+	}
+      }
+      }
+    }
+    if (config.vis_type == VIS_ANALYZER) {
+      for (x = 0; x < 75; x++) {
+	if (config.analyzer_type == ANALYZER_BARS && (x % 4) == 0)
+	  h = vis->data[x >> 2];
+	else if (config.analyzer_type == ANALYZER_LINES)
+	  h = vis->data[x];
+	if (h && (config.analyzer_type == ANALYZER_LINES ||
+		  (x % 4) != 3)) {
+	  if (!vis->scaled) {
+	    ptr = rgb_data + ((16 - h) * 76) + x;
+	    switch (config.analyzer_mode) {
+	    case ANALYZER_NORMAL:
+	      for (y = 0; y < h; y++, ptr += 76)
+		*ptr = 18 - h + y;
+	      break;
+	    case ANALYZER_FIRE:
+	      for (y = 0; y < h; y++, ptr += 76)
+		*ptr = y + 2;
+	      break;
+	    case ANALYZER_VLINES:
+	      for (y = 0; y < h; y++, ptr += 76)
+		*ptr = 18 - h;
+	      break;
+	    }
+	  }
+	  else{
+	    ptr = rgb_data + ((16 - h) * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	    switch (config.analyzer_mode) {
+	    case ANALYZER_NORMAL:
+	      for (y = 0; y < h; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
+		*ptr = 18 - h + y;
+		*(ptr + 1) = 18 - h + y;
+		*(ptr + (guint)(76 * config.scale_factor)) = 18 - h + y;
+		*(ptr + (guint)(76 * config.scale_factor)+1) = 18 - h + y;
+	      }
+	      break;
+	    case ANALYZER_FIRE:
+	      for (y = 0; y < h; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
+		*ptr = y + 2;
+		*(ptr + 1) = y + 2;
+		*(ptr + (guint)(76 * config.scale_factor)) = y + 2;
+		*(ptr + (guint)(76 * config.scale_factor)+1) = y + 2;
+	      }
+	      break;
+	    case ANALYZER_VLINES:
+	      for (y = 0; y < h; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
+		*ptr = 18 - h;
+		*(ptr + 1) = 18 - h;
+		*(ptr + (guint)(76 * config.scale_factor)) = 18 - h;
+		*(ptr + (guint)(76 * config.scale_factor)+1) = 18 - h;
+	      }
+	      
+	      break;
+	    }
+	  }
+	}
+      }
+      if (config.analyzer_peaks) {
+	for (x = 0; x < 75; x++) {
+	  if (config.analyzer_type == ANALYZER_BARS && (x % 4) == 0)
+	    h = vis->peak[x >> 2];
+	  else if (config.analyzer_type == ANALYZER_LINES)
+	    h = vis->peak[x];
+	  if (h && (config.analyzer_type == ANALYZER_LINES || (x % 4) != 3)){
+	    
+	    if (!vis->scaled) {
+	      rgb_data[(16 - h) * 76 + x] = 23;
+	    }
+	    else{
+	      ptr = rgb_data + (16 - h) * (guint)(76 * 4 * config.scale_factor) + (guint)(x * aud_cfg->scale_factor);
+	      *ptr = 23;
+	      *(ptr + 1) = 23;
+	      *(ptr + (guint)(76 * config.scale_factor)) = 23;
+	      *(ptr + (guint)(76 * config.scale_factor)+1) = 23;
+	    }
+	  }
+	}
+      }
+    }
+    else if (config.vis_type == VIS_VOICEPRINT) {
+      if(!audacious_drct_get_paused() && audacious_drct_get_playing()){/*Don't scroll when it's paused or stopped*/
+	for (y = 0; y < 16; y ++)
+	  for (x = 75; x > 0; x--)
+	    voiceprint_data[x + y * 76] = voiceprint_data[x-1+y*76];
+	  for(y=0;y<16;y++)
+	    voiceprint_data[y * 76] = vis->data[y];
+      }
+      if(audacious_drct_get_playing()){ /*Only draw the data if we're playing*/
+	if(config.voiceprint_mode == VOICEPRINT_NORMAL){ 
+	  /* Create color gradient from the skin's background- and foreground color*/
+	  fgc = skin_get_color(aud_active_skin, SKIN_TEXTFG);
+	  bgc = skin_get_color(aud_active_skin, SKIN_TEXTBG);
+	  skin_col[0][0] = fgc->red   >> 8;
+	  skin_col[0][1] = fgc->green >> 8;
+	  skin_col[0][2] = fgc->blue  >> 8;
+	  skin_col[1][0] = bgc->red   >> 8;
+	  skin_col[1][1] = bgc->green >> 8;
+	  skin_col[1][2] = bgc->blue  >> 8;
+	  for(n=0;n<3;n++){
+	    for(x=0;x<256;x++){
+	      if(skin_col[0][n] > skin_col[1][n]){
+		delta = (gfloat)(skin_col[0][n] - skin_col[1][n]) / 256.0;
+		vis_voice_color[x][n] = skin_col[1][n] + (gfloat)(delta * x);
+	      }
+	      else if(skin_col[0][n] == skin_col[1][n]){
+		vis_voice_color[x][n] = skin_col[0][n];
+	      }
+	      else{
+		delta = (gfloat)(skin_col[1][n] - skin_col[0][n]) / 256.0;
+		vis_voice_color[x][n] = skin_col[1][n] - (gfloat)(delta * x);
+	      }
+	    }
+	  }
+	}
+	for (y = 0; y < 16; y ++){
+	  for (x = 0; x < 76; x++){
+	    guint8 d = voiceprint_data[x + y*76];
+	    
+	    if(config.voiceprint_mode == VOICEPRINT_NORMAL){
+	      voice_c[0] = vis_voice_color[d][0];
+	      voice_c[1] = vis_voice_color[d][1];
+	      voice_c[2] = vis_voice_color[d][2];
+	    }
+	    else if(config.voiceprint_mode == VOICEPRINT_FIRE){
+	      voice_c[0] = d < 64 ? (d * 2) : 255;
+	      voice_c[1] = d < 64 ? 0 : (d < 128 ? (d-64) * 2 : 255);
+	      voice_c[2] = d < 128 ? 0 : (d-128) * 2;
+	      /* Test for black->blue->green->red. Isn't pretty, though...
+		 voice_c[0] = d > 192 ? (d - 192) << 2 : 0;
+		 voice_c[1] = d > 64 ? (d < 128 ? (d - 64) << 2 : (d < 192 ? (192 - d) << 2 : 0)) : 0;
+		 voice_c[2] = d < 64 ? d << 2 : (d < 128 ? (128 - d) << 2 : 0);
+	      */
+	    }
+	    else if(config.voiceprint_mode == VOICEPRINT_ICE){	    
+	      voice_c[0] = d;
+	      voice_c[1] = d < 128 ? d * 2 : 255;
+	      voice_c[2] = d < 64 ? d * 4 : 255; 
+	    }
+	    if(!vis->scaled){
+	      for(n=0;n<3;n++)
+		rgb_data[x * 3 + y * 76*3+n] = voice_c[n];
+	    }
+	    else{
+	      ptr = rgb_data + (guint)(x * 3 * config.scale_factor) + (guint) (y * 76 * 3 * aud_cfg->scale_factor);
+	      for(n=0;n<3;n++)
+		{
+		  *(ptr + n) = voice_c[n];
+		  *(ptr + n + 3) = voice_c[n];
+		  *(ptr + (guint)(n + 76 * config.scale_factor * 3)) = voice_c[n];
+		  *(ptr + (guint)(n + 3 + 76 * config.scale_factor * 3)) = voice_c[n];
+		}
+	    }
+	  }
+	}
+      }
+    }
+    if (config.vis_type == VIS_SCOPE) {
+      for (x = 0; x < 75; x++) {
+	switch (config.scope_mode) {
+	case SCOPE_DOT:
+	  h = vis->data[x];
+	  if (!vis->scaled) {
+	  ptr = rgb_data + ((14 - h) * 76) + x;
+	    *ptr = vis_scope_colors[h + 1];
+	  }else{
+	    ptr = rgb_data + ((14 - h) * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	    *ptr = vis_scope_colors[h + 1];
+	    *(ptr + 1) = vis_scope_colors[h + 1];
+	    *(ptr + (guint)(76 * config.scale_factor)) = vis_scope_colors[h + 1];
+	    *(ptr + (guint)(76 * config.scale_factor)+1) = vis_scope_colors[h + 1];
+	  }
+	  break;
+	case SCOPE_LINE:
+	  if (x != 74) {
+	    h = 14 - vis->data[x];
+	    h2 = 14 - vis->data[x + 1];
+	    if (h > h2) {
+	      y = h;
+	      h = h2;
+	      h2 = y;
+	    }
+	    if (!vis->scaled) {
+	    ptr = rgb_data + (h * 76) + x;
+	    for (y = h; y <= h2; y++, ptr += 76)
+	      *ptr = vis_scope_colors[y - 2];
+	    }
+	    else{
+	      ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	      for (y = h; y <= h2; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
+		*ptr = vis_scope_colors[y - 2];
+		*(ptr + 1) = vis_scope_colors[y - 2];
+		*(ptr + (guint)(76 * config.scale_factor)) = vis_scope_colors[y - 2];
+		*(ptr + (guint)(76 * config.scale_factor)+1) = vis_scope_colors[y - 2];
+	      }
+	    }
+	  }
+	  else {
+	    h = 14 - vis->data[x];
+	    if (!vis->scaled) {
+	      ptr = rgb_data + (h * 76) + x;
+	      *ptr = vis_scope_colors[h + 1];
+	    }else{
+	      ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	      *ptr = vis_scope_colors[h + 1];
+	      *(ptr + 1) = vis_scope_colors[h + 1];
+	      *(ptr + (guint)(76 * config.scale_factor)) = vis_scope_colors[h + 1];
+	      *(ptr + (guint)(76 * config.scale_factor)+1) = vis_scope_colors[h + 1];
+	    }
+	  }
+	  break;
+	case SCOPE_SOLID:
+	  h = 14 - vis->data[x];
+	  h2 = 8;
+	  c = vis_scope_colors[(gint) vis->data[x]];
+	  if (h > h2) {
+	    y = h;
+	    h = h2;
+	    h2 = y;
+	  }
+	  if (!vis->scaled) {
+	    ptr = rgb_data + (h * 76) + x;
+	    for (y = h; y <= h2; y++, ptr += 76)
+	      *ptr = c;
+	  }else{
+	    ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	    for (y = h; y <= h2; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
+	      *ptr = c;
+	      *(ptr + 1) = c;
+	      *(ptr + (guint)(76 * config.scale_factor)) = c;
+	      *(ptr + (guint)(76 * config.scale_factor)+1) = c;
+	    }
+	  }
+	  break;
+	}
+      }
+    }
+
+    GdkPixmap *obj = NULL;
+    GdkGC *gc;
+    obj = gdk_pixmap_new(NULL, vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? aud_cfg->scale_factor : 1), gdk_rgb_get_visual()->depth);
+    gc = gdk_gc_new(obj);
+
+    if (!vis->scaled) {
+        if (config.vis_type == VIS_VOICEPRINT) {
+            gdk_draw_rgb_image(obj, gc, 0, 0, vis->width, vis->height,
+                               GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+                               76 * 3);
+        } else {
+            gdk_draw_indexed_image(obj, gc, 0, 0, vis->width, vis->height,
+                                   GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+                                   76 , cmap);
+        }
+    } else {
+        if (config.vis_type == VIS_VOICEPRINT) {
+            gdk_draw_rgb_image(obj, gc, 0 << 1, 0 << 1,
+                               vis->width << 1, vis->height << 1,
+                               GDK_RGB_DITHER_NONE, (guchar *) rgb_data,
+                               76 * 2 * 3);
+        } else {
+            gdk_draw_indexed_image(obj, gc, 0 << 1, 0 << 1,
+                                   vis->width << 1, vis->height << 1,
+                                   GDK_RGB_DITHER_NONE, (guchar *) rgb_data,
+                                   76 * 2 , cmap);
+        }
+    }
+
+    gdk_draw_drawable (widget->window, gc, obj, 0, 0, 0, 0,
+                       vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? aud_cfg->scale_factor : 1));
+    g_object_unref(obj);
+    g_object_unref(gc);
+    gdk_rgb_cmap_free(cmap);
+    return FALSE;
+}
+
+static void ui_vis_toggle_scaled(UiVis *vis) {
+    GtkWidget *widget = GTK_WIDGET (vis);
+    vis->scaled = !vis->scaled;
+
+    gtk_widget_set_size_request(widget, vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? aud_cfg->scale_factor : 1));
+
+    gtk_widget_queue_draw(GTK_WIDGET(vis));
+}
+
+void ui_vis_draw_pixel(GtkWidget *widget, guchar* texture, gint x, gint y, guint8 colour) {
+    UiVis *vis = UI_VIS (widget);
+    if (vis->scaled){
+        texture[y * 76 + x] = colour;
+        texture[y * 76 + x + 1] = colour;
+        texture[y * 76 * 4 + x] = colour;
+        texture[y * 76 * 4 + x + 1] = colour;
+    } else {
+        texture[y * 76 + x] = colour;
+    }
+}
+
+void ui_vis_set_visible(GtkWidget *widget, gboolean window_is_visible)
+{
+    UiVis *vis;
+    gboolean widget_is_visible;
+
+    g_return_if_fail(UI_IS_VIS(widget));
+
+    vis = UI_VIS (widget);
+    widget_is_visible = GTK_WIDGET_VISIBLE(widget);
+
+    vis->visible_window = window_is_visible;
+
+    if (GTK_WIDGET_REALIZED (widget))
+    {
+        if ( widget_is_visible )
+            gtk_widget_hide(widget);
+
+        gtk_widget_unrealize(widget);
+        gtk_widget_realize(widget);
+
+        if ( widget_is_visible )
+            gtk_widget_show(widget);
+    }
+
+    if (widget_is_visible)
+        gtk_widget_queue_resize(widget);
+}
+
+void ui_vis_clear_data(GtkWidget *widget) {
+    gint i;
+    UiVis *vis = UI_VIS (widget);
+
+    memset(voiceprint_data, 0, 16*76);
+    for (i = 0; i < 75; i++) {
+        vis->data[i] = (config.vis_type == VIS_SCOPE) ? 6 : 0;
+        vis->peak[i] = 0;
+    }
+}
+
+void ui_vis_timeout_func(GtkWidget *widget, guchar * data) {
+    UiVis *vis = UI_VIS (widget);
+    static GTimer *timer = NULL;
+    gulong micros = 9999999;
+    gboolean falloff = FALSE;
+    gint i;
+
+    if (!timer) {
+        timer = g_timer_new();
+        g_timer_start(timer);
+    }
+    else {
+      g_timer_elapsed(timer, &micros);
+      if (micros > 14000)
+	g_timer_reset(timer);
+    }
+    if (config.vis_type == VIS_ANALYZER) {
+        if (micros > 14000)
+            falloff = TRUE;
+        if (data || falloff) {
+            for (i = 0; i < 75; i++) {
+                if (data && data[i] > vis->data[i]) {
+                    vis->data[i] = data[i];
+                    if (vis->data[i] > vis->peak[i]) {
+                        vis->peak[i] = vis->data[i];
+                        vis->peak_speed[i] = 0.01;
+
+                    }
+                    else if (vis->peak[i] > 0.0) {
+                        vis->peak[i] -= vis->peak_speed[i];
+                        vis->peak_speed[i] *=
+                            vis_pfalloff_speeds[config.peaks_falloff];
+                        if (vis->peak[i] < vis->data[i])
+                            vis->peak[i] = vis->data[i];
+                        if (vis->peak[i] < 0.0)
+                            vis->peak[i] = 0.0;
+                    }
+                }
+                else if (falloff) {
+                    if (vis->data[i] > 0.0) {
+                        vis->data[i] -=
+                            vis_afalloff_speeds[config.analyzer_falloff];
+                        if (vis->data[i] < 0.0)
+                            vis->data[i] = 0.0;
+                    }
+                    if (vis->peak[i] > 0.0) {
+                        vis->peak[i] -= vis->peak_speed[i];
+                        vis->peak_speed[i] *=
+                            vis_pfalloff_speeds[config.peaks_falloff];
+                        if (vis->peak[i] < vis->data[i])
+                            vis->peak[i] = vis->data[i];
+                        if (vis->peak[i] < 0.0)
+                            vis->peak[i] = 0.0;
+                    }
+                }
+            }
+        }
+    }
+    else if (config.vis_type == VIS_VOICEPRINT && data){
+      for(i = 0; i < 16; i++)
+	{
+	  vis->data[i] = data[15 - i];
+	}
+    }
+    else if (data) {
+        for (i = 0; i < 75; i++)
+            vis->data[i] = data[i];
+    }
+
+    if (micros > 14000) {
+        if (!vis->refresh_delay) {
+            gtk_widget_queue_draw(widget);
+            vis->refresh_delay = vis_redraw_delays[config.vis_refresh];
+        }
+        vis->refresh_delay--;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_vis.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,98 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2007  Audacious development team.
+ *
+ * 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; under version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses>.
+ *
+ * The Audacious team does not consider modular code linking to
+ * Audacious or using our public API to be a derived work.
+ */
+
+#ifndef UIVIS_H
+#define UIVIS_H
+
+#include <gtk/gtk.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UI_VIS(obj)          GTK_CHECK_CAST (obj, ui_vis_get_type (), UiVis)
+#define UI_VIS_CLASS(klass)  GTK_CHECK_CLASS_CAST (klass, ui_vis_get_type (), UiVisClass)
+#define UI_IS_VIS(obj)       GTK_CHECK_TYPE (obj, ui_vis_get_type ())
+
+typedef enum {
+    VIS_ANALYZER, VIS_SCOPE, VIS_VOICEPRINT, VIS_OFF
+} VisType;
+
+typedef enum {
+    ANALYZER_NORMAL, ANALYZER_FIRE, ANALYZER_VLINES
+} AnalyzerMode;
+
+typedef enum {
+    ANALYZER_LINES, ANALYZER_BARS
+} AnalyzerType;
+
+typedef enum {
+    SCOPE_DOT, SCOPE_LINE, SCOPE_SOLID
+} ScopeMode;
+typedef enum {
+  VOICEPRINT_NORMAL, VOICEPRINT_FIRE, VOICEPRINT_ICE
+} VoiceprintMode;
+
+
+typedef enum {
+    VU_NORMAL, VU_SMOOTH
+} VUMode;
+
+typedef enum {
+    REFRESH_FULL, REFRESH_HALF, REFRESH_QUARTER, REFRESH_EIGTH
+} RefreshRate;
+
+typedef enum {
+    FALLOFF_SLOWEST, FALLOFF_SLOW, FALLOFF_MEDIUM, FALLOFF_FAST,
+    FALLOFF_FASTEST
+} FalloffSpeed;
+
+typedef struct _UiVis        UiVis;
+typedef struct _UiVisClass   UiVisClass;
+
+struct _UiVis {
+    GtkWidget        widget;
+
+    gint             x, y, width, height;
+    gfloat           data[75], peak[75], peak_speed[75];
+    gint             refresh_delay;
+    gboolean         scaled;
+    GtkWidget        *fixed;
+    gboolean         visible_window;
+    GdkWindow        *event_window;
+};
+
+struct _UiVisClass {
+    GtkWidgetClass          parent_class;
+    void (* doubled)        (UiVis *vis);
+};
+
+GtkWidget* ui_vis_new (GtkWidget *fixed, gint x, gint y, gint width);
+GtkType ui_vis_get_type(void);
+void ui_vis_set_vis(GtkWidget *widget, gint num);
+void ui_vis_clear_data(GtkWidget *widget);
+void ui_vis_timeout_func(GtkWidget *widget, guchar * data);
+void ui_vis_set_visible(GtkWidget *widget, gboolean window_is_visible);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/util.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,1134 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2008  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+/*#define AUD_DEBUG*/
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "util.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "platform/smartinclude.h"
+#include <errno.h>
+
+#ifdef HAVE_FTS_H
+#  include <sys/types.h>
+#  include <sys/stat.h>
+#  include <fts.h>
+#endif
+
+#include <audacious/input.h>
+#include <audacious/playback.h>
+
+#ifdef USE_CHARDET
+#  include "../libguess/libguess.h"
+#  ifdef HAVE_UDET
+#    include <libudet_c.h>
+#  endif
+#endif
+
+#include "plugin.h"
+
+/*
+ * find <file> in directory <dirname> or subdirectories.  return
+ * pointer to complete filename which has to be freed by calling
+ * "g_free()" after use. Returns NULL if file could not be found.
+ */
+
+/* somebody tell me how to make use of those funcs from string.h that are in core... */
+gboolean
+str_has_suffix_nocase(const gchar * str, const gchar * suffix)
+{
+    return (strcasecmp(str + strlen(str) - strlen(suffix), suffix) == 0);
+}
+
+static gchar *
+str_replace_char(gchar * str, gchar old, gchar new)
+{
+    gchar *match;
+
+    g_return_val_if_fail(str != NULL, NULL);
+
+    match = str;
+    while ((match = strchr(match, old)))
+        *match = new;
+
+    return str;
+}
+
+static gchar *
+str_replace_drive_letter(gchar * str)
+{
+    gchar *match, *match_end;
+
+    g_return_val_if_fail(str != NULL, NULL);
+
+    while ((match = strstr(str, ":\\"))) {
+        match--;
+        match_end = match + 3;
+        *match++ = '/';
+        while (*match_end)
+            *match++ = *match_end++;
+        *match = 0; /* the end of line */
+    }
+
+    return str;
+}
+
+gchar *
+convert_dos_path(gchar * path)
+{
+    g_return_val_if_fail(path != NULL, NULL);
+
+    /* replace drive letter with '/' */
+    str_replace_drive_letter(path);
+
+    /* replace '\' with '/' */
+    str_replace_char(path, '\\', '/');
+
+    return path;
+}
+
+gchar *
+escape_shell_chars(const gchar * string)
+{
+    const gchar *special = "$`\"\\";    /* Characters to escape */
+    const gchar *in = string;
+    gchar *out, *escaped;
+    gint num = 0;
+
+    while (*in != '\0')
+        if (strchr(special, *in++))
+            num++;
+
+    escaped = g_malloc(strlen(string) + num + 1);
+
+    in = string;
+    out = escaped;
+
+    while (*in != '\0') {
+        if (strchr(special, *in))
+            *out++ = '\\';
+        *out++ = *in++;
+    }
+    *out = '\0';
+
+    return escaped;
+}
+
+
+typedef struct {
+    const gchar *to_match;
+    gchar *match;
+    gboolean found;
+} FindFileContext;
+
+static const struct {
+    AFormat afmt;
+    SAD_sample_format sadfmt;
+} format_table[] = {
+    {FMT_U8, SAD_SAMPLE_U8},
+    {FMT_S8, SAD_SAMPLE_S8},
+
+    {FMT_S16_LE, SAD_SAMPLE_S16_LE},
+    {FMT_S16_BE, SAD_SAMPLE_S16_BE},
+    {FMT_S16_NE, SAD_SAMPLE_S16},
+
+    {FMT_U16_LE, SAD_SAMPLE_U16_LE},
+    {FMT_U16_BE, SAD_SAMPLE_U16_BE},
+    {FMT_U16_NE, SAD_SAMPLE_U16},
+
+    {FMT_S24_LE, SAD_SAMPLE_S24_LE},
+    {FMT_S24_BE, SAD_SAMPLE_S24_BE},
+    {FMT_S24_NE, SAD_SAMPLE_S24},
+    
+    {FMT_U24_LE, SAD_SAMPLE_U24_LE},
+    {FMT_U24_BE, SAD_SAMPLE_U24_BE},
+    {FMT_U24_NE, SAD_SAMPLE_U24},
+
+    {FMT_S32_LE, SAD_SAMPLE_S32_LE},
+    {FMT_S32_BE, SAD_SAMPLE_S32_BE},
+    {FMT_S32_NE, SAD_SAMPLE_S32},
+    
+    {FMT_U32_LE, SAD_SAMPLE_U32_LE},
+    {FMT_U32_BE, SAD_SAMPLE_U32_BE},
+    {FMT_U32_NE, SAD_SAMPLE_U32},
+    
+    {FMT_FLOAT, SAD_SAMPLE_FLOAT},
+    {FMT_FIXED32, SAD_SAMPLE_FIXED32},
+};
+
+SAD_sample_format
+sadfmt_from_afmt(AFormat fmt)
+{
+    int i;
+    for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) {
+        if (format_table[i].afmt == fmt) return format_table[i].sadfmt;
+    }
+
+    return -1;
+}
+
+
+static gboolean
+find_file_func(const gchar * path, const gchar * basename, gpointer data)
+{
+    FindFileContext *context = data;
+
+    if (strlen(path) > FILENAME_MAX) {
+        AUDDBG("Ignoring path: name too long (%s)\n", path);
+        return TRUE;
+    }
+
+    if (aud_vfs_file_test(path, G_FILE_TEST_IS_REGULAR)) {
+        if (!strcasecmp(basename, context->to_match)) {
+            context->match = g_strdup(path);
+            context->found = TRUE;
+            return TRUE;
+        }
+    }
+    else if (aud_vfs_file_test(path, G_FILE_TEST_IS_DIR)) {
+        dir_foreach(path, find_file_func, context, NULL);
+        if (context->found)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+gchar *
+find_file_recursively(const gchar * path, const gchar * filename)
+{
+    FindFileContext context;
+    gchar *out = NULL;
+
+    context.to_match = filename;
+    context.match = NULL;
+    context.found = FALSE;
+
+    dir_foreach(path, find_file_func, &context, NULL);
+
+    if (context.match)
+    {
+        out = g_filename_to_uri(context.match, NULL, NULL);
+        g_free(context.match);
+    }
+
+    return out;
+}
+
+gchar *
+find_path_recursively(const gchar * path, const gchar * filename)
+{
+    FindFileContext context;
+
+    context.to_match = filename;
+    context.match = NULL;
+    context.found = FALSE;
+
+    dir_foreach(path, find_file_func, &context, NULL);
+
+    return context.match;
+}
+
+
+typedef enum {
+    ARCHIVE_UNKNOWN = 0,
+    ARCHIVE_DIR,
+    ARCHIVE_TAR,
+    ARCHIVE_TGZ,
+    ARCHIVE_ZIP,
+    ARCHIVE_TBZ2
+} ArchiveType;
+
+typedef gchar *(*ArchiveExtractFunc) (const gchar *, const gchar *);
+
+typedef struct {
+    ArchiveType type;
+    const gchar *ext;
+} ArchiveExtensionType;
+
+static ArchiveExtensionType archive_extensions[] = {
+    {ARCHIVE_TAR, ".tar"},
+    {ARCHIVE_ZIP, ".wsz"},
+    {ARCHIVE_ZIP, ".zip"},
+    {ARCHIVE_TGZ, ".tar.gz"},
+    {ARCHIVE_TGZ, ".tgz"},
+    {ARCHIVE_TBZ2, ".tar.bz2"},
+    {ARCHIVE_TBZ2, ".bz2"},
+    {ARCHIVE_UNKNOWN, NULL}
+};
+
+static gchar *archive_extract_tar(const gchar * archive, const gchar * dest);
+static gchar *archive_extract_zip(const gchar * archive, const gchar * dest);
+static gchar *archive_extract_tgz(const gchar * archive, const gchar * dest);
+static gchar *archive_extract_tbz2(const gchar * archive, const gchar * dest);
+
+static ArchiveExtractFunc archive_extract_funcs[] = {
+    NULL,
+    NULL,
+    archive_extract_tar,
+    archive_extract_tgz,
+    archive_extract_zip,
+    archive_extract_tbz2
+};
+
+
+/* FIXME: these functions can be generalised into a function using a
+ * command lookup table */
+
+static const gchar *
+get_tar_command(void)
+{
+    static const gchar *command = NULL;
+
+    if (!command) {
+        if (!(command = getenv("TARCMD")))
+            command = "tar";
+    }
+
+    return command;
+}
+
+static const gchar *
+get_unzip_command(void)
+{
+    static const gchar *command = NULL;
+
+    if (!command) {
+        if (!(command = getenv("UNZIPCMD")))
+            command = "unzip";
+    }
+
+    return command;
+}
+
+
+static gchar *
+archive_extract_tar(const gchar * archive, const gchar * dest)
+{
+    return g_strdup_printf("%s >/dev/null xf \"%s\" -C %s",
+                           get_tar_command(), archive, dest);
+}
+
+static gchar *
+archive_extract_zip(const gchar * archive, const gchar * dest)
+{
+    return g_strdup_printf("%s >/dev/null -o -j \"%s\" -d %s",
+                           get_unzip_command(), archive, dest);
+}
+
+static gchar *
+archive_extract_tgz(const gchar * archive, const gchar * dest)
+{
+    return g_strdup_printf("%s >/dev/null xzf \"%s\" -C %s",
+                           get_tar_command(), archive, dest);
+}
+
+static gchar *
+archive_extract_tbz2(const gchar * archive, const gchar * dest)
+{
+    return g_strdup_printf("bzip2 -dc \"%s\" | %s >/dev/null xf - -C %s",
+                           archive, get_tar_command(), dest);
+}
+
+
+ArchiveType
+archive_get_type(const gchar * filename)
+{
+    gint i = 0;
+
+    if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+        return ARCHIVE_DIR;
+
+    while (archive_extensions[i].ext) {
+        if (g_str_has_suffix(filename, archive_extensions[i].ext)) {
+            return archive_extensions[i].type;
+        }
+        i++;
+    }
+
+    return ARCHIVE_UNKNOWN;
+}
+
+gboolean
+file_is_archive(const gchar * filename)
+{
+    return (archive_get_type(filename) > ARCHIVE_DIR);
+}
+
+gchar *
+archive_basename(const gchar * str)
+{
+    gint i = 0;
+
+    while (archive_extensions[i].ext) {
+        if (str_has_suffix_nocase(str, archive_extensions[i].ext)) {
+            const gchar *end = g_strrstr(str, archive_extensions[i].ext);
+            if (end) {
+                return g_strndup(str, end - str);
+            }
+            break;
+        }
+        i++;
+    }
+
+    return NULL;
+}
+
+/*
+   decompress_archive
+
+   Decompresses the archive "filename" to a temporary directory,
+   returns the path to the temp dir, or NULL if failed,
+   watch out tho, doesn't actually check if the system command succeeds :-|
+*/
+
+gchar *
+archive_decompress(const gchar * filename)
+{
+    gchar *tmpdir, *cmd, *escaped_filename;
+    ArchiveType type;
+#ifndef HAVE_MKDTEMP
+    mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+#endif
+
+    if ((type = archive_get_type(filename)) <= ARCHIVE_DIR)
+        return NULL;
+
+#ifdef HAVE_MKDTEMP
+    tmpdir = g_build_filename(g_get_tmp_dir(), "audacious.XXXXXXXX", NULL);
+    if (!mkdtemp(tmpdir)) {
+        g_free(tmpdir);
+        AUDDBG("Unable to load skin: Failed to create temporary "
+                  "directory: %s\n", g_strerror(errno));
+        return NULL;
+    }
+#else
+    tmpdir = g_strdup_printf("%s/audacious.%ld", g_get_tmp_dir(), (long) rand());
+    make_directory(tmpdir, mode755);
+#endif
+
+    escaped_filename = escape_shell_chars(filename);
+    cmd = archive_extract_funcs[type] (escaped_filename, tmpdir);
+    g_free(escaped_filename);
+
+    if (!cmd) {
+        AUDDBG("extraction function is NULL!\n");
+        g_free(tmpdir);
+        return NULL;
+    }
+
+    AUDDBG("Attempt to execute \"%s\"\n", cmd);
+
+    if(system(cmd) != 0)
+    {
+        AUDDBG("could not execute cmd %s\n",cmd);
+        g_free(cmd);
+        return NULL;
+    }
+    g_free(cmd);
+
+    return tmpdir;
+}
+
+
+#ifdef HAVE_FTS_H
+
+void
+del_directory(const gchar * dirname)
+{
+    gchar *const argv[2] = { (gchar *) dirname, NULL };
+    FTS *fts;
+    FTSENT *p;
+
+    fts = fts_open(argv, FTS_PHYSICAL, (gint(*)())NULL);
+    while ((p = fts_read(fts))) {
+        switch (p->fts_info) {
+        case FTS_D:
+            break;
+        case FTS_DNR:
+        case FTS_ERR:
+            break;
+        case FTS_DP:
+            rmdir(p->fts_accpath);
+            break;
+        default:
+            unlink(p->fts_accpath);
+            break;
+        }
+    }
+    fts_close(fts);
+}
+
+#else                           /* !HAVE_FTS */
+
+gboolean
+del_directory_func(const gchar * path, const gchar * basename,
+                   gpointer params)
+{
+    if (!strcmp(basename, ".") || !strcmp(path, ".."))
+        return FALSE;
+
+    if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
+        dir_foreach(path, del_directory_func, NULL, NULL);
+        rmdir(path);
+        return FALSE;
+    }
+
+    unlink(path);
+
+    return FALSE;
+}
+
+void
+del_directory(const gchar * path)
+{
+    dir_foreach(path, del_directory_func, NULL, NULL);
+    rmdir(path);
+}
+
+#endif                          /* ifdef HAVE_FTS */
+
+static void
+strip_string(GString *string)
+{
+    while (string->len > 0 && string->str[0] == ' ')
+        g_string_erase(string, 0, 1);
+
+    while (string->len > 0 && string->str[string->len - 1] == ' ')
+        g_string_erase(string, string->len - 1, 1);
+}
+
+static void
+strip_lower_string(GString *string)
+{
+    gchar *lower;
+    strip_string(string);
+
+    lower = g_ascii_strdown(string->str, -1);
+    g_free(string->str);
+    string->str = lower;
+}
+
+static void
+close_ini_file_free_value(gpointer value)
+{
+    g_free((gchar*)value);
+}
+
+static void
+close_ini_file_free_section(gpointer section)
+{
+    g_hash_table_destroy((GHashTable*)section);
+}
+
+INIFile *
+open_ini_file(const gchar *filename)
+{
+    GHashTable *ini_file = NULL;
+    GHashTable *section = NULL;
+    GString *section_name, *key_name, *value;
+    gpointer section_hash, key_hash;
+    gchar *buffer = NULL;
+    gsize off = 0;
+    gsize filesize = 0;
+
+    unsigned char x[] = { 0xff, 0xfe, 0x00 };
+
+    g_return_val_if_fail(filename, NULL);
+    aud_vfs_file_get_contents(filename, &buffer, &filesize);
+    if (buffer == NULL)
+        return NULL;
+
+    /*
+     * Convert UTF-16 into something useful. Original implementation
+     * by incomp@#audacious. Cleanups \nenolod
+     * FIXME: can't we use a GLib function for that? -- 01mf02
+     */
+    if (filesize > 2 && !memcmp(&buffer[0],&x,2))
+    {
+        gchar *outbuf = g_malloc (filesize);   /* it's safe to waste memory. */
+        guint counter;
+
+        for (counter = 2; counter < filesize; counter += 2)
+        {
+            if (!memcmp(&buffer[counter+1], &x[2], 1)) {
+                outbuf[(counter-2)/2] = buffer[counter];
+            } else {
+                g_free(buffer);
+                g_free(outbuf);
+                return NULL;
+            }
+        }
+
+        outbuf[(counter-2)/2] = '\0';
+
+        if ((filesize - 2) / 2 == (counter - 2) / 2)
+        {
+            g_free(buffer);
+            buffer = outbuf;
+        }
+        else
+        {
+            g_free(buffer);
+            g_free(outbuf);
+            return NULL;    /* XXX wrong encoding */
+        }
+    }
+
+    section_name = g_string_new("");
+    key_name = g_string_new(NULL);
+    value = g_string_new(NULL);
+
+    ini_file = g_hash_table_new_full(NULL, NULL, NULL,
+                                     close_ini_file_free_section);
+    section = g_hash_table_new_full(NULL, NULL, NULL,
+                                    close_ini_file_free_value);
+    /* make a nameless section which should store all entries that are not
+     * embedded in a section */
+    section_hash = GINT_TO_POINTER(g_string_hash(section_name));
+    g_hash_table_insert(ini_file, section_hash, section);
+
+    while (off < filesize)
+    {
+        /* ignore the following characters */
+        if (buffer[off] == '\r' || buffer[off] == '\n' ||
+            buffer[off] == ' '  || buffer[off] == '\t')
+        {
+            if (buffer[off] == '\n')
+            {
+                g_string_free(key_name, TRUE);
+                g_string_free(value, TRUE);
+                key_name = g_string_new(NULL);
+                value = g_string_new(NULL);
+            }
+
+            off++;
+            continue;
+        }
+
+        /* if we encounter a possible section statement */
+        if (buffer[off] == '[')
+        {
+            g_string_free(section_name, TRUE);
+            section_name = g_string_new(NULL);
+            off++;
+
+            if (off >= filesize)
+                goto return_sequence;
+
+            while (buffer[off] != ']')
+            {
+                /* if the section statement has not been closed before a
+                 * linebreak */
+                if (buffer[off] == '\n')
+                    break;
+
+                g_string_append_c(section_name, buffer[off]);
+                off++;
+                if (off >= filesize)
+                    goto return_sequence;
+            }
+            if (buffer[off] == '\n')
+                continue;
+            if (buffer[off] == ']')
+            {
+                off++;
+                if (off >= filesize)
+                    goto return_sequence;
+
+                strip_lower_string(section_name);
+                section_hash = GINT_TO_POINTER(g_string_hash(section_name));
+
+                /* if this section already exists, we don't make a new one,
+                 * but reuse the old one */
+                if (g_hash_table_lookup(ini_file, section_hash) != NULL)
+                    section = g_hash_table_lookup(ini_file, section_hash);
+                else
+                {
+                    section = g_hash_table_new_full(NULL, NULL, NULL,
+                                                    close_ini_file_free_value);
+                    g_hash_table_insert(ini_file, section_hash, section);
+                }
+
+                continue;
+            }
+        }
+
+        if (buffer[off] == '=')
+        {
+            off++;
+            if (off >= filesize)
+                goto return_sequence;
+
+            while (buffer[off] != '\n' && buffer[off] != '\r')
+            {
+                g_string_append_c(value, buffer[off]);
+                off++;
+                if (off >= filesize)
+                    break;
+            }
+
+            strip_lower_string(key_name);
+            key_hash = GINT_TO_POINTER(g_string_hash(key_name));
+            strip_string(value);
+
+            if (key_name->len > 0 && value->len > 0)
+                g_hash_table_insert(section, key_hash, g_strdup(value->str));
+        }
+        else
+        {
+            g_string_append_c(key_name, buffer[off]);
+            off++;
+            if (off >= filesize)
+                goto return_sequence;
+        }
+    }
+
+return_sequence:
+    g_string_free(section_name, TRUE);
+    g_string_free(key_name, TRUE);
+    g_string_free(value, TRUE);
+    g_free(buffer);
+    return ini_file;
+}
+
+/**
+ * Frees the memory allocated for inifile.
+ */
+void
+close_ini_file(INIFile *inifile)
+{
+    g_return_if_fail(inifile);
+    g_hash_table_destroy(inifile);
+}
+
+/**
+ * Returns a string that corresponds to correct section and key in inifile.
+ *
+ * Returns NULL if value was not found in inifile. Otherwise returns a copy
+ * of string pointed by "section" and "key". Returned string should be freed
+ * after use.
+ */
+gchar *
+read_ini_string(INIFile *inifile, const gchar *section, const gchar *key)
+{
+    GString *section_string;
+    GString *key_string;
+    gchar *value = NULL;
+    gpointer section_hash, key_hash;
+    GHashTable *section_table;
+    
+    g_return_val_if_fail(inifile, NULL);
+
+    section_string = g_string_new(section);
+    key_string = g_string_new(key);
+    value = NULL;
+
+    strip_lower_string(section_string);
+    strip_lower_string(key_string);
+    section_hash = GINT_TO_POINTER(g_string_hash(section_string));
+    key_hash = GINT_TO_POINTER(g_string_hash(key_string));
+    section_table = g_hash_table_lookup(inifile, section_hash);
+
+    if (section_table) {
+        value = g_strdup(g_hash_table_lookup(section_table,
+                                             GINT_TO_POINTER(key_hash)));
+    }
+
+    g_string_free(section_string, TRUE);
+    g_string_free(key_string, TRUE);
+
+    g_return_val_if_fail(value, NULL);
+    return value;
+}
+
+GArray *
+read_ini_array(INIFile *inifile, const gchar *section, const gchar *key)
+{
+    gchar *temp;
+    GArray *a;
+
+    g_return_val_if_fail((temp = read_ini_string(inifile, section, key)), NULL);
+
+    a = string_to_garray(temp);
+    g_free(temp);
+    return a;
+}
+
+GArray *
+string_to_garray(const gchar * str)
+{
+    GArray *array;
+    gint temp;
+    const gchar *ptr = str;
+    gchar *endptr;
+
+    array = g_array_new(FALSE, TRUE, sizeof(gint));
+    for (;;) {
+        temp = strtol(ptr, &endptr, 10);
+        if (ptr == endptr)
+            break;
+        g_array_append_val(array, temp);
+        ptr = endptr;
+        while (!isdigit((int) *ptr) && (*ptr) != '\0')
+            ptr++;
+        if (*ptr == '\0')
+            break;
+    }
+    return (array);
+}
+
+void
+glist_movedown(GList * list)
+{
+    gpointer temp;
+
+    if (g_list_next(list)) {
+        temp = list->data;
+        list->data = list->next->data;
+        list->next->data = temp;
+    }
+}
+
+void
+glist_moveup(GList * list)
+{
+    gpointer temp;
+
+    if (g_list_previous(list)) {
+        temp = list->data;
+        list->data = list->prev->data;
+        list->prev->data = temp;
+    }
+}
+
+GdkFont *
+util_font_load(const gchar * name)
+{
+    GdkFont *font;
+    PangoFontDescription *desc;
+
+    desc = pango_font_description_from_string(name);
+    font = gdk_font_from_description(desc);
+
+    return font;
+}
+
+/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter
+ * Mattis et al */
+gboolean
+text_get_extents(const gchar * fontname,
+                 const gchar * text,
+                 gint * width, gint * height, gint * ascent, gint * descent)
+{
+    PangoFontDescription *font_desc;
+    PangoLayout *layout;
+    PangoRectangle rect;
+
+    g_return_val_if_fail(fontname != NULL, FALSE);
+    g_return_val_if_fail(text != NULL, FALSE);
+
+    /* FIXME: resolution */
+    layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text);
+
+    font_desc = pango_font_description_from_string(fontname);
+    pango_layout_set_font_description(layout, font_desc);
+    pango_font_description_free(font_desc);
+    pango_layout_get_pixel_extents(layout, NULL, &rect);
+
+    if (width)
+        *width = rect.width;
+    if (height)
+        *height = rect.height;
+
+    if (ascent || descent) {
+        PangoLayoutIter *iter;
+        PangoLayoutLine *line;
+
+        iter = pango_layout_get_iter(layout);
+        line = pango_layout_iter_get_line(iter);
+        pango_layout_iter_free(iter);
+
+        pango_layout_line_get_pixel_extents(line, NULL, &rect);
+
+        if (ascent)
+            *ascent = PANGO_ASCENT(rect);
+        if (descent)
+            *descent = -PANGO_DESCENT(rect);
+    }
+
+    g_object_unref(layout);
+
+    return TRUE;
+}
+
+/* counts number of digits in a gint */
+guint
+gint_count_digits(gint n)
+{
+    guint count = 0;
+
+    n = ABS(n);
+    do {
+        count++;
+        n /= 10;
+    } while (n > 0);
+
+    return count;
+}
+
+gboolean
+dir_foreach(const gchar * path, DirForeachFunc function,
+            gpointer user_data, GError ** error)
+{
+    GError *error_out = NULL;
+    GDir *dir;
+    const gchar *entry;
+    gchar *entry_fullpath;
+
+    if (!(dir = g_dir_open(path, 0, &error_out))) {
+        g_propagate_error(error, error_out);
+        return FALSE;
+    }
+
+    while ((entry = g_dir_read_name(dir))) {
+        entry_fullpath = g_build_filename(path, entry, NULL);
+
+        if ((*function) (entry_fullpath, entry, user_data)) {
+            g_free(entry_fullpath);
+            break;
+        }
+
+        g_free(entry_fullpath);
+    }
+
+    g_dir_close(dir);
+
+    return TRUE;
+}
+
+GtkWidget *
+make_filebrowser(const gchar *title, gboolean save)
+{
+    GtkWidget *dialog;
+    GtkWidget *button;
+
+    g_return_val_if_fail(title != NULL, NULL);
+
+    dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin),
+                                         save ?
+                                         GTK_FILE_CHOOSER_ACTION_SAVE :
+                                         GTK_FILE_CHOOSER_ACTION_OPEN,
+                                         NULL, NULL);
+
+    button = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
+                                   GTK_RESPONSE_REJECT);
+
+    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+    button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ?
+                                   GTK_STOCK_SAVE : GTK_STOCK_OPEN,
+                                   GTK_RESPONSE_ACCEPT);
+
+    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */
+
+    return dialog;
+}
+
+/**
+ * util_info_dialog:
+ * @title: The title of the message to show.
+ * @text: The text of the message to show.
+ * @button_text: The text of the button which will close the messagebox.
+ * @modal: Whether or not the messagebox should be modal.
+ * @button_action: Code to execute on when the messagebox is closed, or %NULL.
+ * @action_data: Optional opaque data to pass to @button_action.
+ *
+ * Displays a message box.
+ *
+ * Return value: A GTK widget handle for the message box.
+ **/
+GtkWidget *
+util_info_dialog(const gchar * title, const gchar * text,
+                 const gchar * button_text, gboolean modal,
+                 GCallback button_action, gpointer action_data)
+{
+  GtkWidget *dialog;
+  GtkWidget *dialog_vbox, *dialog_hbox, *dialog_bbox;
+  GtkWidget *dialog_bbox_b1;
+  GtkWidget *dialog_textlabel;
+  GtkWidget *dialog_icon;
+
+  dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_type_hint( GTK_WINDOW(dialog) , GDK_WINDOW_TYPE_HINT_DIALOG );
+  gtk_window_set_modal( GTK_WINDOW(dialog) , modal );
+  gtk_window_set_title( GTK_WINDOW(dialog) , title );
+  gtk_container_set_border_width( GTK_CONTAINER(dialog) , 10 );
+
+  dialog_vbox = gtk_vbox_new( FALSE , 0 );
+  dialog_hbox = gtk_hbox_new( FALSE , 0 );
+
+  /* icon */
+  dialog_icon = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO , GTK_ICON_SIZE_DIALOG );
+  gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_icon , FALSE , FALSE , 2 );
+
+  /* label */
+  dialog_textlabel = gtk_label_new( text );
+  /* gtk_label_set_selectable( GTK_LABEL(dialog_textlabel) , TRUE ); */
+  gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_textlabel , TRUE , TRUE , 2 );
+
+  gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_hbox , FALSE , FALSE , 2 );
+  gtk_box_pack_start( GTK_BOX(dialog_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 );
+
+  dialog_bbox = gtk_hbutton_box_new();
+  gtk_button_box_set_layout( GTK_BUTTON_BOX(dialog_bbox) , GTK_BUTTONBOX_END );
+  dialog_bbox_b1 = gtk_button_new_with_label( button_text );
+  g_signal_connect_swapped( G_OBJECT(dialog_bbox_b1) , "clicked" ,
+                            G_CALLBACK(gtk_widget_destroy) , dialog );
+  if ( button_action )
+    g_signal_connect( G_OBJECT(dialog_bbox_b1) , "clicked" ,
+                      button_action , action_data );
+
+  gtk_container_add( GTK_CONTAINER(dialog_bbox) , dialog_bbox_b1 );
+  gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_bbox , FALSE , FALSE , 0 );
+
+  gtk_container_add( GTK_CONTAINER(dialog) , dialog_vbox );
+
+  GTK_WIDGET_SET_FLAGS( dialog_bbox_b1 , GTK_CAN_DEFAULT);
+  gtk_widget_grab_default( dialog_bbox_b1 );
+
+  gtk_widget_show_all(dialog);
+
+  return dialog;
+}
+
+
+/**
+ * util_get_localdir:
+ *
+ * Returns a string with the full path of Audacious local datadir (where config files are placed).
+ * It's useful in order to put in the right place custom config files for audacious plugins.
+ *
+ * Return value: a string with full path of Audacious local datadir (should be freed after use)
+ **/
+gchar*
+util_get_localdir(void)
+{
+  gchar *datadir;
+  gchar *tmp;
+
+  if ( (tmp = getenv("XDG_CONFIG_HOME")) == NULL )
+    datadir = g_build_filename( g_get_home_dir() , ".config" , "audacious" ,  NULL );
+  else
+    datadir = g_build_filename( tmp , "audacious" , NULL );
+
+  return datadir;
+}
+
+
+gchar *
+construct_uri(gchar *string, const gchar *playlist_name) // uri, path and anything else
+{
+    gchar *filename = g_strdup(string);
+    gchar *tmp, *path;
+    gchar *uri = NULL;
+
+    /* try to translate dos path */
+    convert_dos_path(filename); /* in place replacement */
+
+    // make full path uri here
+    // case 1: filename is raw full path or uri
+    if (filename[0] == '/' || strstr(filename, "://")) {
+        uri = g_filename_to_uri(filename, NULL, NULL);
+        if(!uri) {
+            uri = g_strdup(filename);
+        }
+        g_free(filename);
+    }
+    // case 2: filename is not raw full path nor uri, playlist path is full path
+    // make full path by replacing last part of playlist path with filename. (using g_build_filename)
+    else if (playlist_name[0] == '/' || strstr(playlist_name, "://")) {
+        path = g_filename_from_uri(playlist_name, NULL, NULL);
+        if (!path) {
+            path = g_strdup(playlist_name);
+        }
+        tmp = strrchr(path, '/'); *tmp = '\0';
+        tmp = g_build_filename(path, filename, NULL);
+        g_free(path); g_free(filename);
+        uri = g_filename_to_uri(tmp, NULL, NULL);
+        g_free(tmp);
+    }
+    // case 3: filename is not raw full path nor uri, playlist path is not full path
+    // just abort.
+    else {
+        g_free(filename);
+        return NULL;
+    }
+
+    AUDDBG("uri=%s\n", uri);
+    return uri;
+}
+
+/* 
+ * minimize number of realloc's:
+ *  - set N to nearest power of 2 not less then N
+ *  - double it
+ *
+ *  -- asphyx
+ *
+ * XXX: what's so smart about this?? seems wasteful and silly. --nenolod
+ */
+gpointer
+smart_realloc(gpointer ptr, gsize *size)
+{
+    *size = (size_t)pow(2, ceil(log(*size) / log(2)) + 1);
+    if (ptr != NULL) free(ptr);
+    ptr = malloc(*size);
+    return ptr;
+}
+
+void
+make_directory(const gchar * path, mode_t mode)
+{
+    if (g_mkdir_with_parents(path, mode) == 0)
+        return;
+
+    g_printerr(_("Could not create directory (%s): %s\n"), path,
+               g_strerror(errno));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/util.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,103 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2008  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team
+ *
+ *  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; under version 3 of the License.
+ *
+ *  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, see <http://www.gnu.org/licenses>.
+ *
+ *  The Audacious team does not consider modular code linking to
+ *  Audacious or using our public API to be a derived work.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef _AUDACIOUS_CORE
+# ifdef HAVE_CONFIG_H
+#  include "config.h"
+# endif
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#include "audacious/plugin.h"
+#include "libSAD/libSAD.h"
+
+#define SWAP(a, b)      { a^=b; b^=a; a^=b; }
+
+typedef gboolean(*DirForeachFunc) (const gchar * path,
+                                   const gchar * basename,
+                                   gpointer user_data);
+
+
+gchar *find_file_recursively(const gchar * dirname, const gchar * file);
+gchar *find_path_recursively(const gchar * dirname, const gchar * file);
+void del_directory(const gchar * dirname);
+gboolean dir_foreach(const gchar * path, DirForeachFunc function,
+                     gpointer user_data, GError ** error);
+
+
+INIFile *open_ini_file(const gchar *filename);
+void close_ini_file(INIFile *key_file);
+gchar *read_ini_string(INIFile *key_file, const gchar *section,
+					   const gchar *key);
+GArray *read_ini_array(INIFile *key_file, const gchar *section,
+                       const gchar *key);
+
+GArray *string_to_garray(const gchar * str);
+
+void glist_movedown(GList * list);
+void glist_moveup(GList * list);
+
+GdkFont *util_font_load(const gchar * name);
+void util_set_cursor(GtkWidget * window);
+gboolean text_get_extents(const gchar * fontname, const gchar * text,
+                          gint * width, gint * height, gint * ascent,
+                          gint * descent);
+
+gboolean file_is_archive(const gchar * filename);
+gchar *archive_decompress(const gchar * path);
+gchar *archive_basename(const gchar * path);
+
+guint gint_count_digits(gint n);
+
+
+GtkWidget *make_filebrowser(const gchar *title, gboolean save);
+
+GtkWidget *util_info_dialog(const gchar * title, const gchar * text,
+    const gchar * button_text, gboolean modal, GCallback button_action,
+    gpointer action_data); 
+
+GdkPixbuf *audacious_create_colorized_pixbuf(GdkPixbuf *src, gint red, gint green, gint blue);
+
+gchar *util_get_localdir(void);
+
+gchar *construct_uri(gchar *string, const gchar *playlist_name);
+
+SAD_sample_format sadfmt_from_afmt(AFormat fmt);
+
+/* minimizes number of realloc's */
+gpointer smart_realloc(gpointer ptr, gsize *size);
+
+void make_directory(const gchar * path, mode_t mode);
+
+G_END_DECLS
+
+#endif
--- a/src/sndfile/plugin.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sndfile/plugin.c	Thu May 22 19:32:39 2008 +0200
@@ -35,7 +35,6 @@
 #include <math.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 #include <audacious/output.h>
 #include "plugin.h"
--- a/src/spectrum/spectrum.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/spectrum/spectrum.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,6 @@
 #include <gtk/gtk.h>
 #include <math.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 #include "logo.xpm"
--- a/src/statusicon/si_ui.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/statusicon/si_ui.c	Thu May 22 19:32:39 2008 +0200
@@ -24,7 +24,7 @@
 #include "si_common.h"
 #include "gtktrayicon.h"
 #include <audacious/ui_fileinfopopup.h>
-#include <audacious/util.h>
+#include <audacious/plugin.h>
 #include <audacious/i18n.h>
 #include <glib.h>
 #include <gdk/gdk.h>
--- a/src/stdio/stdio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/stdio/stdio.c	Thu May 22 19:32:39 2008 +0200
@@ -18,7 +18,6 @@
 
 #include "config.h"
 #include <audacious/plugin.h>
-#include <audacious/strings.h>
 #include <stdio.h>
 
 #include <unistd.h>
--- a/src/stereo_plugin/stereo.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/stereo_plugin/stereo.c	Thu May 22 19:32:39 2008 +0200
@@ -1,7 +1,6 @@
 #include "config.h"
 #include <gtk/gtk.h>
 #include <audacious/i18n.h>
-#include <audacious/util.h>
 #include <audacious/plugin.h>
 
 
@@ -9,6 +8,7 @@
 static void about(void);
 static void configure(void);
 static int mod_samples(gpointer *d, gint length, AFormat afmt, gint srate, gint nch);
+static void query_format(AFormat * fmt, gint * rate, gint * nch);
 
 
 
@@ -18,7 +18,8 @@
 	.init = init,
 	.about = about,
 	.configure = configure,
-	.mod_samples = mod_samples
+	.mod_samples = mod_samples,
+	.query_format = query_format
 };
 
 static const char *about_text = N_("Extra Stereo Plugin\n\n"
@@ -140,6 +141,17 @@
 	gtk_widget_show(conf_dialog);
 }
 
+static void query_format(AFormat * fmt, gint * rate, gint * nch)
+{
+	if (!(*fmt == FMT_S16_NE ||
+	      (*fmt == FMT_S16_LE && G_BYTE_ORDER == G_LITTLE_ENDIAN) ||
+	      (*fmt == FMT_S16_BE && G_BYTE_ORDER == G_BIG_ENDIAN)))
+		*fmt = FMT_S16_NE;
+
+	if (*nch != 2)
+		*nch = 2;
+}
+
 static int mod_samples(gpointer *d, gint length, AFormat afmt, gint srate, gint nch)
 {
 	gint i;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/Makefile	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,14 @@
+PLUGIN = streambrowser${PLUGIN_SUFFIX}
+
+SRCS = streambrowser.c	\
+       streamdir.c \
+       shoutcast.c
+
+include ../../buildsys.mk
+include ../../extra.mk
+
+plugindir := ${plugindir}/${INPUT_PLUGIN_DIR}
+
+CFLAGS += ${PLUGIN_CFLAGS}
+CPPFLAGS += ${PLUGIN_CPPFLAGS} ${MOWGLI_CFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS} ${XML_CPPFLAGS} ${ARCH_DEFINES} -I../..
+LIBS += ${GTK_LIBS} ${GLIB_LIBS} ${MOWGLI_LIBS} ${XML_LIBS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/shoutcast.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,101 @@
+
+#include <string.h>
+#include <glib.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "streambrowser.h"
+#include "shoutcast.h"
+
+
+static streaminfo_t*		shoutcast_streaminfo_fetch(gchar *station_name, gchar *station_id);
+static category_t*		shoutcast_category_fetch(gchar *category_name);
+
+
+static streaminfo_t *shoutcast_streaminfo_fetch(gchar *station_name, gchar *station_id)
+{
+	gchar url[DEF_STRING_LEN];
+	g_snprintf(url, DEF_STRING_LEN, SHOUTCAST_STREAMINFO_URL, station_id);
+
+	streaminfo_t *streaminfo = streaminfo_new(station_name, url);
+
+	// todo: read the .pls file fetched from the above url
+	
+	return streaminfo;
+}
+
+static category_t *shoutcast_category_fetch(gchar *category_name)
+{
+	category_t *category = category_new(category_name);
+
+	gchar url[DEF_STRING_LEN];
+	g_snprintf(url, DEF_STRING_LEN, SHOUTCAST_CATEGORY_URL, category_name);
+
+	xmlDoc *doc = xmlReadFile(url, NULL, 0);
+	if (doc == NULL) {
+		error("    shoutcast: failed to read \"%s\" category file\n", category_name);
+		return NULL;
+	}
+	
+	debug("    shoutcast: category file fetched\n");
+
+	xmlNode *root_node = xmlDocGetRootElement(doc);
+	xmlNode *node;
+	
+	root_node = root_node->children;
+
+	for (node = root_node; node != NULL; node = node->next) {
+		if (node->type == XML_ELEMENT_NODE && !strcmp((char *) node->name, "station")) {
+			gchar *station_name = (gchar*) xmlGetProp(node, (xmlChar *) "name");
+			gchar *station_id = (gchar*) xmlGetProp(node, (xmlChar *) "id");
+
+			debug("        shoutcast: fetching stream info for name = \"%s\" and id = %s\n", station_name, station_id);
+
+			streaminfo_t *streaminfo = shoutcast_streaminfo_fetch(station_name, station_id);
+			streaminfo_add(category, streaminfo);
+	
+			// todo: debug - print info about streaminfon urls		
+			debug("        shoutcast: stream info added for name = \"%s\" and id = %s\n", station_name, station_id);
+		}
+	}
+	
+	return category;
+}
+
+
+streamdir_t* shoutcast_streamdir_fetch()
+{
+	streamdir_t *streamdir = streamdir_new("Shoutcast");
+	
+	debug("shoutcast: fetching streaming directory file\n");
+
+	// todo: replace dummy filename with SHOUTCAST_DIRECTORY_URL
+	xmlDoc *doc = xmlReadFile("shoutcast.xml", NULL, 0);
+	if (doc == NULL) {
+		error("shoutcast: failed to read stream directory file\n");
+		return NULL;
+	}
+	
+	debug("shoutcast: streaming directory file fetched\n");
+
+	xmlNode *root_node = xmlDocGetRootElement(doc);
+	xmlNode *node;
+	
+	root_node = root_node->children;
+
+	for (node = root_node; node != NULL; node = node->next) {
+		if (node->type == XML_ELEMENT_NODE) {
+			gchar *category_name = (gchar*) xmlGetProp(node, (xmlChar *) "name");
+			
+			debug("    shoutcast: fetching category \"%s\"\n", category_name);
+
+			category_t *category = shoutcast_category_fetch(category_name);
+			category_add(streamdir, category);
+			
+			debug("    shoutcast: added category \"%s\"\n", category_name);
+		}
+	}
+	
+	exit(0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/shoutcast.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,17 @@
+
+#ifndef SHOUTCAST_H
+#define SHOUTCAST_H
+
+#include "streambrowser.h"
+#include "streamdir.h"
+
+#define SHOUTCAST_STREAMDIR_URL		"http://www.shoutcast.com/sbin/newxml.phtml"
+#define SHOUTCAST_CATEGORY_URL		"http://www.shoutcast.com/sbin/newxml.phtml?genre=%s"
+#define SHOUTCAST_STREAMINFO_URL	"http://www.shoutcast.com/sbin/shoutcast-playlist.pls?rn=%s&file=filename.pls"
+
+
+streamdir_t*			shoutcast_streamdir_fetch();
+
+
+#endif	// SHOUTCAST_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/shoutcast.xml	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding='UTF-8' standalone="yes"?>
+<genrelist>
+<genre name="24h"></genre>
+<genre name="Acid"></genre>
+<genre name="Adult"></genre>
+<genre name="Adulto"></genre>
+<genre name="African"></genre>
+<genre name="Afrikaans"></genre>
+<genre name="Afro"></genre>
+<genre name="Zouk"></genre>
+</genrelist>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/streambrowser.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,100 @@
+
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <audacious/plugin.h>
+#include <audacious/ui_plugin_menu.h>
+
+#include "streambrowser.h"
+#include "streamdir.h"
+#include "shoutcast.h"
+
+
+static void			sb_init();
+static void 			sb_about();
+static void 			sb_configure();
+static void 			sb_cleanup();
+
+static void 			menu_click();
+static void 			add_plugin_services_menu_item();
+
+
+static GeneralPlugin sb_plugin = 
+{
+	.description = "Stream Browser",
+	.init = sb_init,
+	.about = sb_about,
+	.configure = sb_configure,
+	.cleanup = sb_cleanup
+};
+
+GeneralPlugin *sb_gplist[] = 
+{
+	&sb_plugin,
+	NULL
+};
+
+SIMPLE_GENERAL_PLUGIN(streambrowser, sb_gplist);
+
+
+void debug(const char *fmt, ...)
+{
+	// todo: replace with config->debug
+	if (TRUE) {
+		va_list ap;
+		fprintf(stderr, "* streambrowser: ");
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+	}
+}
+
+void error(const char *fmt, ...)
+{
+	va_list ap;
+	fprintf(stderr, "! streambrowser: ");
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+}
+
+
+static void sb_init()
+{
+	debug("sb_init()\n");
+
+	//shoutcast_streamdir_fetch();	
+}
+
+static void sb_about()
+{
+	debug("sb_about()\n");
+}
+
+static void sb_configure()
+{
+	debug("sb_configure()\n");
+}
+
+static void sb_cleanup()
+{
+	debug("sb_cleanup()\n");
+}
+
+static void menu_click()
+{
+	debug("menu_click()\n");
+}
+
+static void add_plugin_services_menu_item()
+{
+	/*
+	GtkWidget *menu_item;
+
+	menu_item = gtk_image_menu_item_new_with_label("SB Test");
+	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), gtk_image_new_from_stock(GTK_STOCK_CDROM, GTK_ICON_SIZE_MENU));
+	gtk_widget_show(menu_item);
+	audacious_menu_plugin_item_add(AUDACIOUS_MENU_PLAYLIST_RCLICK, menu_item);
+	g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(menu_click), NULL);
+	*/
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/streambrowser.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,13 @@
+
+#ifndef STREAMBROWSER_H
+#define STREAMBROWSER_H
+
+#define DEF_STRING_LEN			256
+
+
+void				debug(const char *fmt, ...);
+void				error(const char *fmt, ...);
+
+
+#endif	// STREAMBROWSER_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/streamdir.c	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,138 @@
+
+#include <string.h>
+#include <glib.h>
+
+#include "streambrowser.h"
+#include "streamdir.h"
+
+
+streamdir_t* streamdir_new(gchar *name)
+{
+	streamdir_t *streamdir = (streamdir_t*) g_malloc(sizeof(streamdir_t));
+	strncpy(streamdir->name, name, DEF_STRING_LEN);
+	streamdir->category_list = NULL;
+	
+	return streamdir;
+}
+
+void streamdir_delete(streamdir_t *streamdir)
+{
+	GList *iterator;
+	category_t *category;
+
+	for (iterator = g_list_first(streamdir->category_list); iterator != NULL; iterator = g_list_next(streamdir->category_list)) {
+		category = iterator->data;		
+		category_delete(category);
+	}
+
+	g_list_free(streamdir->category_list);
+	g_free(streamdir);
+}
+
+
+category_t* category_new(gchar *name)
+{
+	category_t *category = (category_t*) g_malloc(sizeof(category_t));
+	strncpy(category->name, name, DEF_STRING_LEN);
+	category->streaminfo_list = NULL;
+	
+	return category;
+}
+
+void category_delete(category_t *category)
+{
+	GList *iterator;
+	streaminfo_t *streaminfo;
+	
+	for (iterator = g_list_first(category->streaminfo_list); iterator != NULL; iterator = g_list_next(category->streaminfo_list)) {
+		streaminfo = iterator->data;
+		streaminfo_delete(streaminfo);
+	}
+
+	g_list_free(category->streaminfo_list);
+	g_free(category);
+}
+
+void category_add(streamdir_t *streamdir, category_t *category)
+{
+	streamdir->category_list = g_list_append(streamdir->category_list, category);
+}
+
+void category_remove(streamdir_t *streamdir, category_t *category)
+{
+	streamdir->category_list = g_list_remove(streamdir->category_list, category);
+}
+
+category_t* category_get_by_index(streamdir_t *streamdir, gint index)
+{
+	return (category_t*) g_list_nth_data(streamdir->category_list, index);
+}
+
+category_t* category_get_by_name(streamdir_t *streamdir, gchar *name)
+{
+	GList *iterator;
+	category_t *category;
+	
+	for (iterator = g_list_first(streamdir->category_list); iterator != NULL; iterator = g_list_next(streamdir->category_list)) {
+		category = iterator->data;
+		if (!strncasecmp(category->name, name, DEF_STRING_LEN))
+			return category;
+	}
+	
+	return NULL;
+}
+
+gint category_get_count(streamdir_t *streamdir)
+{
+	return g_list_length(streamdir->category_list);
+}
+
+
+streaminfo_t* streaminfo_new(gchar *name, gchar *url)
+{
+	streaminfo_t *streaminfo = (streaminfo_t*) g_malloc(sizeof(streaminfo_t));
+	strncpy(streaminfo->name, name, DEF_STRING_LEN);
+	strncpy(streaminfo->url, url, DEF_STRING_LEN);
+	
+	return streaminfo;
+}
+
+void streaminfo_delete(streaminfo_t *streaminfo)
+{
+	g_free(streaminfo);
+}
+
+void streaminfo_add(category_t *category, streaminfo_t *streaminfo)
+{
+	category->streaminfo_list = g_list_append(category->streaminfo_list, streaminfo);
+}
+
+void streaminfo_remove(category_t *category, streaminfo_t *streaminfo)
+{
+	category->streaminfo_list = g_list_remove(category->streaminfo_list, streaminfo);
+}
+
+streaminfo_t* streaminfo_get_by_index(category_t *category, gint index)
+{
+	return (streaminfo_t*) g_list_nth_data(category->streaminfo_list, index);
+}
+
+streaminfo_t* streaminfo_get_by_name(category_t *category, gchar *name)
+{
+	GList *iterator;
+	streaminfo_t *streaminfo;
+	
+	for (iterator = g_list_first(category->streaminfo_list); iterator != NULL; iterator = g_list_next(category->streaminfo_list)) {
+		streaminfo = iterator->data;
+		if (!strncasecmp(streaminfo->name, name, DEF_STRING_LEN))
+			return streaminfo;
+	}
+	
+	return NULL;
+}
+
+gint streaminfo_get_count(category_t *category)
+{
+	return g_list_length(category->streaminfo_list);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/streambrowser/streamdir.h	Thu May 22 19:32:39 2008 +0200
@@ -0,0 +1,55 @@
+
+#ifndef STREAMDIR_H
+#define STREAMDIR_H
+
+
+#include <glib.h>
+
+#include "streambrowser.h"
+
+
+typedef struct {
+
+	gchar			name[DEF_STRING_LEN];
+	gchar			url[DEF_STRING_LEN];
+
+} streaminfo_t;
+
+typedef struct {
+
+	gchar			name[DEF_STRING_LEN];
+	GList*			streaminfo_list;
+
+} category_t;
+
+typedef struct {
+
+	gchar			name[DEF_STRING_LEN];
+	GList*			category_list;
+
+} streamdir_t;
+
+
+streamdir_t*			streamdir_new(gchar *name);
+void				streamdir_delete(streamdir_t *streamdir);
+
+category_t*			category_new(gchar *name);
+void				category_delete(category_t *category);
+void				category_add(streamdir_t *streamdir, category_t *category);
+void				category_remove(streamdir_t *streamdir, category_t *category);
+category_t*			category_get_by_index(streamdir_t *streamdir, gint index);
+category_t*			category_get_by_name(streamdir_t *streamdir, gchar *name);
+gint				category_get_count(streamdir_t *streamdir);
+
+streaminfo_t*			streaminfo_new(gchar *name, gchar *url);
+void				streaminfo_delete(streaminfo_t *streaminfo);
+void				streaminfo_free(streaminfo_t *streaminfo);
+void				streaminfo_add(category_t *category, streaminfo_t *streaminfo);
+void				streaminfo_remove(category_t *category, streaminfo_t *streaminfo);
+streaminfo_t*			streaminfo_get_by_index(category_t *category, gint index);
+streaminfo_t*			streaminfo_get_by_name(category_t *category, gchar *name);
+gint				streaminfo_get_count(category_t *category);
+
+
+#endif	// STREAMDIR_H
+
--- a/src/sun/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sun/about.c	Thu May 22 19:32:39 2008 +0200
@@ -20,7 +20,6 @@
 #include <glib.h>
 
 #include "sun.h"
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 void sun_about(void)
--- a/src/sun/audio.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sun/audio.c	Thu May 22 19:32:39 2008 +0200
@@ -20,7 +20,6 @@
 /* FIXME: g_error() is used several places, it will exit xmms */
 
 #include <errno.h>
-#include <audacious/util.h>
 #include "sun.h"
 #include "resample.h"
 
--- a/src/sun/configure.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/sun/configure.c	Thu May 22 19:32:39 2008 +0200
@@ -22,7 +22,6 @@
 #include <errno.h>
 
 #include "sun.h"
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 #include "mixer.h"
--- a/src/timidity/xmms-timidity.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/timidity/xmms-timidity.c	Thu May 22 19:32:39 2008 +0200
@@ -19,7 +19,6 @@
 
 #include "config.h"
 
-#include <audacious/util.h>
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <string.h>
--- a/src/tonegen/tonegen.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/tonegen/tonegen.c	Thu May 22 19:32:39 2008 +0200
@@ -20,7 +20,6 @@
 #include "config.h"
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 #include <glib.h>
 #include <stdlib.h>
--- a/src/tta/libtta.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/tta/libtta.c	Thu May 22 19:32:39 2008 +0200
@@ -42,9 +42,7 @@
 #include <string.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/output.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 #include <audacious/id3tag.h>
 
--- a/src/voice_removal/voice_removal.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/voice_removal/voice_removal.c	Thu May 22 19:32:39 2008 +0200
@@ -28,16 +28,29 @@
 
 static int apply_effect (gpointer *d, gint length, AFormat afmt,
 			gint srate, gint nch);
+static void query_format (AFormat *fmt, gint *rate, gint *nch);
 
 static EffectPlugin xmms_plugin = {
 	.description = "Voice Removal Plugin",
 	.mod_samples = apply_effect,
+	.query_format = query_format
 };
 
 EffectPlugin *voice_eplist[] = { &xmms_plugin, NULL };
 
 DECLARE_PLUGIN(voice_removal, NULL, NULL, NULL, NULL, voice_eplist, NULL, NULL, NULL);
 
+static void query_format (AFormat *fmt, gint *rate, gint *nch)
+{
+	if (!((*fmt == FMT_S16_NE) ||
+		(*fmt == FMT_S16_LE && G_BYTE_ORDER == G_LITTLE_ENDIAN) ||
+		(*fmt == FMT_S16_BE && G_BYTE_ORDER == G_BIG_ENDIAN)))
+		*fmt = FMT_S16_NE;
+
+	if (*nch != 2)
+		*nch = 2;
+}
+
 static int apply_effect (gpointer *d, gint length, AFormat afmt,
 			gint srate, gint nch) {
 	int x;
--- a/src/vorbis/configure.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vorbis/configure.c	Thu May 22 19:32:39 2008 +0200
@@ -8,7 +8,6 @@
 #include <string.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 extern GMutex *vf_mutex;
--- a/src/vorbis/vcupdate.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vorbis/vcupdate.c	Thu May 22 19:32:39 2008 +0200
@@ -33,8 +33,6 @@
 #include <unistd.h>
 
 #include <audacious/plugin.h>
-#include <audacious/strings.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 
 #include <mowgli.h>
--- a/src/vorbis/vorbis.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vorbis/vorbis.c	Thu May 22 19:32:39 2008 +0200
@@ -54,9 +54,7 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
-#include <audacious/strings.h>
 
 #include "vorbis.h"
 
--- a/src/vtx/about.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vtx/about.c	Thu May 22 19:32:39 2008 +0200
@@ -1,7 +1,5 @@
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 
 #include <gtk/gtk.h>
--- a/src/vtx/info.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vtx/info.c	Thu May 22 19:32:39 2008 +0200
@@ -1,8 +1,6 @@
 #include "ayemu.h"
 #include "vtx.h"
-#include <audacious/util.h>
 #include <audacious/output.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 
 void vtx_file_info(gchar *filename)
--- a/src/vtx/vtx.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/vtx/vtx.c	Thu May 22 19:32:39 2008 +0200
@@ -19,8 +19,6 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 
 #include "vtx.h"
--- a/src/wavpack/libwavpack.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/wavpack/libwavpack.cxx	Thu May 22 19:32:39 2008 +0200
@@ -11,7 +11,6 @@
 #include <wavpack/wavpack.h>
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
 }
 #include <glib.h>
 #include <gtk/gtk.h>
--- a/src/wavpack/tags.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/wavpack/tags.cxx	Thu May 22 19:32:39 2008 +0200
@@ -4,7 +4,6 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <wchar.h>
-#include <audacious/util.h>
 #include <audacious/plugin.h>
 #include "tags.h"
 
--- a/src/wavpack/ui.cxx	Thu May 22 19:31:48 2008 +0200
+++ b/src/wavpack/ui.cxx	Thu May 22 19:32:39 2008 +0200
@@ -9,7 +9,6 @@
 extern "C" {
 #include <wavpack/wavpack.h>
 #include <audacious/plugin.h>
-#include <audacious/util.h>
 #include <audacious/i18n.h>
 }
 #include <glib.h>
--- a/src/wma/wma.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/wma/wma.c	Thu May 22 19:32:39 2008 +0200
@@ -37,8 +37,6 @@
 
 #include <audacious/plugin.h>
 #include <audacious/output.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 #include <audacious/i18n.h>
 
 #include "avcodec.h"
--- a/src/xspf/xspf.c	Thu May 22 19:31:48 2008 +0200
+++ b/src/xspf/xspf.c	Thu May 22 19:32:39 2008 +0200
@@ -34,8 +34,6 @@
 #include <sys/errno.h>
 
 #include <audacious/plugin.h>
-#include <audacious/util.h>
-#include <audacious/strings.h>
 
 #include <libxml/tree.h>
 #include <libxml/parser.h>