changeset 2647:5808b64d3f68

branch merge
author Paula Stanciu <paula.stanciu@gmail.com>
date Sun, 25 May 2008 21:29:48 +0300
parents 7fbff3287a56 (current diff) 45ef6d7c0174 (diff)
children 6cc858a726b8
files src/alsa/init.c
diffstat 47 files changed, 4538 insertions(+), 551 deletions(-) [+]
line wrap: on
line diff
--- a/src/alsa/Makefile	Sun May 25 21:29:07 2008 +0300
+++ b/src/alsa/Makefile	Sun May 25 21:29:48 2008 +0300
@@ -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/alsa.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/alsa/alsa.c	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:07 2008 +0300
+++ b/src/alsa/alsa.h	Sun May 25 21:29:48 2008 +0300
@@ -35,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;
 
@@ -59,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	Sun May 25 21:29:07 2008 +0300
+++ b/src/alsa/audio.c	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:07 2008 +0300
+++ b/src/alsa/configure.c	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:07 2008 +0300
+++ /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/modplug/archive/arch_bz2.cxx	Sun May 25 21:29:07 2008 +0300
+++ b/src/modplug/archive/arch_bz2.cxx	Sun May 25 21:29:48 2008 +0300
@@ -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/sndmix.cxx	Sun May 25 21:29:07 2008 +0300
+++ b/src/modplug/sndmix.cxx	Sun May 25 21:29:48 2008 +0300
@@ -275,75 +275,77 @@
 //---------------------------
 {
 	if (++m_nTickCount >= m_nMusicSpeed * (m_nPatternDelay+1) + m_nFrameDelay)
-        {
+	{
 		m_nPatternDelay = 0;
 		m_nFrameDelay = 0;
 		m_nTickCount = 0;
 		m_nRow = m_nNextRow;
-		
 		// Reset Pattern Loop Effect
-		if (m_nCurrentPattern != m_nNextPattern) {
-			if (m_nLockedPattern < MAX_ORDERS) {
-				m_nCurrentPattern = m_nLockedPattern;
-				if (!(m_dwSongFlags & SONG_ORDERLOCKED))
-					m_nLockedPattern = MAX_ORDERS;
-			} else {
-				m_nCurrentPattern = m_nNextPattern;
-			}
-
-			// Check if pattern is valid
-			if (!(m_dwSongFlags & SONG_PATTERNLOOP))
+		if (m_nCurrentPattern != m_nNextPattern) m_nCurrentPattern = m_nNextPattern;
+		// Check if pattern is valid
+		if (!(m_dwSongFlags & SONG_PATTERNLOOP))
+		{
+			m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF;
+			if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE;
+			while (m_nPattern >= MAX_PATTERNS)
 			{
+				// End of song ?
+				if ((m_nPattern == 0xFF) || (m_nCurrentPattern >= MAX_ORDERS))
+				{
+					//if (!m_nRepeatCount)
+						return FALSE;     //never repeat entire song
+					if (!m_nRestartPos)
+					{
+						m_nMusicSpeed = m_nDefaultSpeed;
+						m_nMusicTempo = m_nDefaultTempo;
+						m_nGlobalVolume = m_nDefaultGlobalVolume;
+						for (UINT i=0; i<MAX_CHANNELS; i++)
+						{
+							Chn[i].dwFlags |= CHN_NOTEFADE | CHN_KEYOFF;
+							Chn[i].nFadeOutVol = 0;
+							if (i < m_nChannels)
+							{
+								Chn[i].nGlobalVol = ChnSettings[i].nVolume;
+								Chn[i].nVolume = ChnSettings[i].nVolume;
+								Chn[i].nPan = ChnSettings[i].nPan;
+								Chn[i].nPanSwing = Chn[i].nVolSwing = 0;
+								Chn[i].nOldVolParam = 0;
+								Chn[i].nOldOffset = 0;
+								Chn[i].nOldHiOffset = 0;
+								Chn[i].nPortamentoDest = 0;
+								if (!Chn[i].nLength)
+								{
+									Chn[i].dwFlags = ChnSettings[i].dwFlags;
+									Chn[i].nLoopStart = 0;
+									Chn[i].nLoopEnd = 0;
+									Chn[i].pHeader = NULL;
+									Chn[i].pSample = NULL;
+									Chn[i].pInstrument = NULL;
+								}
+							}
+						}
+					}
+//					if (m_nRepeatCount > 0) m_nRepeatCount--;
+					m_nCurrentPattern = m_nRestartPos;
+					m_nRow = 0;
+					if ((Order[m_nCurrentPattern] >= MAX_PATTERNS) || (!Patterns[Order[m_nCurrentPattern]])) return FALSE;
+				} else
+				{
+					m_nCurrentPattern++;
+				}
 				m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF;
 				if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE;
-				while (m_nPattern >= MAX_PATTERNS)
-				{
-					// End of song ?
-					if ((m_nPattern == 0xFF) || (m_nCurrentPattern >= MAX_ORDERS))
-					{
-						if (m_nRepeatCount > 0) m_nRepeatCount--;
-						if (!m_nRepeatCount) return FALSE;
-						m_nCurrentPattern = m_nRestartPos;
-						if ((Order[m_nCurrentPattern] >= MAX_PATTERNS)
-						    || (!Patterns[Order[m_nCurrentPattern]]))
-							return FALSE;
-					} else {
-						m_nCurrentPattern++;
-					}
-					m_nPattern = (m_nCurrentPattern < MAX_ORDERS) ? Order[m_nCurrentPattern] : 0xFF;
-					if ((m_nPattern < MAX_PATTERNS) && (!Patterns[m_nPattern])) m_nPattern = 0xFE;
-				}
-				m_nNextPattern = m_nCurrentPattern;
-			} else if (m_nCurrentPattern < 255) {
-				if (m_nRepeatCount > 0) m_nRepeatCount--;
-				if (!m_nRepeatCount) return FALSE;
 			}
-		}
-#ifdef MODPLUG_TRACKER
-		if (m_dwSongFlags & SONG_STEP)
-		{
-			m_dwSongFlags &= ~SONG_STEP;
-			m_dwSongFlags |= SONG_PAUSED;
-		}
-#endif // MODPLUG_TRACKER
-		if (!PatternSize[m_nPattern] || !Patterns[m_nPattern]) {
-			/* okay, this is wrong. allocate the pattern _NOW_ */
-			Patterns[m_nPattern] = AllocatePattern(64,64);
-			PatternSize[m_nPattern] = 64;
-			PatternAllocSize[m_nPattern] = 64;
+			m_nNextPattern = m_nCurrentPattern;
 		}
 		// Weird stuff?
-		if (m_nPattern >= MAX_PATTERNS) return FALSE;
+		if ((m_nPattern >= MAX_PATTERNS) || (!Patterns[m_nPattern])) return FALSE;
 		// Should never happen
-		// ... sure it should: suppose there's a C70 effect before a 64-row pattern.
-		// It's in fact very easy to make this happen ;)
-		//       - chisel
 		if (m_nRow >= PatternSize[m_nPattern]) m_nRow = 0;
-                m_nNextRow = m_nRow + 1;
+		m_nNextRow = m_nRow + 1;
 		if (m_nNextRow >= PatternSize[m_nPattern])
 		{
 			if (!(m_dwSongFlags & SONG_PATTERNLOOP)) m_nNextPattern = m_nCurrentPattern + 1;
-			else if (m_nRepeatCount > 0) return FALSE;
 			m_nNextRow = 0;
 		}
 		// Reset channel values
@@ -351,18 +353,7 @@
 		MODCOMMAND *m = Patterns[m_nPattern] + m_nRow * m_nChannels;
 		for (UINT nChn=0; nChn<m_nChannels; pChn++, nChn++, m++)
 		{
-			/* skip realtime copyin */
-			if (pChn->nRealtime) continue;
-
-			// this is where we're going to spit out our midi
-			// commands... ALL WE DO is dump raw midi data to
-			// our super-secret "midi buffer"
-			// -mrsb
-			if (_midi_out_note)
-				_midi_out_note(nChn, m);
-
 			pChn->nRowNote = m->note;
-			if (m->instr) pChn->nLastInstr = m->instr;
 			pChn->nRowInstr = m->instr;
 			pChn->nRowVolCmd = m->volcmd;
 			pChn->nRowVolume = m->vol;
@@ -374,14 +365,6 @@
 			pChn->dwFlags &= ~(CHN_PORTAMENTO | CHN_VIBRATO | CHN_TREMOLO | CHN_PANBRELLO);
 			pChn->nCommand = 0;
 		}
-				
-	} else if (_midi_out_note) {
-		MODCOMMAND *m = Patterns[m_nPattern] + m_nRow * m_nChannels;
-		for (UINT nChn=0; nChn<m_nChannels; nChn++, m++)
-		{
-			/* m==NULL allows schism to receive notification of SDx and Scx commands */
-			_midi_out_note(nChn, 0);
-		}
 	}
 	// Should we process tick0 effects?
 	if (!m_nMusicSpeed) m_nMusicSpeed = 1;
--- a/src/neon/neon.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/neon/neon.c	Sun May 25 21:29:48 2008 +0300
@@ -1300,7 +1300,7 @@
 	_LEAVE -1;
     }
 
-    if (newpos > content_length) {
+    if (newpos >= content_length) {
         _ERROR("<%p> Can not seek beyond end of stream", h);
         _LEAVE -1;
     }
--- a/src/sid/xs_config.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_config.c	Sun May 25 21:29:48 2008 +0300
@@ -84,9 +84,9 @@
  * Configuration specific stuff
  */
 XS_MUTEX(xs_cfg);
-struct t_xs_cfg xs_cfg;
+struct xs_cfg_t xs_cfg;
 
-static t_xs_cfg_item xs_cfgtable[] = {
+static xs_cfg_item_t xs_cfgtable[] = {
 { CTYPE_INT,    &xs_cfg.audioBitsPerSample,     "audioBitsPerSample" },
 { CTYPE_INT,    &xs_cfg.audioChannels,          "audioChannels" },
 { CTYPE_INT,    &xs_cfg.audioFrequency,         "audioFrequency" },
@@ -135,10 +135,10 @@
 { CTYPE_INT,    &xs_cfg.subAutoMinTime,         "subAutoMinTime" },
 };
 
-static const gint xs_cfgtable_max = (sizeof(xs_cfgtable) / sizeof(t_xs_cfg_item));
+static const gint xs_cfgtable_max = (sizeof(xs_cfgtable) / sizeof(xs_cfgtable[0]));
 
 
-static t_xs_wid_item xs_widtable[] = {
+static xs_wid_item_t 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 },
@@ -199,7 +199,7 @@
 { 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));
+static const gint xs_widtable_max = (sizeof(xs_widtable) / sizeof(xs_widtable[0]));
 
 
 /* Reset/initialize the configuration
@@ -304,7 +304,7 @@
  */
 #define XS_FITEM (4 * 2)
 
-static gboolean xs_filter_load_into(XS_CONFIG_FILE *cfg, gint nFilter, t_xs_sid2_filter *pResult)
+static gboolean xs_filter_load_into(XS_CONFIG_FILE *cfg, gint nFilter, xs_sid2_filter_t *pResult)
 {
     gchar tmpKey[64], *tmpStr;
     gint i, j;
@@ -339,12 +339,12 @@
 }
 
 
-static t_xs_sid2_filter * xs_filter_load(XS_CONFIG_FILE *cfg, gint nFilter)
+static xs_sid2_filter_t * xs_filter_load(XS_CONFIG_FILE *cfg, gint nFilter)
 {
-    t_xs_sid2_filter *pResult;
+    xs_sid2_filter_t *pResult;
     
     /* Allocate filter struct */
-    if ((pResult = g_malloc0(sizeof(t_xs_sid2_filter))) == NULL)
+    if ((pResult = g_malloc0(sizeof(xs_sid2_filter_t))) == NULL)
         return NULL;
     
     if (!xs_filter_load_into(cfg, nFilter, pResult)) {
@@ -355,7 +355,7 @@
 }
 
 #if 0
-static gboolean xs_filter_save(XS_CONFIG_FILE *cfg, t_xs_sid2_filter *pFilter, gint nFilter)
+static gboolean xs_filter_save(XS_CONFIG_FILE *cfg, xs_sid2_filter_t *pFilter, gint nFilter)
 {
     gchar *tmpValue, tmpKey[64];
     gint i, j;
@@ -402,14 +402,14 @@
     return (inLine[*linePos] == sep);
 }
 
-static gboolean xs_filters_import(const gchar *pcFilename, t_xs_sid2_filter **pFilters, gint *nFilters)
+static gboolean xs_filters_import(const gchar *pcFilename, xs_sid2_filter_t **pFilters, gint *nFilters)
 {
     FILE *inFile;
     gchar inLine[XS_BUF_SIZE], tmpStr[XS_BUF_SIZE];
     gchar *sectName = NULL;
     gboolean sectBegin;
     size_t lineNum, i;
-    t_xs_sid2_filter *tmpFilter;
+    xs_sid2_filter_t *tmpFilter;
 
 fprintf(stderr, "xs_filters_import(%s)\n", pcFilename);
 
@@ -446,7 +446,7 @@
             if (sectBegin) {
                 /* Submit definition */
                 fprintf(stderr, "filter ends: %s\n", sectName);
-                if ((tmpFilter = g_malloc0(sizeof(t_xs_sid2_filter))) == NULL) {
+                if ((tmpFilter = g_malloc0(sizeof(xs_sid2_filter_t))) == NULL) {
                     fprintf(stderr, "could not allocate ..\n");
                 } else {
                     
@@ -478,10 +478,10 @@
 }
 
 
-static gboolean xs_filters_export(const gchar *pcFilename, t_xs_sid2_filter **pFilters, gint nFilters)
+static gboolean xs_filters_export(const gchar *pcFilename, xs_sid2_filter_t **pFilters, gint nFilters)
 {
     FILE *outFile;
-    t_xs_sid2_filter *f;
+    xs_sid2_filter_t *f;
     gint n;
     
     /* Open/create the file */
@@ -575,7 +575,7 @@
     xs_filter_load_into(cfg, 0, &xs_cfg.sid2Filter);
     
     if (xs_cfg.sid2NFilterPresets > 0) {
-        xs_cfg.sid2FilterPresets = g_malloc0(xs_cfg.sid2NFilterPresets * sizeof(t_xs_sid2_filter *));
+        xs_cfg.sid2FilterPresets = g_malloc0(xs_cfg.sid2NFilterPresets * sizeof(xs_sid2_filter_t *));
         if (!xs_cfg.sid2FilterPresets) {
             xs_error("Allocation of sid2FilterPresets structure failed!\n");
         } else {
@@ -896,7 +896,7 @@
 }
 
 
-void xs_cfg_sp2_filter_update(XSCurve *curve, t_xs_sid2_filter *f)
+void xs_cfg_sp2_filter_update(XSCurve *curve, xs_sid2_filter_t *f)
 {
     assert(curve);
     assert(f);
--- a/src/sid/xs_config.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_config.h	Sun May 25 21:29:48 2008 +0300
@@ -68,13 +68,14 @@
 
 
 typedef struct {
-    t_xs_int_point    points[XS_SIDPLAY2_NFPOINTS];
-    gint        npoints;
-    gchar        *name;
-} t_xs_sid2_filter;
+    gint            type;
+    xs_int_point_t  points[XS_SIDPLAY2_NFPOINTS];
+    gint            npoints;
+    gchar           *name;
+} xs_sid2_filter_t;
 
 
-extern struct t_xs_cfg {
+extern struct xs_cfg_t {
     /* General audio settings */
     gint        audioBitsPerSample;
     gint        audioChannels;
@@ -99,8 +100,8 @@
 
     gint        sid2OptLevel;       /* SIDPlay2 emulation optimization */
     gint        sid2Builder;        /* SIDPlay2 "builder" aka SID-emu */
-    t_xs_sid2_filter    sid2Filter; /* Current SIDPlay2 filter */
-    t_xs_sid2_filter    **sid2FilterPresets;
+    xs_sid2_filter_t    sid2Filter; /* Current SIDPlay2 filter */
+    xs_sid2_filter_t    **sid2FilterPresets;
     gint        sid2NFilterPresets;
     
     
@@ -158,7 +159,7 @@
     gint    itemType;   /* Type of item (CTYPE_*) */
     void    *itemData;  /* Pointer to variable */
     gchar   *itemName;  /* Name of configuration item */
-} t_xs_cfg_item;
+} xs_cfg_item_t;
 
 
 typedef struct {
@@ -167,7 +168,7 @@
     gchar   *widName;
     void    *itemData;
     gint    itemSet;
-} t_xs_wid_item;
+} xs_wid_item_t;
 
 
 /* Functions
--- a/src/sid/xs_curve.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_curve.c	Sun May 25 21:29:48 2008 +0300
@@ -252,7 +252,7 @@
     GtkStateType state;
     GtkStyle *style;
     gint i, ox = -1, oy = -1;
-    t_xs_point *p0, *p1, *p2, *p3;
+    xs_point_t *p0, *p1, *p2, *p3;
 
     if (!curve->pixmap)
         return;
@@ -624,7 +624,7 @@
 {
     if (npoints != curve->nctlpoints) {
         curve->nctlpoints = npoints;
-        curve->ctlpoints = (t_xs_point *) g_realloc(curve->ctlpoints,
+        curve->ctlpoints = (xs_point_t *) g_realloc(curve->ctlpoints,
             curve->nctlpoints * sizeof(*curve->ctlpoints));
 
         if (curve->ctlpoints == NULL)
@@ -635,14 +635,14 @@
 }
 
 
-void xs_curve_get_data(XSCurve *curve, t_xs_point ***points, gint **npoints)
+void xs_curve_get_data(XSCurve *curve, xs_point_t ***points, gint **npoints)
 {
     *points = &(curve->ctlpoints);
     *npoints = &(curve->nctlpoints);
 }
 
 
-gboolean xs_curve_set_points(XSCurve *curve, t_xs_int_point *points, gint npoints)
+gboolean xs_curve_set_points(XSCurve *curve, xs_int_point_t *points, gint npoints)
 {
     gint i;
 
@@ -669,13 +669,13 @@
 }
 
 
-gboolean xs_curve_get_points(XSCurve *curve, t_xs_int_point **points, gint *npoints)
+gboolean xs_curve_get_points(XSCurve *curve, xs_int_point_t **points, gint *npoints)
 {
     gint i, n;
     
     n = curve->nctlpoints - 4;
     
-    *points = g_malloc(n * sizeof(t_xs_int_point));
+    *points = g_malloc(n * sizeof(xs_int_point_t));
     if (*points == NULL)
         return FALSE;
     
--- a/src/sid/xs_curve.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_curve.h	Sun May 25 21:29:48 2008 +0300
@@ -8,12 +8,12 @@
 
 /* Macros for type-classing this GtkWidget/object
  */
-#define XS_TYPE_CURVE            (xs_curve_get_type())
-#define XS_CURVE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), XS_TYPE_CURVE, XSCurve))
-#define XS_CURVE_CLASS(luokka)        (G_TYPE_CHECK_CLASS_CAST ((luokka), XS_TYPE_CURVE, XSCurveClass))
-#define XS_IS_CURVE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XS_TYPE_CURVE))
-#define XS_IS_CURVE_CLASS(luokka)    (G_TYPE_CHECK_CLASS_TYPE ((luokka), XS_TYPE_CURVE))
-#define XS_CURVE_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), XS_TYPE_CURVE, XSCurveClass))
+#define XS_TYPE_CURVE               (xs_curve_get_type())
+#define XS_CURVE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), XS_TYPE_CURVE, XSCurve))
+#define XS_CURVE_CLASS(luokka)      (G_TYPE_CHECK_CLASS_CAST ((luokka), XS_TYPE_CURVE, XSCurveClass))
+#define XS_IS_CURVE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XS_TYPE_CURVE))
+#define XS_IS_CURVE_CLASS(luokka)   (G_TYPE_CHECK_CLASS_TYPE ((luokka), XS_TYPE_CURVE))
+#define XS_CURVE_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), XS_TYPE_CURVE, XSCurveClass))
 
 
 /* Structures
@@ -23,11 +23,11 @@
 
 typedef struct {
     gfloat x,y;
-} t_xs_point;
+} xs_point_t;
 
 typedef struct {
     gint x, y;
-} t_xs_int_point;
+} xs_int_point_t;
 
 struct _XSCurve {
     GtkDrawingArea graph;
@@ -42,7 +42,7 @@
 
     /* control points */
     gint nctlpoints;    /* number of control points */
-    t_xs_point *ctlpoints;    /* array of control points */
+    xs_point_t *ctlpoints;    /* array of control points */
 };
 
 struct _XSCurveClass {
@@ -57,9 +57,9 @@
                      gfloat min_x, gfloat min_y,
                      gfloat max_x, gfloat max_y);
 gboolean    xs_curve_realloc_data    (XSCurve *curve, gint npoints);
-void        xs_curve_get_data    (XSCurve *curve, t_xs_point ***points, gint **npoints);
-gboolean    xs_curve_set_points    (XSCurve *curve, t_xs_int_point *points, gint npoints);
-gboolean    xs_curve_get_points    (XSCurve *curve, t_xs_int_point **points, gint *npoints);
+void        xs_curve_get_data    (XSCurve *curve, xs_point_t ***points, gint **npoints);
+gboolean    xs_curve_set_points    (XSCurve *curve, xs_int_point_t *points, gint npoints);
+gboolean    xs_curve_get_points    (XSCurve *curve, xs_int_point_t **points, gint *npoints);
 
 G_END_DECLS
 
--- a/src/sid/xs_sidplay1.cc	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_sidplay1.cc	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:07 2008 +0300
+++ b/src/sid/xs_sidplay2.cc	Sun May 25 21:29:48 2008 +0300
@@ -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"
 
 
@@ -66,7 +94,7 @@
  */
 gboolean xs_sidplay2_probe(xs_file_t *f)
 {
-    gchar tmpBuf[4];
+    gchar tmpBuf[5];
     
     if (!f) return FALSE;
     
@@ -87,16 +115,18 @@
     gint tmpFreq, i;
     xs_sidplay2_t *myEngine;
     sid_filter_t tmpFilter;
-    t_xs_sid2_filter *f;
+    xs_sid2_filter_t *f;
     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) {
@@ -244,6 +281,7 @@
                 return FALSE;
             }
 
+#ifndef HAVE_SIDPLAY2_DISTORTION
             // FIXME FIX ME: support other configurable parameters ...
             // ... WHEN/IF resid-builder+libsidplay2 gets fixed
             rs->sampling(tmpFreq);
@@ -251,6 +289,7 @@
                 xs_error("reSID->sampling(%d) failed.\n", tmpFreq);
                 return FALSE;
             }
+#endif
             
             if (tmpFilter.points > 0)
                 rs->filter((sid_filter_t *) &tmpFilter);
@@ -266,9 +305,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 +336,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 +359,6 @@
 
 
     /* Configure rest of the emulation */
-    myEngine->currConfig.sidEmulation = myEngine->currBuilder;
     
     if (xs_cfg.forceSpeed) { 
         myEngine->currConfig.clockForced = true;
@@ -374,14 +421,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 +441,7 @@
 
     xs_sidplay2_delete(myStatus);
 
-    g_free(myEngine);
+    delete myEngine;
     myStatus->sidEngine = NULL;
 }
 
--- a/src/skins/Makefile	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/Makefile	Sun May 25 21:29:48 2008 +0300
@@ -3,6 +3,7 @@
 SRCS = plugin.c \
        skins_cfg.c \
        pixbuf_effects.c \
+       dnd.c \
        ui_skin.c \
        ui_skinned_window.c \
        ui_dock.c \
@@ -18,10 +19,15 @@
        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
@@ -29,6 +35,6 @@
 
 plugindir := ${plugindir}/${GENERAL_PLUGIN_DIR}
 
-CFLAGS += ${PLUGIN_CFLAGS}
+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}
+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/dnd.c	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -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/images/audacious_eq.xpm	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -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"
+};
--- a/src/skins/plugin.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/plugin.c	Sun May 25 21:29:48 2008 +0300
@@ -25,6 +25,8 @@
 #include "ui_skinned_window.h"
 #include "ui_manager.h"
 #include "icons-stock.h"
+#include "ui_main_evlisteners.h"
+#include "ui_playlist_evlisteners.h"
 #include <audacious/i18n.h>
 #include <libintl.h>
 
@@ -57,17 +59,27 @@
 
     init_skins(config.skin);
 
-    mainwin_real_show();
+    if (config.player_visible) mainwin_real_show();
+    if (config.equalizer_visible) equalizerwin_show(TRUE);
+    if (config.playlist_visible) playlistwin_show();
 
     return;
 }
 
 void skins_cleanup(void) {
     if (plugin_is_active == TRUE) {
+        skins_cfg_save();
+        ui_main_evlistener_dissociate();
+        ui_playlist_evlistener_dissociate();
         skins_cfg_free();
         gtk_widget_destroy(mainwin);
-        skin_free(aud_active_skin);
+        gtk_widget_destroy(equalizerwin);
+        skin_destroy(aud_active_skin);
         aud_active_skin = NULL;
+        mainwin = NULL;
+        equalizerwin = NULL;
+        playlistwin = NULL;
+        mainwin_info = NULL;
         plugin_is_active = FALSE;
     }
 
--- a/src/skins/plugin.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/plugin.h	Sun May 25 21:29:48 2008 +0300
@@ -24,10 +24,12 @@
 #include <glib.h>
 #include <audacious/plugin.h>
 #include "skins_cfg.h"
+#include "ui_main.h"
+#include "ui_equalizer.h"
+#include "ui_playlist.h"
+#include "ui_skin.h"
 
 #define PACKAGE_NAME "audacious-plugins"
-#define DATA_DIR "/usr/local/share/audacious/"
-extern GtkWidget *mainwin;
 
 void skins_init(void);
 void skins_cleanup(void);
--- a/src/skins/skins_cfg.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/skins_cfg.c	Sun May 25 21:29:48 2008 +0300
@@ -22,6 +22,8 @@
 #include "skins_cfg.h"
 #include "ui_skin.h"
 #include "ui_vis.h"
+#include "ui_main.h"
+#include "ui_playlist.h"
 #include <glib.h>
 #include <stdlib.h>
 #include <audacious/plugin.h>
@@ -35,7 +37,10 @@
     .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,
@@ -78,6 +83,13 @@
     .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,
+    .playlist_font = NULL,
+    .mainwin_font = NULL,
+    .show_filepopup_for_tuple = TRUE,
+    .filepopup_delay = 20,             /* delay until the filepopup comes up */
 };
 
 typedef struct skins_cfg_boolent_t {
@@ -112,6 +124,9 @@
     {"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},
+    {"show_filepopup_for_tuple", &config.show_filepopup_for_tuple, TRUE},
 };
 
 static gint ncfgbent = G_N_ELEMENTS(skins_boolents);
@@ -148,42 +163,64 @@
     {"colorize_g", &config.colorize_g, TRUE},
     {"colorize_b", &config.colorize_b, TRUE},
     {"snap_distance", &config.snap_distance, TRUE},
+    {"filepopup_delay", &config.filepopup_delay, TRUE},
 };
 
 static gint ncfgient = G_N_ELEMENTS(skins_numents);
 
+typedef struct skins_cfg_strent_t {
+    char const *se_vname;
+    char **se_vloc;
+    gboolean se_wrt;
+} skins_cfg_strent;
+
+static skins_cfg_strent skins_strents[] = {
+    {"playlist_font", &config.playlist_font, TRUE},
+    {"mainwin_font", &config.mainwin_font, TRUE},
+    {"skin", &config.skin, FALSE},
+};
+
+static gint ncfgsent = G_N_ELEMENTS(skins_strents);
+
 void skins_cfg_free() {
-    if (config.skin) { g_free(config.skin); config.skin = NULL; }
+    gint i;
+    for (i = 0; i < ncfgsent; ++i) {
+        if (*(skins_strents[i].se_vloc) != NULL) {
+            g_free( *(skins_strents[i].se_vloc) );
+            *(skins_strents[i].se_vloc) = 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);
+    for (i = 0; i < ncfgsent; ++i) {
+        aud_cfg_db_get_string(cfgfile, "skins",
+                              skins_strents[i].se_vname,
+                              skins_strents[i].se_vloc);
+    }
+
+    if (!config.mainwin_font)
+        config.mainwin_font = g_strdup(MAINWIN_DEFAULT_FONT);
+
+    if (!config.playlist_font)
+        config.playlist_font = g_strdup(PLAYLISTWIN_DEFAULT_FONT);
 
     if (!aud_cfg_db_get_float(cfgfile, "skins", "scale_factor", &(config.scale_factor)))
         config.scale_factor = 2.0;
@@ -192,18 +229,25 @@
 }
 
 
-void skins_cfg_save(skins_cfg_t * cfg) {
+void skins_cfg_save() {
     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);
+    if (aud_active_skin != NULL) {
+        if (aud_active_skin->path)
+            aud_cfg_db_set_string(cfgfile, "skins", "skin", aud_active_skin->path);
+        else
+            aud_cfg_db_unset_key(cfgfile, "skins", "skin");
+    }
 
     int i;
 
+    for (i = 0; i < ncfgsent; ++i) {
+        if (skins_strents[i].se_wrt)
+            aud_cfg_db_set_string(cfgfile, "skins",
+                                  skins_strents[i].se_vname,
+                                  *skins_strents[i].se_vloc);
+    }
+
     for (i = 0; i < ncfgbent; ++i)
         if (skins_boolents[i].be_wrt)
             aud_cfg_db_set_bool(cfgfile, "skins",
--- a/src/skins/skins_cfg.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/skins_cfg.h	Sun May 25 21:29:48 2008 +0300
@@ -44,7 +44,10 @@
     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;
@@ -69,6 +72,11 @@
     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;
+    gchar *playlist_font, *mainwin_font;
+    gboolean show_filepopup_for_tuple;
+    gint filepopup_delay;
 } skins_cfg_t;
 
 extern skins_cfg_t config;
--- a/src/skins/ui_equalizer.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_equalizer.c	Sun May 25 21:29:48 2008 +0300
@@ -184,14 +184,8 @@
     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
+
+    aud_hook_call("equalizer changed", NULL);
     gtk_widget_queue_draw(equalizerwin_graph);
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_hints.c	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -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 */
--- a/src/skins/ui_main.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_main.c	Sun May 25 21:29:48 2008 +0300
@@ -54,24 +54,19 @@
 #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 "dnd.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_fileopener.h"
-#include "ui_hints.h"
-#include "ui_jumptotrack.h"
 #include "ui_main_evlisteners.h"
-#include "ui_playlist.h"
-#include "ui_preferences.h"
 #include "ui_skinselector.h"
 #include "ui_urlopener.h"
 #include "util.h"
@@ -85,9 +80,7 @@
 #include "ui_skinned_menurow.h"
 #include "ui_skinned_playstatus.h"
 #include "ui_skinned_monostereo.h"
-#if 0
 #include "ui_skinned_playlist.h"
-#endif
 #include <audacious/plugin.h>
 #include "skins_cfg.h"
 
@@ -352,7 +345,11 @@
 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;
@@ -621,13 +618,10 @@
                       gint frequency,
                       gint n_channels)
 {
-#if 0
     gchar *text;
     gchar *title;
     Playlist *playlist = aud_playlist_get_active();
 
-    playback_set_sample_params(bitrate, frequency, n_channels);
-
     GDK_THREADS_ENTER();
     if (bitrate != -1) {
         bitrate /= 1000;
@@ -672,26 +666,25 @@
         g_free(text);
     }
 
-    title = playlist_get_info_text(playlist);
+    title = aud_playlist_get_info_text(playlist);
     mainwin_set_song_title(title);
     g_free(title);
     GDK_THREADS_LEAVE();
-#endif
 }
 
 void
 mainwin_clear_song_info(void)
 {
-#if 0
     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;
 
@@ -706,7 +699,7 @@
     mainwin_refresh_visible();
 
     playlistwin_hide_timer();
-#endif
+
     ui_vis_clear_data(mainwin_vis);
     ui_svis_clear_data(mainwin_svis);
 }
@@ -877,9 +870,7 @@
             aud_playlist_next(playlist);
             break;
         case GDK_KP_Insert:
-#if 0
-            ui_jump_to_track();
-#endif
+            audacious_drct_jtf_show();
             break;
         case GDK_Return:
         case GDK_KP_Enter:
@@ -893,20 +884,18 @@
             mainwin_minimize_cb();
             break;
         case GDK_Tab:
-#if 0
             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));
             }
-#endif
             break;
         case GDK_c:
             if (event->state & GDK_CONTROL_MASK) {
                 Playlist *playlist = aud_playlist_get_active();
-                gint pos = playlist_get_position(playlist);
-                gchar *title = playlist_get_songtitle(playlist, pos);
+                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);
@@ -1010,16 +999,15 @@
 
     /* FIXME: Disable display of current track length. It's not
        updated when track changes */
-#if 0
+
     label = gtk_label_new(_("Track length:"));
     gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 5);
 
-    len = aud_playlist_get_current_length() / 1000;
+    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);
-#endif
 
     bbox = gtk_hbutton_box_new();
     gtk_box_pack_start(GTK_BOX(vbox), bbox, TRUE, TRUE, 0);
@@ -1070,12 +1058,11 @@
                            guint time,
                            gpointer user_data)
 {
-#if 0
     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;
@@ -1084,8 +1071,8 @@
         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);
+        config.playlist_font = g_strconcat(decoded, strrchr(config.playlist_font, ' '), NULL);
+        ui_skinned_playlist_set_font(config.playlist_font);
         playlistwin_update_list(playlist);
 
         g_free(decoded);
@@ -1101,11 +1088,10 @@
             return;
         }
     }
-
+#endif
     aud_playlist_clear(playlist);
     aud_playlist_add_url(playlist, (gchar *) selection_data->data);
-    playback_initiate();
-#endif
+    audacious_drct_initiate();
 }
 
 static void
@@ -1122,15 +1108,14 @@
                       GtkWidget * entry)
 {
     Playlist *playlist = aud_playlist_get_active();
-#if 0
+
     const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
     if (text && *text)
     {
         aud_playlist_clear(playlist);
         aud_playlist_add_url(playlist, text);
-        playback_initiate();
+        audacious_drct_initiate();
     }
-#endif
 }
 
 static void
@@ -1294,9 +1279,7 @@
 void
 mainwin_eject_pushed(void)
 {
-#if 0
-    run_filebrowser(PLAY_BUTTON);
-#endif
+    action_play_file();
 }
 
 void
@@ -1392,24 +1375,19 @@
         audacious_drct_pause();
         return;
     }
-#if 0
-    if (playlist_get_length(aud_playlist_get_active()))
-        playback_initiate();
+
+    if (aud_playlist_get_length(aud_playlist_get_active()))
+        audacious_drct_initiate();
     else
         mainwin_eject_pushed();
-#endif
 }
 
 void
 mainwin_stop_pushed(void)
 {
-#if 0
-    ip_data.stop = TRUE;
     audacious_drct_stop();
     mainwin_clear_song_info();
     ab_position_a = ab_position_b = -1;
-    ip_data.stop = FALSE;
-#endif
 }
 
 void
@@ -1425,9 +1403,7 @@
 void
 mainwin_repeat_pushed(gboolean toggled)
 {
-#if 0
     check_set( toggleaction_group_others , "playback repeat" , toggled );
-#endif
 }
 
 void mainwin_repeat_pushed_cb(void) {
@@ -1451,12 +1427,10 @@
 void
 mainwin_pl_pushed(gboolean toggled)
 {
-#if 0
     if (toggled)
         playlistwin_show();
     else
         playlistwin_hide();
-#endif
 }
 
 gint
@@ -1729,18 +1703,14 @@
 mainwin_set_stopaftersong(gboolean stop)
 {
     aud_cfg->stopaftersong = stop;
-#if 0
     check_set(toggleaction_group_others, "stop after current song", aud_cfg->stopaftersong);
-#endif
 }
 
 void
 mainwin_set_noplaylistadvance(gboolean no_advance)
 {
     aud_cfg->no_playlist_advance = no_advance;
-#if 0
     check_set(toggleaction_group_others, "playback no playlist advance", aud_cfg->no_playlist_advance);
-#endif
 }
 
 static void
@@ -1789,21 +1759,22 @@
     Playlist *playlist = aud_playlist_get_active();
 
     switch (action) {
-#if 0
         case MAINWIN_GENERAL_PREFS:
-            show_prefs_window();
+            action_preferences();
             break;
         case MAINWIN_GENERAL_ABOUT:
-            show_about_window();
+            action_about_audacious();
             break;
-        case MAINWIN_GENERAL_PLAYFILE:
-            run_filebrowser(NO_PLAY_BUTTON);
+        case MAINWIN_GENERAL_PLAYFILE: {
+            gboolean button = FALSE; /* FALSE = NO_PLAY_BUTTON */
+            aud_hook_call("filebrowser show", &button);
             break;
+        }
         case MAINWIN_GENERAL_PLAYLOCATION:
             mainwin_show_add_url_window();
             break;
         case MAINWIN_GENERAL_FILEINFO:
-            ui_fileinfo_show_current(playlist);
+            aud_playlist_fileinfo_current(playlist);
             break;
         case MAINWIN_GENERAL_FOCUSPLWIN:
             gtk_window_present(GTK_WINDOW(playlistwin));
@@ -1817,14 +1788,12 @@
             else
                 playlistwin_hide();
             break;
-#endif
         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;
@@ -1859,9 +1828,7 @@
             mainwin_jump_to_time();
             break;
         case MAINWIN_GENERAL_JTF:
-#if 0
-            ui_jump_to_track();
-#endif
+            audacious_drct_jtf_show();
             break;
         case MAINWIN_GENERAL_EXIT:
             mainwin_quit_cb();
@@ -1952,9 +1919,7 @@
                                          UI_SKINNED_MENUROW(mainwin_menurow)->always_selected );
             break;
         case MENUROW_FILEINFOBOX:
-#if 0
-            ui_fileinfo_show_current(aud_playlist_get_active());
-#endif
+            aud_playlist_fileinfo_current(aud_playlist_get_active());
             break;
         case MENUROW_SCALE:
             gtk_toggle_action_set_active(
@@ -2236,9 +2201,7 @@
 }
 
 static void mainwin_info_double_clicked_cb(void) {
-#if 0
-    ui_fileinfo_show_current(aud_playlist_get_active());
-#endif
+    aud_playlist_fileinfo_current(aud_playlist_get_active());
 }
 
 static void
@@ -2352,7 +2315,7 @@
 
     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);
+    ui_skinned_textbox_set_xfont(mainwin_info, !config.mainwin_use_bitmapfont, config.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);
 
@@ -2399,9 +2362,8 @@
 
     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
+    g_signal_connect(mainwin_about, "clicked", G_CALLBACK(action_about_audacious), NULL);
+
     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);
@@ -2424,12 +2386,6 @@
 
     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
@@ -2462,12 +2418,12 @@
                      G_CALLBACK(mainwin_scrolled), NULL);
     g_signal_connect(mainwin, "button_release_event",
                      G_CALLBACK(mainwin_mouse_button_release), NULL);
-#if 0
+
     aud_drag_dest_set(mainwin);
 
     g_signal_connect(mainwin, "key_press_event",
                      G_CALLBACK(mainwin_keypress), NULL);
-#endif
+
     ui_main_evlistener_init();
 }
 
@@ -2497,9 +2453,9 @@
 
     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);
@@ -2616,9 +2572,7 @@
 action_autoscroll_songname( GtkToggleAction * action )
 {
     mainwin_set_title_scroll(gtk_toggle_action_get_active(action));
-#if 0
     playlistwin_set_sinfo_scroll(config.autoscroll); /* propagate scroll setting to playlistwin_sinfo */
-#endif
 }
 
 void
@@ -2656,9 +2610,7 @@
     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);
-#if 0
     hint_set_always(config.always_on_top);
-#endif
 }
 
 void
@@ -2679,10 +2631,8 @@
 void
 action_view_on_all_workspaces( GtkToggleAction * action )
 {
-#if 0
     config.sticky = gtk_toggle_action_get_active( action );
     hint_set_sticky(config.sticky);
-#endif
 }
 
 void
@@ -2700,9 +2650,7 @@
 void
 action_roll_up_playlist_editor( GtkToggleAction * action )
 {
-#if 0
     playlistwin_set_shade(gtk_toggle_action_get_active(action));
-#endif
 }
 
 void
@@ -2717,12 +2665,10 @@
 void
 action_show_playlist_editor( GtkToggleAction * action )
 {
-#if 0
     if (gtk_toggle_action_get_active(action))
         playlistwin_show();
     else
         playlistwin_hide();
-#endif
 }
 
 void
@@ -2800,17 +2746,15 @@
 void
 action_about_audacious( void )
 {
-#if 0
-    show_about_window();
-#endif
+    gboolean show = TRUE;
+    aud_hook_call("aboutwin show", &show);
 }
 
 void
 action_play_file( void )
 {
-#if 0
-    run_filebrowser(PLAY_BUTTON);
-#endif
+    gboolean button = TRUE; /* TRUE = PLAY_BUTTON */
+    aud_hook_call("filebrowser show", &button);
 }
 
 void
@@ -2861,17 +2805,13 @@
 void
 action_current_track_info( void )
 {
-#if 0
-    ui_fileinfo_show_current(aud_playlist_get_active());
-#endif
+    aud_playlist_fileinfo_current(aud_playlist_get_active());
 }
 
 void
 action_jump_to_file( void )
 {
-#if 0
-    ui_jump_to_track();
-#endif
+    audacious_drct_jtf_show();
 }
 
 void
@@ -2922,9 +2862,8 @@
 void
 action_preferences( void )
 {
-#if 0
-    show_prefs_window();
-#endif
+    gboolean show = TRUE;
+    aud_hook_call("prefswin show", &show);
 }
 
 void
--- a/src/skins/ui_main.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_main.h	Sun May 25 21:29:48 2008 +0300
@@ -98,7 +98,6 @@
 extern gboolean mainwin_moving;
 extern gboolean mainwin_focus;
 
-extern GtkWidget *mainwin_jtf;
 extern GtkWidget *mainwin_eq, *mainwin_pl;
 extern GtkWidget *mainwin_info;
 
@@ -172,6 +171,7 @@
                                 guint time,
                                 gpointer user_data);
 
+void run_no_output_device_dialog(gpointer hook_data, gpointer user_data);
 void mainwin_setup_menus(void);
 gboolean change_timer_mode_cb(GtkWidget *widget, GdkEventButton *event);
 
--- a/src/skins/ui_main_evlisteners.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_main_evlisteners.c	Sun May 25 21:29:48 2008 +0300
@@ -32,13 +32,11 @@
 #include <audacious/plugin.h>
 #if 0
 #include "ui_credits.h"
+#endif
 #include "ui_equalizer.h"
-#include "ui_fileopener.h"
-#include "ui_jumptotrack.h"
-#endif
 #include "ui_main.h"
+#include "ui_playlist.h"
 #if 0
-#include "ui_playlist.h"
 #include "ui_preferences.h"
 #endif
 #include "ui_skinned_playstatus.h"
@@ -49,6 +47,12 @@
 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
@@ -68,10 +72,7 @@
     gchar *text = (gchar *) hook_data;
 
     ui_skinned_textbox_set_text(mainwin_info, text);
-#if 0
-    playlistwin_update_list(playlist_get_active());
-#endif
-    g_free(text);
+    playlistwin_update_list(aud_playlist_get_active());
 }
 
 static void
@@ -97,36 +98,17 @@
         b = 0;
 
     mainwin_set_volume_slider(v);
-#if 0
     equalizerwin_set_volume_slider(v);
-#endif
     mainwin_set_balance_slider(b);
-#if 0
     equalizerwin_set_balance_slider(b);
-#endif
-}
-
-static void
-ui_main_evlistener_playback_initiate(gpointer hook_data, gpointer user_data)
-{
-#if 0
-    playback_initiate();
-#endif
 }
 
 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();
@@ -201,22 +183,17 @@
 ui_main_evlistener_playlist_end_reached(gpointer hook_data, gpointer user_data)
 {
     mainwin_clear_song_info();
-#if 0
-    if (cfg.stopaftersong)
+
+    if (aud_cfg->stopaftersong)
         mainwin_set_stopaftersong(FALSE);
-#endif
 }
 
 static void
 ui_main_evlistener_playlist_info_change(gpointer hook_data, gpointer user_data)
 {
-#if 0
     PlaylistEventInfoChange *msg = (PlaylistEventInfoChange *) hook_data;
 
     mainwin_set_song_info(msg->bitrate, msg->samplerate, msg->channels);
-
-    g_free(msg);
-#endif
 }
 
 static void
@@ -236,64 +213,8 @@
 static void
 ui_main_evlistener_equalizerwin_show(gpointer hook_data, gpointer user_data)
 {
-#if 0
     gboolean *show = (gboolean*)hook_data;
     equalizerwin_show(*show);
-#endif
-}
-
-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
@@ -324,31 +245,56 @@
 void
 ui_main_evlistener_init(void)
 {
-#if 0
     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 initiate", ui_main_evlistener_playback_initiate, NULL);
-#endif
     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);
-#if 0
     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);
-    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);
+
+    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);
 }
 
+void
+ui_main_evlistener_dissociate(void)
+{
+    aud_hook_dissociate("title change", ui_main_evlistener_title_change);
+    aud_hook_dissociate("hide seekbar", ui_main_evlistener_hide_seekbar);
+    aud_hook_dissociate("volume set", ui_main_evlistener_volume_change);
+    aud_hook_dissociate("playback begin", ui_main_evlistener_playback_begin);
+    aud_hook_dissociate("playback stop", ui_main_evlistener_playback_stop);
+    aud_hook_dissociate("playback pause", ui_main_evlistener_playback_pause);
+    aud_hook_dissociate("playback unpause", ui_main_evlistener_playback_unpause);
+    aud_hook_dissociate("playback seek", ui_main_evlistener_playback_seek);
+    aud_hook_dissociate("playback play file", ui_main_evlistener_playback_play_file);
+    aud_hook_dissociate("playlist end reached", ui_main_evlistener_playlist_end_reached);
+    aud_hook_dissociate("playlist info change", ui_main_evlistener_playlist_info_change);
+    aud_hook_dissociate("mainwin set always on top", ui_main_evlistener_mainwin_set_always_on_top);
+    aud_hook_dissociate("mainwin show", ui_main_evlistener_mainwin_show);
+    aud_hook_dissociate("equalizerwin show", ui_main_evlistener_equalizerwin_show);
+#if 0
+    aud_hook_dissociate("prefswin show", ui_main_evlistener_prefswin_show);
+    aud_hook_dissociate("aboutwin show", ui_main_evlistener_aboutwin_show);
+    aud_hook_dissociate("ui jump to track show", ui_main_evlistener_ui_jump_to_track_show);
+#endif
+    aud_hook_dissociate("visualization timeout", ui_main_evlistener_visualization_timeout);
+    aud_hook_dissociate("config save", ui_main_evlistener_config_save);
+
+    aud_hook_dissociate("playback audio error", (void *) mainwin_stop_pushed);
+    aud_hook_dissociate("playback audio error", (void *) run_no_output_device_dialog);
+
+    aud_hook_dissociate("playback seek", (HookFunction) mainwin_update_song_info);
+}
--- a/src/skins/ui_main_evlisteners.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_main_evlisteners.h	Sun May 25 21:29:48 2008 +0300
@@ -24,5 +24,6 @@
 #define AUDACIOUS_UI_MAIN_EVLISTENERS_H
 
 void ui_main_evlistener_init(void);
+void ui_main_evlistener_dissociate(void);
 
 #endif /* AUDACIOUS_UI_MAIN_EVLISTENERS_H */
--- a/src/skins/ui_manager.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_manager.c	Sun May 25 21:29:48 2008 +0300
@@ -202,7 +202,7 @@
 	{ "anafoff", NULL, N_("Analyzer Falloff") },
 	{ "peafoff", NULL, N_("Peaks Falloff") }
 };
-#if 0
+
 static GtkActionEntry action_entries_playlist[] = {
 
 	{ "playlist", NULL, N_("Playlist") },
@@ -376,7 +376,7 @@
 	  N_("Sorts the list by playlist entry."),
 	  G_CALLBACK(action_playlist_sort_selected_by_playlist_entry) },
 };
-#endif
+
 static GtkActionEntry action_entries_others[] = {
 
 	{ "dummy", NULL, "dummy" },
@@ -559,17 +559,17 @@
     gtk_action_group_add_actions(
     action_group_playback , action_entries_playback ,
     G_N_ELEMENTS(action_entries_playback) , NULL );
-#if 0
+
   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 );
-#endif
+
   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 );
-#if 0
+
   action_group_view = ui_manager_new_action_group("action_view");
     gtk_action_group_add_actions(
     action_group_view , action_entries_view ,
@@ -599,7 +599,7 @@
   gtk_action_group_add_actions(
     action_group_playlist_sort, action_entries_playlist_sort,
     G_N_ELEMENTS(action_entries_playlist_sort), NULL );
-#endif
+
   action_group_equalizer = ui_manager_new_action_group("action_equalizer");
   gtk_action_group_add_actions(
     action_group_equalizer, action_entries_equalizer,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist.c	Sun May 25 21:29:48 2008 +0300
@@ -0,0 +1,1940 @@
+/*  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"
+#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)
+{
+    g_return_if_fail(playlist != NULL);
+
+    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);
+}
+
+static void
+playlistwin_update_sinfo(Playlist *playlist)
+{
+    g_return_if_fail(playlist != NULL);
+
+    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);
+}
+
+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(config.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)
+{
+    action_playlist_add_files();
+}
+
+static void
+playlistwin_fileinfo(void)
+{
+    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) {
+        aud_playlist_fileinfo(playlist, GPOINTER_TO_INT(list->data));
+        g_list_free(list);
+    }
+    else
+        aud_playlist_fileinfo_current(playlist);
+}
+
+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(config.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(config.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)
+{
+    gboolean button = FALSE; /* FALSE = NO_PLAY_BUTTON */
+    aud_hook_call("filebrowser show", &button);
+}
+
+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	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -0,0 +1,64 @@
+/*
+ * 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);
+}
+
+void ui_playlist_evlistener_dissociate(void)
+{
+    aud_hook_dissociate("playlist update", ui_playlist_evlistener_playlist_update);
+    aud_hook_dissociate("playlistwin show", ui_playlist_evlistener_playlistwin_show);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_playlist_evlisteners.h	Sun May 25 21:29:48 2008 +0300
@@ -0,0 +1,27 @@
+/*
+ * 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);
+void ui_playlist_evlistener_dissociate(void);
+
+#endif /* AUDACIOUS_UI_PLAYLIST_EVLISTENERS_H */
--- a/src/skins/ui_skin.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_skin.c	Sun May 25 21:29:48 2008 +0300
@@ -42,8 +42,8 @@
 #include "util.h"
 #include "ui_main.h"
 #include "ui_equalizer.h"
+#include "ui_playlist.h"
 #if 0
-#include "ui_playlist.h"
 #include "ui_skinselector.h"
 #endif
 #include "debug.h"
@@ -184,11 +184,10 @@
 
     ui_skinned_window_draw_all(mainwin);
     ui_skinned_window_draw_all(equalizerwin);
-#if 0
     ui_skinned_window_draw_all(playlistwin);
 
-    playlistwin_update_list(playlist_get_active());
-#endif
+    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) */
@@ -407,7 +406,7 @@
 
     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);
+    pm->pixbuf = audacious_create_colorized_pixbuf(pix, config.colorize_r, config.colorize_g, config.colorize_b);
     g_object_unref(pix);
     pm->width = gdk_pixbuf_get_width(pm->pixbuf);
     pm->height = gdk_pixbuf_get_height(pm->pixbuf);
@@ -535,9 +534,7 @@
     {
         mainwin_create();
         equalizerwin_create();
-#if 0
         playlistwin_create();
-#endif
     }
 
     if (!aud_active_skin_load(path)) {
@@ -1749,7 +1746,7 @@
     pixmap = skin_get_pixmap(skin, pixmap_id);
     g_return_if_fail(pixmap != NULL);
     g_return_if_fail(pixmap->pixbuf != NULL);
-#if 0
+
     /* perhaps we should use transparency or resize widget? */
     if (xsrc+width > pixmap->width || ysrc+height > pixmap->height) {
         if (widget) {
@@ -1798,7 +1795,7 @@
         } else
             return;
     }
-#endif
+
     width = MIN(width, pixmap->width - xsrc);
     height = MIN(height, pixmap->height - ysrc);
     gdk_pixbuf_copy_area(pixmap->pixbuf, xsrc, ysrc, width, height,
@@ -2056,8 +2053,8 @@
     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);
+        GdkPixbuf *image = gdk_pixbuf_scale_simple(obj, width * config.scale_factor, height* config.scale_factor, GDK_INTERP_NEAREST);
+        gdk_draw_pixbuf(widget->window, NULL, image, 0, 0, 0, 0, width * config.scale_factor , height * config.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);
--- a/src/skins/ui_skin.h	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_skin.h	Sun May 25 21:29:48 2008 +0300
@@ -30,14 +30,8 @@
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
 
-#if 0
-#include "audconfig.h"
-
 #define BMP_DEFAULT_SKIN_PATH \
   DATA_DIR G_DIR_SEPARATOR_S "Skins" G_DIR_SEPARATOR_S "Default"
-#else
-#define BMP_DEFAULT_SKIN_PATH "/usr/local/share/audacious/Skins/Default"
-#endif
 
 typedef enum {
     SKIN_MAIN = 0,
@@ -216,6 +210,7 @@
 gboolean skin_reload_forced(void);
 void skin_reload(Skin * skin);
 void skin_free(Skin * skin);
+void skin_destroy(Skin * skin);
 
 GdkBitmap *skin_get_mask(Skin * skin, SkinMaskId mi);
 GdkColor *skin_get_color(Skin * skin, SkinColorId color_id);
--- a/src/skins/ui_skinned_equalizer_slider.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_skinned_equalizer_slider.c	Sun May 25 21:29:48 2008 +0300
@@ -22,9 +22,7 @@
  */
 
 #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"
@@ -259,9 +257,7 @@
                     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);
@@ -305,9 +301,7 @@
 
         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);
     }
 
@@ -333,9 +327,7 @@
     }
 
     priv->value = ((gfloat) (25 - priv->position) * EQUALIZER_MAX_GAIN / 25.0 );
-#if 0
     equalizerwin_eq_changed();
-#endif
     gtk_widget_queue_draw(widget);
     return TRUE;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/skins/ui_skinned_playlist.c	Sun May 25 21:29:48 2008 +0300
@@ -0,0 +1,1101 @@
+/*
+ * 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"
+#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));
+
+    GtkWidget *popup = audacious_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));
+}
+
+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 (config.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) {
+    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 = aud_playlist_get_tuple(pl_active, pos);
+        if ((tuple == NULL) || (aud_tuple_get_int(tuple, FIELD_LENGTH, NULL) < 1)) {
+           gchar *title = aud_playlist_get_songtitle(pl_active, pos);
+           audacious_fileinfopopup_show_from_title(popup, title);
+           g_free(title);
+        } else {
+           audacious_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;
+}
+
+static void ui_skinned_playlist_popup_hide(GtkWidget *widget) {
+    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));
+        audacious_fileinfopopup_hide(popup, NULL);
+    }
+}
+
+static void ui_skinned_playlist_popup_timer_start(GtkWidget *widget) {
+    gint timer_id = g_timeout_add(config.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	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -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	Sun May 25 21:29:48 2008 +0300
@@ -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 */
--- a/src/skins/ui_skinned_textbox.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_skinned_textbox.c	Sun May 25 21:29:48 2008 +0300
@@ -707,6 +707,8 @@
 
 void ui_skinned_textbox_set_scroll(GtkWidget *widget, gboolean scroll) {
     g_return_if_fail(widget != NULL);
+    g_return_if_fail(UI_SKINNED_IS_TEXTBOX(widget));
+
     UiSkinnedTextbox *textbox = UI_SKINNED_TEXTBOX(widget);
     UiSkinnedTextboxPrivate *priv = UI_SKINNED_TEXTBOX_GET_PRIVATE(textbox);
 
--- a/src/skins/ui_skinned_window.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_skinned_window.c	Sun May 25 21:29:48 2008 +0300
@@ -18,20 +18,20 @@
  * 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 "platform/smartinclude.h"
+#include "ui_skin.h"
+#include "skins_cfg.h"
 #include "ui_dock.h"
 #include "ui_skinned_window.h"
+#include "ui_main.h"
+#include "ui_playlist.h"
 
 static void ui_skinned_window_class_init(SkinnedWindowClass *klass);
 static void ui_skinned_window_init(GtkWidget *widget);
@@ -134,10 +134,8 @@
             height = 116 * (config.scaled ? config.scale_factor : 1) ;
             break;
         case WINDOW_PLAYLIST:
-#if 0
             width = playlistwin_get_width();
             height = config.playlist_height;
-#endif
             break;
         default:
             return FALSE;
@@ -275,10 +273,9 @@
 }
 
 void ui_skinned_window_draw_all(GtkWidget *widget) {
-#if 0
     if (SKINNED_WINDOW(widget)->type == WINDOW_MAIN)
         mainwin_refresh_hints();
-#endif
+
     gtk_widget_queue_draw(widget);
     GList *iter;
     for (iter = GTK_FIXED (SKINNED_WINDOW(widget)->fixed)->children; iter; iter = g_list_next (iter)) {
--- a/src/skins/ui_svis.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_svis.c	Sun May 25 21:29:48 2008 +0300
@@ -353,7 +353,7 @@
     }
     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);
+      memset(rgb_data, 0, SVIS_WIDTH * config.scale_factor * SVIS_HEIGHT * config.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){
@@ -452,7 +452,7 @@
     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_set_size_request(widget, svis->width* config.scale_factor, svis->height * config.scale_factor);
 
     gtk_widget_queue_draw(widget);
 }
--- a/src/skins/ui_vis.c	Sun May 25 21:29:07 2008 +0300
+++ b/src/skins/ui_vis.c	Sun May 25 21:29:48 2008 +0300
@@ -278,7 +278,7 @@
     cmap = gdk_rgb_cmap_new(colors, 24);
 
     if (!vis->scaled) {
-      if(config.vis_type == VIS_VOICEPRINT /*&& aud_cfg->voiceprint_mode != VOICEPRINT_NORMAL*/){
+      if(config.vis_type == VIS_VOICEPRINT /*&& config.voiceprint_mode != VOICEPRINT_NORMAL*/){
 	memset(rgb_data, 0, 76 * 16 * 3);
       }
       else{
@@ -291,7 +291,7 @@
       }
     }
     else{
-      if(config.vis_type == VIS_VOICEPRINT /*&& aud_cfg->voiceprint_mode != VOICEPRINT_NORMAL*/){
+      if(config.vis_type == VIS_VOICEPRINT /*&& config.voiceprint_mode != VOICEPRINT_NORMAL*/){
 	memset(rgb_data, 0, 3 * 4 * 16 * 76);
       }
       else{
@@ -333,7 +333,7 @@
 	    }
 	  }
 	  else{
-	    ptr = rgb_data + ((16 - h) * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	    ptr = rgb_data + ((16 - h) * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * config.scale_factor);
 	    switch (config.analyzer_mode) {
 	    case ANALYZER_NORMAL:
 	      for (y = 0; y < h; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
@@ -376,7 +376,7 @@
 	      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 = rgb_data + (16 - h) * (guint)(76 * 4 * config.scale_factor) + (guint)(x * config.scale_factor);
 	      *ptr = 23;
 	      *(ptr + 1) = 23;
 	      *(ptr + (guint)(76 * config.scale_factor)) = 23;
@@ -450,7 +450,7 @@
 		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);
+	      ptr = rgb_data + (guint)(x * 3 * config.scale_factor) + (guint) (y * 76 * 3 * config.scale_factor);
 	      for(n=0;n<3;n++)
 		{
 		  *(ptr + n) = voice_c[n];
@@ -472,7 +472,7 @@
 	  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 = rgb_data + ((14 - h) * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * config.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];
@@ -494,7 +494,7 @@
 	      *ptr = vis_scope_colors[y - 2];
 	    }
 	    else{
-	      ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * aud_cfg->scale_factor);
+	      ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * config.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];
@@ -509,7 +509,7 @@
 	      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 = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * config.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];
@@ -531,7 +531,7 @@
 	    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);
+	    ptr = rgb_data + (h * (guint)(76 * 4 * config.scale_factor)) + (guint)(x * config.scale_factor);
 	    for (y = h; y <= h2; y++, ptr += (guint)(76 * 4 * config.scale_factor)) {
 	      *ptr = c;
 	      *(ptr + 1) = c;
@@ -546,7 +546,7 @@
 
     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);
+    obj = gdk_pixmap_new(NULL, vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? config.scale_factor : 1), gdk_rgb_get_visual()->depth);
     gc = gdk_gc_new(obj);
 
     if (!vis->scaled) {
@@ -574,7 +574,7 @@
     }
 
     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));
+                       vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? config.scale_factor : 1));
     g_object_unref(obj);
     g_object_unref(gc);
     gdk_rgb_cmap_free(cmap);
@@ -585,7 +585,7 @@
     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_set_size_request(widget, vis->width*(vis->scaled ? config.scale_factor : 1), vis->height*(vis->scaled ? config.scale_factor : 1));
 
     gtk_widget_queue_draw(GTK_WIDGET(vis));
 }